"""
     qtgui/mgWidgets.py: CCP4MG Molecular Graphics Program
     Copyright (C) 2001-2008 University of York, CCLRC
     Copyright (C) 2009-2011 University of York
     Copyright (C) 2012 STFC

     This library is free software: you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public License
     version 3, modified in accordance with the provisions of the 
     license to address the requirements of UK law.
 
     You should have received a copy of the modified GNU Lesser General 
     Public License along with this library.  If not, copies may be 
     downloaded from http://www.ccp4.ac.uk/ccp4license.php
 
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU Lesser General Public License for more details.
"""

import sys
import os
from PyQt4 import QtGui,QtCore
import sip

import guiUtils
import global_definitions
import ColourBrowser
import MGSimpleDialog

import copy

from mgSliders import *
import mgapp

#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------
class mgLineEdit(QtGui.QLineEdit):
#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------
# This class is just a kludge to intercept the return key press which
# in many dialog boxes activates default button 
  def __init__(self,parent=None):
    QtGui.QLineEdit.__init__(self,parent)

  def keyPressEvent(self,event=None):
    #print 'mgLineEdit.keyPressEvent',event,event.key()
    if event.key() == QtCore.Qt.Key_Return:
      #print 'mgLineEdit emmit apply'
      self.emit(QtCore.SIGNAL('apply'))
    else:
      QtGui.QLineEdit.keyPressEvent(self,event)


#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------
class mgCidValidator(QtGui.QValidator):
#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------

  def __init__(self,parent=None,mode='atom'):
    QtGui.QValidator.__init__(self,parent)
    self.mode = mode

    self.useMMDB = 1
    self.selHnd = -1
    self.MolData_name = ''

  '''
  def __del__(self):
    print 'mgCidValidator.__del__'    
    if self.useMMDB:
      dobj.molHnd.DeleteSelection(self.selHnd)
    QtGui.QValidator.__del__(self)
  '''

  def deleteSelHnd(self):
    if not self.MolData_name or self.selHnd<0: return
    dobj = data(self.MolData_name)
    if dobj: dobj.molHnd.DeleteSelection(self.selHnd)
    self.MolData_name = ''
    self.selHnd = -1
      
  def createSelHnd(self):
    self.MolData_name = self.parent.MolData_name
    dobj = self.parent().getDataobj()
    self.selHnd = dobj.molHnd.NewSelection()

#----------------------------------------------------------------------
  def validate(self,text='',pos=0):
#----------------------------------------------------------------------
    import string
    import pygl_coord
    selindexp = pygl_coord.intp()
    #print 'mgCidValidator.validate',text,self.parent().getDataobj()
    text = string.strip(str(text))
    if not text: return  (QtGui.QValidator.Intermediate,pos)
    dobj = self.parent().getDataobj()
    if not dobj: return  (QtGui.QValidator.Intermediate,pos)

    if self.useMMDB:
      import mmdb2 as mmdb
      self.selHnd = dobj.molHnd.NewSelection()
      if self.selHnd<0: self.createSelHnd()
      if self.mode == 'atom':
        selAtoms = mmdb.newPPCAtom()
        dobj.molHnd.Select(self.selHnd,mmdb.STYPE_ATOM,text,mmdb.SKEY_NEW)
        #nHits = dobj.molHnd.GetSelIndex(self.selHnd,selAtoms)
        selAtoms = mmut.GetAtomSelIndex(dobj.molHnd,self.selHnd,selindexp)
        nHits = selindexp.value()
      elif self.mode == 'residue':
        selRes = mmdb.newPPCResidue()
        dobj.molHnd.Select(self.selHnd,mmdb.STYPE_RESIDUE,text,mmdb.SKEY_NEW)
        #nHits = dobj.molHnd.GetSelIndex(self.selHnd,selRes)
        selRes = mmut.GetResidueSelIndex(dobj.molHnd,self.selHnd,selindexp)
        nHits = selindexp.value()

      dobj.molHnd.DeleteSelection(self.selHnd)
      if nHits == 1:
        return (QtGui.QValidator.Acceptable,pos)
      else: 
        return (QtGui.QValidator.Intermediate,pos)
    else:
      splitText = string.split(text,'/')
    
    return (QtGui.QValidator.Acceptable,pos)

#----------------------------------------------------------------------
  def fixup(self,text=''):
#----------------------------------------------------------------------
    print 'mgCidValidator.fixup',text



#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------
class mgSelectionCombo(QtGui.QFrame):
#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------

  pendingColour = QtCore.Qt.lightGray
  regularColour = ''
  
  def __init__(self,parent=None,abstractModel=None,modelGroups=['monomers','metal','peptide','nucleic','solvent','solute','saccharide'],multi=0,selectionWidgets=[],highlightPending=0):
    super(mgSelectionCombo,self).__init__(parent)

    if not mgSelectionCombo.regularColour:
      mgSelectionCombo.regularColour = self.palette().color(QtGui.QPalette.Normal,QtGui.QPalette.Button)

    self.treeView = None
    self.dataViewWindow = None
    self.seleDialog = None
    self.seleWidget = None
    self.highlightPending = highlightPending
    self.lastLineText = ''
    self.selParams = {}
    self.seleWidgetMode = ''
    self.modelGroups = modelGroups
    self.setFrameStyle(QtGui.QFrame.Panel|QtGui.QFrame.Plain)

    margin = 0

    layout = QtGui.QHBoxLayout()   
    layout.setContentsMargins(margin,margin,margin,margin)
    layout.setSpacing(0)

    if multi:
      self.dataobjCombo = mgDataObjCombo(object_type='MolData',parent=self)
      layout.addWidget(self.dataobjCombo)
    else:
      self.dataobjCombo = None
      

    self.lineEdit = mgLineEdit(self)
    #self.lineEdit = mgLineEdit(self)
    ht = self.lineEdit.height()
    self.lineEdit.setMinimumWidth(200)
    wid = 200 + 2*ht
    if selectionWidgets: wid = wid + ht
    self.setMinimumWidth(wid)
    layout.addWidget(self.lineEdit,5)
    
    import guiUtils
    icon = QtGui.QIcon(guiUtils.loadPixmap('seleTree','disptab'))
    self.iconButton = QtGui.QToolButton(self)
    self.iconButton.setPopupMode(QtGui.QToolButton.InstantPopup)
    self.iconButton.setIcon(icon)
    self.iconButton.setMaximumSize(QtCore.QSize(ht-2,ht-2))
    self.iconButton.setToolTip('Open selection browser')
    layout.addWidget(self.iconButton)
    
    icon = QtGui.QIcon(guiUtils.loadPixmap('gtk-clear','disptab'))
    self.clearButton =  QtGui.QToolButton(self)   
    self.clearButton.setIcon(icon)
    self.clearButton.setMaximumSize(QtCore.QSize(ht-2,ht-2))
    self.clearButton.setToolTip('Clear selection')
    layout.addWidget(self.clearButton)

    if selectionWidgets:
      icon = QtGui.QIcon(guiUtils.loadPixmap('lasso'))
      self.seleButton =  QtGui.QToolButton(self)   
      self.seleButton.setIcon(icon)
      self.seleButton.setMaximumSize(QtCore.QSize(ht-2,ht-2))
      self.seleButton.setToolTip('Open selection widgets')
      #print 'mgSelectionCombo selectionWidgets',selectionWidgets
      menu = QtGui.QMenu(self)
      guiUtils.populateMenu(self,menu,selectionWidgets,default_icon='')
      self.seleButton.setMenu(menu)
      self.seleButton.setPopupMode(QtGui.QToolButton.InstantPopup)

      layout.addWidget(self.seleButton)

    self.setLayout(layout)

    if not  mgSelectionCombo.regularColour:
      mgSelectionCombo.regularColour = self.lineEdit.palette().color( QtGui.QPalette.Base)

    self.abstractModel = None
    self.setAbstractModel(abstractModel=abstractModel)

    if multi:
      self.connect(self.dataobjCombo,QtCore.SIGNAL('currentIndexChanged(int)'), self.setAbstractModel)


    self.connect (self.clearButton,QtCore.SIGNAL("released()"),self.clear)
    self.connect(self.iconButton,QtCore.SIGNAL("released()"),self.toggleDataView)
    self.connect(self.iconButton,QtCore.SIGNAL("pressed()"),self.returnPressed)
    self.connect(self.lineEdit,QtCore.SIGNAL("apply"),self.returnPressed)
    if self.highlightPending:
      self.connect(self.lineEdit,QtCore.SIGNAL("textEdited(const QString)"),self.updateTextHighlight)


      
#----------------------------------------------------------------------
  def toggleDataView(self):
#----------------------------------------------------------------------

    #print "in toggleDataView"
    geom = self.lineEdit.geometry().bottomLeft()
    pos = self.mapToGlobal( self.lineEdit.geometry().bottomLeft() )
    #print "mgSelectionCombo.toggleDataView",geom.x(),geom.y(),pos.x(),pos.y()
    xx = pos.x()
    yy = pos.y()
    wid = self.lineEdit.width()
    #print pos,xx,yy,wid
    
    if not self.dataViewWindow:

      self.dataViewWindow = QtGui.QDialog(self,QtCore.Qt.Popup)
      #self.dataViewWindow = QtGui.QDialog(self)
      layout = QtGui.QGridLayout()
      layout.setContentsMargins(0,0,0,0)

      import mgTreeView
      self.treeView = mgTreeView.mgTreeView(self,self.abstractModel)
      self.treeView.setExpandsOnDoubleClick(0)
      self.treeView.loadGroups(self.modelGroups)
      layout.addWidget(self.treeView,0,0,1,1)

      self.dataViewWindow.setLayout(layout)


      #self.connect(self.treeView,QtCore.SIGNAL('doubleClicked(QModelIndex)'),self.handleDoubleClick)
        
      self.dataViewWindow.show()
      self.dataViewWindow.setGeometry(xx,yy,wid,300)
      self.updateTreeView()
      self.connect(self.treeView,QtCore.SIGNAL('changed'),self.updateLineEdit)

    elif self.dataViewWindow.isHidden():
      
      self.dataViewWindow.show()
      self.dataViewWindow.setGeometry(xx,yy,wid,300)
      #self.updateTreeView()

    else:
      self.dataViewWindow.hide()

#----------------------------------------------------------------------
  def returnPressed(self):
#----------------------------------------------------------------------
    #print "returnPressed"
    self.updateTreeView()
    self.lastLineText = self.text()
    self.updateTextHighlight()
    self.emit(QtCore.SIGNAL("changed"))

#----------------------------------------------------------------------
  def updateTextHighlight(self,text=''):
#----------------------------------------------------------------------
    if not self.highlightPending: return
    palette = self.lineEdit.palette()
    #print 'updateTextHighlight',self.lastLineText,'*',self.text(),'*'
    if self.text() != self.lastLineText:
      self.lastLineText = self.text()
      palette.setColor( QtGui.QPalette.Base, mgSelectionCombo.pendingColour )
      #print 'updateTextHighlight pending'
    else:
      palette.setColor( QtGui.QPalette.Base,  mgSelectionCombo.regularColour )
      self.lastLineText = self.text()
      #print 'updateTextHighlight regular'
    self.lineEdit.setPalette(palette)

    
#----------------------------------------------------------------------
  def updateLineEdit(self):
#----------------------------------------------------------------------
    selected = self.treeView.selectedIndexes()
    #print "mgSelectionCombo.updateLineEdit selected",selected
    ret = self.abstractModel.interpretTreeSelection(selected,returnCommand=1)
    if not ret[0]:
      self.setText(ret[1])
      self.emit(QtCore.SIGNAL("changed"))
      
#----------------------------------------------------------------------
  def text(self):
#----------------------------------------------------------------------
    return str(self.lineEdit.text())

#----------------------------------------------------------------------
  def setText(self,text):
#----------------------------------------------------------------------
    self.lineEdit.setText(text)
    self.lastLineText = text
    self.updateTextHighlight()

#----------------------------------------------------------------------
  def getParams(self):
#----------------------------------------------------------------------
    if self.seleWidget: self.selParams.update(self.seleWidget.getParams())
    return self.selParams

#----------------------------------------------------------------------
  def setParams(self,params={},**kw):
#----------------------------------------------------------------------
    params.update(kw)
    self.selParams.update(params)
    if self.seleWidget: self.seleWidget.setParams(self.selParams)
    if self.selParams.has_key('selection'): self.setText(self.selParams['selection'])

#----------------------------------------------------------------------
  def updateTreeView(self):
#----------------------------------------------------------------------
    if self.treeView:
      sele_text = str(self.lineEdit.text())
      #Test if this is a named selection (eg all/peptide)
      rv = self.treeView.setGroupSelection(sele_text)
      if not rv: return

      # Make sure command will actually parse
      molobj = self.getCurrentDataObj()
      rv = molobj.parse_selection(command=sele_text)
      #print "mgSelectionCombo.updateTreeView rv",rv

      # Update tree view
      if not rv[0]:
        #self.disconnect(self.treeView,QtCore.SIGNAL('changed'),self.updateLineEdit)
        self.treeView.blockSignals(True)
        self.treeView.clearSelection()       
        self.treeView.loadMMDBSelection(rv[1])
        molobj.molHnd.DeleteSelection(rv[1])
        #self.connect(self.treeView,QtCore.SIGNAL('changed'),self.updateLineEdit)
        self.treeView.blockSignals(False)

#----------------------------------------------------------------------
  def clear(self):
#----------------------------------------------------------------------
    #print "mgSelectionCombo.clear",self.treeView
    if self.treeView:
      #self.disconnect(self.treeView,QtCore.SIGNAL('changed'),self.updateLineEdit)
      self.treeView.blockSignals(True)
      self.treeView.clearSelection()
      #print "mgSelectionCombo from treeView.clearSelection"
    self.setText('')
    if self.treeView:
      #self.connect(self.treeView,QtCore.SIGNAL('changed'),self.updateLineEdit)
      self.treeView.blockSignals(False)
    self.emit(QtCore.SIGNAL("changed"))

#------------------------------------------------------------------------
  def setAbstractModel(self,index=-1,abstractModel=''):
#------------------------------------------------------------------------
    '''
    Set the abstract model - beware used as slot and receives index from
    comboBox
    '''
    if abstractModel:
      if abstractModel == self.abstractModel: return
      self.abstractModel = abstractModel
    elif self.dataobjCombo:
      dobj = self.dataobjCombo.getCurrentDataObj()
      if dobj and hasattr(dobj,'treeModel'):
        if dobj.treeModel == self.abstractModel: return
        self.abstractModel = dobj.treeModel
    else:
      return
    self.dataViewWindow = None
    self.setText('')

    
#----------------------------------------------------------------------
  def handleDoubleClick(self):
