001    /*
002     * Copyright 2007, 2012 Stephen Fisher and Junhyong Kim, University of
003     * Pennsylvania.
004     *
005     * This file is part of Glo-DB.
006     * 
007     * Glo-DB is free software: you can redistribute it and/or modify it
008     * under the terms of the GNU General Public License as published by
009     * the Free Software Foundation, either version 3 of the License, or
010     * (at your option) any later version.
011     * 
012     * Glo-DB is distributed in the hope that it will be useful, but
013     * WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015     * General Public License for more details.
016     * 
017     * You should have received a copy of the GNU General Public License
018     * along with Glo-DB. If not, see <http://www.gnu.org/licenses/>.
019     *
020     * @(#)TrackBrowser.java
021     */
022    
023    package edu.upenn.gloDB.gui;
024    
025    import edu.upenn.gloDB.*;
026    import java.awt.*;
027    import java.awt.event.*;
028    import javax.swing.*;
029    import javax.swing.event.*;
030    import java.util.StringTokenizer;
031    import java.util.HashMap;
032    import java.util.TreeSet;
033    import java.util.Set;
034    import java.util.Iterator;
035    import java.util.ArrayList;
036    
037    /**
038     * Browse existing Tracks.
039     *
040     * @author  Stephen Fisher
041     * @version $Id: TrackBrowser.java,v 1.1.2.20 2007/03/01 21:17:33 fisher Exp $
042     */
043    
044    public class TrackBrowser {
045             private static BrowserFrame browserFrame = null;
046    
047             public static JFrame show() {
048                      return show("");
049             }
050    
051        public static JFrame show(String id) {
052                      // only allow one instance of BrowserFrame
053                      if (browserFrame == null) browserFrame = new BrowserFrame();
054    
055                      browserFrame.selectTrack(id);
056                      browserFrame.show();
057    
058                      return browserFrame;
059             }
060                      
061             private static class BrowserFrame extends JFrame {
062                      /** 
063                            * This is the total number of features that will be displayed in
064                            * the feature list.
065                            */
066                      private final long MAX_FEATURES = 40000;
067    
068                      private JComboBox trackCB;
069                      private JList attributeL;
070                      private JButton addAttributeB;
071                      private JButton editAttributeB;
072                      private JButton delAttributeB;
073                      private JList sequenceL;
074                      private JList featureL;
075                      private JLabel statusBar;
076                      
077                      private JLabel numFeaturesL = new JLabel("");
078                      private JLabel contiguousL = new JLabel("");
079                      private JLabel minL = new JLabel("");
080                      private JLabel maxL = new JLabel("");
081                      private JLabel lengthL = new JLabel("");
082                      
083                      private Track track;
084                      private Object[] sequences;
085                      private Object[] features;
086                      
087                      BrowserFrame thisFrame;
088                      
089                      //              public BrowserFrame(String id) {
090                      public BrowserFrame() {
091                                    super("Track Browser");
092                                    // keep pointer to self so can 'dispose' Frame below
093                                    thisFrame = this;
094                                    
095                                    setDefaultCloseOperation(HIDE_ON_CLOSE);
096                                    
097                                    // ***** SETUP TRACK INFO *****
098                                    JToolBar trackP = new JToolBar();
099                                    trackP.setFloatable(false);
100                                    trackP.setBorder(BorderFactory.createEtchedBorder());
101                                    // add Track ComboBox
102                                    trackCB = new JComboBox(ObjectHandles.getTrackList());
103                                    trackCB.setEditable(false);
104                                    //                              selectTrack("");
105                                    trackCB.addActionListener(new ActionListener() {
106                                                      public void actionPerformed(ActionEvent e) {
107                                                                    String selection = (String) ((JComboBox) e.getSource()).getSelectedItem();
108                                                                    trackSelected(selection);
109                                                      }
110                                             });
111                                    // add Buttons
112                                    JButton loadB = new JButton(new ImageIcon("icons/load.png"));
113                                    loadB.setToolTipText("Load");
114                                    loadB.addMouseListener(new MouseAdapter() {
115                                                      // add status bar text when mouse moves over button
116                                                      public void mouseEntered(MouseEvent e) { statusBar.setText("Load track"); }
117                                                      // clear status bar when mouse moves off button
118                                                      public void mouseExited(MouseEvent e) { statusBar.setText(""); }
119                                             });
120                                    loadB.addActionListener(new ActionListener() {
121                                                      public void actionPerformed(ActionEvent e) {
122                                                                    Cursor hourglassCursor = new Cursor(Cursor.WAIT_CURSOR);
123                                                                    setCursor(hourglassCursor);
124                                                                    Track track = GUITrackIO.loadTrack();
125                                                                    Cursor normalCursor = new Cursor(Cursor.DEFAULT_CURSOR);
126                                                                    setCursor(normalCursor);
127                                                                    if (track != null) {
128                                                                             trackCB.setSelectedIndex(ObjectHandles.getTrackList().getIndexOf(track.getID()));
129                                                                    }
130                                                      }
131                                             });
132                                    JButton saveB = new JButton(new ImageIcon("icons/save.png"));
133                                    saveB.setToolTipText("Save");
134                                    saveB.addMouseListener(new MouseAdapter() {
135                                                      // add status bar text when mouse moves over button
136                                                      public void mouseEntered(MouseEvent e) { statusBar.setText("Save track"); }
137                                                      // clear status bar when mouse moves off button
138                                                      public void mouseExited(MouseEvent e) { statusBar.setText(""); }
139                                             });
140                                    saveB.addActionListener(new ActionListener() {
141                                                      public void actionPerformed(ActionEvent e) {
142                                                                    if (track != null) GUITrackIO.saveTrack(track.getID());
143                                                      }
144                                             });
145                                    JButton viewB = new JButton(new ImageIcon("icons/view.png"));
146                                    viewB.setToolTipText("View Data");
147                                    viewB.addMouseListener(new MouseAdapter() {
148                                                      // add status bar text when mouse moves over button
149                                                      public void mouseEntered(MouseEvent e) { statusBar.setText("View track data"); }
150                                                      // clear status bar when mouse moves off button
151                                                      public void mouseExited(MouseEvent e) { statusBar.setText(""); }
152                                             });
153                                    viewB.addActionListener(new ActionListener() {
154                                                      public void actionPerformed(ActionEvent e) {
155                                                                    if (track != null) new ViewSequencePanel();
156                                                      }
157                                             });
158                                    // add info "rename" button
159                                    JButton renameB= new JButton(new ImageIcon("icons/rename.png"));
160                                    renameB.setToolTipText("Rename");
161                                    renameB.addMouseListener(new MouseAdapter() {
162                                                      // add status bar text when mouse moves over button
163                                                      public void mouseEntered(MouseEvent e) { statusBar.setText("Rename track"); }
164                                                      // clear status bar when mouse moves off button
165                                                      public void mouseExited(MouseEvent e) { statusBar.setText(""); }
166                                             });
167                                    renameB.addActionListener(new ActionListener() {
168                                                      public void actionPerformed(ActionEvent e) {
169                                                                    if (track != null) {
170                                                                             String id = GUIUtils.renameTrack(track.getID());
171                                                                             trackCB.setSelectedIndex(ObjectHandles.getTrackList().getIndexOf(id));
172                                                                    }
173                                                      }
174                                             });
175                                    // add info "remove" button
176                                    JButton deleteB= new JButton(new ImageIcon("icons/delete.png"));
177                                    deleteB.setToolTipText("Delete");
178                                    deleteB.addMouseListener(new MouseAdapter() {
179                                                      // add status bar text when mouse moves over button
180                                                      public void mouseEntered(MouseEvent e) { statusBar.setText("THIS WILL PERMANENTLY DELETE THE TRACK!"); }
181                                                      // clear status bar when mouse moves off button
182                                                      public void mouseExited(MouseEvent e) { statusBar.setText(""); }
183                                             });
184                                    deleteB.addActionListener(new ActionListener() {
185                                                      public void actionPerformed(ActionEvent e) {
186                                                                    if (track == null) return;  
187                                                                    
188                                                                    String msg = "Are you sure you want to remove this track?\n";
189                                                                    msg += "Track: " + track.getID();
190                                                                    Object[] options = {"Delete Track", "Cancel"};
191                                                                    int flag = JOptionPane.showOptionDialog(null, msg,
192                                                                                                                                                                             "Delete Confirmation",
193                                                                                                                                                                             JOptionPane.YES_NO_OPTION,
194                                                                                                                                                                             JOptionPane.QUESTION_MESSAGE,
195                                                                                                                                                                             null,
196                                                                                                                                                                             options,
197                                                                                                                                                                             options[1]);
198                                                                    if (flag == JOptionPane.YES_OPTION) { // "Delete"
199                                                                             Root.runCommand("removeTrack(\"" + track.getID() + "\")", true);
200                                                                             //                                                                      ObjectHandles.removeTrack(track);
201                                                                             if (trackCB.getItemCount() > 0) {
202                                                                                      trackCB.setSelectedIndex(0);
203                                                                             } else {
204                                                                                      trackSelected(null);
205                                                                             }
206                                                                    }
207                                                      }
208                                             });
209                                    JButton closeB = new JButton(new ImageIcon("icons/close.png"));
210                                    closeB.setToolTipText("Close");
211                                    closeB.addMouseListener(new MouseAdapter() {
212                                                      // add status bar text when mouse moves over button
213                                                      public void mouseEntered(MouseEvent e) { statusBar.setText("Close the track browser"); }
214                                                      // clear status bar when mouse moves off button
215                                                      public void mouseExited(MouseEvent e) { statusBar.setText(""); }
216                                             });
217                                    closeB.addActionListener(new ActionListener() {
218                                                      public void actionPerformed(ActionEvent e) {
219                                                                    thisFrame.hide();
220                                                      }
221                                             });
222                                    // add Track Label
223                                    trackP.addSeparator(new Dimension(15, 0));
224                                    trackP.add(loadB);
225                                    trackP.addSeparator(new Dimension(30, 25));
226                                    trackP.add(saveB);
227                                    trackP.addSeparator(new Dimension(30, 25));
228                                    trackP.add(new JLabel("Track: "));
229                                    trackP.add(trackCB);
230                                    trackP.addSeparator(new Dimension(15, 0));
231                                    trackP.add(viewB);
232                                    trackP.addSeparator(new Dimension(30, 25));
233                                    trackP.add(renameB);
234                                    trackP.addSeparator(new Dimension(30, 25));
235                                    trackP.add(deleteB);
236                                    trackP.addSeparator(new Dimension(30, 25));
237                                    trackP.add(closeB);
238                                    trackP.addSeparator(new Dimension(15, 0));
239                                    
240                                    // ***** SETUP ATTRIBUTE INFO *****
241                                    JPanel attributeP = new JPanel(new BorderLayout());
242                                    // add attributes List
243                                    attributeL = new JList();
244                                    attributeL.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
245                                    attributeL.addListSelectionListener(new ListSelectionListener() {
246                                                      public void valueChanged(ListSelectionEvent e) {
247                                                                    if (e.getValueIsAdjusting()) return;
248                                                                    
249                                                                    JList list = (JList) e.getSource();
250                                                                    if (list.isSelectionEmpty()) {
251                                                                             // disable the "Delete" button if no selection
252                                                                             editAttributeB.setEnabled(false);
253                                                                             delAttributeB.setEnabled(false);
254                                                                    } else {
255                                                                             // enable the "Delete" button if something selected
256                                                                             editAttributeB.setEnabled(true);
257                                                                             delAttributeB.setEnabled(true);
258                                                                    }
259                                                      }
260                                             });
261                                    JScrollPane attributeSP = new JScrollPane(attributeL);
262                                    attributeP.add(attributeSP, BorderLayout.CENTER);
263                                    // add button sub-panel
264                                    JPanel attributeBP = new JPanel(new GridLayout(1,0,5,5));
265                                    // add attributes "add" Button
266                                    addAttributeB = new JButton(new ImageIcon("icons/new.png"));
267                                    addAttributeB.setToolTipText("Add New Attribute");
268                                    addAttributeB.addActionListener(new ActionListener() {
269                                                      public void actionPerformed(ActionEvent e) {
270                                                                    if (track == null) return;
271                                                                    
272                                                                    String[] labels = {" Key:", " Value:"};
273                                                                    // use this to get the return value from FieldEditDialog
274                                                                    ArrayList textFields = new ArrayList();
275                                                                    textFields.add(new JTextField(20));
276                                                                    textFields.add(new JTextField(20));
277                                                                    new FieldEditDialog("Add Attribute", labels, textFields);
278                                                                    
279                                                                    // check "exit code" for FieldEditDialog, if false then exit
280                                                                    Boolean exitCode = (Boolean) textFields.get(textFields.size()-1);
281                                                                    if (! exitCode.booleanValue()) return;
282    
283                                                                    JTextField tf = (JTextField) textFields.get(0);
284                                                                    String key = tf.getText();
285                                                                    if (key.length() > 0) { // make sure a key is valid
286                                                                             tf = (JTextField) textFields.get(1);  // get value
287                                                                             String value = tf.getText();
288                                                                             String msg = "getTrack(\"" + track.getID() + "\")";
289                                                                             msg += ".addAttribute(\"" + key + "\", \"" + value + "\")";
290                                                                             Root.runCommand(msg, true);
291                                                                             updateAttributes();
292                                                                    }
293                                                      }
294                                             });
295                                    attributeBP.add(addAttributeB);
296                                    // add attributes "edit" Button
297                                    editAttributeB = new JButton(new ImageIcon("icons/edit.png"));
298                                    editAttributeB.setToolTipText("Edit Attribute");
299                                    editAttributeB.setEnabled(false);
300                                    editAttributeB.addActionListener(new ActionListener() {
301                                                      public void actionPerformed(ActionEvent e) {
302                                                                    StringTokenizer st = new StringTokenizer((String) 
303                                                                                                                                                                              attributeL.getSelectedValue(),
304                                                                                                                                                                              ":", true);
305                                                                    String key = st.nextToken();
306                                                                    String value = st.nextToken(); // this is the ":"
307                                                                    value = st.nextToken();  // this is the actual value
308                                                                    value = value.substring(1, value.length());  // remove leading " "
309                                                                    // add any remaining tokens
310                                                                    while (st.hasMoreTokens()) { 
311                                                                             value += st.nextToken(); 
312                                                                    }
313                                                                    
314                                                                    String[] labels = {" Key:", " Value:"};
315                                                                    // use this to get the return value from FieldEditDialog
316                                                                    ArrayList textFields = new ArrayList();
317                                                                    JTextField tf = new JTextField(20);
318                                                                    tf.setText(key);
319                                                                    tf.setEditable(false);
320                                                                    textFields.add(tf);
321                                                                    tf = new JTextField(20);
322                                                                    tf.setText(value);
323                                                                    textFields.add(tf);
324                                                                    new FieldEditDialog("Edit Attribute", labels, textFields);
325                                                                    
326                                                                    // check "exit code" for FieldEditDialog, if false then exit
327                                                                    Boolean exitCode = (Boolean) textFields.get(textFields.size()-1);
328                                                                    if (! exitCode.booleanValue()) return;
329    
330                                                                    tf = (JTextField) textFields.get(1);
331                                                                    String newValue = tf.getText();
332                                                                    if (value.compareTo(newValue) != 0) { // make sure value changed
333                                                                             String msg = "getTrack(\"" + track.getID() + "\")";
334                                                                             msg += ".addAttribute(\"" + key + "\", \"" + newValue + "\")";
335                                                                             Root.runCommand(msg, true);
336                                                                             updateAttributes();
337                                                                    }
338                                                      }
339                                             });
340                                    attributeBP.add(editAttributeB);
341                                    // add attributes "delete" Button
342                                    delAttributeB = new JButton(new ImageIcon("icons/delete.png"));
343                                    delAttributeB.setToolTipText("Delete Attribute");
344                                    delAttributeB.setEnabled(false);
345                                    delAttributeB.addActionListener(new ActionListener() {
346                                                      public void actionPerformed(ActionEvent e) {
347                                                                    String msg = "Are you sure you want to delete the following attribute?\n";
348                                                                    msg += "   " + attributeL.getSelectedValue();
349                                                                    Object[] options = {"Delete Attribute", "Cancel"};
350                                                                    int flag = JOptionPane.showOptionDialog(null, msg,
351                                                                                                                                                                             "Delete Confirmation",
352                                                                                                                                                                             JOptionPane.YES_NO_OPTION,
353                                                                                                                                                                             JOptionPane.QUESTION_MESSAGE,
354                                                                                                                                                                             null,
355                                                                                                                                                                             options,
356                                                                                                                                                                             options[1]);
357                                                                    if (flag == JOptionPane.YES_OPTION) { // "Delete"
358                                                                             StringTokenizer st = new StringTokenizer((String) 
359                                                                                                                                                                                            attributeL.getSelectedValue(),
360                                                                                                                                                                                            ":");
361                                                                             String key = st.nextToken();
362                                                                             msg = "getTrack(\"" + track.getID() + "\")";
363                                                                             msg += ".delAttribute(\"" + key + "\")";
364                                                                             Root.runCommand(msg, true);
365                                                                             updateAttributes();
366                                                                    }
367                                                      }
368                                             });
369                                    attributeBP.add(delAttributeB);
370                                    attributeP.add(attributeBP, BorderLayout.SOUTH);
371                                    // add attributes Label
372                                    attributeP.add(new JLabel(" Attributes:"), BorderLayout.NORTH);
373                                    // provide a preferred size for attributesP
374                                    attributeP.setPreferredSize(new Dimension(300, 200));
375                                    
376                                    // ***** SETUP TRACK SPECs *****
377                                    JPanel infoP = new JPanel(new BorderLayout());
378                                    // add info sub-panel
379                                    JPanel infoSP = new JPanel(new GridLayout(5,2,5,5));
380                                    infoSP.add(new JLabel(" Min:"));
381                                    infoSP.add(minL);
382                                    infoSP.add(new JLabel(" Max:"));
383                                    infoSP.add(maxL);
384                                    infoSP.add(new JLabel(" Length:"));
385                                    infoSP.add(lengthL);
386                                    infoSP.add(new JLabel(" Num Features:"));
387                                    infoSP.add(numFeaturesL);
388                                    infoSP.add(new JLabel(" Contiguous:"));
389                                    infoSP.add(contiguousL);
390                                    infoP.add(infoSP, BorderLayout.CENTER);
391                                    // add button sub-panel
392                                    JPanel infoBP = new JPanel(new GridLayout(0,1,5,5));
393                                    // add info "contiguous" button
394                                    JButton contiguousB = new JButton("Compute Contiguity");
395                                    contiguousB.addActionListener(new ActionListener() {
396                                                      public void actionPerformed(ActionEvent e) {
397                                                                    if (track != null) {
398                                                                             contiguousL.setText(Boolean.toString(track.isContiguous()));
399                                                                    }
400                                                      }
401                                             });
402                                    infoBP.add(contiguousB);
403                                    // add info "mergeContiguous" button
404                                    JButton mergeContiguousB = new JButton("Merge Contiguous");
405                                    mergeContiguousB.addActionListener(new ActionListener() {
406                                                      public void actionPerformed(ActionEvent e) {
407                                                                    if (track == null) return;  
408                                                                    
409                                                                    String msg = "Are you sure you want to merge all contiguous features?\n";
410                                                                    msg += "This can not be undone.";
411                                                                    Object[] options = {"Merge", "Cancel"};
412                                                                    int flag = JOptionPane.showOptionDialog(null, msg,
413                                                                                                                                                                             "Merge Confirmation",
414                                                                                                                                                                             JOptionPane.YES_NO_OPTION,
415                                                                                                                                                                             JOptionPane.QUESTION_MESSAGE,
416                                                                                                                                                                             null,
417                                                                                                                                                                             options,
418                                                                                                                                                                             options[1]);
419                                                                    if (flag == JOptionPane.YES_OPTION) { // "Merge"
420                                                                             // don't know why but the track order changes so
421                                                                             // need to reselect this track.  thus save the
422                                                                             // ID and contiguous info.
423                                                                             String id = track.getID();
424                                                                             String contiguous = contiguousL.getText();
425                                                                             
426                                                                             // XXX this should have a popup dialog that
427                                                                             // confirms this action
428                                                                             track.mergeContiguous();
429                                                                             
430                                                                             // reset the track
431                                                                             trackCB.setSelectedIndex(ObjectHandles.getTrackList().getIndexOf(id));
432                                                                             contiguousL.setText(contiguous);
433                                                                    }
434                                                      }
435                                             });
436                                    infoBP.add(mergeContiguousB);
437                                    // add info "flip" button
438                                    /*
439                                      JButton flipB= new JButton("Flip Features");
440                                      flipB.addActionListener(new ActionListener() {
441                                      public void actionPerformed(ActionEvent e) {
442                                      if (track == null) return;  
443                                      GloDBUtils.printMsg("button not yet implemented");
444                                      }
445                                      });
446                                      infoBP.add(flipB);
447                                    */
448                                    infoP.add(infoBP, BorderLayout.SOUTH);
449                                    
450                                    // ***** SETUP SEQUENCE INFO *****
451                                    JPanel sequenceP = new JPanel(new BorderLayout());
452                                    // setup list of Sequences for selected Track
453                                    sequenceL = new JList();
454                                    sequenceL.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
455                                    sequenceL.setToolTipText("Double click on a Sequence name to edit the item in a Sequence Browser");
456                                    /*
457                                      sequenceL.addListSelectionListener(new ListSelectionListener() {
458                                      public void valueChanged(ListSelectionEvent e) {
459                                      if (e.getValueIsAdjusting()) return;
460                                      
461                                      JList list = (JList) e.getSource();
462                                      if (list.isSelectionEmpty()) {
463                                      statusBar.setText("");
464                                      } else {
465                                      Sequence sequence = ObjectHandles.getSequence((String) sequences[list.getSelectedIndex()]);
466                                      String msg = "Sequence ID: " + sequence.getID();
467                                      msg += " :: Length: " + sequence.length();
468                                      msg += " :: Attributes: " + sequence.getAttributes();
469                                      statusBar.setText(msg);
470                                      }
471                                      }
472                                      });
473                                    */
474                                    sequenceL.addMouseMotionListener(new MouseMotionListener() {
475                                                      public void mouseMoved(MouseEvent e) {
476                                                                    int index = sequenceL.locationToIndex(e.getPoint());
477                                                                    if (index > -1) {  // make sure there is a valid item
478                                                                             Sequence sequence = ObjectHandles.getSequence((String) sequences[index]);
479                                                                             String msg = "Sequence ID: " + sequence.getID();
480                                                                             if (sequence.isDataLoaded()) {
481                                                                                      // only include length if data already
482                                                                                      // loaded, else this will cause the
483                                                                                      // Sequence data to be loaded which can
484                                                                                      // take a long time
485                                                                                      msg += " :: Length: " + sequence.length();
486                                                                             }
487                                                                             msg += " :: Attributes: " + sequence.getAttributes();
488                                                                             statusBar.setText(msg);
489                                                                    }
490                                                      }
491                                                      public void mouseDragged(MouseEvent e) { ; }
492                                             });
493                                    sequenceL.addMouseListener(new MouseAdapter() {
494                                                      // clear status bar when mouse moves out of sequenceL
495                                                      public void mouseExited(MouseEvent e) {
496                                                                    statusBar.setText("");
497                                                      }
498                                                      
499                                                      public void mouseClicked(MouseEvent e) {
500                                                                    if (e.getClickCount() == 2) {
501                                                                             int index = sequenceL.locationToIndex(e.getPoint());
502                                                                             
503                                                                             if (index > -1) {  // make sure there is a valid item
504                                                                                      // launch the Sequence Browser through Root so
505                                                                                      // that the command gets added to the history.
506                                                                                      String id = (String) sequences[index];
507                                                                                      String cmd = "sequenceBrowser(\"" + id + "\")";
508                                                                                      Root.runCommand(cmd, true);
509                                                                                      /*
510                                                                                             String msg = (ObjectHandles.getSequence((String) sequences[index])).toString();
511                                                                                             JOptionPane.showMessageDialog(null, msg,
512                                                                                             "Sequence Viewer",
513                                                                                             JOptionPane.INFORMATION_MESSAGE);
514                                                                                      */
515                                                                             }
516                                                                    }
517                                                      }
518                                             });
519                                    JScrollPane sequenceSP = new JScrollPane(sequenceL);
520                                    sequenceP.add(sequenceSP, BorderLayout.CENTER);
521                                    // add Sequence Label
522                                    sequenceP.add(new JLabel(" Sequences:"), BorderLayout.NORTH);
523                                    sequenceP.add(infoP, BorderLayout.SOUTH);
524                                    
525                                    // ***** SETUP FEATURE INFO *****
526                                    JPanel featureP = new JPanel(new BorderLayout());
527                                    // setup list of Features for selected Track
528                                    featureL = new JList();
529                                    featureL.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
530                                    featureL.setToolTipText("Double click on a Feature name to view the item's information");
531                                    /*
532                                      featureL.addListSelectionListener(new ListSelectionListener() {
533                                      public void valueChanged(ListSelectionEvent e) {
534                                      if (e.getValueIsAdjusting()) return;
535                                      
536                                      JList list = (JList) e.getSource();
537                                      if (list.isSelectionEmpty()) {
538                                      GloDBUtils.printMsg("empty");
539                                      } else {
540                                      GloDBUtils.printMsg(list.getSelectedIndex());
541                                      }
542                                      }
543                                      });
544                                    */
545                                    featureL.addMouseListener(new MouseAdapter() {
546                                                      public void mouseClicked(MouseEvent e) {
547                                                                    if (e.getClickCount() == 2) {
548                                                                             int index = featureL.locationToIndex(e.getPoint());
549                                                                             if (index > -1) {  // make sure there is a valid item
550                                                                                      String msg = ((Feature) features[index]).toStringFull();
551                                                                                      JOptionPane.showMessageDialog(null, msg,
552                                                                                                                                                                      "Feature Viewer",
553                                                                                                                                                                      JOptionPane.INFORMATION_MESSAGE);
554                                                                             }
555                                                                    }
556                                                      }
557                                             });
558                                    JScrollPane featureSP = new JScrollPane(featureL);
559                                    featureP.add(featureSP, BorderLayout.CENTER);
560                                    // add Features Label
561                                    featureP.add(new JLabel(" Features:"), BorderLayout.NORTH);
562                                    
563                                    // setup split pane for Sequence/Feature Panels
564                                    JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, 
565                                                                                                                                      sequenceP, featureP);
566                                    splitPane.setOneTouchExpandable(true);
567                                    splitPane.setDividerLocation(200);
568                                    // provide minimum sizes for the two components in the split pane
569                                    Dimension minimumSize = new Dimension(150, 200);
570                                    sequenceP.setMinimumSize(minimumSize);
571                                    featureP.setMinimumSize(minimumSize);
572                                    // provide a preferred size for the split pane
573                                    splitPane.setPreferredSize(new Dimension(400, 200));
574                                    
575                                    // add status bar at bottom of window
576                                    JLabel statusBarSpacer = new JLabel(" ");
577                                    statusBar = new JLabel(" ");
578                                    JPanel statusBarP = new JPanel(false);
579                                    statusBarP.setBorder(BorderFactory.createLoweredBevelBorder());
580                                    statusBarP.setLayout(new BorderLayout());
581                                    statusBarP.add(statusBarSpacer, BorderLayout.WEST);
582                                    statusBarP.add(statusBar, BorderLayout.CENTER);
583                                    
584                                    getContentPane().add(trackP, BorderLayout.NORTH);
585                                    getContentPane().add(attributeP, BorderLayout.WEST);
586                                    getContentPane().add(splitPane, BorderLayout.CENTER);
587                                    getContentPane().add(statusBarP, BorderLayout.SOUTH);
588                                    pack();
589                                    
590                                    // set the default window size
591                                    setSize(getSize().width + 100, getSize().height + 150);
592                                    
593                                    // initialize the browser to the current Track
594                                    trackSelected((String) trackCB.getSelectedItem());
595                                    
596                                    // display the window
597                                    show();
598                      }
599                      
600                      /** 
601                            * This will update the TrackBrowser for the currently selected
602                            * Track.
603                            */
604                      private void trackSelected(String id) {
605                                    if ((id == null) || (trackCB.getItemCount() == 0)) {
606                                             // if null item or empty list, then clear display
607                                             sequences = new Object[0];
608                                             sequenceL.setListData(sequences);
609                                             features = new Object[0];
610                                             featureL.setListData(features);
611                                             attributeL.setListData(new String[0]);
612                                             
613                                             minL.setText("");
614                                             maxL.setText("");
615                                             lengthL.setText("");
616                                             numFeaturesL.setText("");
617                                             contiguousL.setText("uncomputed");
618                                             
619                                             // make sure this is reset if no items in list
620                                             track = null;
621                                             
622                                             return;
623                                    }
624                                    
625                                    track = ObjectHandles.getTrack(id);
626                                    
627                                    updateAttributes();
628                                    Set tmpSeq = track.getSourceSet();
629                                    if (tmpSeq != null) { sequences = tmpSeq.toArray(); }
630                                    else { sequences = new Object[0]; }
631                                    sequenceL.setListData(sequences);
632                                    
633                                    if (track.numFeatures() < MAX_FEATURES) {
634                                             TreeSet tmpFeat = track.getFeatures();
635                                             if (tmpFeat != null) { features = tmpFeat.toArray(); }
636                                             else { features = new Object[0]; }
637                                    } else {
638                                             features = new Object[0];
639    
640                                             String msg = "Too many features to display in the feature selection list."; 
641                                             GloDBUtils.printMsg(msg, GloDBUtils.WARNING);
642                                    }
643                                    featureL.setListData(features);
644                                    
645                                    minL.setText(Integer.toString(track.getMin()));
646                                    maxL.setText(Integer.toString(track.getMax()));
647                                    lengthL.setText(Integer.toString(track.length()));
648                                    numFeaturesL.setText(Integer.toString(track.numFeatures()));
649                                    contiguousL.setText("uncomputed");
650                      }
651                      
652                      /**
653                            * This is separate from trackSelected() so it can be called
654                            * separately for when the attributes are changed in the attribute
655                            * panel.
656                            */
657                      private void updateAttributes() {
658                                    // don't do anything if track not set
659                                    if (track == null) return;
660                                    
661                                    HashMap attributes = track.getAttributes();
662                                    if ((attributes == null) || (attributes.size() == 0)) {
663                                             attributeL.setListData(new String[0]);
664                                    } else {
665                                             String[] attributeArray = new String[attributes.size()];
666                                             Set keys = attributes.keySet();
667                                             int cnt = 0;
668                                             for (Iterator i = keys.iterator(); i.hasNext();) {
669                                                      String key = (String) i.next();
670                                                      attributeArray[cnt] = key + ": " + attributes.get(key);
671                                                      cnt++;
672                                             }
673                                             attributeL.setListData(attributeArray);
674                                    }
675                      }
676                      
677                      /** Displays the Sequence data. */
678                      class ViewSequencePanel extends JFrame {
679                                    ViewSequencePanel thisFrame;
680                                    
681                                    public ViewSequencePanel() {
682                                             super("Sequence Data");
683                                             
684                                             // keep pointer to self so can 'dispose' Frame below
685                                             thisFrame = this;
686                                             
687                                             setDefaultCloseOperation(DISPOSE_ON_CLOSE);
688                                             
689                                             // setup text area to display data
690                                             JTextArea textArea = new JTextArea(track.getDataFASTA());
691                                             if (textArea.getText().length() == 0) {
692                                                      textArea.setText("No data, check sequence data source");
693                                             }
694                                             textArea.setLineWrap(false);
695                                             textArea.setWrapStyleWord(false);
696                                             textArea.setEditable(false);
697                                             textArea.setFont(new Font("Courier", Font.PLAIN, 14));
698                                             JScrollPane areaSP = new JScrollPane(textArea);
699                                             areaSP.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
700                                             areaSP.setPreferredSize(new Dimension(580, 300));
701                                             
702                                             // setup close button
703                                             JButton closeB = new JButton("Close Viewer");
704                                             closeB.addActionListener(new ActionListener() {
705                                                                    public void actionPerformed(ActionEvent e) {
706                                                                             thisFrame.dispose();
707                                                                    }
708                                                      });
709                                             
710                                             getContentPane().setLayout(new BorderLayout());
711                                             getContentPane().add(areaSP, BorderLayout.CENTER);
712                                             getContentPane().add(closeB, BorderLayout.SOUTH);
713                                             pack();
714                                             
715                                             // set the default window size
716                                             setSize(getSize().width + 100, getSize().height + 30);
717                                             
718                                             // display the window
719                                             show();
720                                    }
721                      }
722    
723                      public void selectTrack(String id) {
724                                    if ((id.length() == 0) || (trackCB.getItemCount() == 0)) return;
725                                    int index = ObjectHandles.getTrackList().getIndexOf(id);
726                                    // only change selection if valid ID
727                                    if (index > -1) {
728                                             trackCB.setSelectedIndex(index);
729                                    } else {
730                                             GloDBUtils.printMsg("Invalid track id (\"" + id + "\").", GloDBUtils.WARNING);
731                                    }
732                      }
733             }
734    } // TrackBrowser.java