00001 package org.gel.mauve.gui;
00002
00003 import java.awt.BorderLayout;
00004 import java.awt.Color;
00005 import java.awt.Component;
00006 import java.awt.Dimension;
00007 import java.awt.Frame;
00008 import java.awt.Toolkit;
00009 import java.awt.event.ActionListener;
00010 import java.awt.event.ActionEvent;
00011 import java.awt.event.KeyListener;
00012 import java.awt.event.KeyEvent;
00013 import java.awt.event.WindowAdapter;
00014 import java.awt.event.WindowEvent;
00015 import java.util.Collections;
00016 import java.util.Iterator;
00017 import java.util.LinkedList;
00018 import java.util.Vector;
00019
00020 import javax.swing.*;
00021 import javax.swing.event.AncestorEvent;
00022 import javax.swing.event.AncestorListener;
00023
00024 import org.gel.mauve.Genome;
00025 import org.gel.mauve.BaseViewerModel;
00026 import org.gel.mauve.MauveConstants;
00027 import org.gel.mauve.SeqFeatureData;
00028 import org.gel.mauve.gui.navigation.NavigationPanel;
00029 import org.gel.mauve.gui.navigation.SearchResultPanel;
00030 import org.gel.mauve.gui.sequence.RRSequencePanel;
00031 import org.gel.mauve.gui.sequence.SeqPanel;
00032
00033 import org.biojava.bio.seq.Feature;
00034
00035
00045 public class SequenceNavigator extends JSplitPane implements ActionListener,
00046 KeyListener, MauveConstants {
00047
00051 protected Component parent_component;
00052 protected RearrangementPanel rrpanel;
00053 protected BaseViewerModel data_model;
00057 protected JFrame frame;
00058
00062 protected JComboBox genomes;
00063
00067 protected LinkedList nav_panels;
00068
00072 protected JButton search;
00073 protected JButton cancel;
00074 protected JButton add;
00075
00079 protected JButton reset;
00080
00084 protected JPanel nav_panel_holder;
00085
00089 protected SearchResultPanel result_pane;
00090
00094 protected JCheckBox clear;
00095
00099 protected JScrollPane result_scroller;
00100
00104 protected JScrollPane nav_scroll;
00105 protected LinkedList window_listeners;
00106 protected WindowAdapter adapt;
00107
00112 protected AncestorListener ancestListener;
00113
00117 protected Vector genome_choices;
00118
00122 public static int MAX_HEIGHT = 400;
00123
00127 public static int MAX_WIDTH = 850;
00128
00129
00133 static {
00134 READ_TO_ACTUAL.put(NAME, LOC_NAME);
00135 READ_TO_ACTUAL.put(PRODUCT, PRODUCT_NAME);
00136 READ_TO_ACTUAL.put(ID, ID_NUMBER);
00137 READ_TO_ACTUAL.put(GO, GO_FEATS);
00138 ANNOTATION_KEYS.add ("biovar");
00139 ANNOTATION_KEYS.add ("codon_start");
00140 ANNOTATION_KEYS.add ("db_xref");
00141 ANNOTATION_KEYS.add ("function");
00142 ANNOTATION_KEYS.add ("gene");
00143 ANNOTATION_KEYS.add ("insertion_seq");
00144 ANNOTATION_KEYS.add ("internal_data");
00145 ANNOTATION_KEYS.add ("locus_tag");
00146 ANNOTATION_KEYS.add ("mol_type");
00147 ANNOTATION_KEYS.add ("note");
00148 ANNOTATION_KEYS.add ("organism");
00149 ANNOTATION_KEYS.add ("product");
00150 ANNOTATION_KEYS.add ("protein_id");
00151 ANNOTATION_KEYS.add ("pseudo");
00152 ANNOTATION_KEYS.add ("strain");
00153 ANNOTATION_KEYS.add ("transl_except");
00154 ANNOTATION_KEYS.add ("transl_table");
00155 ANNOTATION_KEYS.add ("translation");
00156 }
00157
00161 private Boolean current_search = Boolean.FALSE;
00162
00163
00169 public SequenceNavigator (MauveFrame frame) {
00170 super ();
00171 parent_component = frame.getRootPane();
00172 data_model = frame.getModel();
00173 nav_panels = new LinkedList ();
00174 rrpanel = frame.getRearrangementPanel ();
00175 initGUI ();
00176 }
00177 public SequenceNavigator (Component parent, RearrangementPanel rrpanel, BaseViewerModel dataModel)
00178 {
00179 super ();
00180 parent_component = parent;
00181 data_model = dataModel;
00182 this.rrpanel = rrpanel;
00183 nav_panels = new LinkedList ();
00184 initGUI ();
00185 }
00186
00191 private void initGUI () {
00192 makeFrame ();
00193
00194 setResizeWeight (0);
00195 JPanel left = new JPanel (new BorderLayout (BORDER, BORDER));
00196
00197 JPanel top1 = new JPanel ();
00198 top1.setLayout(new BoxLayout (top1, BoxLayout.X_AXIS));
00199 top1.add (new JLabel ("Choose Genome:"));
00200 genomes = new JComboBox ();
00201 loadGenomeList ();
00202 top1.add (genomes);
00203 left.add (top1, BorderLayout.NORTH);
00204 left.setBorder (BorderFactory.createEmptyBorder(3, 3, 3, 3));
00205 JPanel middle = new JPanel (new BorderLayout ());
00206 nav_panel_holder = new JPanel ();
00207 middle.setBorder (BorderFactory.createTitledBorder(
00208 BorderFactory.createCompoundBorder (BorderFactory.createLineBorder (Color.black),
00209 BorderFactory.createEmptyBorder(BORDER, BORDER, 0, BORDER)),
00210 "Find features with the following qualifying information:"));
00211 nav_panel_holder.setLayout (new BoxLayout (nav_panel_holder, BoxLayout.Y_AXIS));
00212 result_scroller = result_pane.getScrollPane ();
00213 nav_scroll = new JScrollPane (nav_panel_holder);
00214 new NavigationPanel (this);
00215 middle.add (nav_scroll, BorderLayout.CENTER);
00216 left.add (middle, BorderLayout.CENTER);
00217 makeBottomPanel (left);
00218 setLeftComponent (left);
00219 setRightComponent (result_scroller);
00220 frame.getContentPane ().add (this);
00221 Dimension preferred = middle.getPreferredSize ();
00222 preferred.width += 6;
00223 preferred.height += 6;
00224 left.setMinimumSize (new Dimension (preferred.width,
00225 left.getPreferredSize ().height));
00226 left.setMaximumSize(new Dimension (preferred.width, -1));
00227 frame.pack ();
00228 reloadGUI ();
00229 moveFromBehind ();
00230 }
00231
00237 private void makeFrame () {
00238 window_listeners = new LinkedList ();
00239 frame = new JFrame ("Sequence Navigator");
00240 frame.setIconImage(MauveFrame.mauve_icon.getImage());
00241 ((JPanel) frame.getContentPane ()).setBorder (
00242 BorderFactory.createEmptyBorder(10, 10, 10, 10));
00243 adapt = new WindowAdapter () {
00244 public void windowClosing (WindowEvent e) {
00245 frame.setVisible (false);
00246 }
00247 };
00248 ancestListener = new AncestorListener(){
00249 public void ancestorMoved(AncestorEvent ae){}
00250 public void ancestorAdded(AncestorEvent ae){}
00251 public void ancestorRemoved(AncestorEvent ae){
00252 frame.setVisible(false);
00253 }
00254 };
00255 if(parent_component instanceof Frame)
00256 ((Frame)parent_component).addWindowListener(adapt);
00257 else if(parent_component instanceof JComponent)
00258 ((JComponent)parent_component).addAncestorListener(ancestListener);
00259
00260
00261
00262
00263
00264 }
00265
00271 private void makeBottomPanel (JPanel holder) {
00272 search = new JButton ("Search");
00273 cancel = new JButton ("Close");
00274 add = new JButton ("Add Constraint");
00275 reset = new JButton ("Reset Constraints");
00276 JPanel all = new JPanel (new BorderLayout ());
00277 clear = new JCheckBox ("Clear previous results when adding new");
00278 clear.setSelected(true);
00279 JPanel bottom = new JPanel ();
00280 bottom.add(add);
00281 bottom.add(reset);
00282 bottom.add (search);
00283 bottom.add (cancel);
00284 search.addActionListener (this);
00285 search.addKeyListener (this);
00286 cancel.addActionListener (this);
00287 add.addActionListener (this);
00288 reset.addActionListener (this);
00289 JPanel temp = new JPanel ();
00290 temp.add (clear);
00291 all.add(temp, BorderLayout.NORTH);
00292 all.add(bottom, BorderLayout.SOUTH);
00293
00294 holder.add (all, BorderLayout.SOUTH);
00295 }
00296
00302
00303 protected void moveFromBehind () {
00304 Dimension needed = frame.getSize();
00305 int area = 0;
00306 Dimension total = Toolkit.getDefaultToolkit().getScreenSize();
00307 Dimension taken = parent_component.getSize ();
00308 int extra = total.width - taken.width;
00309 int x = 0;
00310 int y = 0;
00311 if (extra >= needed.width)
00312 x = taken.width;
00313 else {
00314 area = extra * needed.height;
00315 extra = total.height - taken.height;
00316 if (extra >= needed.height)
00317 y = taken.height;
00318 else if (area > extra * needed.width)
00319 x = total.width - needed.width;
00320 else
00321 y = total.height - needed.height;
00322 }
00323 frame.setLocation(x, y);
00324 }
00325
00330 public void loadGenomeList () {
00331 genome_choices = SeqFeatureData.userSelectableGenomes (data_model, true, true);
00332 genomes.setRenderer (GenomeCellRenderer.getListCellRenderer ());
00333 genomes.setModel (new DefaultComboBoxModel (genome_choices));
00334 result_pane = new SearchResultPanel (SeqFeatureData.userSelectableGenomes (
00335 data_model, false, false), this);
00336 }
00337
00343 public boolean shouldClear () {
00344 return clear.isSelected ();
00345 }
00346
00350 public void addNavigationPanel (NavigationPanel pane) {
00351 nav_panels.addFirst (pane);
00352 nav_panel_holder.add (pane);
00353 pane.setMaximumSize (pane.getPreferredSize());
00354 reloadGUI ();
00355
00356 }
00357
00361 public void removeNavigationPanel (NavigationPanel pane) {
00362 if (nav_panels.size() != 1) {
00363 nav_panels.remove(pane);
00364 nav_panel_holder.remove(pane);
00365 reloadGUI ();
00366 }
00367 }
00368
00372 public void reset () {
00373 Object [] panels = nav_panels.toArray();
00374 for (int i = 0; i < panels.length - 1; i++)
00375 removeNavigationPanel ((NavigationPanel) panels [i]);
00376 }
00377
00383 public void showNavigator () {
00384 frame.setVisible (true);
00385 }
00386
00387
00398 public void actionPerformed (final ActionEvent e) {
00399 if (e.getSource () == search) {
00400 Thread t = new Thread () {
00401 public void run () {
00402 synchronized (current_search) {
00403 if (current_search == Boolean.FALSE) {
00404 current_search = Boolean.TRUE;
00405 }
00406 else
00407 return;
00408 }
00409 try {
00410 doNavigation ();
00411 } catch (Exception e) {
00412 e.printStackTrace ();
00413 }
00414 current_search = Boolean.FALSE;
00415 }
00416 };
00417
00418 t.start ();
00419 }
00420 if (current_search == Boolean.FALSE) {
00421 if (e.getSource () == add)
00422 new NavigationPanel (SequenceNavigator.this);
00423 else if (e.getSource() == cancel)
00424 frame.setVisible (false);
00425 else if (e.getSource () == reset)
00426 reset ();
00427 }
00428 }
00429
00436 public void reloadGUI () {
00437 Dimension size = frame.getSize ();
00438 expandIfNecessary (nav_scroll, size);
00439 expandIfNecessary (result_scroller, size);
00440 size.width = (size.width > MAX_WIDTH) ? MAX_WIDTH : size.width;
00441 size.height = (size.height > MAX_HEIGHT) ? MAX_HEIGHT : size.height;
00442 frame.setSize(size);
00443 revalidate ();
00444 repaint ();
00445 }
00446
00447
00456 public static void expandIfNecessary (JScrollPane pane, Dimension size) {
00457 JViewport port = pane.getViewport ();
00458 Dimension preferred = port.getView ().getPreferredSize ();
00459 Dimension actual = port.getSize ();
00460 if (preferred.width > actual.width)
00461 size.width += preferred.width - actual.width;
00462 if (preferred.height > actual.height)
00463 size.height += preferred.height - actual.height;
00464 }
00465
00469 public void keyPressed (KeyEvent e) {
00470 }
00471
00475 public void keyReleased (KeyEvent e) {
00476 }
00477
00482 public void keyTyped (KeyEvent e) {
00483 if (e.getKeyChar () == '\n') {
00484 ActionEvent ae = new ActionEvent (search, ActionEvent.ACTION_PERFORMED, null);
00485 actionPerformed (ae);
00486 }
00487 }
00488
00494 public void makeConstraintVisible (NavigationPanel panel) {
00495 nav_scroll.getViewport ().scrollRectToVisible (panel.getBounds ());
00496 }
00497
00506 public int getValidCount () {
00507 int count = 0;
00508 boolean hole = false;
00509 Object [] items = nav_panels.toArray ();
00510 for (int i = items.length - 1; i >= 0; i--) {
00511 NavigationPanel pan = (NavigationPanel) items [i];
00512 if (pan.dataValid ()) {
00513 if (hole) {
00514 JOptionPane.showMessageDialog(frame, "Missing or invalid data.\n" +
00515 "Can't perform search.", "Navigation Error", JOptionPane.ERROR_MESSAGE);
00516 return 0;
00517 }
00518 else
00519 count++;
00520 }
00521 else
00522 hole = true;
00523 }
00524 return count;
00525 }
00526
00527
00537 public static void goToSeqPos (Component parentComponent, BaseViewerModel dataModel, RearrangementPanel rrpanel) {
00538 Genome [] chosen = SeqFeatureData.userSelectedGenomes (parentComponent,
00539 dataModel, false, false);
00540 if (chosen != null) {
00541 long pos = -1;
00542 do {
00543 try {
00544 String input = JOptionPane.showInputDialog (parentComponent,
00545 "Enter sequence coordinate to jump to...");
00546 if (input != null)
00547 pos = Long.parseLong (input);
00548 else
00549 break;
00550 } catch (NumberFormatException e) {
00551 JOptionPane.showMessageDialog(parentComponent, "Invalid position entered",
00552 "Invalid Data", JOptionPane.ERROR_MESSAGE);
00553 }
00554 } while (pos == -1);
00555 if (pos != -1)
00556 goToPosition (pos, chosen [0], rrpanel);
00557 }
00558 }
00559
00560
00567 public void goToFeatureByName () {
00568 Genome [] chosen = SeqFeatureData.userSelectedGenomes (
00569 parent_component, data_model, true, true);
00570 if (chosen != null) {
00571 String input = JOptionPane.showInputDialog (parent_component,
00572 "Enter name of desired feature. . .");
00573 if (input != null) {
00574 String [][] data = new String [1][3];
00575 data [0][FIELD] = LOC_NAME;
00576 data [0][VALUE] = input;
00577 data [0][EXACT] = Boolean.toString(false);
00578 int count = 0;
00579 LinkedList first = null;
00580 LinkedList [] tree_data = SeqFeatureData.findFeatures (chosen, data);
00581 for (int i = 0; i < chosen.length; i++) {
00582 Object genome = tree_data [i].removeFirst ();
00583 SeqFeatureData.removeLocationDuplicates (tree_data [i]);
00584 count += tree_data [i].size();
00585 tree_data [i].addFirst (genome);
00586 if (count == 1) {
00587 first = tree_data [i];
00588 }
00589 }
00590 if (count == 1)
00591 displayFeature ((Feature) first.get(1), (Genome) first.getFirst());
00592 else if (count == 0) {
00593 JOptionPane.showMessageDialog(parent_component,
00594 "No Features were found with specified name.");
00595 }
00596 else {
00597 frame.setVisible(true);
00598 result_pane.displayFeatures(tree_data);
00599 }
00600 }
00601 }
00602
00603 }
00604
00610 protected void doNavigation () {
00611 int index = genomes.getSelectedIndex();
00612 int valid = getValidCount ();
00613 if (valid > 0) {
00614 result_pane.waitForResults ();
00615 String [][] criteria = new String [valid][3];
00616 for (int i = 0; i < criteria.length; i++)
00617 criteria [i] = ((NavigationPanel) nav_panels.get(
00618 nav_panels.size() - 1 - i)).getSearchCriteria ();
00619 showResultTree (SeqFeatureData.convertIndexToSequence (
00620 genome_choices, index), criteria);
00621 }
00622 }
00623
00624
00633 public void showResultTree (final Genome [] nomes, final String [][] data) {
00634 SwingUtilities.invokeLater(new Runnable(){
00635 public void run(){
00636 result_pane.displayFeatures (SeqFeatureData.findFeatures (nomes, data));
00637 }
00638 });
00639 }
00640
00647 public void displayFeature (Feature feat, Genome genome) {
00648 try {
00649 adjustZoom(feat);
00650 goToPosition(SeqFeatureData.centerOfFeature(feat), genome, rrpanel);
00651 data_model.highlightRange(genome,
00652 feat.getLocation().getMin(), feat.getLocation().getMax());
00653 } catch (Exception e) {
00654 e.printStackTrace ();
00655 }
00656 }
00657
00662 public void adjustZoom (Feature feat) {
00663 int length = feat.getLocation().getMax () - feat.getLocation ().getMin();
00664 long vis_length = ((Genome) genomes.getItemAt(
00665 genomes.getItemCount() == 1 ? 0 : 1)).getViewLength ();
00666 double percent = 0;
00667 double new_vis = 0;
00668 int count = 0;
00669 if (length < vis_length) {
00670 new_vis = length * 10;
00671 if (new_vis < vis_length) {
00672 percent = ((double) vis_length) / new_vis;
00673 percent *= 100;
00674 }
00675 }
00676 else {
00677 new_vis = length + 1/3*((double) length);
00678 percent = vis_length/new_vis * 100;
00679 while (percent < 1) {
00680 percent *= 2;
00681 count++;
00682 }
00683 }
00684 if (percent != 0) {
00685 data_model.zoomAndMove ((int) percent, 0);
00686 while (count > 0) {
00687 data_model.zoomAndMove (50, 0);
00688 count--;
00689 }
00690 }
00691 }
00692
00700 public static void goToPosition (long position, Genome chosen, RearrangementPanel rrpanel) {
00701 Object [] panels = rrpanel.newPanels.toArray ();
00702 for (int i = 0; i < panels.length; i++) {
00703 RRSequencePanel panel = (RRSequencePanel) ((SeqPanel)
00704 panels [i]).getSequencePanel ();
00705 if (panel.isForGenome (chosen)) {
00706 panel.goTo (position);
00707 break;
00708 }
00709 }
00710 }
00711
00718 public void goToPosition (long position, Genome chosen) {
00719 goToPosition (position, chosen, rrpanel);
00720 }
00721
00728 public Vector getGenomeKeys () {
00729 Vector readable = new Vector ();
00730 Iterator itty = ANNOTATION_KEYS.iterator();
00731 while (itty.hasNext())
00732 readable.add(((String) itty.next ()).replace ('_', ' '));
00733 Collections.sort (readable);
00734 return readable;
00735 }
00736
00737 public void dispose () {
00738 if(parent_component instanceof Frame)
00739 ((Frame)parent_component).removeWindowListener (adapt);
00740 else if(parent_component instanceof JComponent)
00741 ((JComponent)parent_component).removeAncestorListener(ancestListener);
00742 frame.dispose ();
00743 }
00744
00745 }