#----------------------------------------------------------------------
    #print "emit doubleClick"
    self.emit(QtCore.SIGNAL("doubleClicked()"))
    self.dataViewWindow.hide()
    
#----------------------------------------------------------------------
  def getCurrentDataObj(self):
#----------------------------------------------------------------------
    if self.dataobjCombo:
      return self.dataobjCombo.getCurrentDataObj()
    else:
      return self.abstractModel.MolDataObj

#----------------------------------------------------------------------
  def setCurrentDataObj(self,MolData=''):
#----------------------------------------------------------------------
    if self.dataobjCombo:
      self.dataobjCombo.setCurrentDataObj(MolData)

      
#----------------------------------------------------------------------
  def getActionDef(self,name):
#----------------------------------------------------------------------
    if name == 'neighb':
      return dict (
        text = 'Neighbourhood..',
        icon = '',
        tip = 'Select atoms/residues around central atom(s)',
        signal = 'triggered()',
        slot = guiUtils.partial(self.openSelectionWidget,name)
        )
    if name == 'buried':
      return dict (
        text = 'Buried area..',
        icon = '',
        tip = 'Select atoms/residues buried by another set of atoms',
        signal = 'triggered()',
        slot = guiUtils.partial(self.openSelectionWidget,name)
        )
    if name == 'secStr':
       return dict (
        text = 'Secondary structure..',
        icon = '',
        tip = 'Select secondary structure elements',
        signal = 'triggered()',
        slot = guiUtils.partial(self.openSelectionWidget,name)
        )
    if name == 'resType':
       return dict (
        text = 'Residue types..',
        icon = '',
        tip = 'Select residue types',
        signal = 'triggered()',
        slot = guiUtils.partial(self.openSelectionWidget,name)
        )
    if name == 'chains':
       return dict (
        text = 'Chains..',
        icon = '',
        tip = 'Select whole chains',
        signal = 'triggered()',
        slot = guiUtils.partial(self.openSelectionWidget,name)
        )
    if name == 'property':
       return dict (
        text = 'Property..',
        icon = '',
        tip = 'Select by the value of atom property',
        signal = 'triggered()',
        slot = guiUtils.partial(self.openSelectionWidget,name)
        )
     
    if name == 'namedSelection':
       return dict (
        text = 'Save/restore selection..',
        icon = '',
        tip = 'Save or restore a selection',
        signal = 'triggered()',
        slot = guiUtils.partial(self.openSelectionWidget,name)
        )

    return {}
 
#----------------------------------------------------------------------
  def openSelectionWidget(self,name):
#----------------------------------------------------------------------
    import selectionBrowser


    if self.seleWidget:
      if self.seleWidgetMode == name:
        self.seleDialog.show()
        self.seleDialog.raise_()
        return
      else:
        self.selParams.update(self.seleWidget.getParams())
        self.seleWidget.close()
        #self.seleWidget.deleteLater()
        self.seleWidget=None
        self.seleWidgetMode = ''
        
    if self.seleDialog:
      #self.seleDialog.deleteLater()
      self.seleDialog.close()

    self.seleDialog = QtGui.QDialog(self) 
    layout = QtGui.QVBoxLayout()
    layout.setContentsMargins(0,0,0,0)
    layout.setSpacing(0)
    

    if name == 'neighb':
      self.seleDialog.setWindowTitle('Select neighbourhood for '+self.getCurrentDataObj().name_label)
      self.seleWidget = selectionBrowser.neighbGui(self.seleDialog,self.getCurrentDataObj().name_label )
      self.seleWidget.setParams()
      self.connect(self.seleWidget,QtCore.SIGNAL('accepted()'),guiUtils.partial(self.updateFromTabWidget,'neighb'))
    if name == 'buried':
      self.seleDialog.setWindowTitle('Select buried area for '+self.getCurrentDataObj().name_label)
      self.seleWidget = selectionBrowser.buriedGui(self.seleDialog,self.getCurrentDataObj().name_label )
      self.seleWidget.setParams()
      self.connect(self.seleWidget,QtCore.SIGNAL('accepted()'),guiUtils.partial(self.updateFromTabWidget,'buried'))
 
    elif name == 'namedSelection':
      self.seleDialog.setWindowTitle('Save/restore selection for '+self.getCurrentDataObj().name_label)
      self.seleWidget = selectionBrowser.namedSelectionGui(self.seleDialog,self.getCurrentDataObj().name_label,delete=0 )
      self.connect(self.seleWidget,QtCore.SIGNAL('save'),self.saveNamedSelection)
      self.connect(self.seleWidget,QtCore.SIGNAL('restore'),self.restoreNamedSelection)

    elif name == 'secStr':
      self.seleDialog.setWindowTitle('Select secondary structure for '+self.getCurrentDataObj().name_label)
      self.seleWidget = selectionBrowser.secStrGui(self.seleDialog,self.getCurrentDataObj().name_label )
      self.seleWidget.setGeometry(0,0,selectionBrowser.tabWidth,selectionBrowser.tabHeight)
      self.seleDialog.setFixedHeight(selectionBrowser.tabHeight+20)
      self.seleDialog.setFixedWidth(selectionBrowser.tabWidth+20)
      self.connect(self.seleWidget,QtCore.SIGNAL('accepted()'),guiUtils.partial(self.updateFromTabWidget,'secStr'))
    elif name == 'property':
      self.seleDialog.setWindowTitle('Select by property for '+self.getCurrentDataObj().name_label)
      self.seleWidget = selectionBrowser.propertyGui(self.seleDialog,self.getCurrentDataObj().name_label )
      self.connect(self.seleWidget,QtCore.SIGNAL('accepted()'),guiUtils.partial(self.updateFromTabWidget,'property'))
    elif name == 'resType':
      self.seleDialog.setWindowTitle('Select by residue type for '+self.getCurrentDataObj().name_label)
      self.seleWidget = selectionBrowser.resTypeGui(self.seleDialog,self.getCurrentDataObj().name_label )
      self.seleWidget.setGeometry(0,0,selectionBrowser.tabWidth,selectionBrowser.tabHeight)
      self.seleDialog.setFixedHeight(selectionBrowser.tabHeight+20)
      self.seleDialog.setFixedWidth(selectionBrowser.tabWidth+20)
      self.connect(self.seleWidget,QtCore.SIGNAL('accepted()'),guiUtils.partial(self.updateFromTabWidget,'resType'))

    elif name == 'chains':
      self.seleDialog.setWindowTitle('Select chains for '+self.getCurrentDataObj().name_label)
      self.seleWidget = selectionBrowser.chainsGui(self.seleDialog,self.getCurrentDataObj().name_label )
      self.seleWidget.setGeometry(0,0,selectionBrowser.tabWidth,selectionBrowser.tabHeight)
      self.seleDialog.setFixedHeight(selectionBrowser.tabHeight+20)
      self.seleDialog.setFixedWidth(selectionBrowser.tabWidth+20)
      self.connect(self.seleWidget,QtCore.SIGNAL('accepted()'),guiUtils.partial(self.updateFromTabWidget,'chains'))

    
    #print "openSelectionWidget name",name
    if name != 'namedSelection' and self.seleWidget: self.seleWidget.setParams(self.selParams)
    layout.addWidget(self.seleWidget)
    self.seleDialog.setLayout(layout)
    self.seleWidget.show()
    self.seleDialog.show()
    
    self.seleWidgetMode = name
    
#---------------------------------------------------------------------------
  def restoreNamedSelection(self):
#---------------------------------------------------------------------------
    
    set,name = self.seleWidget.getSelection()
    if name:
      import model_selection
      SelHandle = model_selection.SelHandle(self.getCurrentDataObj().name)
      SelHandle.restoreSelection(name)
      #print "restoreNamedSelection command",SelHandle.getCommand()
      self.setText(SelHandle.getCommand())
      del SelHandle
      self.updateTreeView()
      self.emit(QtCore.SIGNAL("changed"))
      
#---------------------------------------------------------------------------
  def saveNamedSelection(self):
#---------------------------------------------------------------------------
    pars = self.seleWidget.getSaveParams()
    name = pars.get('name','')
    if name:
      save_mode = pars.get('save_mode','this')
      import model_selection
      SelHandle = model_selection.SelHandle(self.getCurrentDataObj().name)
      #print "saveNamedSelection command",self.text()
      SelHandle.setCommand(self.text())
      SelHandle.saveSelection (name=name,save_mode=save_mode,overwrite=1)
      del SelHandle
      DISPLAYTABLE().getDataObj(self.getCurrentDataObj().name).handleSavedSelection(save_mode)
      #self.seleWidget.populateList()

  
#----------------------------------------------------------------------
  def updateFromTabWidget(self,mode=''):
#----------------------------------------------------------------------
    # Update browser based on input to one of the tab widgets

    # First get the widget to return its selection in the
    # form of a SelHandle 
    widgetSelHandle = self.seleWidget.getSelHandle()      
    if not widgetSelHandle: return
    
    #print "updateFromTabWidget command",widgetSelHandle.getCommand()
    self.setText(widgetSelHandle.getCommand())
    del widgetSelHandle
    self.updateTreeView()
    self.emit(QtCore.SIGNAL("changed"))
      
    if self.seleDialog:
      self.seleDialog.close()

    return 0

#----------------------------------------------------------------------
  def close(self):
#----------------------------------------------------------------------
       
    if self.seleDialog:
      self.seleDialog.close()
      #self.seleDilog.deleteLater()
      self.seleDialog = None
      
    QtGui.QFrame.close(self)
 
    
#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------
#class mgDataObjCombo(QtGui.QComboBox,dataobj.Dependency):
class mgDataObjCombo(QtGui.QComboBox):
#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------

  def __init__(self,object_type='',default='',null='',parent=None):
    # default should be a name_label for the model
    super(mgDataObjCombo,self).__init__(parent)
    # Maintain list of data objects that are excluded
    self.exclude_list = []
    self.null = null
    #dataobj.Dependency.__init__(self)
    self.object_type = object_type
    self.setEditable(0)
    self.populateMenu(default=default)

    #import model
    #if object_type == 'MolData':
    #  self.add_dependency(model.MolData,['create'],[self.populateMenu])

    self.connect(global_definitions.DISPLAYTABLE(),QtCore.SIGNAL('addDataObj'),self.resetMenu)
    self.connect(global_definitions.DISPLAYTABLE(),QtCore.SIGNAL('deleteDataObj'),self.resetMenu)
    self.connect(global_definitions.DISPLAYTABLE(),QtCore.SIGNAL('loadAllDataObj'),self.resetMenu)
    self.connect(global_definitions.DISPLAYTABLE(),QtCore.SIGNAL('update'),self.resetMenu)
    self.connect(global_definitions.DISPLAYTABLE(),QtCore.SIGNAL('update_labels'),self.resetMenu)

#----------------------------------------------------------------------
  def disconnectGlobalSignals(self):
#----------------------------------------------------------------------
    self.disconnect(global_definitions.DISPLAYTABLE(),QtCore.SIGNAL('addDataObj'),self.resetMenu)
    self.disconnect(global_definitions.DISPLAYTABLE(),QtCore.SIGNAL('deleteDataObj'),self.resetMenu)
    self.disconnect(global_definitions.DISPLAYTABLE(),QtCore.SIGNAL('loadAllDataObj'),self.resetMenu)
    self.disconnect(global_definitions.DISPLAYTABLE(),QtCore.SIGNAL('update'),self.resetMenu)
    self.disconnect(global_definitions.DISPLAYTABLE(),QtCore.SIGNAL('update_labels'),self.resetMenu)

#----------------------------------------------------------------------
  def resetMenu(self):
#----------------------------------------------------------------------
    #print 'mgDataObjCombo.resetMenu'
    self.blockSignals(1)
    current = self.getCurrentDataObj()
    if current:
      default = current.name
    else:
      default = ''
      self.emit(QtCore.SIGNAL('currentDataObjDeleted()'))
    self.populateMenu(default = default)
    self.blockSignals(0)
    
#----------------------------------------------------------------------
  def populateMenu(self,default='',attributes=[],**keywords):
#----------------------------------------------------------------------
    #print "mgDataObjCombo populateMenu",default
    
    self.clear()

    if self.null: self.addItem(self.null,QtCore.QVariant(self.null))
    
    obj_list = global_definitions.get_dataobj(object_type = self.object_type)
    #print "mgDataObjCombo.populateMenu obj_list",obj_list
    if obj_list:
      for obj in obj_list:
        if not self.exclude_list.count(obj.name):
          self.addItem(obj.name_label,QtCore.QVariant(obj.name))
    elif self.null:
      # make sure ther is a 'blank' option in case user deletes all objects
      self.addItem(self.null,QtCore.QVariant(self.null))

      # Set up dependencies
      #self.add_dependency(obj,['delete','rename'],[self.populateMenu])

    default_index = 0 
    if default:
      default_obj = global_definitions.get_dataobj(name=default)

      if default_obj and len(default_obj)>0  and obj_list.count(default_obj[0]):
        default_index = obj_list.index(default_obj[0])
      if self.null: default_index = default_index + 1
      self.setCurrentIndex(default_index)
    elif len(obj_list) == 1 and self.null:
      default_index = 1
      self.setCurrentIndex(default_index)
      
    #print "currentIndex",self.currentIndex()


   ## **********delete the dependencies!!!

#----------------------------------------------------------------------
  def setCurrentDataObj(self,name):
#----------------------------------------------------------------------
    for n in range(0,self.count()):
      if str(self.itemData(n).toString())==name:
        self.setCurrentIndex(n)
        break
    
#----------------------------------------------------------------------
  def getCurrentDataObj(self):
#----------------------------------------------------------------------
    
    if self.currentIndex()<0:
      return None
    else:
      # * beware the text is the DataObj.name_label which is not necessarilly
      # unique - check the userData for the DataObj.name
      name = str(self.itemData(self.currentIndex()).toString())
      obj_list = global_definitions.get_dataobj(name = name)
      #print 'getCurrentDataObj',name,obj_list
      if obj_list:
        return obj_list[0]
      else:
        return None

  def setExcludeDataObjList(self,excluded=[]):
    self.exclude_list = excluded
    self.resetMenu()

  def getExcludeDataObjList(self):
    return self.exclude_list
    
 
