00001 package org.gel.mauve.gui.navigation;
00002
00003 import java.awt.Color;
00004 import java.awt.Component;
00005 import java.awt.FlowLayout;
00006 import java.awt.Graphics;
00007 import java.util.Collections;
00008 import java.util.Comparator;
00009 import java.util.Enumeration;
00010 import java.util.Hashtable;
00011 import java.util.LinkedList;
00012 import java.util.StringTokenizer;
00013 import java.util.Vector;
00014
00015 import javax.swing.Icon;
00016 import javax.swing.JLabel;
00017 import javax.swing.JPanel;
00018 import javax.swing.JScrollBar;
00019 import javax.swing.JScrollPane;
00020 import javax.swing.JTree;
00021 import javax.swing.SwingUtilities;
00022 import javax.swing.UIManager;
00023 import javax.swing.event.TreeModelListener;
00024 import javax.swing.event.TreeSelectionEvent;
00025 import javax.swing.event.TreeSelectionListener;
00026 import javax.swing.plaf.ColorUIResource;
00027 import javax.swing.tree.DefaultMutableTreeNode;
00028 import javax.swing.tree.DefaultTreeCellRenderer;
00029 import javax.swing.tree.DefaultTreeModel;
00030 import javax.swing.tree.TreeCellRenderer;
00031 import javax.swing.tree.TreeModel;
00032 import javax.swing.tree.TreePath;
00033 import javax.swing.tree.TreeSelectionModel;
00034
00035 import org.biojava.bio.Annotation;
00036 import org.biojava.bio.seq.Feature;
00037 import org.biojava.bio.symbol.Location;
00038 import org.gel.mauve.Genome;
00039 import org.gel.mauve.MauveConstants;
00040 import org.gel.mauve.SeqFeatureData;
00041 import org.gel.mauve.gui.SequenceNavigator;
00042
00051 public class SearchResultPanel extends JPanel implements TreeModel,
00052 TreeCellRenderer, TreeSelectionListener, MauveConstants {
00053
00057 protected JTree tree;
00058
00062 protected DefaultTreeModel model;
00063
00067 protected DefaultTreeCellRenderer renderer;
00068
00072 protected SequenceNavigator navigator;
00073
00077 protected DefaultMutableTreeNode root;
00078
00082 protected JScrollPane scroller;
00083
00088 protected int result_state;
00089
00093 protected boolean searching;
00094
00098 public static final String NO_RESULTS = "No results to display. . .";
00099
00103 public static final String MATCHLESS = "No features found. . .";
00104
00108 protected static final String SEARCHING = "Searching. . .";
00109
00113 protected static final String ROOT = "Root";
00114
00118 public static final Icon PLUS_ICON = SignedIcon.getSignedIcon (true);
00119
00123 public static final Icon MINUS_ICON = SignedIcon.getSignedIcon (false);
00124
00128 protected Hashtable genome_data;
00129
00133 protected Object [] genome_indexes;
00134
00143 public SearchResultPanel (Vector genomes, SequenceNavigator nav) {
00144 super (new FlowLayout (FlowLayout.LEFT));
00145 navigator = nav;
00146 genome_data = new Hashtable ();
00147 genome_indexes = new Object [genomes.size ()];
00148 for (int i = 0; i < genomes.size (); i++) {
00149 genome_data.put (genomes.get (i), new LinkedList ());
00150 genome_indexes[i] = genomes.get (i);
00151 }
00152 initGUI ();
00153 }
00154
00159 protected void initGUI () {
00160 UIManager.put ("Tree.hash", new ColorUIResource (Color.BLACK));
00161 tree = new JTree ();
00162 tree.setRootVisible (false);
00163 tree.setBackground (navigator.getBackground ());
00164 tree.getSelectionModel ().setSelectionMode (
00165 TreeSelectionModel.SINGLE_TREE_SELECTION);
00166 model = (DefaultTreeModel) tree.getModel ();
00167 renderer = (DefaultTreeCellRenderer) tree.getCellRenderer ();
00168 renderer.setBackgroundNonSelectionColor (navigator.getBackground ());
00169 renderer.setClosedIcon (PLUS_ICON);
00170 renderer.setOpenIcon (MINUS_ICON);
00171 renderer.setLeafIcon (null);
00172 tree.setCellRenderer (this);
00173 tree.setModel (this);
00174 tree.setEditable (false);
00175 tree.addTreeSelectionListener (this);
00176 tree.setToggleClickCount (1);
00177 root = new DefaultMutableTreeNode (ROOT);
00178 add (tree);
00179 scroller = new JScrollPane (this);
00180 scroller.getVerticalScrollBar ().setUnitIncrement (
00181 renderer.getPreferredSize ().height);
00182 }
00183
00189 public JScrollPane getScrollPane () {
00190 return scroller;
00191 }
00192
00200 public void displayFeatures (Object [] data) {
00201 resetData (data);
00202 int prev_result = result_state;
00203 Object [] first = new Object [3];
00204 for (int i = 0; i < genome_indexes.length; i++) {
00205 LinkedList list = (LinkedList) genome_data.get (genome_indexes[i]);
00206 if (result_state == 0 && list.size () > 0) {
00207 first[2] = (Feature) list.get (0);
00208 first[1] = genome_indexes[i];
00209 first[0] = ROOT;
00210 }
00211 result_state += list.size ();
00212 if (result_state > 1)
00213 break;
00214 }
00215 searching = false;
00216 model.nodeStructureChanged (root);
00217 Object [] path = new Object [(result_state == 0) ? 1 : 2];
00218 path[0] = ROOT;
00219 if (result_state > 0) {
00220 for (int i = 0; i < getChildCount (ROOT); i++) {
00221 Object kid = getChild (ROOT, i);
00222 path[1] = kid;
00223 tree.expandPath (new TreePath (path));
00224 }
00225 if (prev_result == 0)
00226 tree.setSelectionPath (new TreePath (first));
00227 } else {
00228 tree.expandPath (new TreePath (path));
00229 try {
00230 Thread.currentThread ().wait (100);
00231 } catch (Exception e) {
00232
00233 }
00234 }
00235 navigator.reloadGUI ();
00236 }
00237
00242 public void waitForResults () {
00243 searching = true;
00244 SwingUtilities.invokeLater(new Runnable(){
00245 public void run(){
00246 model.nodeStructureChanged (root);
00247 }
00248 });
00249 }
00250
00259 protected void resetData (Object [] data) {
00260 Enumeration keys = null;
00261 if (result_state != 0 && navigator.shouldClear ()) {
00262 keys = genome_data.keys ();
00263 while (keys.hasMoreElements ()) {
00264 LinkedList remove = (LinkedList) genome_data.get (keys
00265 .nextElement ());
00266 remove.clear ();
00267 }
00268 result_state = 0;
00269 }
00270 for (int i = 0; i < data.length; i++) {
00271 LinkedList new_data = (LinkedList) data[i];
00272 Object key = new_data.remove (0);
00273 LinkedList old = (LinkedList) genome_data.get (key);
00274 old.addAll (new_data);
00275 SeqFeatureData.removeLocationDuplicates (old);
00276 }
00277 }
00278
00286 public static String getDisplayText (Feature feat) {
00287 String data = "";
00288 Annotation note = feat.getAnnotation ();
00289 StringTokenizer fields = SeqFeatureData.separateFields (LOC_NAME);
00290 while (fields.hasMoreTokens () && data.length () == 0) {
00291 String field = fields.nextToken ();
00292 if (note.containsProperty (field))
00293 data += (String) note.getProperty (field);
00294 }
00295 Location loci = feat.getLocation ();
00296 data += " (" + loci.getMin () + "-" + loci.getMax () + ")";
00297 data.trim ();
00298 return data;
00299 }
00300
00305 public void valueChanged (TreeSelectionEvent event) {
00306 if (event.getNewLeadSelectionPath () != null) {
00307 Object [] path = event.getNewLeadSelectionPath ().getPath ();
00308 if (path != null && path.length == 3 && path[2] != MATCHLESS)
00309 navigator.displayFeature ((Feature) path[2], (Genome) path[1]);
00310 }
00311 }
00312
00316 public Component getTreeCellRendererComponent (JTree tree, Object val,
00317 boolean selected, boolean expanded, boolean leaf, int row,
00318 boolean focus) {
00319 JLabel label = (JLabel) renderer.getTreeCellRendererComponent (tree,
00320 val, selected, expanded, leaf, row, focus);
00321 if (val instanceof Feature)
00322 label.setText (getDisplayText ((Feature) val));
00323 return label;
00324 }
00325
00329 public void addTreeModelListener (TreeModelListener listen) {
00330 model.addTreeModelListener (listen);
00331 }
00332
00342 public Object getChild (Object parent, int index) {
00343 if (parent == ROOT) {
00344 if (searching)
00345 return SEARCHING;
00346 else if (result_state == 0)
00347 return NO_RESULTS;
00348 else
00349 return genome_indexes[index];
00350 }
00351 LinkedList children = (LinkedList) genome_data.get (parent);
00352 if (children != null) {
00353 try {
00354 return children.get (index);
00355 } catch (IndexOutOfBoundsException e) {
00356 return MATCHLESS;
00357 }
00358 } else
00359 return null;
00360
00361 }
00362
00370 public int getChildCount (Object parent) {
00371 if (parent == ROOT) {
00372 if (searching || result_state == 0)
00373 return 1;
00374 else
00375 return genome_data.size ();
00376 }
00377 LinkedList children = (LinkedList) genome_data.get (parent);
00378 if (children != null) {
00379 int size = ((LinkedList) genome_data.get (parent)).size ();
00380 if (size == 0)
00381 return 1;
00382 else
00383 return size;
00384 } else
00385 return 0;
00386 }
00387
00397 public int getIndexOfChild (Object parent, Object child) {
00398 if (parent == ROOT) {
00399 if (child == SEARCHING) {
00400 if (searching)
00401 return 0;
00402 else
00403 return -1;
00404 }
00405 if (child == NO_RESULTS) {
00406 if (result_state == 0)
00407 return 0;
00408 else
00409 return -1;
00410 } else {
00411 for (int i = 0; i < genome_indexes.length; i++) {
00412 if (genome_indexes[i].equals (child))
00413 return i;
00414 }
00415 }
00416 } else if (child == MATCHLESS)
00417 return 0;
00418 else if (genome_data.contains (parent)) {
00419 LinkedList kids = (LinkedList) genome_data.get (parent);
00420 return kids.indexOf (child);
00421 }
00422 return -1;
00423 }
00424
00430 public Object getRoot () {
00431 return ROOT;
00432 }
00433
00441 public boolean isLeaf (Object node) {
00442 if (node != ROOT && !genome_data.containsKey (node))
00443 return true;
00444 else
00445 return false;
00446 }
00447
00451 public void removeTreeModelListener (TreeModelListener list) {
00452 model.removeTreeModelListener (list);
00453 }
00454
00458 public void valueForPathChanged (TreePath path, Object val) {
00459 model.valueForPathChanged (path, val);
00460 }
00461
00469 public static class SignedIcon implements Icon {
00470
00474 private boolean plus;
00475
00479 private static SignedIcon PLUS = new SignedIcon (true);
00480
00481 private static SignedIcon MINUS = new SignedIcon (false);
00482
00489 private SignedIcon (boolean p) {
00490 plus = p;
00491 }
00492
00501 public static SignedIcon getSignedIcon (boolean plus) {
00502 return plus ? PLUS : MINUS;
00503 }
00504
00505 public int getIconHeight () {
00506 return 16;
00507 }
00508
00509 public int getIconWidth () {
00510 return 16;
00511 }
00512
00516 public void paintIcon (Component comp, Graphics g, int x, int y) {
00517 g.drawRect (3, 3, 8, 8);
00518 g.drawLine (5, 7, 9, 7);
00519 if (plus)
00520 g.drawLine (7, 5, 7, 9);
00521 }
00522
00523 }
00524 }