def createGui (self):

    panel = JPanel ()
    panel.setLayout (BorderLayout ())

    chooserPanel = JPanel (); # JScrollPane (conditionsList)
    chooserPanel.setLayout (BorderLayout ())

    root = DefaultMutableTreeNode ('Experiments')
    assert (len (self.dataDirectories) >= 1)
    self.experimentNavigator = MetaDataNavigator (self.dataDirectories [0])
      
    experimentsTree = self.experimentNavigator.getTree ()
    #print 'experimentTree: %s' % experimentsTree
    self.createTreeNodes (root, experimentsTree)
    self.treeWidget = JTree (root)
    #tree.setRootVisible (0)
    self.treeWidget.getSelectionModel().setSelectionMode (
         TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION)
    self.treeWidget.addTreeSelectionListener (self)
    
    treePane = JScrollPane (self.treeWidget)

    treePanel = JPanel ()
    treePanel.setLayout (BorderLayout ())
    treePanel.add (treePane, BorderLayout.CENTER)
    
    self.resultsPanel = JPanel ()
    self.resultsPanel.setLayout (BorderLayout ())

    self.outerSplitPane = JSplitPane (JSplitPane.HORIZONTAL_SPLIT, 
                                      treePanel,
                                      self.resultsPanel)

    self.outerSplitPane.setDividerLocation (200)
    self.outerSplitPane.setOneTouchExpandable (1)
    self.dataBrowserHasEntireWindow = 0


    panel.add (self.outerSplitPane, BorderLayout.CENTER)
    
    dismissButton = JButton (actions.IconFactory.getDismissIcon(), actionPerformed=self.dismiss)
    dismissButton.setBackground(Color.WHITE)
    dismissButton.setToolTipText('Close This Window')

    buttonAndReadoutPanel = JPanel ()
    buttonAndReadoutPanel.setLayout (GridBagLayout ())

    gbc = GridBagConstraints ()
    gbc.gridx = 0
    gbc.gridy = 0
    gbc.weightx = 1.0
    gbc.anchor = GridBagConstraints.LINE_START
    gbc.insets = Insets (5,20,5,0)
    selectionPanel = JPanel ()
    buttonAndReadoutPanel.add (selectionPanel, gbc)
    label = JLabel ('Explicit Selection Count: ')
    selectionPanel.add (label)

    self.conditionCounterTextField = JTextField (5)
    selectionPanel.add (self.conditionCounterTextField)

    gbc.weightx = 0.5
    gbc.gridx = 3
    gbc.anchor = GridBagConstraints.LINE_END
    gbc.insets = Insets (5,0,5,20)
    buttonAndReadoutPanel.add (dismissButton, gbc)

    panel.add (buttonAndReadoutPanel, BorderLayout.SOUTH)

    toolbar = JToolBar ()
    panel.add (toolbar, BorderLayout.NORTH)
    #toolbar.add (JButton ('Add Data Source', actionPerformed=self.addSource))
    self.loadSelectedConditionsButton = JButton (actions.IconFactory.getLoadSelectedConditionsIcon(), 
                                                 actionPerformed=self.loadSelectedConditions)
    self.loadSelectedConditionsButton.setBackground(Color.WHITE)
    self.loadSelectedConditionsButton.setToolTipText('Load Selected Conditions')                                             
    self.loadSelectedConditionsButton.setEnabled (0)
    toolbar.add (self.loadSelectedConditionsButton)

    expandButton = JButton(actions.IconFactory.getExpandAndContractIcon(),
        actionPerformed=self.expandContractDataBrowser)
    expandButton.setToolTipText('Expand/Contract')
    expandButton.setBackground(Color.WHITE)
    toolbar.add (expandButton)
    

    # Volcano plot button
    volcanoPlotButton = JButton(actions.IconFactory.getVolcanoPlotterIcon(), actionPerformed=self.plot)
    volcanoPlotButton.setToolTipText("Scatter Plotter")
    volcanoPlotButton.setBackground(Color.WHITE)
    toolbar.add (volcanoPlotButton)
    return panel