#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------     
class MGOpenFileDialog(QtGui.QDialog):
#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------

  def  __init__(self,parent=None,title='Open file',filters=[],defaultSuffix='',defaultFileName=''):
    QtGui.QDialog.__init__(self,parent)
    self.setWindowTitle(self.tr(title))
    layout = QtGui.QGridLayout()

    filed = MGFileDialog(self)
    self.fileDialog = filed.getFileDialog()

    self.fileDialog.setNameFilters(filters)

    selectedFilter =  str(self.fileDialog.selectedNameFilter().toAscii())
    needDefaultSuffix = False
    if selectedFilter.find('(')>-1 and selectedFilter.find(')')>selectedFilter.find('('):
       splitFilters = selectedFilter[selectedFilter.find('(')+1:selectedFilter.find(')')].strip().split(' ')
       if (len(splitFilters) == 1 and splitFilters[0]=='*.*') or (len(splitFilters) > 0 and splitFilters[0]=='*.*') or len(splitFilters) > 1:
          needDefaultSuffix = True

    if defaultSuffix and ((len(self.fileDialog.filters())==1 and str((self.fileDialog.filters()[0]).toAscii()).find(self.tr('All Files'))>-1)) or (len(self.fileDialog.filters()))==0:
      self.fileDialog.setDefaultSuffix(defaultSuffix)
    if defaultFileName: self.fileDialog.selectFile(defaultFileName)

    self.connect(self.fileDialog, QtCore.SIGNAL('rejected()'),self.close)

    children = self.fileDialog.findChildren(QtGui.QPushButton,"")
    for child in children:
      if child.text() == self.tr("&Open"):
        self.connect(child, QtCore.SIGNAL('pressed()'),self.openFile)
    layout.addWidget(filed)
    self.setLayout(layout)


  def openFile(self):
    files = self.fileDialog.selectedFiles()
    if files:
      self.emit(QtCore.SIGNAL('open'),str(files[0].toUtf8()))
    self.close()

  def getFilename(self):
    files = self.fileDialog.selectedFiles()
    if files:
      return str(files[0].toUtf8())
    else:
      return ''


#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------
class MGSaveFileDialog(QtGui.QDialog):
#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------

  def exec_(self):
      QtGui.QDialog.exec_(self)

  def __init__(self,parent=None,title='Save to file',filters=[],defaultSuffix='',defaultFileName=''):
  
    QtGui.QDialog.__init__(self,parent)
    self.setWindowTitle(self.tr(title))
    layout = QtGui.QGridLayout()

    filed = MGFileDialog(self)
    self.fileDialog = filed.getFileDialog()
    self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
    self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
    self.fileDialog.setConfirmOverwrite(False)

    self.fileDialog.setNameFilters(filters)

    selectedFilter =  str(self.fileDialog.selectedNameFilter().toAscii())
    needDefaultSuffix = False
    if selectedFilter.find('(')>-1 and selectedFilter.find(')')>selectedFilter.find('('):
       splitFilters = selectedFilter[selectedFilter.find('(')+1:selectedFilter.find(')')].strip().split(' ')
       if (len(splitFilters) == 1 and splitFilters[0]=='*.*') or (len(splitFilters) > 0 and splitFilters[0]=='*.*') or len(splitFilters) > 1:
          needDefaultSuffix = True

    if defaultSuffix and (((len(self.fileDialog.filters())==1 and str((self.fileDialog.filters()[0]).toAscii()).find(self.tr('All Files'))>-1)) or (len(self.fileDialog.filters()))==0 or needDefaultSuffix): 
      self.fileDialog.setDefaultSuffix(defaultSuffix)
    if defaultFileName: self.fileDialog.selectFile(defaultFileName)

    self.connect(self.fileDialog, QtCore.SIGNAL('rejected()'),self.close)

    children = self.fileDialog.findChildren(QtGui.QPushButton,"")
    for child in children:
      if child.text() == self.tr("&Save"):
        self.connect(child, QtCore.SIGNAL('pressed()'),self.save)
    layout.addWidget(filed)
    self.setLayout(layout)
    self.connect(self.fileDialog, QtCore.SIGNAL('filesSelected(const QStringList&)'),self.saveSelected)
    self.defaultSuffix = defaultSuffix

  def setDefaultFileName(self,defaultFileName):
     self.fileDialog.selectFile(defaultFileName)

  def saveSelected(self,files=None):
    if files:
      self.emit(QtCore.SIGNAL('save'),str(files[0].toUtf8()))
    self.close()

  def save(self):
    files = self.fileDialog.selectedFiles()
    theFile = str(files[0].toUtf8())
    if files:
      if files[0].count('.') == 0:
        selectedFilter =  str(self.fileDialog.selectedNameFilter().toAscii())
        if selectedFilter.find('(')>-1 and selectedFilter.find(')')>selectedFilter.find('('):
           splitFilters = selectedFilter[selectedFilter.find('(')+1:selectedFilter.find(')')].strip().split(' ')
           if (len(splitFilters) == 1 and splitFilters[0]=='*.*') or (len(splitFilters) > 0 and splitFilters[0]=='*.*') or len(splitFilters) > 1:
             useSuffix = self.defaultSuffix
             useSuffix = useSuffix[useSuffix.find('.'):]
             theFile = theFile + useSuffix
           elif len(splitFilters) == 1:
             useSuffix = splitFilters[0]
             useSuffix = useSuffix[useSuffix.find('.'):]
             theFile = theFile + useSuffix
      self.emit(QtCore.SIGNAL('save'),theFile)
    self.close()

  def getFilename(self):
    files = self.fileDialog.selectedFiles()
    if files:
      return str(files[0].toUtf8())
    else:
      return ''
    
#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------
class MGFileDialog(QtGui.QWidget):
#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------

  last_directory = ''

  def event(self,event=None):
	if event.type()==QtCore.QEvent.WhatsThisClicked:
		import global_definitions
		global_definitions. HELPBROWSER().loadMgHelp(file=str(event.href()))
	return QtGui.QWidget.event(self,event)

  def __init__(self,parent=None,editAlias=0):
    
    QtGui.QWidget.__init__(self,parent)
    self.setWhatsThis(self.tr("A file dialog for opening data files supported by ccp4mg with a wizard to select drawing styles of molecular structure <a href=\"model.html\">more help</a>"))

    layout = QtGui.QVBoxLayout()

    ccp4_dirs,ccp4_aliases =  global_definitions.CCP4DATABASE().getProjectsList()
    if len(ccp4_dirs)>0 or len(ccp4_aliases)>0:
      
      layout0 = QtGui.QHBoxLayout()
      self.ccp4i_combo = QtGui.QComboBox()
      self.load_ccp4i_combo()
      ccp4i_label1 = QtGui.QLabel()
      ccp4i_label1.setText("Look in ccp4 project or aliased directory:")
      if editAlias:
        ccp4i_button = QtGui.QPushButton('Edit aliases',self)
      ccp4i_label2 = QtGui.QLabel(" or ...")
      
      
      layout0.addWidget(ccp4i_label1)
      layout0.addWidget(self.ccp4i_combo)
      if editAlias: layout0.addWidget(ccp4i_button)
      layout0.addWidget(ccp4i_label2)
      layout0.addStretch()
      layout.addLayout(layout0)

      self.connect(self.ccp4i_combo,QtCore.SIGNAL("activated(const QString&)"),self.ccp4iDirectoryChanged)
      if editAlias: self.connect(ccp4i_button,QtCore.SIGNAL("clicked()"),self.editAlias)
      self.connect(global_definitions.CCP4DATABASE(),QtCore.SIGNAL('directoriesChanged'),self.load_ccp4i_combo)

    self.filedialog = MGFileDialog0(parent)
    self.filedialog.setObjectName('fileDialog')
    layout.addWidget( self.filedialog)
    if len(ccp4_dirs)>0:
      self.connect( self.filedialog, QtCore.SIGNAL('directoryEntered(QString)'),self.update_ccp4i_combo)

    self.setLayout(layout)

    # Set keyboard focus to text input widget
    self.filedialog.findChildren(QtGui.QLineEdit)[0].setFocus(QtCore.Qt.ActiveWindowFocusReason)

    if MGFileDialog.last_directory:
      #print "setting last_directory",MGFileDialog.last_directory
      self.filedialog.setDirectory(MGFileDialog.last_directory)
    if len(ccp4_dirs)>0 or len(ccp4_aliases)>0:
      self.update_ccp4i_combo(self.filedialog.directory().path())

  def load_ccp4i_combo(self):
    self.ccp4i_combo.clear()
    self.ccp4i_combo.addItem('  -Projects-  ')
    ccp4_dirs,ccp4_aliases =  global_definitions.CCP4DATABASE().getProjectsList()
    print ccp4_dirs,ccp4_aliases
    for project in ccp4_dirs:
      #self.ccp4i_combo.addItem(project)
      self.ccp4i_combo.addItem(unicode(project,'utf-8'))
    self.ccp4i_combo.addItem('  -Aliases- ')
    for project in ccp4_aliases: self.ccp4i_combo.addItem(project)
    
  def ccp4iDirectoryChanged(self,alias):
      path = global_definitions.CCP4DATABASE().get_project_dir(str(alias.toUtf8()))
      if path:
         self.filedialog.setDirectory(unicode(path,'utf-8'))
         MGFileDialog.last_directory = path
         
  def getFileDialog(self):
    return self.filedialog

  
  def setSidebar(self):
      url_list = self.filedialog.sidebarUrls ()
      #print "in setSidebar",url_list
      for item in url_list:
        #print "side bar",item.path(),item.port(),'*',item.host()
        pass
      #self.filedialog.setSidebarUrls(side)
      
      #for path in ['/work/stuart']:       
      #  url = QtCore.QUrl("file://"+path)
      #  url_list.append(url)
        
      self.filedialog.setSidebarUrls(url_list )

  def update_ccp4i_combo(self,directory):
    MGFileDialog.last_directory = directory
    proj = global_definitions.CCP4DATABASE().aliasForDir(str(directory.toUtf8()))
    if proj:
      indx = self.ccp4i_combo.findText(unicode(proj,'utf-8'))
      if indx>=0:
        self.ccp4i_combo.setCurrentIndex(indx)
        return
    self.ccp4i_combo.setCurrentIndex(0)

  def editAlias(self):
    import ccp4iGui
    ccp4iGui.ccp4iAliasGui(self)


  def setLabelText(self,mode,text):
    self.filedialog.setLabelText(mode,text)

  def selectFile(self,file):
    #print 'MGFileDialog.selectFile',file
    self.filedialog.selectFile(file)

class IconProvider(QtGui.QFileIconProvider):
	def __init__(self):
		QtGui.QFileIconProvider.__init__(self)
		self._filters = {}
		self._icon_cache = {}

	def setIconForFilter(self,icon,filter):
		self._filters[icon] = filter

	def setIconsForFilters(self,filters):
		self._filters = filters

	def icon(self,info):
		if info and hasattr(info,'filePath'):
			for k in self._filters:
				for f in self._filters[k]:
					if repr(info.filePath()).endswith(f):
						if not self._icon_cache.has_key(k):
							self._icon_cache[k] = QtGui.QIcon(k)
						return self._icon_cache[k]
		return QtGui.QFileIconProvider.icon(self,info)


class ProxyModel(QtGui.QSortFilterProxyModel):
	def __init__(self,parent=None):
		QtGui.QSortFilterProxyModel.__init__(self,parent)

	def filterAcceptsRow(self, sourceRow, sourceParent):
		if sourceParent.row() == -1 or sourceParent.column():
			return True
		index0 = self.sourceModel().index(sourceRow, 0, sourceParent);
		if self.sourceModel().isDir(index0):
			return True
		for f in self.sourceModel().nameFilters():
			if str(self.sourceModel().fileName(index0).toUpper()).endswith(str(f).lstrip('*').upper()):
				return True
		return False


#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------
class MGFileDialog0(QtGui.QFileDialog):
#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------

  # Reimplement to stop widget closing immediately a file is selected
  
  def addFilters(self,filters_in):
    filters = self.filters()
    for filter in filters_in:
      filters.append(filter)
    self.setNameFilters(filters)
    
  def addFilter(self,filter):
    filters = self.filters()
    filters.append(filter)
    self.setNameFilters(filters)

  def setNameFilters(self,filters,all=1):
    import types
    self.suffix_lookup = {}
    all_filters = "All files ("
    new_filters = []
    for filt in filters:
      #print "setNameFilters filt",filt
      if isinstance(filt,types.ListType):
        self.suffix_lookup[filt[0]] = filt[1]
        filter = str(filt[0])
      else:
        filter = str(filt)
      if len(filter)>11 and filter[:11] == 'All files (' and (filter).rfind(')') > 0:
        pass
      else:
        new_filters.append(filter)
        left_par = filter.rfind('(')
        right_par = filter.rfind(')')
        if left_par > -1 and right_par > 0 and right_par > left_par:
          if (filter[left_par+1:right_par]).find('*')>-1:
             all_filters = all_filters + " " + filter[left_par+1:right_par]
          else:
            all_filters = all_filters + " " + filter
        else:
          all_filters = all_filters + " " + filter
    all_filters = all_filters + " )"
    total_filters = new_filters
    # Put all_filters at end of list when saving file and
    # at beginning when reading file
    if all:
      if self.acceptMode() == QtGui.QFileDialog.AcceptSave:
        total_filters.append(all_filters)
      else:
        total_filters.insert(0,all_filters)
        
    QtGui.QFileDialog.setNameFilters(self,total_filters)
    if self.suffix_lookup:
      self.setSuffix(filters[0][0])
      self.connect(self,QtCore.SIGNAL('filterSelected(QString)'),self.setSuffix)

  def checkMounts(self,**args):
		try:
			import subprocess
			import signal
			signal.siginterrupt(signal.SIGCHLD,False)
		except:
			pass

		mounts = []
	
		try:
			syscmd = "/sbin/mount"
			p = subprocess.Popen([syscmd],stdout=subprocess.PIPE)
			output = p.communicate()[0].split('\n')
		except:
			return
		for o in  output:
			if o and o.find('/dev/disk')==0:
				t = o[o.find(' ')+1:]
				t2 = t[t.find(' ')+1:]
				mounts.append(t2[:t2.find('(')-1])
		if mounts != self._old_mounts:
			#print "CHANGE!!", mounts
			urls = self.sidebarUrls()
			newUrls = []
			_urls = []
			for url in urls:
				if url.host() == "" and url.port() == -1:
					path = str(url.toLocalFile().toAscii())
					if path == '': path = '/'
					if path.find('/Volumes') == 0:
						if path in mounts:
							newUrls.append(url)
							_urls.append(path)
					else:
						newUrls.append(url)
						_urls.append(path)
			for m in mounts:
				if m != '/':
					if not m in _urls:
						newUrls.append(QtCore.QUrl.fromLocalFile(m))
						_urls.append(path)
						
			self.setSidebarUrls(newUrls)
			self._old_mounts = mounts

  def checkMountsNew(self,**args):
    fd = self
    sb = fd.sidebarUrls()
    for url in sb[:]:
      path = str(url.toLocalFile().toAscii())
      if path.find('/Volumes') == 0:
         if not os.path.exists(path):
            sb.remove(url)

    # Clear everything
    fd.setSidebarUrls([])

    for loc in [QtGui.QDesktopServices.HomeLocation,QtGui.QDesktopServices.DesktopLocation,QtGui.QDesktopServices.DocumentsLocation]:
      path = QtGui.QDesktopServices.storageLocation(loc)
      url = QtCore.QUrl.fromLocalFile(path)
      if not url in sb:
        sb.append(url)

    # The following will hopefully be replaced by QStorageInfo in Qt5.

    if sys.platform == "darwin":
      for v in os.listdir('/Volumes'):
        url = QtCore.QUrl.fromLocalFile(os.path.join("/Volumes/",v))
        if not url in sb:
          sb.append(url)

    """
    # This would probably be good on windows
    roots = QtCore.QDir.drives()
    for r in roots:
      print r.filePath()
    """

    # Add "My Computer"
    computer = QtCore.QUrl.fromLocalFile('')
    if not computer in sb:
      sb.insert(0,computer)

    fd.setSidebarUrls(sb)



  def __init__(self,parent=None):
    QtGui.QFileDialog.__init__(self,parent)
    self.setSizeGripEnabled(0)
    """
    if sys.platform == "darwin": # We will need to implement the low level stuff for other oses.
      self.fstimer = QtCore.QTimer();
      self.connect(self.fstimer, QtCore.SIGNAL("timeout()"), self.checkMounts);
      self.fstimer.start(2000);
      self._old_mounts = []
      self.checkMounts()
    """
    self.checkMountsNew()
    #self.show()
    """
    pm = ProxyModel()
    self.setProxyModel(pm)
    iconProvider = IconProvider()
    iconProvider.setIconsForFilters({
       os.path.join(os.environ["CCP4MG"],'qticons','dispobj','MolData.png'):['.pdb','.pdb.gz','.ent','.ent.gz','.brk','.brk.gz'],
       os.path.join(os.environ["CCP4MG"],'qticons','dispobj','MapData.png'):['.map','mtz'],
       os.path.join(os.environ["CCP4MG"],'qticons','dispobj','VectorsData.svg'):['.vector']
       })
    self.setIconProvider(iconProvider)
    """

  def close(self):
    pass

  def done(self,mode):
    pass

  def reject(self):
    #print "MGFileDialog reject",self.parent(),self.window()
    if  self.window():
      self.window().close()
    else:
      self.close()

  def setSuffix(self,filter):
    filter = str(filter)
    #print "setSuffix",filter
    if self.suffix_lookup.has_key(filter):
      self.setDefaultSuffix(self.suffix_lookup[filter])

  
  def selectFile(self,file):
    #print 'MGFileDialog0.selectFile',file
    #QFileDialog.selectFile does not work for selecting non-existing files
    # as you would want in Save mode - so try this kludge
    if self.fileMode() == QtGui.QFileDialog.ExistingFile:
      widget = self.findChild(QtGui.QLineEdit,'fileNameEdit')
      #print 'MGFileDialog0.selectFile widget',widget
      if widget: widget.setText(file)
    else:
      QtGui.QFileDialog.selectFile(self,file)


