text: #!/usr/bin/env python
  #
  # plotlistpanel.py - The PlotListPanel class
  #
  # Author: Paul McCarthy <pauldmccarthy@gmail.com>
  #
  """This module provides the :class:`PlotListPanel` a *FSLeyes control* panel
  which allows the user to add/remove :class:`.DataSeries` from an
  :class:`.OverlayPlotPanel`.
  """
  
  
  import numpy as np
  
  import wx
  
  import fsleyes_props                 as props
  import fsleyes_widgets.elistbox      as elistbox
  
  import fsleyes.controls.controlpanel as ctrlpanel
  import fsleyes.views.plotpanel       as plotpanel
  import fsleyes.tooltips              as fsltooltips
  import fsleyes.strings               as strings
  
  
  class PlotListPanel(ctrlpanel.ControlPanel):
      """The ``PlotListPanel`` is a *FSLeyes control* panel for use with
      :class:`.OverlayPlotPanel` views. It allows the user to add and remove
      :class:`.DataSeries` instances from the :attr:`.PlotPanel.dataSeries`
      list.
  
  
      For every :class:`.DataSeries` instance in the
      :attr:`.PlotPanel.dataSeries` list of the :class:`.OverlayPlotPanel`, the
      ``PlotListPanel`` creates a :class:`.DataSeriesWidget`, which allows the
      user to change the display settings of the :class:`.DataSeries`
      instance. A :class:`.EditableListBox` is used to display the labels for
      each :class:`.DataSeries` instance, and the associated
      :class:`.DataSeriesWidget` controls.
      """
  
  
      @staticmethod
      def supportedViews():
          """The ``PlotListPanel`` is restricted for use with
          :class:`.OverlayPlotPanel` views. This method may be overridden by
          sub-classes.
          """
          return [plotpanel.OverlayPlotPanel]
  
  
      @staticmethod
      def defaultLayout():
          """Returns a dictionary containing layout settings to be passed to
          :class:`.ViewPanel.togglePanel`.
          """
          return {'location' : wx.LEFT}
  
  
      def __init__(self, parent, overlayList, displayCtx, plotPanel):
          """Create a ``PlotListPanel``.
  
          :arg parent:      The :mod:`wx` parent object.
  
          :arg overlayList: The :class:`.OverlayList`.
  
          :arg displayCtx:  The :class:`.DisplayContext` instance.
  
          :arg plotPanel:   The :class:`.OverlayPlotPanel` associated with this
                            ``PlotListPanel``.
          """
  
          ctrlpanel.ControlPanel.__init__(
              self, parent, overlayList, displayCtx, plotPanel)
  
          self.__plotPanel = plotPanel
          self.__dsList    = elistbox.EditableListBox(
              self, style=(elistbox.ELB_NO_MOVE  |
                           elistbox.ELB_EDITABLE |
                           elistbox.ELB_WIDGET_RIGHT))
  
          self.__sizer = wx.BoxSizer(wx.VERTICAL)
          self.SetSizer(self.__sizer)
  
          self.__sizer.Add(self.__dsList, flag=wx.EXPAND, proportion=1)
  
          self.__dsList.Bind(elistbox.EVT_ELB_ADD_EVENT,    self.onListAdd)
          self.__dsList.Bind(elistbox.EVT_ELB_REMOVE_EVENT, self.onListRemove)
          self.__dsList.Bind(elistbox.EVT_ELB_EDIT_EVENT,   self.onListEdit)
          self.__dsList.Bind(elistbox.EVT_ELB_SELECT_EVENT, self.onListSelect)
  
          self.__plotPanel.canvas.addListener('dataSeries',
                                              self.name,
                                              self.dataSeriesChanged)
  
          self.dataSeriesChanged()
          self.Layout()
          self.SetMinSize(self.__sizer.GetMinSize())
  
  
      def destroy(self):
          """Must be called when this ``PlotListPanel`` is no longer
          needed. Removes some property listeners, and calls the
          :meth:`.ControlPanel.destroy` method.
          """
  
          # the plot panel may have already
          # cleared its canvas ref before we
          # get destroyed
          if self.__plotPanel.canvas is not None:
              self.__plotPanel.canvas.removeListener('dataSeries', self.name)
          self.__plotPanel = None
          self.__dsList.Clear()
          ctrlpanel.ControlPanel.destroy(self)
  
  
      @property
      def dsList(self):
          """Return a reference to the :class:`.EditableListBox` widget that
          contains the list of :class:`.DataSeries`.
          """
          return self.__dsList
  
  
      def dataSeriesChanged(self, *a):
          """Called when the :attr:`.PlotPanel.dataSeries` list of the
          :class:`.OverlayPlotPanel` changes. Updates the list of
          :class:`.TimeSeriesWidget` controls.
          """
  
          self.__dsList.Clear()
  
          for ds in self.__plotPanel.canvas.dataSeries:
              widg = DataSeriesWidget(self, ds)
              self.__dsList.Append(
                  ds.label,
                  clientData=ds,
                  tooltip=fsltooltips.properties[ds, 'label'],
                  extraWidget=widg)
  
  
      def onListAdd(self, ev):
          """Called when the user pushes the *add* button on the
          :class:`.EditableListBox`.  Adds the :class:`.DataSeries` associated
          with the currently selected overlay to the
          :attr:`.PlotPanel.dataSeries` list of the :class:`.OverlayPlotPanel`.
          """
  
          self.__plotPanel.addDataSeries()
  
  
      def onListEdit(self, ev):
          """Called when the user edits a label on the
          :class:`.EditableListBox`. Updates the :attr:`.DataSeries.label`
          property of the corresponding :class:`DataSeries` instance.
          """
          ev.data.label = ev.label
  
  
      def onListSelect(self, ev):
          """Called when the user selects an item in the
          :class:`.EditableListBox`. Sets the
          :attr:`.DisplayContext.selectedOverlay` to the overlay associated
          with the corresponding :class:`.DataSeries` instance.
          """
  
          ds      = ev.data
          overlay = ds.overlay
  
          if overlay is None:
              return
  
          opts = self.displayCtx.getOpts(overlay)
  
          self.displayCtx.selectedOverlay = self.overlayList.index(overlay)
  
          # See hacky things in __onListAdd
          if hasattr(ds, '_volume'):
              opts.volume = ds._volume
  
          elif hasattr(ds, '_location'):
              voxLoc = np.array(ds._location)
              disLoc = opts.transformCoords([voxLoc], 'voxel', 'display')[0]
              self.displayCtx.location = disLoc
  
  
      def onListRemove(self, ev):
          """Called when the user removes an item from the
          :class:`.EditableListBox`. Removes the corresponding
          :class:`.DataSeries` instance from the :attr:`.PlotPanel.dataSeries`
          list of the :class:`.OverlayPlotPanel`.
          """
          with props.skip(self.__plotPanel.canvas, 'dataSeries', self.name):
              self.__plotPanel.canvas.dataSeries.remove(ev.data)
  
  
  class DataSeriesWidget(wx.Panel):
      """The ``DataSeriesWidget`` class is a panel which contains controls
      that modify the properties of a :class:`.DataSeries` instance. A
      ``DataSeriesWidget`` is created by the :class:`PlotListPanel` for
      every ``DataSeries`` in the :attr:`.PlotPanel.dataSeries` list.
      """
  
  
      def __init__(self, parent, dataSeries):
          """Create a ``DataSeriesWidget``.
  
          :arg parent:     The :mod:`wx` parent object.
  
          :arg dataSeries: The :class:`.DataSeries` instance.
          """
  
          wx.Panel.__init__(self, parent)
  
          self.__enabled   = props.makeWidget(self, dataSeries, 'enabled')
          self.__colour    = props.makeWidget(self, dataSeries, 'colour')
          self.__lineWidth = props.makeWidget(self, dataSeries, 'lineWidth')
          self.__lineStyle = props.makeWidget(
              self,
              dataSeries,
              'lineStyle',
              labels=strings.choices['DataSeries.lineStyle'])
  
          self.__enabled.SetToolTip(
              wx.ToolTip(fsltooltips.properties[dataSeries, 'enabled']))
          self.__colour.SetToolTip(
              wx.ToolTip(fsltooltips.properties[dataSeries, 'colour']))
          self.__lineWidth.SetToolTip(
              wx.ToolTip(fsltooltips.properties[dataSeries, 'lineWidth']))
          self.__lineStyle.SetToolTip(
              wx.ToolTip(fsltooltips.properties[dataSeries, 'lineStyle']))
  
          self.__sizer = wx.BoxSizer(wx.HORIZONTAL)
          self.SetSizer(self.__sizer)
  
          self.__sizer.Add(self.__enabled)
          self.__sizer.Add(self.__colour)
          self.__sizer.Add(self.__lineWidth)
          self.__sizer.Add(self.__lineStyle)
  
          self.Layout()
  