class TreeDataBrowser (AbstractPlugin,  TreeSelectionListener, WindowListener):

  def __init__ (self, cytoscapeWindow=None):

    self. dataDirectories = []
    self.currentTreeSelection = [] 
    self.dataMatrixBrowser = None
    self.treeWidget = None
    self.experimentNavigator = None
    self.currentlySelectedExperiments = []
    #repository = '/users/pshannon/data/halo/microarrayXml/sampleData/small'
    #repository = 'file:///users/pshannon/cy/csplugins/isb/pshannon/experimentNavigator/sampleData/bat'
    #repository = '/users/pshannon/data/halo/microarrayXml/FeSO4-2004.07.07'
    #repository = 'file:///home/pshannon/data/halo/microarray/'
    #repository = 'file://c:\halo\metals'
    #repository = 'http://www.sewardpark.net/isb'
    #repository = 'http://db:8060/halo/data/xmlv2'
    #repository = 'http://db:8060/halo/data/test'
    #repository = 'http://db.systemsbiology.net/cytoscape/projects/static/mjohnson/2004.oct'
    #repository = 'file:///users/pshannon/data/halo/microarrayXml/tbpTfbKnockout'
    #repository = 'http://db.systemsbiology.net:8060/halo/data/'
    repository = 'httpIndirect://db.systemsbiology.net:8080/halo/DataFetcher.py'
    
    self.dataDirectories.append (repository)

    self.cytoscapeWindow = cytoscapeWindow
    if (self.cytoscapeWindow):
      menuItem = JMenuItem ('Data Browser & Chooser', actionPerformed=self.createFrame)
      self.cytoscapeWindow.getOperationsMenu().add (menuItem)
    else:
      self.createFrame ()

  #-------------------------------------------------------------------------------
  def menuItemCreateFrame (self, event):

    self.createFrame ()

  #-------------------------------------------------------------------------------
  def createFrameTitle (self):

    id = '$Revision: 18 $'
    signature = 'Revision: '
    start = id.find (signature)
    start += len (signature)
    end = id.find (' $', start)
    versionNumber = id [start:end]
    return 'Data Matrix Browser %s' % versionNumber

  #-------------------------------------------------------------------------------
  def createFrame (self, event=None):

    self.jframe = JFrame (title='%s' % self.createFrameTitle (), size = (800, 600))
    self.jframe.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE)
    self.jframe.addWindowListener (self)

    self.currentDirectory = File (os.getcwd ())
    self.experiments = {}

    self.SEPARATOR = ': ' # used in listbox names, e.g., copper: concentration: 10

    self.jframe.getContentPane().add (self.createGui ())
    self.jframe.show ()

  #------------------------------------------------------------------------------------------
  def windowClosing (self, event):

    self.dismiss (None)

  #------------------------------------------------------------------------------------------
  def windowClosed (self, event):
    pass

  def windowOpened (self, event):
    pass

  def windowIconified (self, event):
    pass

  def windowDeiconified (self, event):
    pass

  def windowActivated (self, event):
    pass

  def windowDeactivated (self, event):
    pass

  def windowGainedFocus (self, event):
    pass

  def windowLostFocus (self, event):
    pass
  #------------------------------------------------------------------------------------------
  def createTreeNodes (self, root, hash):

    kids = [x for x in hash.keySet().toArray ()]

    if (isArrayOfNumbers (kids)):
      kids.sort (lambda x, y: float(x) > float (y))
    else:
      kids.sort ()

    for kid in kids:
      newNode = DefaultMutableTreeNode (kid)
      root.add (newNode)
      self.createTreeNodes (newNode, hash [kid])

  #-----------------------------------------------------------------------------------
  def __updateCurrentTreeSelection (self, treeSelectionEvent):
    """
     update class member variable 'currentTreeSelection' which holds a list
     of TreePath's (a TreePath is an array of Objects (here, Strings)
     specifying the tree branch.  paths are either added or removed
   """

    for p in treeSelectionEvent.getPaths ():
      added = treeSelectionEvent.isAddedPath (p)
      if (added):
        self.currentTreeSelection.append (p)
      else:
        if (p in self.currentTreeSelection):
          self.currentTreeSelection.remove (p)


  #-----------------------------------------------------------------------------------
  def __convertTreePathsToHash (self, treePaths):

    # translate from, eg,
    #
    # [[Experiments, environmental, metals, FeSO4, time, -1],
    #  [Experiments, environmental, metals, FeSO4, time, 20]]
    #
    # to
    #
    # {'environmental:metals:FeSO4': ['time:-1', 'time:20']}
    #

    result = {}
    for path in self.currentTreeSelection:
      stringList = ['%s' % element for element in path.getPath ()]
      if (len (stringList) <= 1):
        return   # the selection is simply the root of the tree, and
                 # we don't support selection of just the root
      trimmedList = stringList [1:]
      experimentKeys = \
         self.experimentNavigator.findExperimentKeyForPerturbation (trimmedList)
      for element in experimentKeys:
        [experimentKey, condition] = element
        #print 'experimentKey: %s   condition: %s' % (experimentKey, condition)
        if (not experimentKey in result.keys ()):
          result [experimentKey] = []
        list = result [experimentKey]
        if (not condition in list):
          list.append (condition)
        result [experimentKey] = list

    return result

  #-----------------------------------------------------------------------------------
  def valueChanged (self, treeSelectionEvent):

    # respond to a change in the selection state of the treeWidget.
    # the goal is to translate the current selection state of the JTree widget
    # into selected conditions in all of the relevant experiements -- which are
    # here actually MetaData objects.  these MetaData objects originally supplied
    # the JTree hierarchy.  they also carry information about every observed condition
    # in the experiment (typically a dosage level, or a point in a time course);
    # any of these conditions may be selected, and that's the result we seek here:
    # that all JTree selections are mirrored by selections in the MetaData objects
    # these MetaData objects can then be used to create matrices that consist only
    # of the selected columns (data columns are, in effect, experimental conditions)

    self.__updateCurrentTreeSelection (treeSelectionEvent)

    if (len (self.currentTreeSelection) == 0):
      self.loadSelectedConditionsButton.setEnabled (0)
      self.conditionCounterTextField.setText ('%s' % 0)
      self.currentTreeSelection = []
      for experiment in self.currentlySelectedExperiments:
        experiment.clearSelectionCriteria ()
      return

      # there is at least one selection.  translate the TreePath's
      # into a hash.  

    self.loadSelectedConditionsButton.setEnabled (1)
    selectedConditions = self.__convertTreePathsToHash (self.currentTreeSelection)
    if (selectedConditions == None or len (selectedConditions) == 0):
      self.loadSelectedConditionsButton.setEnabled (0)
      self.conditionCounterTextField.setText ('%s' % 0)
      self.currentTreeSelection = []
      for experiment in self.currentlySelectedExperiments:
        experiment.clearSelectionCriteria ()
      return

    self.currentlySelectedExperiments = []
    selectedConditionCount = 0

    for experimentKey in selectedConditions.keys ():

       # the experiment key is just a convenenient form of the tree paths selected in
       # the jtree. it might be an explicit, single experiment
       #    'environmental:metals:FeSO4'
       # or implicitly, a whole group of experiments
       #    'environmental:metals'  
       # which implies FeSO4, cobalt, copper, iron, manganese, ...

      experimentList = self.experimentNavigator.getExperimentByPerturbationList (experimentKey)
         # now select conditions in each experiment
      for experiment in experimentList: 
        experiment.clearSelectionCriteria ()
        if (not experiment in self.currentlySelectedExperiments):
          self.currentlySelectedExperiments.append (experiment)
        conditions = selectedConditions [experimentKey]
        experiment = self.__makeSelections (experiment, conditions)
        selectedConditionCount += len (experiment.getSelectedConditionsAsAliases ())

    self.conditionCounterTextField.setText ('%s' % selectedConditionCount)
    
  #---------------------------------------------------------------------------------
  def __makeSelections (self, experiment, conditions):

    # selected conditions for the current experiment take several forms,
    # directly reflecting how deeply into the JTree the user clicked
    #   treePath                             experimentKey                 conditions
    #  --------------------------          -------------------------       ----------
    # environmental:metals:FeSO4           environmental:metals:FeSO4         null
    # environmental:metals:FeSO4:time      environmental:metals:FeSO4         time
    # environmental:metals:FeSO4:time:-1   environmental:metals:FeSO4         time:-1

    if (conditions == [None]):
      experiment.selectAllConditions ()
    else:
      for conditionPairRaw in conditions:
        conditionPair = conditionPairRaw.split (':')
        if (len (conditionPair) == 1):   # select all values for this condition
          experiment.selectConditionByName (conditionPair [0])
        elif (len (conditionPair) == 2):
          name = conditionPair [0]
          value = conditionPair [1]
          experiment.addSelectionCriterion (name, value)

    return experiment

  #---------------------------------------------------------------------------------
  def expandTree (self):

    done = 0
    while (not done):
      rowCount = self.treeWidget.getRowCount ()
      for r in range (rowCount, 0, -1):
        self.treeWidget.expandRow (r)
      if (rowCount == self.treeWidget.getRowCount ()):
        done = 1


  #---------------------------------------------------------------------------------
  def createGui (self):

    panel = JPanel ()
    panel.setLayout (BorderLayout ())

    chooserPanel = JPanel (); # JScrollPane (conditionsList)
    chooserPanel.setLayout (BorderLayout ())

    root = DefaultMutableTreeNode ('Experiments')
    assert (len (self.dataDirectories) >= 1)
    self.experimentNavigator = MetaDataNavigator (self.dataDirectories [0])
      
    experimentsTree = self.experimentNavigator.getTree ()
    #print 'experimentTree: %s' % experimentsTree
    self.createTreeNodes (root, experimentsTree)
    self.treeWidget = JTree (root)
    #tree.setRootVisible (0)
    self.treeWidget.getSelectionModel().setSelectionMode (
         TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION)
    self.treeWidget.addTreeSelectionListener (self)
    
    treePane = JScrollPane (self.treeWidget)

    treePanel = JPanel ()
    treePanel.setLayout (BorderLayout ())
    treePanel.add (treePane, BorderLayout.CENTER)
    
    self.resultsPanel = JPanel ()
    self.resultsPanel.setLayout (BorderLayout ())

    self.outerSplitPane = JSplitPane (JSplitPane.HORIZONTAL_SPLIT, 
                                      treePanel,
                                      self.resultsPanel)

    self.outerSplitPane.setDividerLocation (200)
    self.outerSplitPane.setOneTouchExpandable (1)
    self.dataBrowserHasEntireWindow = 0


    panel.add (self.outerSplitPane, BorderLayout.CENTER)
    
    dismissButton = JButton (actions.IconFactory.getDismissIcon(), actionPerformed=self.dismiss)
    dismissButton.setBackground(Color.WHITE)
    dismissButton.setToolTipText('Close This Window')

    buttonAndReadoutPanel = JPanel ()
    buttonAndReadoutPanel.setLayout (GridBagLayout ())

    gbc = GridBagConstraints ()
    gbc.gridx = 0
    gbc.gridy = 0
    gbc.weightx = 1.0
    gbc.anchor = GridBagConstraints.LINE_START
    gbc.insets = Insets (5,20,5,0)
    selectionPanel = JPanel ()
    buttonAndReadoutPanel.add (selectionPanel, gbc)
    label = JLabel ('Explicit Selection Count: ')
    selectionPanel.add (label)

    self.conditionCounterTextField = JTextField (5)
    selectionPanel.add (self.conditionCounterTextField)

    gbc.weightx = 0.5
    gbc.gridx = 3
    gbc.anchor = GridBagConstraints.LINE_END
    gbc.insets = Insets (5,0,5,20)
    buttonAndReadoutPanel.add (dismissButton, gbc)

    panel.add (buttonAndReadoutPanel, BorderLayout.SOUTH)

    toolbar = JToolBar ()
    panel.add (toolbar, BorderLayout.NORTH)
    #toolbar.add (JButton ('Add Data Source', actionPerformed=self.addSource))
    self.loadSelectedConditionsButton = JButton (actions.IconFactory.getLoadSelectedConditionsIcon(), 
                                                 actionPerformed=self.loadSelectedConditions)
    self.loadSelectedConditionsButton.setBackground(Color.WHITE)
    self.loadSelectedConditionsButton.setToolTipText('Load Selected Conditions')                                             
    self.loadSelectedConditionsButton.setEnabled (0)
    toolbar.add (self.loadSelectedConditionsButton)

    expandButton = JButton(actions.IconFactory.getExpandAndContractIcon(),
        actionPerformed=self.expandContractDataBrowser)
    expandButton.setToolTipText('Expand/Contract')
    expandButton.setBackground(Color.WHITE)
    toolbar.add (expandButton)
    

    # Volcano plot button
    volcanoPlotButton = JButton(actions.IconFactory.getVolcanoPlotterIcon(), actionPerformed=self.plot)
    volcanoPlotButton.setToolTipText("Scatter Plotter")
    volcanoPlotButton.setBackground(Color.WHITE)
    toolbar.add (volcanoPlotButton)
    return panel
  
  #---------------------------------------------------------------------------------
  def combineSelectedConditions (self):

    """
     create a single matrix from all of the selected columns in
     all selected experiments
    """
    if (len (self.currentlySelectedExperiments) == 0):
      return

    allSelectedMatrices = {}

    for experiment in self.currentlySelectedExperiments:
      name = experiment.getTitle ()
      #print '   checking experiment with name %s' % name
      columnNames = experiment.getSelectedConditionsAsAliases ()
      #print '%s: columnNames: %s' % (name, columnNames)
      if (len (columnNames) > 0):
        slicer = datamatrix.MatrixSlicer (experiment)
        slicedMatrices = slicer.slice ()  # there may be one, two, or more...
        #print 'slicer returned %d matrices: ' % len (slicedMatrices)
        
        matrixTypes = [x for x in slicedMatrices.keySet().toArray ()]
        for type in matrixTypes:
          m = slicedMatrices [type]
          #print 'matrix shape: %d x %d' % (m.getColumnCount (), m.getRowCount ())
          #print 'colum names:  %s' % m.getColumnTitles ()
          if (not allSelectedMatrices.has_key (type)):
            allSelectedMatrices [type] = []
          allSelectedMatrices [type].append (m)

    finalMatrices = []
    for type in allSelectedMatrices.keys ():
      combiner = datamatrix.MatrixCombiner (allSelectedMatrices [type])
      combinedMatrix = combiner.combine ()
      combinedMatrix.setShortName (type)
      finalMatrices.append (combinedMatrix)

    return finalMatrices

  #---------------------------------------------------------------------------------
  def expandContractDataBrowser (self, event):

    if (self.dataBrowserHasEntireWindow):
      lastLocation = self.outerSplitPane.getLastDividerLocation ()
      self.outerSplitPane.setDividerLocation (lastLocation)
      self.dataBrowserHasEntireWindow = 0
    else:
      self.outerSplitPane.setDividerLocation (0)
      self.dataBrowserHasEntireWindow = 1

  #---------------------------------------------------------------------------------
  def plot (self, event):

    if (self.dataMatrixBrowser == None):
      return

    matrices = self.dataMatrixBrowser.getMatrices ()
    if (not len (matrices) == 2):
      return

    plotController = PlotControllerListBox (matrices)

  #---------------------------------------------------------------------------------
  def loadSelectedConditions (self, event):

    matrices = self.combineSelectedConditions ()

    if (matrices == None or len (matrices) == 0):
      return

    if (self.dataMatrixBrowser):
      for matrix in matrices:
        #print 'adding matrix to *PRE-EXISTING* dataMatrixBrowser'
        self.dataMatrixBrowser.addMatrixToGui (matrix)
    else:
      #print 'creating *NEW* dataMatrixBrowser'
      self.dataMatrixBrowser = gui.DataMatrixBrowser (self.cytoscapeWindow, matrices)
      self.resultsPanel.add (self.dataMatrixBrowser, BorderLayout.CENTER)
      self.resultsPanel.paintAll (self.resultsPanel.getGraphics ())
      
  #---------------------------------------------------------------------------------
  def dismiss (self, event):

    self.dataMatrixBrowser = None
    self.jframe.dispose ()

  #---------------------------------------------------------------------------------
  def addSource (self, event):

    chooser = JFileChooser (self.currentDirectory)
    chooser.setFileSelectionMode (JFileChooser.DIRECTORIES_ONLY)
    status = chooser.showOpenDialog (JFrame ())
    if (status == JFileChooser.APPROVE_OPTION):

      self.sourceDirectory = chooser.getSelectedFile()
      directory = self.sourceDirectory.getPath ()
      #self.sourcesList.getModel ().addElement (directory)
      self.currentDirectory = chooser.getCurrentDirectory()
      self.loadXmlFiles (directory)

  #---------------------------------------------------------------------------------
  def loadXmlFiles (self, source):

    if (source.find ('http://') == 0):
      fetcher = PasswordProtectedMetaDataFetcher (source)
      fileHash = fetcher.getXmlFilesList ()
      xmlFiles = fileHash.keys ()
    else:
      candidates = os.listdir (source)
      #print 'candidates: %s' % candidates
      xmlFiles = [f for f in candidates if f.count ('.xml') > 0]
      if (len (xmlFiles) == 0):
        return

    xmlFiles.sort ()
    for file in xmlFiles:
      #print 'xmlFile: %s' % file
      pathAsString = '%s/%s' % (source, file)
      parser = ExperimentXmlParser (pathAsString)
      self.experiments [parser.experiment.name] = parser.experiment

    #print 'experiment count: %d' % len (self.experiments)
    newExperimentNames = self.experiments.keys ()
    newExperimentNames.sort ()

    #existingExperimentNames = [n for n in self.sourcesList.getSelectedValues ()]
    #print 'existing: %s' % existingExperimentNames

    for experimentName in newExperimentNames:
      if (experimentName in existingExperimentNames):
        continue
      self.chooserList1.getModel ().addElement (experimentName)
      #print '----------------- %s' % experimentName
      experiment = self.experiments [experimentName]