#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
class mgGroupSelector(QtGui.QWidget):
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------

  def __init__(self,parent=None,groups=[],items=[],reMatch=0,closeButton=0,
               height=200,width=400):
    super(mgGroupSelector,self).__init__(parent)
    self.groupOrder = []
    self.groupSelection = {}
    self.itemOrder = []
    self.itemText = {}    
    self.reMatch = reMatch
    self.closeButton = closeButton
    self.widgetHeight = height
    self.widgetWidth = width
    

    self.addItems(items)
    self.addGroups(groups)
    self.draw()

    
#-----------------------------------------------------------------------
  def addItems(self,items=[]):
#-----------------------------------------------------------------------
    import types
    for it in items:
      if len(it) >= 2 and isinstance(it[0],types.StringType) and  isinstance(it[1],types.StringType):
        self.itemOrder.append(it[0])
        self.itemText[it[0]] = it[1]
    #print "addItems",self.itemOrder
    
      

#-----------------------------------------------------------------------
  def addGroups(self,groups=[]):
#-----------------------------------------------------------------------
    import types
    for gp in groups:
      if len(gp) >= 2  and isinstance(gp[0],types.StringType) and  isinstance(gp[1],types.ListType):
        self.groupOrder.append(gp[0])
        self.groupSelection[gp[0]] = gp[1]
      elif len(gp) == 1:
        self.groupOrder.append(gp[0])
        

    
#-----------------------------------------------------------------------
  def select(self,group='',state=1):
#-----------------------------------------------------------------------
    #print "select group",group,self.itemOrder
    if group:
      if group == 'all':
        for item in self.itemOrder:
          but = self.itemList.findChild(QtGui.QCheckBox,item)
          if but: but.setChecked(1)
      elif group == 'clear':
        for item in self.itemOrder:
          but = self.itemList.findChild(QtGui.QCheckBox,item)
          if but: but.setChecked(0)
      elif self.groupSelection.has_key(group):
        if self.reMatch:
          for item in self.groupSelection[group]:
            #print "searching for",item
            but_list = self.itemList.findChildren(QtGui.QCheckBox,item)
            #print "but_list",but_list
            for but in but_list:  but.setChecked(state)
        else:
          for item in self.groupSelection[group]:
            but = self.itemList.findChild(QtGui.QCheckBox,item)
            if but: but.setChecked(state)
          
    
#-----------------------------------------------------------------------
  def draw(self):
#-----------------------------------------------------------------------
    # Scrolled list of items with checkboxes on left side
    self.listArea =  QtGui.QScrollArea(self)
    margin = 0
    layout0 = QtGui.QHBoxLayout()

    self.listArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
    #self.listArea.setFixedHeight(self.widgetHeight)
    #self.listArea.setMinimumWidth(self.widgetWidth-220)
    
    self.itemList = QtGui.QWidget()
    layout = QtGui.QVBoxLayout()
    layout.setContentsMargins(margin,margin,margin,margin)
    layout.setSpacing(margin)
    for item in self.itemOrder:
      cb = QtGui.QCheckBox(self.itemText[item].strip(),self)
      cb.setObjectName(item)
      layout.addWidget(cb)

    self.itemList.setLayout(layout)
    self.listArea.setWidget(self.itemList)
    layout0.addWidget(self.listArea)

    if self.groupOrder:
      self.custom_box =  QtGui.QWidget()
      layout = QtGui.QVBoxLayout()
      layout.setContentsMargins(margin,margin,margin,margin)
      layout.setSpacing(margin)   
      layout.addStretch(5)

      for gp in self.groupOrder:
        w =  QtGui.QPushButton(gp,self)
        w.setObjectName(gp)
        layout.addWidget(w)
        self.connect(w,QtCore.SIGNAL('released()'),guiUtils.partial(self.select,gp))

      self.custom_box.setLayout(layout)
      layout0.addWidget(self.custom_box)

    rbox =  QtGui.QWidget()
    layout = QtGui.QVBoxLayout()
    layout.setContentsMargins(margin,margin,margin,margin)
    layout.setSpacing(margin)

    clear = QtGui.QPushButton('Clear',self)
    clear.setContentsMargins(margin,margin,margin,margin)
    layout.addWidget(clear)
    self.connect(clear,QtCore.SIGNAL('released()'),guiUtils.partial(self.select,'clear'))

    all = QtGui.QPushButton('Select all',self)
    all.setContentsMargins(margin,margin,margin,margin)
    layout.addWidget(all)
    self.connect(all,QtCore.SIGNAL('released()'),guiUtils.partial(self.select,'all'))

    layout.addStretch(5)
    apply = QtGui.QPushButton('Apply',self)
    layout.addWidget(apply)
    self.connect(apply,QtCore.SIGNAL('released()'),self.apply)

    if self.closeButton:
      close = QtGui.QPushButton('Close',self)
      layout.addWidget(close)
      self.connect(apply,QtCore.SIGNAL('released()'),self.close)

    rbox.setLayout(layout)
    layout0.addSpacing(5)
    layout0.addWidget(rbox)
    self.setLayout(layout0)

    
#-----------------------------------------------------------------------
  def apply(self):
#-----------------------------------------------------------------------
    self.emit(QtCore.SIGNAL("accepted()"))

#-----------------------------------------------------------------------
  def getSelection(self):
#-----------------------------------------------------------------------
    sele_list = []
    for item in self.itemOrder:
      but = self.itemList.findChild(QtGui.QCheckBox,item)
      if but and but.isChecked(): sele_list.append(item)
      
    return sele_list

#-----------------------------------------------------------------------
  def setSelection(self,selectedList=[]):
#-----------------------------------------------------------------------
    # Input a list of selected items
    # Also set items not in that list to unchecked
    for item in self.itemOrder:
      but = self.itemList.findChild(QtGui.QCheckBox,item)
      if but: but.setChecked(selectedList.count(item))
        
      

#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------
class extendingFrame(QtGui.QWidget):
#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------

  LINE_HEIGHT = 32
    
#-------------------------------------------------------------------
  def __init__(self,parent=None,widget='',nLines=1,ifApply=1,
               ifEditable=1,ifClose=0,ifClear=0,ifSave=0,height=200,width=400):
#-------------------------------------------------------------------
    QtGui.QWidget.__init__(self,parent)

    margin = 0
    self.widgetClass = widget
    self.lineHeight = extendingFrame.LINE_HEIGHT
    self.ifClear = ifClear
    self.ifEditable = ifEditable
    self.ifAddLine = ifEditable
    self.ifSave = ifSave
    self.widgetHeight = height
    if width:
      self.widgetWidth = width
    else:
      tmp_widget = self.widgetClass(self.parent())
      tmp_widget.setAttribute(QtCore.Qt.WA_DeleteOnClose)
      tmp_widget.show()
      self.widgetWidth = tmp_widget.width() + extendingFrame.LINE_HEIGHT + 5
      #print 'extendingFrame widgetWidth',self.widgetWidth
      tmp_widget.close()

    # Buttons
    buttonLayout =  QtGui.QHBoxLayout()
    buttonLayout.addStretch()
    if ifApply:
      apply_button =  QtGui.QPushButton('Apply')
      apply_button.setObjectName('applyButton')
      apply_button.setFocusPolicy(QtCore.Qt.ClickFocus)
      apply_button.setAutoDefault(0)
      buttonLayout.addWidget(apply_button)
      self.connect(apply_button,QtCore.SIGNAL('released()'),guiUtils.partial(self.emit,QtCore.SIGNAL('accepted()')))
    if ifSave:
      save_button =  QtGui.QPushButton('Save')
      save_button.setObjectName('saveButton')
      save_button.setAutoDefault(0)
      buttonLayout.addWidget(save_button)
      self.connect(save_button,QtCore.SIGNAL('released()'),guiUtils.partial(self.emit,QtCore.SIGNAL('save')))
    if self.ifClear:
      clear_button =  QtGui.QPushButton('Clear')
      clear_button.setObjectName('clearButton')
      clear_button.setAutoDefault(0)
      clear_button.setFocusPolicy(QtCore.Qt.ClickFocus)
      buttonLayout.addWidget(clear_button)
    if self.ifAddLine:
      add_button =  QtGui.QPushButton('Add line')
      add_button.setObjectName('addButton')
      add_button.setAutoDefault(0)
      add_button.setFocusPolicy(QtCore.Qt.ClickFocus)
      buttonLayout.addWidget(add_button)
    if ifClose:
      close_button =  QtGui.QPushButton('Close')
      close_button.setObjectName('closeButton')
      close_button.setAutoDefault(0)
      buttonLayout.addWidget(close_button)
      self.connect(close_button,QtCore.SIGNAL('released()'),guiUtils.partial(self.emit,QtCore.SIGNAL('close()')))


    # Body in which sub-widgets will be drawn
    self.scroll_frame = QtGui.QFrame(self)
    #if self.widgetHeight>0: self.scroll_frame.setFixedHeight(self.widgetHeight-40)

    scroll_layout = QtGui.QGridLayout()
    scroll_layout.setSpacing(0)
    scroll_layout.setContentsMargins(0,0,0,0)  
    scroll = QtGui.QScrollArea(self)
    scroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
    
    #scroll.setFixedWidth(self.widgetWidth+scroll.verticalScrollBar().height())
    
    #if self.widgetHeight>0: scroll.setFixedHeight(self.widgetHeight-40-2*margin)
    scroll_layout.addWidget(scroll,0,0,1,1)
    self.scroll_frame.setLayout(scroll_layout)
    
    self.body =  QtGui.QWidget(self)
    self.body.setFixedWidth(self.widgetWidth-10)
    body_layout = QtGui.QVBoxLayout()
    body_layout.setSpacing(0)
    body_layout.setContentsMargins(0,0,0,0)
    self.body.setLayout(body_layout)   
    scroll.setWidget(self.body)
    
    v_layout = QtGui.QVBoxLayout()
    v_layout.setSpacing(margin)
    v_layout.setContentsMargins(margin,margin,margin,margin)
    v_layout.addWidget(self.scroll_frame)
    v_layout.addLayout(buttonLayout)
    self.setLayout(v_layout)

    if self.ifAddLine:
      self.connect(add_button,QtCore.SIGNAL('clicked()'),self.addLine)
    if self.ifClear:
      self.connect(clear_button,QtCore.SIGNAL('released()'),self.clear)


    self.setNofLines(nLines)


#-------------------------------------------------------------------
  def clear(self):
#-------------------------------------------------------------------
    self.setNofLines(0)
    self.emit(QtCore.SIGNAL('cleared'))

    
#-------------------------------------------------------------------
  def setNofLines(self,nLines):
#-------------------------------------------------------------------
    # Add or delete lines to get to NLines
    curNLines = self.body.layout().count()
    #print 'exFrame.setNofLines',curNLines,nLines
    if nLines == curNLines:
      return
    elif nLines > curNLines:
      for ii in range(curNLines,nLines):
        self.addLine()

    elif nLines < curNLines and nLines>=0:
      for ii in range(curNLines,nLines,-1):
        #print 'exFrame.setNofLines deleteLine',ii-1
        self.deleteLine(line=ii-1)


#-------------------------------------------------------------------
  def getNofLines(self):
#-------------------------------------------------------------------
    return self.body.layout().count()

#-------------------------------------------------------------------
  def getLine(self,index):
#-------------------------------------------------------------------
    item = self.body.layout().itemAt(index)
    if not item:
      return None
    else:
      return item.widget().child
    
#-------------------------------------------------------------------
  def setWidget(self,widget):
#-------------------------------------------------------------------
    self.widgetClass = widget


#-------------------------------------------------------------------
  def addLine(self,index=-1):
#-------------------------------------------------------------------
    widget = self.widgetClass(self.parent())
    line = extendingFrameLine(parent=self,child=widget,ifEditable=self.ifEditable)
    line.show()
    self.body.layout().insertWidget(index,line)
    self.setBodyHeight()
    if index<0: index = self.body.layout().count()-1
    self.emit(QtCore.SIGNAL('lineAdded(int)'),index)

#-------------------------------------------------------------------
  def setBodyHeight(self):
#-------------------------------------------------------------------
    nLines = self.body.layout().count()
    #print "setBodyHeight",nLines
    if self.lineHeight<0:
      widget = self.widgetClass()
      self.lineHeight = self.widgetClass().height()+6
      #print "lineHeight", self.lineHeight
    self.body.setMinimumHeight( self.lineHeight * nLines)
    self.body.setMaximumHeight( self.lineHeight * nLines)
    self.body.updateGeometry()


#-------------------------------------------------------------------
  def deleteLine(self,widget=None,line=-1):
#-------------------------------------------------------------------
    #print "deleteLine",line,widget
    if line>=0:
      item = self.body.layout().itemAt(line)
      #print 'exFrame deleteLine item',item,item.widget()
      if item:
        # Beware have seen crashes with the two following
        # lines in reversed order (close after removeWidget)
        item.widget().close()
        self.body.layout().removeWidget(item.widget())
        self.setBodyHeight()
        self.emit(QtCore.SIGNAL('lineDeleted'))
    elif widget:
      ii = self.body.layout().indexOf(widget)
      if ii>=0:
        widget.close()
        self.body.layout().removeWidget(widget)
        self.setBodyHeight()
        self.emit(QtCore.SIGNAL('lineDeleted'))


#-------------------------------------------------------------------
  def insertLine(self,args):
#-------------------------------------------------------------------
    widget = args[0]
    mode = args[1]
    #print "insertLine",widget,mode
    ii = self.body.layout().indexOf(widget)
    if ii>=0:
      if mode == 'below': ii = ii +1
    self.addLine(index=ii)

#-------------------------------------------------------------------
  def getParams(self,params={},index=-1):
#-------------------------------------------------------------------
    keyList = params.keys()
    if index<0:
      first = 0
      last = self.body.layout().count()
    else:
      first = index
      last = index + 1
    for line in range(first,last):
      item = self.body.layout().itemAt(line)    
      if item:
        lineWidget = item.widget().child
        try:
          pars = lineWidget.getParams()
          for key in keyList:
            params[key].append(pars.get(key,''))
        except:
          for key in keyList:
            widget = lineWidget.findChild(QtGui.QWidget,key)
            value = ''
            if widget:
              if isinstance(widget,QtGui.QComboBox):
                value = str(widget.itemData(widget.currentIndex()).toString())
                if not value: value = str(widget.currentText())
              elif isinstance(widget,QtGui.QLabel):
                value = str(widget.text())
              elif isinstance(widget,QtGui.QLineEdit):
                value = str(widget.text())
              elif isinstance(widget,QtGui.QAbstractButton):
                value = int(widget.isChecked())
              elif isinstance(widget,mgSelectionCombo):
                value = str(widget.text())
              elif isinstance(widget,mgColourCombo):
                value = str(widget.get_colour())
              elif isinstance(widget,mgAtomPicker):
                value = str(widget.selection())
              elif isinstance(widget,QtGui.QDoubleSpinBox):
                value = float(widget.value())
              elif isinstance(widget,QtGui.QSpinBox):
                value = int(widget.value())

            params[key].append(value)

    #print "exframe.getParams",params
    return params

#----------------------------------------------------------------
  def setParams(self,params,index=0):
#----------------------------------------------------------------
    # Beware - do not assume that all items in params are
    # in our widget
    # index in the first line in the extending frame that the params
    # should be assigned to
    keyList = params.keys()

    #print "exframe.setParams",params,keyList

    # This will be tricky without an instance of the line
    # so create one if one does not exist
    if  self.body.layout().count() == 0: self.addLine()
    lineWidget = self.body.layout().itemAt(0).widget()

    # Need find at least one item in keyList that is definitely in the line
    widget = None
    ii = -1
    while not widget and ii<len(keyList)-1:
      ii = ii + 1
      widget = lineWidget.findChild(QtGui.QWidget,keyList[ii])
    # Bummer - input params dont match what we need for this widget
    if not widget: return -1

    #How many items in the input params? Set the number of lines
    # to that value
    nL = len(params[keyList[ii]])
    #print "exframe.setParams nL",nL
    if not index: self.setNofLines(nL)

    # If the 'line' widget has a setParams method use it
    done = 1
    try:
      for jj in range(nL):
        pars = {}
        for key in keyList: pars[key] = params[key][jj]
        lineWidget = self.body.layout().itemAt(index+jj).widget()
        lineWidget.child.setParams(pars)
    except:
      done = 0

    if not done:
      for key in keyList:
        #print 'extendingFrame key',key
        for jj in range(0,min(nL,len(params[key]))):
          #print "extendingFrame.setParams index,jj",index,jj,key,params[key][jj]
          lineWidget = self.body.layout().itemAt(index+jj).widget()
          widget = lineWidget.findChild(QtGui.QWidget,key)      
          if widget:
            if isinstance(widget,QtGui.QComboBox):
              ic = widget.findText(QtCore.QString(params[key][jj]))
              if ic < 0:
                ic = widget.findData(QtCore.QVariant(params[key][jj]))
              if ic>=0: widget.setCurrentIndex(ic)
            elif isinstance(widget,QtGui.QLineEdit):
              widget.setText(str(params[key][jj]))
            elif isinstance(widget,QtGui.QLabel):
              widget.setText(str(params[key][jj]))
            elif isinstance(widget,QtGui.QAbstractButton):
              widget.setChecked(params[key][jj])
            elif isinstance(widget,mgSelectionCombo):
              widget.setText(params[key][jj])
            elif isinstance(widget,mgColourCombo):
              widget.set_colour(params[key][jj])
            elif isinstance(widget,mgAtomPicker):
              widget.setSelection(atomName=params[key][jj])
            elif isinstance(widget,QtGui.QDoubleSpinBox):
              widget.setValue(float(params[key][jj]))
            elif isinstance(widget,QtGui.QSpinBox):
              widget.setValue(int(params[key][jj]))
          else:
            # If cant find widget in this line then wont find
            # one in any subsequent lines
            break

    return nL
  
              
#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------
class extendingFrameLine(QtGui.QWidget):
#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------

  iconPixmap = None
  
  def __init__(self,parent=None,child=None,ifEditable=1):
    QtGui.QWidget.__init__(self,parent)

    self.child = child
    layout = QtGui.QHBoxLayout()
    margin = 0
    layout.setSpacing(margin)
    layout.setContentsMargins(margin,margin,margin,margin)
    if child:
      layout.addWidget(child)
      #ht = child.height()

    if not extendingFrameLine.iconPixmap:
      extendingFrameLine.iconPixmap = guiUtils.loadPixmap(name='extendingFrameLine',group='actions')

    if ifEditable:
      icon = QtGui.QIcon(extendingFrameLine.iconPixmap)
      iconButton = QtGui.QToolButton(self)
      iconButton.setPopupMode(QtGui.QToolButton.InstantPopup)
      iconButton.setIcon(icon)
      iconButton.setMaximumSize(QtCore.QSize(extendingFrame.LINE_HEIGHT-4,extendingFrame.LINE_HEIGHT-4))
      iconButton.setToolTip('Add or delete a row')
      iconMenu = QtGui.QMenu(self)
      deleteAction = QtGui.QAction('Delete',parent)
      self.connect(deleteAction,QtCore.SIGNAL('triggered()'),guiUtils.partial(parent.deleteLine,self))
      iconMenu.addAction(deleteAction)
      aboveAction = QtGui.QAction('Insert above',parent)
      self.connect(aboveAction,QtCore.SIGNAL('triggered()'),guiUtils.partial(parent.insertLine,[self,'above']))
      iconMenu.addAction(aboveAction)
      belowAction = QtGui.QAction('Insert below',parent)
      self.connect(belowAction,QtCore.SIGNAL('triggered()'),guiUtils.partial(parent.insertLine,[self,'below']))
      iconMenu.addAction(belowAction)
      resetAction = QtGui.QAction('Reset',parent)
      self.connect(resetAction,QtCore.SIGNAL('triggered()'),self.child.setDefault)
      iconMenu.addAction(resetAction)

      
      iconButton.setMenu(iconMenu)

      layout.addWidget(iconButton)
      
    self.setLayout(layout)

class frameLine(QtGui.QWidget):

  def __init__(self,parent=None):
      QtGui.QWidget.__init__(self,parent)


      
      
#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------
class mgColourCombo( QtGui.QComboBox ):
#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------

#----------------------------------------------------------------------
  def __init__(self,parent=None,colour='black',browser=0):
#----------------------------------------------------------------------
     QtGui.QComboBox.__init__(self,parent)
     self.ifBrowser= browser
     self.loadColours()
     self.old_colour = colour
     self.set_colour(colour)
     self.connect(ColourBrowserManager(),QtCore.SIGNAL('coloursChanged'),self.loadColours)
     if self.ifBrowser:
       self.connect(self,QtCore.SIGNAL('currentIndexChanged(const QString)'),self.handleIndexChanged)
     #print 'mgColourCombo.init',self,self.old_colour
     
#----------------------------------------------------------------------
  def loadColours(self):
#----------------------------------------------------------------------
     #print 'mgColourCombo.loadColours'
     current_colour = self.currentText()
     self.blockSignals(1)
     self.clear()

     for col in global_definitions.MGCOLOUR().colourlist():
        colour =  global_definitions.MGCOLOUR().RGB255(col)
        qcol = QtGui.QColor(colour[0],colour[1],colour[2])
        pixmap = QtGui.QPixmap(20,10)
        pixmap.fill(qcol)
        icon = QtGui.QIcon(pixmap)
        self.addItem(icon,col)

     idx = self.findText(current_colour)
     if idx>=0: self.setCurrentIndex(idx)
     
     if self.ifBrowser: self.addItem('Colour browser')
     self.blockSignals(0)

#----------------------------------------------------------------------
  def handleIndexChanged(self,label):
#----------------------------------------------------------------------
    #print 'mgColourCombo.handleIndexChanged',label,self.old_colour
    if label == 'Colour browser':
      idx = global_definitions.MGCOLOUR().index(self.old_colour)
      if idx>=0: self.setCurrentIndex( idx )
      if not hasattr(self,'browser'):
        self.browser = mgColourBrowser(self)
        self.connect(self.browser,QtCore.SIGNAL('ColourSelected'),self.handleBrowser)
      else:
        self.browser.show()


#----------------------------------------------------------------------
  def handleBrowser(self,col):
#----------------------------------------------------------------------
    #print 'mgColourCombo.handleBrowser',col
    self.loadColours()
    self.set_colour(col)

#----------------------------------------------------------------------
  def set_colour(self,col):
#----------------------------------------------------------------------
   #print 'colourCombo.set_colour',col; sys.stdout.flush()
   #self.blockSignals(1)
   if col == 'Colour browser':
     idx = self.findText(self.old_colour)
     if idx>=0: self.setCurrentIndex( idx )
   else:
     idx = self.findText(col)
     if idx>=0:
       self.setCurrentIndex( idx )
       self.old_colour = col
   #self.blockSignals(0)  
   #print 'colourCombo.set_colour',col,idx; sys.stdout.flush()
     
#----------------------------------------------------------------------
  def get_colour(self):
#----------------------------------------------------------------------
   return str(self.currentText())



#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------
class mgAtomPicker(QtGui.QFrame):
#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------


   def textChanged0(self):
     txt = str(self.textBox.text())
     #print 'mgAtomPicker.textChanged0',txt
     return self.textChanged(txt)
   
   def textChanged(self,text=''):
       self.blockComboSignals(1)
       text = (str(text)).strip()
       model,chain,residue,residue2,atom = self.getDataobj().splitAtomID(text,res_range=1)
       #print 'mgAtomPicker.textChanged',self.mode,model,chain,residue,residue2,atom
           
       if self.showModelCombo:
         match = self.modelCombo.findText(QtCore.QString(model),QtCore.Qt.MatchExactly)
         #print 'modelCombo match',model,match
         if match>=0: self.modelCombo.setCurrentIndex(match)

       if not chain and self.blankOption:
         self.chainCombo.setCurrentIndex(0)
       else:
         match = self.chainCombo.findText(chain,QtCore.Qt.MatchExactly)
         #print 'mgAtomPicker.textChanged chainCombo match',chain,'*',match
         if match>=0:
           self.chainCombo.setCurrentIndex(match)
           self.chainChanged(match,updateText=0)

       if not residue and self.blankOption:
         self.residueCombo.setCurrentIndex(0)
       else:
         match = self.residueCombo.findText(residue,QtCore.Qt.MatchExactly)
         if match<0: match = self.residueCombo.findText(residue+'(',QtCore.Qt.MatchStartsWith)
         #print 'residueCombo match',residue,match
         if match>=0:
           self.residueCombo.setCurrentIndex(match)
           self.residueChanged(match,updateText=0)

       if self.mode == 'atom':
         
         if not atom and self.blankOption:
           self.atomCombo.setCurrentIndex(0)
         else:
           match = self.atomCombo.findText(atom,QtCore.Qt.MatchExactly)
           #print 'atomCombo match',atom,match
           if match>=0:
             self.atomCombo.setCurrentIndex(match)
           else:
             pass #print "atom:--"+atom+"-- not found"
           
       elif self.mode == 'range':
         if residue2:
           match = self.rangeCombo.findText(residue2,QtCore.Qt.MatchExactly)
           if match<0: match = self.rangeCombo.findText(residue2+'(',QtCore.Qt.MatchStartsWith)
           #print 'residueCombo match',residue,match
           if match>=0:  self.rangeCombo.setCurrentIndex(match)
         else:
           self.rangeCombo.setCurrentIndex(0)
       self.blockComboSignals(0)
       dobj = self.getDataobj()
       if  not self.signalBlocked and (dobj.name,model,chain,residue,residue2,atom) != self.lastEmitSelection:
         self.emit(QtCore.SIGNAL("AtomChanged"),(dobj.name,model,chain,residue,residue2,atom))
         self.lastEmitSelection =  (dobj.name,model,chain,residue,residue2,atom)


   def atomChanged(self,iatom,updateText=1):
       if iatom < 0: return
       if self.showDataCombo and self.dataCombo.count()==0: return
       if self.showModelCombo and self.modelCombo.count()==0: return
       if self.chainCombo.count()==0: return
       if self.residueCombo.count()==0: return
       dobj = self.getDataobj()
       imodel = self.getModelNumber()
       ichain = self.chainCombo.currentIndex()
       iresidue   = self.residueCombo.currentIndex()
       changed = False
       if self.showDataCombo and dobj.name != self.dataName:
	  self.dataName = dobj.name
          changed = True
       if imodel != self.imodel:
	  self.imodel = imodel
          changed = True
       if ichain != self.ichain:
	  self.ichain = ichain
          changed = True
       if iresidue != self.iresidue:
	  self.iresidue = iresidue
          changed = True
       if iatom != self.iatom:
	  self.iatom = iatom
          changed = True
       if changed:
         #print dataName+"/"+str(imodel)+"/"+str(ichain)+"/"+str(iresidue)+"/"+str(iatom)

         if updateText:self.updateTextBox()


   def rangeChanged(self,ires,updateText=1):
     #print 'rangeChanged',ires
     if updateText: self.updateTextBox()


   def updateTextBox(self):
      dobj = self.getDataobj()
      imodel = self.getModelNumber()
      ichain = self.chainCombo.currentIndex()-self.blankOption
      iresidue   = self.residueCombo.currentIndex()-self.blankOption
      iresidue2 = -1
      #print 'updateTextBox',self.signalBlocked,imodel,ichain,iresidue
      iatom = -1

      text = ''
      if self.showModelCombo and imodel>0: text = '/'+str(imodel)
      if ichain>=0:
        chainID = dobj.molHnd.GetModel(imodel).GetChain(ichain).GetChainID()
        if text:
          text = text + "/"+str(chainID)+ "/"
        elif len(chainID)>0:
          text = str(chainID)+ "/"
        else:
          text = '// /'
        pRes =  dobj.molHnd.GetModel(imodel).GetChain(ichain).GetResidue(iresidue)
        if pRes: 
           text = text +str(pRes.GetSeqNum())
           if self.mode == 'atom':
             iatom = self.atomCombo.currentIndex()-self.blankOption
             if iatom>=0:             
               theAtom = pRes.GetAtom(iatom)
               atomID = theAtom.GetAtomName()
               text = text +'/' + (atomID).strip()
               altLoc = theAtom.GetAltLoc().strip()
               if altLoc:
                 text = text + ':' + altLoc
           elif self.mode == 'range':
             irange =  self.rangeCombo.currentIndex()-self.blankOption
             if irange>=0:
               resNo2 = dobj.molHnd.GetModel(imodel).GetChain(ichain).GetResidue(irange).GetSeqNum()
               if irange>iresidue:
                 text = text +'-'+str(resNo2)
               elif iresidue>irange:
                 text = "/"+str(imodel)+"/"+str(chainID)+"/"+str(resNo2)+'-'+str(pRes.GetSeqNum())
               iresidue2 = resNo2

      self.textBox.blockSignals(1)
      self.textBox.setText(text)
      self.textBox.blockSignals(0)
      
      if not self.signalBlocked:
        if self.lastEmitSelection != (dobj.name,imodel,ichain,iresidue,iresidue2,iatom):
          self.emit(QtCore.SIGNAL("AtomChanged"),(dobj.name,imodel,ichain,iresidue,iresidue2,iatom))
          self.lastEmitSelection =  (dobj.name,imodel,ichain,iresidue,iresidue2,iatom)
      

   def reset(self):
     if self.showDataCombo: self.dataCombo.setCurrentIndex(0)
     if self.showModelCombo: self.modelCombo.setCurrentIndex(0)
     self.chainCombo.setCurrentIndex(0)
     self.residueCombo.setCurrentIndex(0)
     self.rangeCombo.setCurrentIndex(0)

   def blockSignals(self,mode):
     self.signalBlocked = mode

   def blockComboSignals(self,mode):
     if self.showDataCombo: self.dataCombo.blockSignals(mode)
     if self.showModelCombo: self.modelCombo.blockSignals(mode)
     self.chainCombo.blockSignals(mode)
     self.residueCombo.blockSignals(mode)
     if  self.rangeCombo: self.rangeCombo.blockSignals(mode)
     self.atomCombo.blockSignals(mode)
     
     
   def setSelection(self,modelName='',atomName='',MolData=None,pAtom=None):
       #print 'mgAtomPicker setSelection atomName',atomName
       if MolData:
         dobj = MolData
         modelName = dobj.name
       elif modelName:
         dobj = global_definitions.data(modelName)
       else:
         dobj = self.getDataobj()
       if not dobj: return
       
       if pAtom: atomName = pAtom.GetAtomID()
       #print 'mgAtomPicker setSelection',dobj,atomName

       if dobj and self.showDataCombo and self.dataCombo.findText(modelName,QtCore.Qt.MatchExactly)>-1:
	   self.dataCombo.setCurrentIndex(self.dataCombo.findText(modelName,QtCore.Qt.MatchExactly))
       self.textBox.blockSignals(1)
       self.textBox.setText(atomName)
       self.textBox.blockSignals(0)
       self.textChanged(atomName)
       
       '''
       full_id = dobj.splitAtomID(atomName,res_range=1)
       print 'mgAtomPicker setSelection atomName',atomName,full_id
       

       if self.showModelCombo:
         if full_id[0]:
           iModel = self.modelCombo.findText(full_id[0])
           if iModel>= 0:
             self.modelCombo.setCurrentIndex(iModel)
             
       iChain = self.chainCombo.findText(full_id[1])
       if iChain>=0:
           self.chainCombo.setCurrentIndex(iChain)
           iRes = self.residueCombo.findText(full_id[2])
           if iRes<0: iRes = self.residueCombo.findText(full_id[2]+'(',QtCore.Qt.MatchStartsWith)
           if iRes>=0:
             self.residueCombo.setCurrentIndex(iRes)
             if self.mode == 'atom':
               iAtom = self.atomCombo.findText(full_id[4])
               if iAtom>=0:
                 self.atomCombo.setCurrentIndex(iAtom)
           iRes2 = self.rangeCombo.findText(full_id[3])
           if iRes2<0: iRes2 = self.rangeCombo.findText(full_id[3]+'(',QtCore.Qt.MatchStartsWith)
           print 'mgAtomPicker.setSelection iRes2',iRes,iRes2
           if iRes2>=0:
             self.rangeCombo.setCurrentIndex(iRes2)
       '''
           
                                                   
       return 0

   def selection(self):
       return str(self.textBox.text())

   def modelAtomPtr(self):
     dobj = self.getDataobj()
     if not dobj: return (None,None)
     
     atm_ptr = dobj.interpretAtomID(dobj,str(self.textBox.text()),force_one_atom=1)
     #print "modelAtomPtr",self.dataName,str(self.textBox.text()),atm_ptr
     return dobj,atm_ptr

   def modelNameAtomName(self):
     # Use modelAtomPtr to  check that atom id is interpretable
     ret = self.modelAtomPtr()
     #print "modelNameAtomName ret",ret
     if ret:
       return ret[0].name,str(self.textBox.text())
     else:
       return ()

   def residueChanged(self,ires,updateText=1):
       if ires < 0: return
       if self.showDataCombo and self.dataCombo.count()==0: return
       if self.showModelCombo and self.modelCombo.count()==0: return
       if self.chainCombo.count()==0: return
       dobj = self.getDataobj()
       imodel = self.getModelNumber()
       ichain = self.chainCombo.currentIndex()-self.blankOption
       ires = ires - self.blankOption
       if self.mode == 'atom':
         self.atomCombo.clear()
         if self.blankOption: self.atomCombo.addItem('')
         if ires>=0 and ichain>=0 and imodel>=0:
           natoms = dobj.molHnd.GetModel(imodel).GetChain(ichain).GetResidue(ires).GetNumberOfAtoms()
           if natoms == 0: 
  	      return
           pres = dobj.molHnd.GetModel(imodel).GetChain(ichain).GetResidue(ires)
           init_atom = 0
           for j in range(natoms):
              pat = pres.GetAtom(j)
	      atomID = pat.GetAtomName().strip()
              if atomID == 'CA': init_atom = j
              if pat.GetAltLoc(): atomID =atomID+':'+ pat.GetAltLoc()
              self.atomCombo.addItem(atomID)
           if self.atomCombo.currentIndex !=init_atom: self.atomCombo.setCurrentIndex(init_atom)
           self.atomChanged(init_atom,updateText)
       else:
         if updateText: self.updateTextBox()

   def chainChanged(self,ichain,updateText=1):
       if ichain < 0: return
       if self.showDataCombo and self.dataCombo.count()==0: return
       if self.showModelCombo and self.modelCombo.count()==0: return
       ichain = ichain - self.blankOption
       self.residueCombo.clear()
       self.rangeCombo.clear()
       if self.blankOption:
           self.residueCombo.addItem('')
           self.rangeCombo.addItem('')
       dobj = self.getDataobj()
       imodel = self.getModelNumber()
           
       if ichain>=0 and imodel>=0:
         nres = dobj.molHnd.GetModel(imodel).GetChain(ichain).GetNumberOfResidues()
         if nres == 0: 
            if self.mode == 'atom': self.atomCombo.clear()
	    return
         pch = dobj.molHnd.GetModel(imodel).GetChain(ichain)
         for j in range(nres):
            pres = pch.GetResidue(j)
	    resID = pres.GetResName()
	    resNo = pres.GetSeqNum()
            resIns = pres.GetInsCode()
            if resIns:
              name = str(resNo)+"."+resIns+"("+resID+")"
            else:
              name = str(resNo)+"("+resID+")"
            self.residueCombo.addItem(name)
            #if self.mode == 'range': self.rangeCombo.addItem(name)
            self.rangeCombo.addItem(name)
       self.residueChanged(0,updateText)
       if self.residueCombo.currentIndex !=0: self.residueCombo.setCurrentIndex(0)
       #if self.mode == 'range':
       if self.rangeCombo.currentIndex !=0: self.rangeCombo.setCurrentIndex(0)

         
   def modelChanged(self,imodel,updateText=1):
       if imodel < 0: return
       if self.showDataCombo and self.dataCombo.count()==0: return
       imodel = (imodel - self.blankOption) + 1
       self.chainCombo.clear()
       if self.blankOption: self.chainCombo.addItem('  ')
       self.residueCombo.clear()
       if self.mode == 'atom': self.atomCombo.clear()
       if self.mode == 'range': self.rangeCombo.clear()
       if imodel>0:
         dobj = self.getDataobj()
         nchains = dobj.molHnd.GetModel(imodel).GetNumberOfChains()
         if nchains == 0: return
         for j in range(nchains):
	    chainID = dobj.molHnd.GetModel(imodel).GetChain(j).GetChainID()
            self.chainCombo.addItem(chainID)
         self.chainChanged(0,updateText)
         if self.chainCombo.currentIndex !=0: self.chainCombo.setCurrentIndex(0)

   def dataObjChanged(self,i,updateText=1):
       dobj = self.getDataobj()
       if not dobj: return
       
       if self.showModelCombo:
         self.modelCombo.clear()
         nmodels = dobj.molHnd.GetNumberOfModels()
         if nmodels == 0:
            self.chainCombo.clear()
            self.residueCombo.clear()
            if self.mode == 'range': self.rangeCombo.clear()
            if self.mode == 'atom': self.atomCombo.clear()
	    return
         if self.blankOption: self.modelCombo.addItem('')
         for j in range(nmodels):
            self.modelCombo.addItem(str(j+1))
         self.modelChanged(0,updateText)
         if self.modelCombo.currentIndex !=0: self.modelCombo.setCurrentIndex(0)
       else:
         # Set the chainCombo
         self.modelChanged(self.blankOption,updateText)

       
   def __init__(self,parent=None,MolData_name='',mode = 'atom',blank=0):
       QtGui.QFrame.__init__(self)
       self.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Plain)
       self.showModelCombo = 1
       self.mode = mode
       if MolData_name:
         self.showDataCombo = 0
         dobj = global_definitions.data(MolData_name)
         if dobj and dobj.molHnd.GetNumberOfModels() == 1: self.showModelCombo = 0
       else:
         self.showDataCombo = 1
       self.blankOption = blank
       self.dataName = MolData_name
       self.imodel = 0
       self.ichain = 0
       self.iresidue = 0
       self.iatom = 0
       self.signalBlocked = 0
       self.lastEmitSelection = [self.dataName,0,0,0,0,0]
       vblayout = QtGui.QHBoxLayout()
       vblayout.setContentsMargins(0,0,0,0)
       vblayout.setSpacing(0)
       if self.showDataCombo:
         self.dataCombo = mgDataObjCombo(object_type='MolData',parent=self)
         vblayout.addWidget(self.dataCombo)
       if self.showModelCombo:
         self.modelCombo = QtGui.QComboBox()
         self.modelCombo.setMaxVisibleItems(10)
         self.modelCombo.setMinimumContentsLength(1)
         vblayout.addWidget(self.modelCombo)
       self.chainCombo = QtGui.QComboBox()
       self.chainCombo.setMaxVisibleItems(10)
       self.chainCombo.setMinimumContentsLength(1)
       vblayout.addWidget(self.chainCombo)
       self.residueCombo = QtGui.QComboBox()
       self.residueCombo.setMaxVisibleItems(10)
       self.residueCombo.setMinimumContentsLength(8)
       vblayout.addWidget(self.residueCombo)
       self.atomCombo = QtGui.QComboBox()
       self.atomCombo.setMinimumContentsLength(4)
       if self.mode == 'atom':vblayout.addWidget(self.atomCombo)
       self.to_label = QtGui.QLabel('to',self)
       self.rangeCombo = QtGui.QComboBox()
       self.rangeCombo.setMinimumContentsLength(8)
       if self.mode == 'range':
         vblayout.addWidget(self.to_label)  
         vblayout.addWidget(self.rangeCombo)
       else:
         self.to_label.hide()
         self.rangeCombo.hide()
       #self.textBox = QtGui.QLineEdit()
       self.textBox = mgLineEdit()
       someText = "/99/Z/999.2/HG51"
       dummy = QtGui.QTextEdit()
       fn = dummy.currentFont()
       fm = QtGui.QFontMetrics(fn)
       textWidth = fm.width(someText)
       vblayout.addWidget(self.textBox)
       vblayout.addStretch(5)
       self.textBox.setMinimumWidth(textWidth)
       #self.textBox.setValidator(mgCidValidator(self,'atom'))
       self.dataObjChanged(0)
       if self.showDataCombo and self.dataCombo.count()>0: self.dataCombo.setCurrentIndex(0)

       if self.showDataCombo: self.connect(self.dataCombo,QtCore.SIGNAL("currentIndexChanged(int)"),self.dataObjChanged)
       if self.showModelCombo: self.connect(self.modelCombo,QtCore.SIGNAL("currentIndexChanged(int)"),self.modelChanged)

       self.setToolTip()
       self.connect(self.chainCombo,QtCore.SIGNAL("currentIndexChanged(int)"),self.chainChanged)
       self.connect(self.residueCombo,QtCore.SIGNAL("currentIndexChanged(int)"),self.residueChanged)
       self.connect(self.atomCombo,QtCore.SIGNAL("currentIndexChanged(int)"),self.atomChanged)
       self.connect(self.rangeCombo,QtCore.SIGNAL("currentIndexChanged(int)"),self.rangeChanged)
       #self.connect(self.textBox,QtCore.SIGNAL("textEdited(const QString&)"),self.textChanged)
       self.connect(self.textBox,QtCore.SIGNAL("editingFinished()"),self.textChanged0)
       self.connect(self.textBox,QtCore.SIGNAL("apply"),self.textChanged0)
       #self.connect(self.textBox,QtCore.SIGNAL("mouseRelease()"),self.pasteFromAtomPick)
       self.setLayout(vblayout)

   def getDataobj(self):
       if self.showDataCombo:
          name = str(self.dataCombo.itemText(self.dataCombo.currentIndex()))
       else:
          name = self.dataName
       return global_definitions.data(name)

   def getModelNumber(self):
     if self.showModelCombo:
       return (self.modelCombo.currentIndex()-self.blankOption)+1
     else:
       return 1

   def setToolTip(self,text=''):
     if text: text = text + ' '
     if self.showDataCombo:
         self.dataCombo.setToolTip(text+'Model object - equivalent to PDB file')
     if self.showModelCombo:
         self.modelCombo.setToolTip(text + "'NMR' or symmetry model number")
     self.chainCombo.setToolTip(text+"Chain ID")
     self.residueCombo.setToolTip(text+"Residue sequence number and type")
     if self.mode == 'atom': self.atomCombo.setToolTip(text+"Atom name")
     if self.mode == 'range': self.rangeCombo.setToolTip(text+"end of residue range")
     self.textBox.setToolTip(text+"Full atom id")

   def pasteFromAtomPick(self):
     # This is NOT used - instead uses the standard QT paster buffer
     # But this would have advatage that user does not have to delete
     # any existing text in the text input
     #print 'mgAtomPicker.pasteFromAtomPick'
     pickevent = MAINWINDOW().getLastPickEvent()
     if not pickevent: return
     atoms = pickevent.getPickedAtoms()
     if len(atoms) == 1:
      #print 'pasteFromAtomPick',atoms,atoms[0].GetAtomID()
      self.textChanged(atoms[0].GetAtomID())

   def setMode(self,mode):
     if mode == self.mode: return
     self.blockSignals(1)
     if self.mode == 'atom':
       self.atomCombo.clear()
       self.layout().removeWidget(self.atomCombo)
       self.atomCombo.hide()
     elif self.mode == 'range':
       self.layout().removeWidget(self.to_label)
       self.layout().removeWidget(self.rangeCombo)
       self.rangeCombo.clear()
       self.rangeCombo.hide()
       self.to_label.hide()
     idx = self.layout().indexOf(self.textBox)
     if mode == 'atom':
       self.atomCombo.show()
       self.layout().insertWidget(idx,self.atomCombo)
       self.residueChanged(self.residueCombo.currentIndex())
     elif mode == 'range':
       self.to_label.show()
       self.rangeCombo.show()
       self.layout().insertWidget(idx,self.to_label)
       self.layout().insertWidget(idx+1,self.rangeCombo)
       self.chainChanged(self.chainCombo.currentIndex())
     self.mode = mode
     self.updateTextBox()
     self.blockSignals(0)
    
#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------
class CartesianPickerDialog(MGSimpleDialog.MultiDialog):
#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------
  """
  Pick a point in 3D Cartesian space.
  """
  def Apply2(self,pos):
    self.emit(QtCore.SIGNAL("PositionChanged"),pos)
    
  def Apply(self):
    xyz = ['x','y']
    if self.dim >=3:xyz.append('z')
    pos = []
    #for item in xyz: pos.append(self.cartesian.widgets[item].value())
    for item in xyz:
      p = self.cartesian.widgets[item].text()
      if not p: p = 0.0
      pos.append(float(p))
    self.emit(QtCore.SIGNAL("PositionChanged"),pos)

  def ApplyAndClose(self):
    self.Apply()
    self.Close()

  def __init__(self,parent=None,x=0.0,y=0.0,z=0.0,dim=3):
    MGSimpleDialog.MultiDialog.__init__(self,parent)
    self.setWindowTitle(self.tr("Cartesian point picker"))
    self.dim = dim
    mylayout = QtGui.QVBoxLayout()
    self.cartesian = CartesianPicker(self,x=x,y=y,z=z,dim=dim)
    if QtCore.PYQT_VERSION >= 0x040500 and sip.SIP_VERSION >= 0x040800:
      self.cartesian.PositionChanged.connect(self.Apply2)
    else:
      self.connect(self.cartesian,QtCore.SIGNAL("PositionChanged"),self.Apply2)
    mylayout.addWidget(self.cartesian)
    dialog_buttons = QtGui.QDialogButtonBox()
    # 'Enter' or 'return' key anywhere in the dialog box seems to fire
    # the ok button and exit the dialog box. Remove ok button for now
    #ok_button = dialog_buttons.addButton(QtGui.QDialogButtonBox.Ok)
    apply_button = dialog_buttons.addButton(QtGui.QDialogButtonBox.Apply)
    cancel_button = dialog_buttons.addButton(QtGui.QDialogButtonBox.Cancel)
    self.connect(cancel_button,QtCore.SIGNAL('clicked()'),self.Close)
    #self.connect(ok_button,QtCore.SIGNAL('clicked()'),self.ApplyAndClose)
    self.connect(apply_button,QtCore.SIGNAL('clicked()'),self.Apply)
    mylayout.addWidget(dialog_buttons)
    self.setLayout(mylayout)
    self.setSizeGripEnabled(0)
    self.CreateMenuEntry()

  def setParams(self,params={},**kw):
    params.update(kw)
    self.cartesian.setParams(params)
    
#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------
class CartesianPicker(QtGui.QWidget):
#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------

  if QtCore.PYQT_VERSION >= 0x040500 and sip.SIP_VERSION >= 0x040800:
		PositionChanged = QtCore.pyqtSignal(list)

  def __init__(self,parent=None,dim=3,params={},**kw):
    QtGui.QWidget.__init__(self,parent)
   
    hlayout = QtGui.QHBoxLayout()
    self.widgets = {}
    items = ['x','y']
    if  dim>=3: items.append('z')
    self.validator = QtGui.QDoubleValidator(-1000.0,1000.0,2,self)
    for item in items:
      label = QtGui.QLabel(item+':')
      self.widgets[item] = QtGui.QLineEdit(self)
      self.widgets[item].setValidator(self.validator)
      #self.widgets[item] = mgDeltaSlider(QtCore.Qt.Vertical,self)
      #self.widgets[item].setRange(-100.0,100.0)
      #self.widgets[item].setSingleStep(0.5)
      #self.widgets[item].setMaximumHeight(30)
      self.widgets[item].setObjectName('CartesianPicker-'+item)
      self.connect(self.widgets[item],QtCore.SIGNAL("valueChanged(double)"),self.Apply)
      hlayout.addWidget(label)
      hlayout.addWidget(self.widgets[item])
      self.setLayout(hlayout)
   
    params.update(kw)
    self.setParams(params)

  def setParams(self,params={},**kw):
    params.update(kw)
    for key,value in params.items():
      if self.widgets.has_key(key):
        self.widgets[key].setText(str(value))
        #self.widgets[key].setValue(value)

  def Apply(self):
    pos = []
    #for item in ['x','y']: pos.append(self.widgets[item].value())
    #if self.widgets.has_key('z'):pos.append(self.widgets['z'].value())
    xyz =  ['x','y']
    if self.widgets.has_key('z'): xyz.append('z')
    for item in items:
      p = self.widgets[item].text()
      if not p: p = 0.0
      pos.append(float(p))
    if QtCore.PYQT_VERSION >= 0x040500 and sip.SIP_VERSION >= 0x040800:
      self.PositionChanged.emit(pos)
    else:
      self.emit(QtCore.SIGNAL("PositionChanged"),pos)

#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------
class animationIntervalWidget(mgSlider):
#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------

  def __init__(self,parent):
    mgSlider.__init__(self,QtCore.Qt.Horizontal,parent)
    # Enable range of 0 - 1000 ms
    self.setRange(1,1000,integer=1)
    self.setValue(global_definitions.MAINWINDOW().animationInterval())
    self.connect(self,QtCore.SIGNAL("valueChanged(int)"),self.apply)

#-------------------------------------------------------------------
  def apply(self):
#-------------------------------------------------------------------
    #print "animationIntervalGUI.apply",self.value()
    global_definitions.MAINWINDOW().setAnimationInterval(int(self.value()))

    # ?? Test for other instances of this widget and update  them



def ColourBrowserManager():
  if not ColBrowserManager.insts:
    ColBrowserManager()
  return ColBrowserManager.insts

class ColBrowserManager(QtCore.QObject):
  insts = None
  def __init__(self):
    QtCore.QObject.__init__(self)
    ColBrowserManager.insts = self

  def coloursChanged(self):
    self.emit(QtCore.SIGNAL('coloursChanged'))

#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------
class mgColourBrowser(ColourBrowser.ColourBrowser):
#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------

#-------------------------------------------------------------------
  def __init__(self,parent=None,selected_colour=''):
#-------------------------------------------------------------------
    COL = global_definitions.MGCOLOUR()
    if selected_colour == 'complement': selected_colour = 'white'
    ColourBrowser.ColourBrowser.__init__(self,parent,standard_colours=COL.get_standard_colours(),custom_colours=COL.get_custom_colours(),new_name_number=COL.new_name_number,selected_colour=selected_colour)
    
    self.show()
    self.connect(self,QtCore.SIGNAL("clearCustomColours"),self.handleClearCustomColours)
    self.connect(self,QtCore.SIGNAL("deleteColour"),self.handleDeleteColour)
    self.connect(self,QtCore.SIGNAL("newColour"),self.handleNewColour)
    self.connect(self,QtCore.SIGNAL("colourNameEdit(PyQt_PyObject)"),self.handleRenameColour)

    self.connect(ColourBrowserManager(),QtCore.SIGNAL('coloursChanged'),self.updateCustomColours)

  def updateCustomColours(self):
    ColourBrowser.ColourBrowser.updateCustomColours(self,global_definitions.MGCOLOUR().get_custom_colours())
                 
#-------------------------------------------------------------------
  def handleNewColour(self,colour=[]):
#-------------------------------------------------------------------
    rgb = []
    for ii in range(1,4):
      rgb.append(float(colour[ii])/float(256.0))
    #print "handleNewColour rgb",rgb
    global_definitions.MGCOLOUR().new(colour[0],rgb)
    ColourBrowserManager().coloursChanged()
    
#-------------------------------------------------------------------
  def handleRenameColour(self,args):
#-------------------------------------------------------------------
    #print 'mgColourBrowser.handleRenameColour',args
    if not len(args)==2: return
    global_definitions.MGCOLOUR().rename(args[0],args[1])
    ColourBrowserManager().coloursChanged()
    
#-------------------------------------------------------------------
  def handleDeleteColour(self,colour):
#-------------------------------------------------------------------
    global_definitions.MGCOLOUR().delete(colour)
    ColourBrowserManager().coloursChanged()

#-------------------------------------------------------------------
  def handleClearCustomColours(self):
#-------------------------------------------------------------------
    global_definitions.MGCOLOUR().clear_custom_colours()
    ColourBrowserManager().coloursChanged()

class GridWidget(QtGui.QWidget):
  def __init__(self,parent=None,rows=0,cols=0):
     QtGui.QWidget.__init__(self,parent)
     layout = QtGui.QGridLayout()
     self.setLayout(layout)
     self.validator = QtGui.QDoubleValidator(self)
     for i in range(rows):
       for j in range(cols):
         le = QtGui.QLineEdit(self)
         le.setValidator(self.validator)
         layout.addWidget(le,i,j)
    
#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------
class TransformMatrixDialog(MGSimpleDialog.MultiDialog):
#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------

  def __init__(self,parent=None,title="Transformation matrix"):
    MGSimpleDialog.MultiDialog.__init__(self,parent)
    self.setWindowTitle(title)

    layout = QtGui.QVBoxLayout()
    widget = QtGui.QLabel('Rotation matrix:',self)
    layout.addWidget(widget)
    self.rotmat=GridWidget(self,3,3)
    self.rotmat.setMaximumSize(QtCore.QSize(400,100))
    layout.addWidget(self.rotmat)

    line_layout =  QtGui.QHBoxLayout()
    widget = QtGui.QLabel('Not doing what you expected? Try',self)
    line_layout.addWidget(widget)
    widget = QtGui.QPushButton('Flip matrix',self)
    line_layout.addWidget(widget)
    self.connect(widget,QtCore.SIGNAL('clicked()'),self.flipMatrix)
    layout.addLayout(line_layout)
    
    widget = QtGui.QLabel('Translation vector:',self)
    layout.addWidget(widget)
    self.transvect = GridWidget(self,1,3)
    layout.addWidget(self.transvect)

    absLayout = QtGui.QHBoxLayout()
    absLabel = QtGui.QLabel(self.tr('Apply transformation to '))
    absLayout.addWidget(absLabel)
    self.absCombo = QtGui.QComboBox()
    self.absCombo.addItem(self.tr('absolute coordinates'))
    self.absCombo.addItem(self.tr('current view'))
    absLayout.addWidget(self.absCombo)
    absLayout.addStretch()

    layout.addLayout(absLayout)

    dialog_buttons = QtGui.QDialogButtonBox()
    apply_button = dialog_buttons.addButton(QtGui.QDialogButtonBox.Apply)
    undo_button = dialog_buttons.addButton('Undo transformation',QtGui.QDialogButtonBox.ResetRole)
    cancel_button = dialog_buttons.addButton(QtGui.QDialogButtonBox.Cancel)
    self.connect(cancel_button,QtCore.SIGNAL('clicked()'),self.Close)
    #self.connect(ok_button,QtCore.SIGNAL('clicked()'),self.ApplyAndClose)
    self.connect(apply_button,QtCore.SIGNAL('clicked()'),self.Apply)
    self.connect(undo_button,QtCore.SIGNAL('clicked()'),self.Undo)
    layout.addWidget(dialog_buttons)
    self.setLayout(layout)
    self.setSizeGripEnabled(0)
    self.CreateMenuEntry()


#-------------------------------------------------------------------
  def setParams(self,params={},**kw):
#-------------------------------------------------------------------
    params.update(kw)
    #print 'TransformMatrixDialog.setParams',params
    import types
    import re,string

    if params.has_key('rotation'):
      if (isinstance(params['rotation'],types.ListType) or isinstance(params['rotation'],types.TupleType) ) and len(params['rotation'])==9:
        self.setGrid(self.rotmat,params['rotation'])
      elif isinstance(params['rotation'],types.StringType):
        rotmat = string.split(re.sub(',',' ',params['rotation']))
        self.setGrid(self.rotmat,rotmat)

    if params.has_key('translation'):
      if (isinstance(params['translation'],types.ListType) or isinstance(params['translation'],types.TupleType) )  and len(params['translation'])==3:
        self.setGrid(self.transvect,params['translation'])
      elif isinstance(params['translation'],types.StringType):
        rotmat = string.split(re.sub(',',' ',params['translation']))
        self.setGrid(self.transvect,rotmat)


#-------------------------------------------------------------------
  def getParams(self):
#-------------------------------------------------------------------
    import re,string
    pars = {}
    text = self.absCombo.currentText()
    if self.tr(text) == self.tr('absolute coordinates'):
       pars['transmode'] = 'absolute'
    else:
       pars['transmode'] = 'relative'
    #text = str(self.transvect.text())
    if False:
      pars['translation'] = [0.0,0.0,0.0]
    else:
      numlist = self.extractGrid(self.transvect)
      if len(numlist)==3: pars['translation'] = numlist
    #text = str(self.rotmat.toPlainText())
    if False:
      pars['rotation'] = [1.0,0.0,0.0,  0.0,1.0,0.0,  0.0,0.0,1.0]
    else:
      tmtrx = self.extractGrid(self.rotmat)
      print tmtrx
      if len(tmtrx)==9:
        det = ( tmtrx[0] * ( tmtrx[4]* tmtrx[8] -  tmtrx[5]* tmtrx[7] ) ) + \
            ( tmtrx[1] * ( tmtrx[5]* tmtrx[6] -  tmtrx[3]* tmtrx[8] ) ) + \
            ( tmtrx[2] * ( tmtrx[3]* tmtrx[7] -  tmtrx[4]* tmtrx[6] ) )

        print "det",det
        import math
        det = math.fabs(det)
        if det > 0.99 and det < 1.01:
          pars['rotation'] = tmtrx
    return pars

  def setGrid(self,grid,vals):
    ij = 0
    for i in range(grid.layout().rowCount()):
      for j in range(grid.layout().columnCount()):
        try:
          grid.layout().itemAtPosition(i,j).widget().setText(str(vals[ij]))
        except:
          print ij,"out of range ? in", grid.layout().rowCount(),"x",grid.layout().columnCount()
        ij += 1

  def extractGrid(self,grid):
    vals = []
    for i in range(grid.layout().rowCount()):
      for j in range(grid.layout().columnCount()):
        vals.append(float(grid.layout().itemAtPosition(i,j).widget().text()))
    return vals
    

#-------------------------------------------------------------------
  def extractVector(self,text):
#-------------------------------------------------------------------
    import re,string
    strlist = string.split(re.sub(',',' ',text))
    numlist = []
    for item in strlist:
       try:
         numlist.append(float(item))
       except:
         pass
    return numlist

#-------------------------------------------------------------------
  def Apply(self):
#-------------------------------------------------------------------
    pars = self.getParams()
    errmess = ''
    if not pars.has_key('rotation'): errmess = errmess + 'Rotation is not correct\n'
    if not pars.has_key('translation'): errmess = errmess + 'Translation is not correct\n'
    if errmess:
      QtGui.QMessageBox.warning(None,self.windowTitle(),'Can not apply transformation\n'+errmess)
    else:
      self.emit(QtCore.SIGNAL('transformationChanged'))
              
#-------------------------------------------------------------------
  def Undo(self):
#-------------------------------------------------------------------
    self.setParams(rotation = ( 1.0, 0.0, 0.0,
                                0.0, 1.0, 0.0,
                                0.0, 0.0, 1.0 ),
                   translation = ( 0.0, 0.0, 0.0 ) )
    self.emit(QtCore.SIGNAL('transformationUndone'))

#-------------------------------------------------------------------
  def flipMatrix(self):
#-------------------------------------------------------------------
    params = self.getParams()
    if not params.has_key('rotation'):
      QtGui.QMessageBox.warning(None,'Can not flip matrix\n'+'Rotation is not correct')
      return
    mx = params['rotation']
    self.setParams(rotation = ( mx[0] , mx[3], mx[6],
                                mx[1],  mx[4], mx[7],
                                mx[2],  mx[5], mx[8] ) )
    
#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------
class saveMultiDataObj(QtGui.QDialog):
#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------

  def __init__(self,parent,title='Save all data files',tag='',version='',message=''):
    QtGui.QDialog.__init__(self,parent)
    self.setWindowTitle(title)
    layout = QtGui.QVBoxLayout()

    if message:
      line_layout = QtGui.QHBoxLayout()
      line_layout.addWidget(QtGui.QLabel(message))
      line_layout.addStretch(5)
      layout.addLayout(line_layout)

    line_layout = QtGui.QHBoxLayout()
    line_layout.addWidget(QtGui.QLabel('Save files with tag',self))
    self.tag = QtGui.QLineEdit(self)
    line_layout.addWidget(self.tag)
    line_layout.addWidget(QtGui.QLabel('and version number'))
    self.tag.setText(tag)
    self.version = QtGui.QSpinBox(self)
    self.version.setRange(0,100)
    self.version.setSingleStep(1)
    self.version.setValue(version)
    line_layout.addWidget(self.version)
    line_layout.addStretch(5)
    layout.addLayout(line_layout)

    self.mode = QtGui.QButtonGroup(self)
    line_layout = QtGui.QHBoxLayout()
    self.append_tag = QtGui.QRadioButton('Save to filename with tag appended',self)
    self.mode.addButton(self.append_tag,0)
    line_layout.addWidget(self.append_tag)
    line_layout.addStretch(5)
    layout.addLayout(line_layout)

    line_layout = QtGui.QHBoxLayout()
    self.backup = QtGui.QRadioButton('Save to same filename and backup to file with tag',self)
    self.mode.addButton(self.backup,0)
    line_layout.addWidget(self.backup)
    line_layout.addStretch(5)
    layout.addLayout(line_layout)

    dialog_buttons = QtGui.QDialogButtonBox()
    apply_button = dialog_buttons.addButton('Save',QtGui.QDialogButtonBox.ApplyRole)
    cancel_button = dialog_buttons.addButton('Close',QtGui.QDialogButtonBox.RejectRole)
    self.connect(apply_button,QtCore.SIGNAL('clicked()'),guiUtils.partial(self.emit,QtCore.SIGNAL('save')))
    self.connect(apply_button,QtCore.SIGNAL('clicked()'),self.Apply)
    self.connect(cancel_button,QtCore.SIGNAL('clicked()'),self.close)
    layout.addWidget(dialog_buttons)
    
    self.setLayout(layout)
    self.show()

  def getMode(self):
      return ['append','backup'][int(self.mode.checkedId())]

  def getTag(self):
      return str(self.tag.text()) + str(self.version.value())

#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------
class RoundToMultipleSpin(QtGui.QSpinBox):
#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------
	def __init__(self,parent=None):
		QtGui.QSpinBox.__init__(self,parent)
		self.setMultiple(1)

	def setMultiple(self,value):
		self.multiple = value
		self.setSingleStep(value)

	def fixValue(self):
		v = self.value()
		vnew = (v+(self.multiple-1))/self.multiple * self.multiple
		self.setValue(vnew)

	def fixup(self,input):
		v = int(input.toAscii())
		vnew = (v+(self.multiple-1))/self.multiple * self.multiple
		input.replace(0,len(input),str(vnew))

	def validate(self,input,pos):
		try:
			v = int(input.toAscii())
			if v/self.multiple *self.multiple == v:
				return (QtGui.QValidator.Acceptable,pos)
		except:
			return (QtGui.QValidator.Invalid,pos)
		return (QtGui.QValidator.Intermediate,pos)
#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------
class RestrictedSpinBox(QtGui.QSpinBox):
#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------
	def __init__(self,parent=None):
		QtGui.QSpinBox.__init__(self,parent)
		self.restriction = []
	def setRestriction(self,value):
		self.restriction = value
	def fixup(self,input):
		v = int(input.toAscii())
		if len(self.restriction)>0:
			if self.restriction.count(v)==0:
				input.replace(0,len(str(self.restriction[0])),str(self.restriction[0]))
	def validate(self,input,pos):
		try:
			v = int(input.toAscii())
			if len(self.restriction)>0:
				if self.restriction.count(v)>0:
					return (QtGui.QValidator.Acceptable,pos)
			else:
				return (QtGui.QValidator.Acceptable,pos)
		except:
			return (QtGui.QValidator.Invalid,pos)
		return (QtGui.QValidator.Intermediate,pos)

class DoubleColourSlider(QtGui.QWidget):
  def __init__(self,parent=None):
    QtGui.QWidget. __init__(self,parent)
    colourLayout = QtGui.QGridLayout()
    #colourLayout.setContentsMargins(0,0,0,0)
    unAlignedLabel = QtGui.QLabel(self.tr("Unaligned colour"),self)
    self.nonAlignedCombo = mgColourCombo(self,'white',browser=1)
    interpLabel = QtGui.QLabel(self.tr("Blend from "),self)
    interpLabel2 = QtGui.QLabel(self.tr(" to "),self)
    self.interpCombo = QtGui.QComboBox(self)
    self.interpCombo.addItems(["Between RGB values","Colour wheel clockwise","Colour wheel anti-clockwise"])
    self.slider = UpperLowerSlider(QtCore.Qt.Horizontal,self)
    self.slider.setRange(0,1,minDecimals=4)
    self.lowColourCombo = mgColourCombo(self,'yellow',browser=1)
    self.highColourCombo = mgColourCombo(self,'red',browser=1)
    editMax = QtGui.QPushButton("Change max. value")
    colourLayout.addWidget(interpLabel,0,0)
    colourLayout.addWidget(self.lowColourCombo,0,1)
    colourLayout.addWidget(interpLabel2,0,2)
    colourLayout.addWidget(self.highColourCombo,0,3)
    colourLayout.addWidget(self.interpCombo,1,1,1,3)
    colourLayout.addWidget(self.slider,2,0,1,4)
    colourLayout.addWidget(unAlignedLabel,3,0)
    colourLayout.addWidget(self.nonAlignedCombo,3,1)
    colourLayout.addWidget(editMax,3,3)
    self.setLayout(colourLayout)
    self.connect(self.lowColourCombo,QtCore.SIGNAL('currentIndexChanged(const QString&)'),self.valuesChanged)
    self.connect(self.highColourCombo,QtCore.SIGNAL('currentIndexChanged(const QString&)'),self.valuesChanged)
    self.connect(self.nonAlignedCombo,QtCore.SIGNAL('currentIndexChanged(const QString&)'),self.valuesChanged)
    self.connect(self.interpCombo,QtCore.SIGNAL('currentIndexChanged(const QString&)'),self.valuesChanged)
    self.connect(self.slider,QtCore.SIGNAL("rangeChanged"),self.valuesChanged)
    self.connect(self, QtCore.SIGNAL('finished(int)'), self.hide)
    self.connect(editMax, QtCore.SIGNAL('clicked(bool)'), self.editMaximumValue)

  def editMaximumValue(self):
    w = QtGui.QDialog()
    layout = QtGui.QVBoxLayout()
    lineEdit = QtGui.QLineEdit()
    validator = QtGui.QDoubleValidator(lineEdit)
    lineEdit.setText(str(self.slider.maximum()))
    lineEdit.setValidator(validator)
    layout.addWidget(lineEdit)
    w.setLayout(layout)
    w.setWindowTitle("Set maximum slider value")
    def setEditMax():
      minVal = self.slider.minimum()
      maxVal = float(lineEdit.text())
      decimals = self.slider.decimals()
      lower = self.slider.lowerSpin.value()
      upper = self.slider.upperSpin.value()
      self.slider.setRange(minVal,maxVal,decimals)
      self.slider.lowerSpin.setValue(lower)
      self.slider.upperSpin.setValue(upper)
    self.connect(lineEdit,QtCore.SIGNAL("textEdited(const QString&)"),setEditMax)
    w.exec_()

  def getValues(self):
    import atom_util
    mode = 1
    dirn = atom_util.COLOUR_WHEEL_CLOCK
    if str(self.interpCombo.currentText().toUtf8()) == "Colour wheel clockwise":
      mode = 2
    if str(self.interpCombo.currentText().toUtf8()) == "Colour wheel anti-clockwise":
      mode = 2
      dirn = atom_util.COLOUR_WHEEL_ANTICLOCK
    colourInfo = {'lowerColour':str(self.lowColourCombo.currentText().toUtf8()),'upperColour':str(self.highColourCombo.currentText().toUtf8()),'lower':self.slider.getValues()[0],'upper':self.slider.getValues()[1],"mode":mode,"direction":dirn,'noneColour':str(self.nonAlignedCombo.currentText().toUtf8())}
    return colourInfo

  def getValuesAsColourInfo(self):
    import atom_util
    mode = 1
    dirn = atom_util.COLOUR_WHEEL_CLOCK
    if str(self.interpCombo.currentText().toUtf8()) == "Colour wheel clockwise":
      mode = 2
    if str(self.interpCombo.currentText().toUtf8()) == "Colour wheel anti-clockwise":
      mode = 2
      dirn = atom_util.COLOUR_WHEEL_ANTICLOCK
        
    colourInfo = mgapp.DoubleColourSliderInfo(str(self.lowColourCombo.currentText().toUtf8()),str(self.highColourCombo.currentText().toUtf8()),self.slider.getValues()[0],self.slider.getValues()[1],mode,dirn,str(self.nonAlignedCombo.currentText().toUtf8()))
    return colourInfo

  def valuesChanged(self,val=None):
    import atom_util
    if QtCore.QString('Colour browser') == val:
       return
    mode = 1
    dirn = atom_util.COLOUR_WHEEL_CLOCK
    if str(self.interpCombo.currentText().toUtf8()) == "Colour wheel clockwise":
      mode = 2
    if str(self.interpCombo.currentText().toUtf8()) == "Colour wheel anti-clockwise":
      mode = 2
      dirn = atom_util.COLOUR_WHEEL_ANTICLOCK
        
    colourInfo = mgapp.DoubleColourSliderInfo(str(self.lowColourCombo.currentText().toUtf8()),str(self.highColourCombo.currentText().toUtf8()),self.slider.getValues()[0],self.slider.getValues()[1],mode,dirn,str(self.nonAlignedCombo.currentText().toUtf8()))
    self.emit(QtCore.SIGNAL("valuesChanged"),colourInfo)
