
"""
     CCP4ProjectManagerGui.py: CCP4 GUI Project
     Copyright (C) 2010 University of York

     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 sstatusW
     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.
"""

"""
     Liz Potterton Feb 2010 - Create CCP4ProjectManager mostly as placeholder
     Liz Potterton May 2010 - Rename CCP4ProjectManagerGui 
"""

##@package CCP4ProjectManagerGui (QtGui) Project handling tools for web browser (currently just a Project menu)
import functools
from PyQt4 import QtGui,QtCore
from CCP4Modules import PROJECTSMANAGER,WEBBROWSER,PREFERENCES,MIMETYPESHANDLER
from CCP4ErrorHandling import *
import CCP4Widgets
from CCP4Config import DEVELOPER

DIAGNOSTIC = True

def openProject(projectId=None,projectName=None):
    print 'openProject',projectId,projectName,
    if projectId is None: projectId = PROJECTSMANAGER().db().getProjectId(projectName=projectName)
    if projectId is None: return None
    import CCP4ProjectViewer
    for window in CCP4ProjectViewer.CProjectViewer.Instances:
      if  window.projectId() == projectId:
        window.show()
        window.raise_()
        return window
   
    p = CCP4ProjectViewer.CProjectViewer(projectId=projectId)
    p.show()
    p.raise_()
    return p


def formatDate(fTime):
  import time
  import CCP4ProjectWidget
  if fTime is None: return ''
  try:
    date = time.strftime(CCP4ProjectWidget.CTreeItemJob.DATE_FORMAT,time.localtime(int(fTime)))
    if date == CCP4ProjectWidget.CTreeItemJob.TODAY:
      date = time.strftime(CCP4ProjectWidget.CTreeItemJob.TIME_FORMAT,time.localtime(int(fTime)))
    elif date.split(' ')[-1] == CCP4ProjectWidget.CTreeItemJob.THISYEAR:
      date=date.rsplit(' ',1)[0]
    else:
      date=date.split(' ',1)[1]
    return  date
  except:
    return ''


    
class CNewProjectGui(QtGui.QDialog):

  insts = None

  def __init__(self,parent=None):
    QtGui.QDialog.__init__(self,parent)
    # This makes this window stay on top even when we want the file browser to be on top
    #self.setModal(True)
    self.setWindowTitle('Create a New Project')
    self.setLayout(QtGui.QVBoxLayout())

    import CCP4Data
    import CCP4File
    import CCP4DataManager
    self.name = CCP4Data.CString(parent=self)
    self.directory = CCP4File.CDataFile(parent=self,qualifiers={'isDirectory' : True, 'mustExist': False, 'fromPreviousJob' : False  })


    MARGIN = 1

    line = QtGui.QHBoxLayout()
    line.addWidget(QtGui.QLabel('Name of project/folder',self))
    self.nameWidget = CCP4DataManager.DATAMANAGER().widget(model=self.name,parentWidget=self)
    self.nameWidget.setToolTip('Project name - may be used by programs that only allow\none word of alphanumeric characters, dash or underscore.')
    line.addWidget(self.nameWidget)
    self.layout().addLayout(line)

    for textLine in [   "By default all projects go in the 'CCP4I2_PROJECTS' directory in your",
                     "home area - click 'Select directory' to choose an alternative.",
                     "Hint to organise your projects: in the 'Manage projects' window",
                     "you can use a project as a folder and drag other projects into it" ]:
                     
      line = QtGui.QHBoxLayout()
      line.addWidget(QtGui.QLabel(textLine,self))
      self.layout().addLayout(line)
    line = QtGui.QHBoxLayout()
    self.descriptionWidget = CProjectDescription(self,projectId=None,projectName='New project')
    line.addWidget(self.descriptionWidget)
    self.layout().addLayout(line)

    line = QtGui.QHBoxLayout()
    buttonBox = QtGui.QDialogButtonBox(self)
    but = buttonBox.addButton('Create project',QtGui.QDialogButtonBox.AcceptRole)
    but.setFocusPolicy(QtCore.Qt.NoFocus)
    but.setDefault(True)
    self.connect(but,QtCore.SIGNAL('released()'),self.addProject)
    but = buttonBox.addButton('Select directory',QtGui.QDialogButtonBox.ActionRole)
    but.setFocusPolicy(QtCore.Qt.NoFocus)
    self.connect(but,QtCore.SIGNAL('released()'),self.selectDir)
    but = buttonBox.addButton(QtGui.QDialogButtonBox.Cancel)
    but.setFocusPolicy(QtCore.Qt.NoFocus)
    self.connect(but,QtCore.SIGNAL('released()'),self.close)
    but = buttonBox.addButton(QtGui.QDialogButtonBox.Help)
    but.setFocusPolicy(QtCore.Qt.NoFocus)
    self.connect(but,QtCore.SIGNAL('released()'),self.help)
    line.addStretch(0.5)
    line.addWidget(buttonBox)
    line.addStretch(0.5)
    self.layout().addLayout(line)

    self.connect(self.directory,QtCore.SIGNAL('dataChanged'),self.setProjectName)
    self.connect(self,QtCore.SIGNAL('rejected()'),self.close)


  def setProjectName(self):
    import os
    if not self.directory.isSet() or self.name.isSet(): return
    tail = os.path.split(self.directory.__str__())[1]
    #print 'setProjectName',tail
    self.name.set(tail)
    self.nameWidget.updateViewFromModel()
      
  def help(self):
    import CCP4Modules
    CCP4Modules.WEBBROWSER().loadWebPage(helpFileName='general/tutorial' , target='projects')

  def clear(self):
    self.name.unSet()
    self.directory.unSet()
    #self.directoryWidget.updateViewFromModel()
    self.nameWidget.updateViewFromModel()

  def selectDir(self):
    self.nameWidget.updateModelFromView()
    name = self.name.get()
    if name is None:
      QtGui.QMessageBox.warning(self,'No project name','You must provide a project name')
      return
    elif PROJECTSMANAGER().projectNameStatus(name) is not 0:
      QtGui.QMessageBox.warning(self,'Project exists','A project of that name already exists')
      return
  
    rv = QtGui.QFileDialog.getExistingDirectory(caption='Select directory for saving project '+str(name))
    #print 'selectDir getExistingDirectory',rv,len(rv)
    if rv is not None and len(rv)>0: self.addProject(str(rv))
    
  def addProject(self,directory = None):
    #print 'CNewProjectGui.addProject',directory,'*'
    import os
    import CCP4Utils
    self.nameWidget.updateModelFromView()
    name = self.name.get()
    #print 'CNewProjectGui.addProject',name 
    if name is None:
      QtGui.QMessageBox.warning(self,'No project name','You must provide a project name')
      return
    elif PROJECTSMANAGER().projectNameStatus(name) is not 0:
      QtGui.QMessageBox.warning(self,'Project exists','A project of that name already exists')
      return

    name0 = CCP4Utils.safeOneWord(name)
    if name0 != name:
      print 'Fixing name to ',name0
  
    if directory is None:
      directory = CCP4Utils.getProjectDirectory()
      if directory is None:
        QtGui.QMessageBox.warning(self,'CCP4I2_PROJECTS directory','Failed creating a CCP4I2_PROJECTS directory in your home directory')
        return
      directory = os.path.join(directory,name0)
    else:
      directory = os.path.normpath(directory)
      if os.path.isfile(directory):
        QtGui.QMessageBox.warning(self,'Create project','Selected directory is a file')
        return
      elif CCP4Utils.samefile(directory,CCP4Utils.getHOME()):
        QtGui.QMessageBox.warning(self,'Create project','Selected directory is users home area')
        return
      elif CCP4Utils.samefile(directory,CCP4Utils.getProjectDirectory()):
        QtGui.QMessageBox.warning(self,'Create project','Selected directory is the CCP4 master project directory')
        return


          
    altName = PROJECTSMANAGER().aliasForDirectory(directory)
    if altName is not None:
         QtGui.QMessageBox.warning(self,'Directory already a project','This directory is already used by the project: '+altName)
         return

    parentProject,relpath,parentProjectId =  PROJECTSMANAGER().interpretDirectory(directory)
    if parentProject is not None:
      rv = QtGui.QMessageBox.question(self,'Make sub-project',
          'This is sub-directory of another project \nMake this project a sub-project of '+parentProject,
          QtGui.QMessageBox.Ok|QtGui.QMessageBox.Cancel)
      if rv == QtGui.QMessageBox.Cancel: return

    if not os.path.exists(directory):
      parentDir = os.path.split(directory)[0]
      if not os.path.exists(parentDir):
        QtGui.QMessageBox.warning(self,'Directory does not exist','The parent directory for the project directory does not exist: ' +parentDir )
        return

    
    
      '''
      rv = QtGui.QMessageBox.question(self,'Make new directory','Make a new directory for the project',
                            QtGui.QMessageBox.Ok|QtGui.QMessageBox.Cancel)
      if rv == QtGui.QMessageBox.Cancel: return
      '''

      try:
        os.mkdir(directory)
      except:
         QtGui.QMessageBox.warning(self,'Error making directory','Error making directory: '+directory)
         return

    if DEVELOPER():
      projectId = PROJECTSMANAGER().createProject(projectName=name0,projectPath=directory)
    else:
      try:
        projectId = PROJECTSMANAGER().createProject(projectName=name0,projectPath=directory)
      except CException as e:
        e.warningMessage(parent=self)
        return
      except:
        QtGui.QMessageBox.warning(self,'Error creating project','Unknown error creating project')
        return

    self.descriptionWidget.save(projectId)

    '''
    if self.xtalContents.isChecked() and pView is not None:
      pView.openTask(taskName='newProject')
    '''
    self.emit(QtCore.SIGNAL('projectCreated'),projectId)
    pView = openProject(projectId)
      
    #print 'CNewProjectGui.addProject to close'
    self.close()

  def close(self):
    for child in self.findChildren(QtGui.QDialog):
      child.close()
    QtGui.QDialog.close(self)

class CExportOptionsDialog(QtGui.QDialog):

  def __init__(self,parent=None,projectId=None,projectName=None):
    QtGui.QDialog.__init__(self,parent)
    #self.setModal(True)
    self.setWindowTitle('Options for saving project: '+str(projectName))
    self.setLayout(QtGui.QVBoxLayout())

    self.modeGroup = QtGui.QButtonGroup(self)
    self.modeGroup.setExclusive(True)

    line = QtGui.QHBoxLayout()
    but = QtGui.QRadioButton('Export entire project',self)
    self.modeGroup.addButton(but,1)
    but.setChecked(True)
    line.addWidget(but)
    self.layout().addLayout(line)
    line = QtGui.QHBoxLayout()
    but = QtGui.QRadioButton('Export every job after previous import/export',self)
    self.modeGroup.addButton(but,2)
    line.addWidget(but)
    self.layout().addLayout(line)

    line = QtGui.QHBoxLayout()
    self.impExpWidget = CImportExportListWidget(self,projectId=projectId)
    line.addSpacing(20)
    line.addWidget(self.impExpWidget)
    self.layout().addLayout(line)
    
    line = QtGui.QHBoxLayout()
    but = QtGui.QRadioButton('Select jobs to export',self)
    self.modeGroup.addButton(but,3)
    line.addWidget(but)
    self.layout().addLayout(line)

    line = QtGui.QHBoxLayout()
    self.selectionWidget = CCP4Widgets.CJobSelectionLineEdit(self,projectId)
    line.addSpacing(20)
    line.addWidget(self.selectionWidget)
    self.layout().addLayout(line)


    #if DEVELOPER():
    if False:
      line = QtGui.QHBoxLayout()
      self.excludeI2files = QtGui.QCheckBox(self)
      self.excludeI2files.setChecked(True)
      line.addWidget(self.excludeI2files)
      line.addWidget(QtGui.QLabel('Exclude demo data files from CCP4I2 distribution',self))
      self.layout().addLayout(line)
    
    line = QtGui.QHBoxLayout()
    buttonBox = QtGui.QDialogButtonBox(self)
    but = buttonBox.addButton('Export',QtGui.QDialogButtonBox.AcceptRole)
    but.setFocusPolicy(QtCore.Qt.NoFocus)
    self.connect(but,QtCore.SIGNAL('released()'),self.handleExport)
    but = buttonBox.addButton(QtGui.QDialogButtonBox.Cancel)
    self.connect(but,QtCore.SIGNAL('released()'),self.close)
    but = buttonBox.addButton(QtGui.QDialogButtonBox.Help)
    but.setFocusPolicy(QtCore.Qt.NoFocus)
    self.connect(but,QtCore.SIGNAL('released()'),self.help)
    line.addStretch(0.5)
    line.addWidget(buttonBox)
    line.addStretch(0.5)
    self.layout().addLayout(line)

  def handleExport(self):
    self.emit(QtCore.SIGNAL('export'),self.modeGroup.checkedId())

  def help(self):
    pass

class CExportJobSelection(QtGui.QLineEdit):
    def __init__(self,parent,projectId=None):
      QtGui.QLineEdit.__init__(self,parent)
      self.projectId = projectId
      self.setToolTip("Enter list of jobs e.g. '27-29,31'")

    def getSelection(self):
      errList = []
      seleList = []
      text = str(self.text())
      splitList = text.split(',')
      #print 'CExportJobSelection.getSelection',text,type(text)
      for item in splitList:
        #print 'CExportJobSelection.getSelection split',item
        rSplit = item.split('-')
        if len(rSplit)==1:
          try:
            jobId = PROJECTSMANAGER().db().getJobId(projectId=self.projectId,jobNumber=item.strip())
          except:
            errList.append(item)
          else:
            seleList.append(jobId)
        elif len(rSplit)==2:
          try:
            jobList = PROJECTSMANAGER().db().getJobsInRange(projectId=self.projectId,jobNumberRange=[rSplit[0].strip(),rSplit[1].strip()])
          except:
            errList.append(item)
          else:
            if len(jobList)==0:
              errList.append(item)
            else:
              seleList.extend(jobList)
        else:
           errList.append(item)
      #print 'CExportJobSelection.getSelection', seleList,errList
      return seleList,errList

class CImportExportListWidget(QtGui.QComboBox):
    
    def __init__(self,parent,projectId=None):
      QtGui.QComboBox.__init__(self,parent)
      self.projectId = projectId

      self.setEditable(False)
      self.load()

    def load(self):
      import time
      exportList = PROJECTSMANAGER().db().getProjectExportInfo(projectId=self.projectId)
      importList = PROJECTSMANAGER().db().getProjectImportInfo(projectId=self.projectId)
      #print 'CImportExportListWidget.load',len(exportList),len(importList)
      def formatImport(data):
        imDate = time.strftime( '%a %d %b %H:%M' , time.localtime(data[1]))
        exDate = time.strftime( '%a %d %b %H:%M' , time.localtime(data[2]))
        text = 'Import '+imDate+' from '+importList[iIm][3]+ ' on '+exDate
        return text,QtCore.QVariant(data[0])
      def formatExport(data):
        exDate = time.strftime( '%a %d %b %H:%M' , time.localtime(data[1]))
        text = 'Export '+exDate
        return text,QtCore.QVariant(data[0])
      iEx = 0
      iIm = 0
      while ( iEx<len(exportList) or iIm<len(importList) ):
        #print 'CImportExportListWidget.load',iEx,iIm
        if iEx>=len(exportList):
          text,qVar = formatImport(importList[iIm])
          iIm += 1
        elif iIm>=len(importList):
          text,qVar = formatExport(exportList[iEx])
          iEx += 1
        elif importList[iIm][1]>exportList[iEx][1]:
          text,qVar = formatImport(importList[iIm])
          iIm += 1
        else:
          text,qVar = formatExport(exportList[iEx])
          iEx += 1
        self.addItem(text,qVar)

    def getCurrentItem(self):
      qVar = self.itemData(self.currentIndex())
      #print 'CImportExportListWidget.getCurrentItem',qVar
      return qVar.toString().__str__()

    def getTime(self):
      tid = self.getCurrentItem()
      try:
        info = PROJECTSMANAGER().db().getProjectExportInfo(projectExportId=tid)
      except:
        info = {}
      if info.get('projectexporttime',None) is not None:
        return info['projectexporttime']
      else:
        info = PROJECTSMANAGER().db().getProjectImportInfo(projectImportId=tid)
        return info['projectimporttime']


class CImportNewProjectDialog(QtGui.QDialog):

  def __init__(self,parent=None):
    MARGIN = 2
    QtGui.QDialog.__init__(self,parent)
    #self.setModal(True)
    self.setWindowTitle('Import a New Project?')
    self.setLayout(QtGui.QVBoxLayout())

    import CCP4Data
    import CCP4File
    import CCP4DataManager

    self.projectInfoDisplay = CProjectInfoDisplay(self)
    self.layout().addWidget(self.projectInfoDisplay)

    line = QtGui.QHBoxLayout()
    lab = QtGui.QLabel('The project id in the file does not match any existing project.')
    lab.setObjectName('emphasise')
    line.addWidget(lab)
    self.layout().addLayout(line)
    self.modeButs = QtGui.QButtonGroup(self)
    self.modeButs.setExclusive(True)
    but =  QtGui.QRadioButton('Create a new project directory',self)
    but.setChecked(True)
    self.modeButs.addButton(but,1)
    self.layout().addWidget(but)
    
    line = QtGui.QFrame()
    line.setLayout(QtGui.QHBoxLayout())
    line.layout().setContentsMargins(MARGIN,MARGIN,MARGIN,MARGIN)
    line.layout().setSpacing(MARGIN)
    line.layout().addSpacing(30)
    self.directory0 = CCP4File.CDataFile(parent=self,qualifiers={ 'isDirectory' : True,'mustExist': False,'fromPreviousJob' : False  })
    self.directoryWidget0 = CCP4DataManager.DATAMANAGER().widget(model=self.directory0,parentWidget=self,qualifiers={'jobCombo': False, 'projectBrowser' : False} )
    self.directoryWidget0.fileLineEdit.setCharWidth(60,mode='minimum')
    line.layout().addWidget(self.directoryWidget0)
    self.layout().addWidget(line)

    line = QtGui.QHBoxLayout()
    line.addSpacing(30)
    line.addWidget(QtGui.QLabel('Project name',self))
    self.projectNameWidget = QtGui.QLineEdit(self)
    line.addWidget(self.projectNameWidget)
    self.layout().addLayout(line)

    but = QtGui.QRadioButton('Add to exisiting project',self)
    self.modeButs.addButton(but,2)
    self.layout().addWidget(but)

    line = QtGui.QHBoxLayout()
    line.layout().addSpacing(30)
    self.dbTreeWidget = CProjectsTreeWidget(self)
    self.dbTreeWidget.populate()
    line.addWidget(self.dbTreeWidget)
    self.layout().addLayout(line)

    line = QtGui.QHBoxLayout()
    buttonBox = QtGui.QDialogButtonBox(self)
    but = buttonBox.addButton('Import',QtGui.QDialogButtonBox.AcceptRole)
    self.connect(but,QtCore.SIGNAL('released()'),self.handleAccept)
    but.setFocusPolicy(QtCore.Qt.NoFocus)
    but = buttonBox.addButton(QtGui.QDialogButtonBox.Cancel)
    but.setFocusPolicy(QtCore.Qt.NoFocus)
    but.setDefault(True)
    self.connect(but,QtCore.SIGNAL('released()'),self.close)
    line.addStretch(0.1)
    line.addWidget(buttonBox)
    line.addStretch(0.1)
    self.layout().addLayout(line)

  def reset(self,importProjectInfo):
    self.directory0.unSet()
    self.projectInfoDisplay.load(importProjectInfo)
    self.projectNameWidget.setText(importProjectInfo['projectName'])

  def handleAccept(self):
    if self.modeButs.checkedId() == 1:
      if not self.directory0.isSet():
        QtGui.QMessageBox.warning(self,'Import a New Project?','Please enter a new project directory')
        return
      projectName = self.projectName()
      if len(projectName)==0:
        QtGui.QMessageBox.warning(self,'Import a New Project?','Please enter a name for the new project')
        return
      try:
        projectId = PROJECTSMANAGER().db().getProjectId(projectName = projectName)
      except:
        pass
      else:
        QtGui.QMessageBox.warning(self,'Import a New Project?','There is already a project in the database called '+projectName)
        return
    else:
      pid = self.dbTreeWidget.selectedProjectId()
      if pid is None:
        QtGui.QMessageBox.warning(self,'Import a New Project?','Please select a project to merge with')
        return
    self.emit(QtCore.SIGNAL('import'))

  def mode(self):
    return self.modeButs.checkedId()

  def newDirectory(self):
    return  self.directory0.__str__()

  def projectName(self):
    name = str(self.projectNameWidget.text()).strip()
    if len(name)==0: name = None
    return name

  def mergeProject(self):
    return self.dbTreeWidget.selectedProjectId()
    
class CImportExistingProjectDialog(QtGui.QDialog):

  def __init__(self,parent=None,projectName=None):
    MARGIN = 2
    QtGui.QDialog.__init__(self,parent)
    #self.setModal(True)
    self.setWindowTitle('Import to an Existing Project?')
    self.setLayout(QtGui.QVBoxLayout())

    import CCP4Data
    import CCP4File
    import CCP4DataManager

    self.projectInfoDisplay = CProjectInfoDisplay(self)
    self.layout().addWidget(self.projectInfoDisplay)

    line = QtGui.QHBoxLayout()
    lab = QtGui.QLabel('The project id in the file matches project '+projectName)
    lab.setObjectName('emphasise')
    line.addWidget(lab)
    self.layout().addLayout(line)
    line = QtGui.QHBoxLayout()
    line.addWidget( QtGui.QLabel('Import jobs to that existing project'))
    self.layout().addLayout(line)
    
    line = QtGui.QHBoxLayout()
    buttonBox = QtGui.QDialogButtonBox(self)
    but = buttonBox.addButton('Import',QtGui.QDialogButtonBox.AcceptRole)
    self.connect(but,QtCore.SIGNAL('released()'),functools.partial(self.emit,QtCore.SIGNAL('import')))
    but.setFocusPolicy(QtCore.Qt.NoFocus)
    but = buttonBox.addButton(QtGui.QDialogButtonBox.Cancel)
    but.setFocusPolicy(QtCore.Qt.NoFocus)
    but.setDefault(True)
    self.connect(but,QtCore.SIGNAL('released()'),self.close)
    line.addStretch(0.1)
    line.addWidget(buttonBox)
    line.addStretch(0.1)
    self.layout().addLayout(line)

  def reset(self,importProjectInfo):
    self.projectInfoDisplay.load(importProjectInfo)


class CProjectInfoDisplay(QtGui.QFrame):
  def __init__(self,parent,projectInfo={}):
    QtGui.QFrame.__init__(self,parent)
    self.setObjectName('highlight')
    self.setLayout(QtGui.QVBoxLayout())
    self.widgets = {}
    for item in ['projectName','hostName','userId','creationTime']:
       self.widgets[item] = QtGui.QLabel(self)

    line = QtGui.QHBoxLayout()
    line.addWidget(QtGui.QLabel('The compressed file contains data for project: ',self))
    line.addWidget(self.widgets['projectName'])
    line.addStretch(0.5)
    self.layout().addLayout(line)

    line = QtGui.QHBoxLayout()
    line.addWidget(QtGui.QLabel('Created on',self))
    line.addWidget(self.widgets['hostName'])
    line.addWidget(QtGui.QLabel('by:',self))
    line.addWidget(self.widgets['userId'])
    line.addWidget(QtGui.QLabel('on:',self))
    line.addWidget(self.widgets['creationTime'])
    line.addStretch(0.5)
    self.layout().addLayout(line)
    
    self.load(projectInfo)

  def load(self,projectInfo):
    #print 'CProjectInfoDisplay.load',projectInfo
    for item in ['projectName','hostName','userId']:
       self.widgets[item].setText(str(projectInfo.get(item,'')))
    import time
    t = projectInfo.get('creationTime',None)
    if t is None:
      text = ''
    else:
      text = time.strftime(PROJECTSMANAGER().db().TIMEFORMAT,time.localtime(float(t)))
    self.widgets['creationTime'].setText(text)
       
class CProjectsTreeWidget(QtGui.QTreeWidget):

  PROJECTICON = None
  
  def __init__(self,parent):
    QtGui.QTreeWidget.__init__(self,parent)
    self.projInfo = {}
    self.setColumnCount(2)
    self.expandAll()
    self.setHeaderLabels(['Name','Directory','Created','Last active','Tags'])
    self.setItemsExpandable(True)
    self.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
    self.setDragEnabled(True)
    self.setAcceptDrops(True)
    self.header().resizeSection(0,300)
    #self.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
    self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('projectTagsChanged'),self.handleProjectTagsChanged)

  def projectIcon(self):
    if CProjectsTreeWidget.PROJECTICON is None:
      import CCP4Utils,os
      fileName = os.path.join(CCP4Utils.getCCP4I2Dir(),'qticons','project.png')
      CProjectsTreeWidget.PROJECTICON = QtGui.QIcon(QtGui.QPixmap(fileName))
    return CProjectsTreeWidget.PROJECTICON

  def populate(self,args={}):
    self.clear()
    proj_dir_list0=PROJECTSMANAGER().db().getProjectDirectoryList()
    #print 'CProjectsView.populate',proj_dir_list
    proj_dir_list = []
    self.projInfo = {}
    for pid,pName,pDir,parent,created,lastAccess in proj_dir_list0:
      if parent is None: proj_dir_list.append(pid)
      self.projInfo[pid] = { 'name' : pName,
                        'dir' : pDir,
                        'children' : [],
                        'created' : created,
                        'lastAccess' : lastAccess,
                        'tags' : '',
                        'annotation' : '' }
     
    for pid,pName,pDir,parent,created,lastAccess in proj_dir_list0:
      if parent is not None:
        self.projInfo[parent]['children'].append(pid)

    pTagList = PROJECTSMANAGER().db().getProjectTagList()
    for pid,tagText in pTagList:
      self.projInfo[pid]['tags'] += ','+tagText
    
    commentList = PROJECTSMANAGER().db().getProjectCommentsList()
    for pid,comment in commentList:
      self.projInfo[pid]['annotation'] = comment
    #print 'CProjectsTreeWidget',self.projInfo
    self.drawTree(projectList=proj_dir_list,parent=None)

  def handleProjectTagsChanged(self,projectId):
    #print 'CProjectsTreeWidget.handleProjectTagsChanged',projectId,self.projInfo[projectId]['name']
    tagList = PROJECTSMANAGER().db().getProjectTags(projectId,tagText=True)
    text = ''
    for tid,tagText in tagList:
      text += ','+tagText
    treeWidgetItemList = self.findItems(self.projInfo[projectId]['name'],QtCore.Qt.MatchExactly|QtCore.Qt.MatchRecursive,0)
    #print 'handleProjectTagsChanged',treeWidgetItemList,text
    if len(treeWidgetItemList)==1:
      treeWidgetItemList[0].setData(4,QtCore.Qt.DisplayRole,QtCore.QVariant(text[1:]))
      self.update(self.indexFromItem(treeWidgetItemList[0],4))

  def makeTreeWidgetItem(self,pid):
    
    t1 = formatDate(self.projInfo[pid]['created'])
    t2 = formatDate(self.projInfo[pid]['lastAccess'])
    item = QtGui.QTreeWidgetItem([self.projInfo[pid]['name'],self.projInfo[pid]['dir'],t1,t2,self.projInfo[pid]['tags'][1:]],1001)
    item.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled|QtCore.Qt.ItemIsDragEnabled)
    item.setData(0,QtCore.Qt.UserRole,QtCore.QVariant(pid))
    item.setData(0,QtCore.Qt.DecorationRole,self.projectIcon())
    item.setData(0,QtCore.Qt.ToolTipRole,self.projInfo[pid]['annotation'])
    return item

  def drawTree(self,projectList=None,parent=None):
    for pid in projectList:
      item = self.makeTreeWidgetItem(pid)
      if parent is None:
        self.addTopLevelItem(item)
      else:
        parent.addChild(item)
      if len(self.projInfo[pid]['children'])>0:
        self.drawTree(self.projInfo[pid]['children'],item)

  def setHighlights(self,treeItem=None,highlightList=[],colour=['red','pink']):
    #print 'setHighlights',highlightList
    nHits = 0
    if treeItem is None:
      for n in range(self.topLevelItemCount()):
        item = self.topLevelItem(n)
        childHits = self.setHighlights(item,highlightList,colour)
        if highlightList.count(item.data(0,QtCore.Qt.UserRole).toString().__str__()):
          item.setData(0,QtCore.Qt.ForegroundRole,QtGui.QBrush(QtGui.QColor(colour[0])))
          nHits += 1
        elif childHits>0:
         item.setData(0,QtCore.Qt.ForegroundRole,QtGui.QBrush(QtGui.QColor(colour[1])))
        else:
          item.setData(0,QtCore.Qt.ForegroundRole,QtGui.QBrush(QtGui.QColor('black')))
        #if childHits>0: item.setExpanded(True)
        nHits+= childHits
      self.update()
    else:
      for n in range(treeItem.childCount()):
        item = treeItem.child(n)
        childHits = self.setHighlights(item,highlightList,colour)
        if highlightList.count(item.data(0,QtCore.Qt.UserRole).toString().__str__()):
          item.setData(0,QtCore.Qt.ForegroundRole,QtGui.QBrush(QtGui.QColor(colour[0])))
          nHits += 1
        elif childHits>0:
         item.setData(0,QtCore.Qt.ForegroundRole,QtGui.QBrush(QtGui.QColor(colour[1])))
        else:
          item.setData(0,QtCore.Qt.ForegroundRole,QtGui.QBrush(QtGui.QColor('black')))
        #if childHits>0: item.setExpanded(True)
        nHits+= childHits
    #print 'setHighlights nHits',nHits
    return nHits 

  
  def sizeHint(self):
    return QtCore.QSize(600,200)
  
  
  def mousePressEvent(self,event):
    if event.button() == QtCore.Qt.RightButton:
      self.emit(QtCore.SIGNAL('rightMousePress'),event)      
    QtGui.QTreeWidget.mousePressEvent(self,event)

  def selectedItem(self):
    seleList = self.selectedItems()
    #print 'contentsTree.selectedItem',seleList
    if len(seleList) == 0:
      return None
    elif len(seleList) == 1:
      return seleList[0]
    else:
      #print 'Error in CContentsTree - more than one selected item'
      return seleList[0]
    
  def mimeData(self,widgetItemList):
    import CCP4Data
    #print 'CProjectsTreeWidget.mimeData',widgetItemList
    encodedData = QtCore.QByteArray()
    for widgetItem in widgetItemList:
      pid = CCP4Data.varToUUID(widgetItem.data(0,QtCore.Qt.UserRole))
      #print 'CProjectsTreeWidget.mimeData',pid
      encodedData.append(str(pid))
    # With mime type as text the data can be dropped on desktop
    # but the type of the data is lost
    mimeData = QtCore.QMimeData()
    mimeData.setData('project',encodedData)
    #mimeData.setText('project '+str(pid))
    return mimeData

  def dragEnterEvent(self,event):
    if event.mimeData().hasFormat('project'):
      event.accept()
    else:
      event.ignore()

  def dragMoveEvent(self,event):
    dropItem = self.itemAt(event.pos().x(),event.pos().y())
    #print 'dragMoveEvent',event.mimeData().hasFormat('project')
    #if event.mimeData().hasFormat('project') and dropItem is not None:
    if event.mimeData().hasFormat('project'):
      import CCP4DbApi
      movedProject = CCP4DbApi.UUIDTYPE(event.mimeData().data('project').data())
      targetProjectId = self.item2ProjectId(dropItem)
      if movedProject == targetProjectId:
        event.ignore()
      else:
        event.setDropAction(QtCore.Qt.MoveAction)
        event.accept()
    else:
      event.ignore()

  def dropEvent(self,event):
    targetItem = self.itemAt(event.pos().x(),event.pos().y())
    #print 'CProjectsTreeWidget.dropEvent',event,targetItem  
    if event.mimeData().hasFormat('project'):
      import CCP4DbApi
      movedProject = CCP4DbApi.UUIDTYPE(event.mimeData().data('project').data())
      targetProjectId = self.item2ProjectId(targetItem)
      if movedProject == targetProjectId:
        print 'ERROR trying to drop project in itself'
        return
      #print 'dropEvent targetProject',targetItem,targetProjectId
      try:
        PROJECTSMANAGER().db().updateProject(movedProject,key='parentProjectId',value=targetProjectId)
      except CException as e:
        print 'Failed to move project',e.report()
        event.setDropAction(QtCore.Qt.IgnoreAction)
        event.ignore()
        return
      except Exception as e:
        print 'Failed to move project',e
        event.setDropAction(QtCore.Qt.IgnoreAction)
        event.ignore()
        return
      #self.drawTree([movedProject],targetItem)
      #if targetProjectId is not None:
      #  self.projInfo[targetProjectId]['children'].append(movedProject)
      self.populate()
      event.setDropAction(QtCore.Qt.MoveAction)
      event.accept()
    else:
      event.ignore()

  def startDrag(self,dropActions):
    item = self.currentItem()
    projectId = self.item2ProjectId(item)
    mimeData = self.mimeData([item])
    drag = QtGui.QDrag(self)
    drag.setMimeData(mimeData)
    pixmap = item.icon(0).pixmap(18,18)
    drag.setHotSpot(QtCore.QPoint(9,9))
    drag.setPixmap(pixmap)
    if drag.exec_(QtCore.Qt.MoveAction) == QtCore.Qt.MoveAction:
      #self.takeItem(self.row(item))
      try:
        if item.parent() is None:
          indx = self.indexOfTopLevelItem(item)
          self.takeTopLevelItem(indx)
        else:
          parent = item.parent()
          parent.removeChild(item)
          parentId = self.item2ProjectId(parent)
          self.projInfo[parentId]['children'].remove(projectId)
      except:
          pass
          #print 'WARNING from CProjectsTreeWidget.startDrag'

  def selectedProjectId(self):
    selList = self.selectedItems()
    #print 'CProjectsTreeWidget.selectedProjectId',selList
    if len(selList)==0: return None
    return self.item2ProjectId(selList[0])
      
  def item2ProjectId(self,item):
    if item is None: return None
    import CCP4Data
    projectId = CCP4Data.varToUUID(item.data(0,QtCore.Qt.UserRole))
    return  projectId
  
  '''
  def mimeTypes(self):
    return ['project']

  def supportedDropActions(self):
    print 'supportedDropActions'
    return QtCore.Qt.MoveAction
      
  def dropMimeData(self,parent,index,data,action):
    print 'CProjectsTreeWidget.dropMimeData',parent,index,data,action
    return True
  '''

class CProjectsStatusBar(QtGui.QStatusBar):
  def __init__(self,parent):
    QtGui.QStatusBar.__init__(self,parent)
    self.setObjectName('statusWidget')
    self.setSizeGripEnabled(False)
    self.progressWidget =QtGui.QProgressBar(self)
    self.progressWidget.hide()

  def showMessage(self,text=None,timeout=0):
    #print 'CProjectsStatusBar.showMessage',text
    QtGui.QStatusBar.showMessage(self,text,timeout*1000)
    self.show()

  def clear(self):
    self.clearMessage()
    self.hideProgress()

  def showProgress(self,max=None,min=None):
    self.addPermanentWidget(self.progressWidget)
    self.progressWidget.show()
    self.progressWidget.setMaximum(max)

  def setProgress(self,value):
    self.progressWidget.setValue(value)

  def hideProgress(self):
    self.removeWidget(self.progressWidget)

class CProjectManagerDialog(QtGui.QDialog):

  def __init__(self,parent=None):
    QtGui.QDialog.__init__(self,parent)
    self.newProjectGui = None
    self.importProjectManager = None

    layout = QtGui.QGridLayout()
    self.setLayout(layout)
  
    self.projectsView = CProjectsTreeWidget(self)
    self.connect(self.projectsView,QtCore.SIGNAL('itemDoubleClicked(QTreeWidgetItem*,int)'),self.handleDoubleClick)
    self.projectsView.populate()
    self.layout().addWidget(self.projectsView,1,0)

    self.buttonFrame = QtGui.QFrame(self)
    self.buttonFrame.setLayout(QtGui.QVBoxLayout())
    buttonDef =  [      ['Open',self.openProject,True,True],
                        ['Add project or folder',self.addProject,True,False],
                        ['Edit description',self.handleEditDescription,True,False],
                        ['Rename project',self.handleRenameProject,True,False],
                        ['Move directory',self.handleMoveProject,True,False],
                        ['Delete',self.handleDeleteProject,True,False],
                        ['Cleanup files',self.handleCleanup,True,False],
                        ['Export',self.handleExport1,True,False],
                        ['Import',self.handleImportProject,True,False] ]
                        
    if DEVELOPER():
        buttonDef.append( ['Rerun test project',self.handleRerun,True,False] )
    
    for label,slot,enabled,default in buttonDef:
      button =QtGui.QPushButton(self,text=label)
      button.setEnabled(enabled)
      if default: button.setDefault(True)
      self.buttonFrame.layout().addWidget(button)
      self.connect(button,QtCore.SIGNAL('clicked()'),slot)
      
    self.layout().addWidget(self.buttonFrame,1,1)

    self.statusWidget = CProjectsStatusBar(self)
    self.layout().addWidget(self.statusWidget,3,0,1,2)

    self.searchProjectWidget = CSearchProjectWidget(self)
    self.layout().addWidget(self.searchProjectWidget,2,0,1,2)

    self.openButton = QtGui.QPushButton(self,text='Open')
    self.openButton.setMaximumWidth(100)
    self.connect(self.openButton,QtCore.SIGNAL('clicked()'),self.openProject)
    self.layout().addWidget(self.openButton,4,0)
    
    self.exportThread= None
    
    self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('projectsListChanged'),self.projectsView.populate)
    # Beware this projectUpdated signal has arguments that should be handled by the target method
    self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('projectUpdated'),self.projectsView.populate)


  def setMode(self,mode = 'all'):
    if mode == 'all':
      self.buttonFrame.show()
      self.statusWidget.show()
      self.openButton.hide()
    else:    
      self.buttonFrame.hide()
      self.statusWidget.hide()
      self.openButton.show()
                 
  def openSearchProjects(self):
    if (self,'searchProjectWidget',None) is None:
      self.searchProjectWidget = CSearchProjectWidget(self)
      self.layout().addWidget(self.searchProjectWidget,2,0,1,2)
    self.searchProjectWidget.show()
    
  def addProject(self):
    if self.newProjectGui is None:
      self.newProjectGui = CNewProjectGui(parent=self)
    #self.newProjectGui.start()
    self.newProjectGui.show()

  def handleEditDescription(self):
    projectId=self.projectsView.selectedProjectId()
    if projectId is None: return
    for w in self.findChildren(CProjectDescriptionDialog):
      if w.widget.projectId == projectId:
        w.show()
        w.raise_()
        return
    pName = PROJECTSMANAGER().db().getProjectInfo(projectId=projectId,mode='projectname')
    descriptionDialog = CProjectDescriptionDialog(self,projectId,pName)
    descriptionDialog.show()
    descriptionDialog.raise_()


  def closeEvent(self,event):
    # On closing project manager delete project description children
    #print 'CProjectManagerDialog.closeEvent'
    for w in self.findChildren(CProjectDescriptionDialog):
      w.deleteLater()
    return QtGui.QDialog.closeEvent(self,event)


  def handleRenameProject(self):
    projectId=self.projectsView.selectedProjectId()
    if projectId is None: return
    pName = PROJECTSMANAGER().db().getProjectInfo(projectId=projectId,mode='projectname')
    
    self.renameDialog = QtGui.QDialog(self)
    self.renameDialog.setModal(True)
    self.renameDialog.setWindowTitle('Rename project?')
    self.renameDialog.setLayout(QtGui.QVBoxLayout())
    self.renameDialog.layout().addWidget(QtGui.QLabel('Rename project: '+pName,self))
    self.renameWidget = QtGui.QLineEdit(self)
    self.renameWidget.setText(pName)
    self.renameDialog.layout().addWidget(self.renameWidget)
    butBox = QtGui.QDialogButtonBox(self)
    but = butBox.addButton('Rename',QtGui.QDialogButtonBox.ApplyRole)
    but.setDefault(False)
    self.connect(but,QtCore.SIGNAL('released()'),functools.partial(self.renameProject,projectId))
    but = butBox.addButton(QtGui.QDialogButtonBox.Cancel)
    self.connect(but,QtCore.SIGNAL('released()'),self.renameDialog.close)
    self.renameDialog.layout().addWidget(butBox)
    self.renameDialog.show()
    self.renameDialog.raise_()

  def renameProject(self,projectId):
    self.renameDialog.hide()   
    import CCP4Utils
    #print 'renameProject',projectId,self.renameWidget.text()
    name0 = self.renameWidget.text().__str__()
    if len(name0)==0: return
    name = CCP4Utils.safeOneWord(name0)
    if name0 != name:
      print 'Fixing name to ',name
    try:
      PROJECTSMANAGER().db().getProjectId(name)
    except:
      # Failed finding project of this name - good!
      pass
    else:
      QtGui.QMessageBox.warning(self,'Rename project','A project called '+name+' already exists')
      return

    try:
      PROJECTSMANAGER().db().updateProject(projectId,'projectname',name)
    except CException as e:
      e.warningMessage(parent=self,windowTitle=self.windowTitle(),message='Error changing project name')
    
    
  def openProject(self):
    projectId=self.projectsView.selectedProjectId()
    if projectId is not None: openProject(projectId)
    

  def handleDoubleClick(self,item,col=None):
    #print 'handleDoubleCLick',item,col
    nameVar = item.data(0,QtCore.Qt.DisplayRole)
    name = nameVar.toString().__str__()
    openProject(projectName=name)
    

  def handleExport1(self):
    if getattr(self,'exportThread',None) is not None:
        QtGui.QMessageBox.warning(self,'Export project','Please wait - another project export currently in progress')
        return
    projectId=self.projectsView.selectedProjectId()
    if projectId is None:
      #print 'handleExport calling statusBar'
      self.statusWidget.showMessage("Select a project before selecting 'Export'",5)
      return
    projectInfo =  PROJECTSMANAGER().db().getProjectInfo(projectId=projectId)

    if not hasattr(self,'exportOptions'):
      self.exportOptionsWidget = CExportOptionsDialog(self,projectId,projectInfo['projectname'])
      self.connect(self.exportOptionsWidget,QtCore.SIGNAL('export'),functools.partial(self.handleExport2,projectId))
    self.exportOptionsWidget.show()
  
  def handleExport2(self,projectId,mode):
    #print 'handleExport2',projectId,mode
    if mode == 0: return
    if mode == 1:
      after = None
      jobList = None
    if mode == 2:
      after = self.exportOptionsWidget.impExpWidget.getTime()
      jobList = None
    elif mode == 3:
      after = None
      jobList,errList = self.exportOptionsWidget.selectionWidget.getSelection()
      if len(errList)>0:
        errText = 'Unable to interpret selection: '
        for err in errList: errText = errText +"'"+err+"' "
        mess = QtGui.QMessageBox.warning(self,'Export project',errText)
        return

    #if DEVELOPER():
    if False:
      excludeI2files = self.exportOptionsWidget.excludeI2files.isChecked()
    else:
      excludeI2files = False
    
    #print 'handleExport2',after,jobList
    self.exportOptionsWidget.close()
    projectInfo =  PROJECTSMANAGER().db().getProjectInfo(projectId=projectId)
    
    import CCP4FileBrowser,CCP4Export
    self.browser = CCP4FileBrowser.CFileDialog(self,
           title='Save project data to compressed file',
           filters= ['CCP4 Project Database (*.'+CCP4Export.COMPRESSED_SUFFIX+')'],
           defaultSuffix=CCP4Export.COMPRESSED_SUFFIX,
           fileMode=QtGui.QFileDialog.AnyFile  )
    self.connect(self.browser,QtCore.SIGNAL('selectFile'),functools.partial(self.compressProject,projectId,after,jobList,excludeI2files))
    self.browser.show()

  def export(self,projectId,fileName):
    jobNumberList,errReport = PROJECTSMANAGER().db().exportProjectXml(projectId,fileName)
    if len(errReport)>0: errReport.warningMessage(parent=self,windowTitle='Errors exporting project',message='Some errors in exporting project to file:\n'+fileName)


  def compressProject(self,projectId,after=None,jobList=None,excludeI2files=False,fileName=None):
    #print 'CProjectManagerDialog.compressProject',after,jobList,excludeI2files,fileName
    self.browser.hide()
    self.browser.deleteLater()

    projectInfo = PROJECTSMANAGER().db().getProjectInfo(projectId=projectId)
    # If there is a limited set of jobs then find the input jobs that are not output by jobs on that list
    inputFilesList,inputFileIdList,fromJobList,errReport =  PROJECTSMANAGER().getJobInputFiles(projectDir=projectInfo['projectdirectory'],jobIdList=jobList,useDb=True,excludeI2files=excludeI2files)
    #print 'CProjectManagerDialog.compressProject inputFilesList,fromJobIdList',inputFilesList,fromJobIdList
    fromJobIdList = []
    fromJobNumberList = []
    for item in fromJobList:
      fromJobIdList.append(item[0])
      fromJobNumberList.append(item[1])
      
    import time,os
    dbxml = os.path.join( projectInfo['projectdirectory'],'CCP4_TMP','DATABASE'+str(int(time.time()))+'.db.xml')
    self.statusWidget.showMessage('Creating XML database:'+dbxml)
    #print 'Creating temporary database xml file in:',dbxml
    # exportProjectXml returns list of TOP-LEVEL jobNumbers for the export
    jobNumberList,errReport = PROJECTSMANAGER().db().exportProjectXml(projectId,fileName=dbxml,recordExport=True,status='exportable',after=after,jobList=jobList,inputFileList=inputFileIdList,inputFileFromJobList=fromJobIdList)
    #print 'CProjectManagerDialog.compressProject jobNumberList',jobNumberList
    if errReport.maxSeverity()>SEVERITY_WARNING:
        self.statusWidget.clearMessage()
        errReport.warningMessage('Export project','Error creating XML database file',parent=self)
        return

   
    #except:
    #  jobList = []
    #  err = CErrorReport(self.__class__,171)
    #  err.warningMessage(title,'Error getting project jobs from database',parent=self)
    #print 'compressProject',jobList

    if jobList is not None:
        directoriesList = []
    else:
        directoriesList = ['CCP4_IMPORTED_FILES','CCP4_PROJECT_FILES']
    import CCP4Export
    self.exportThread = CCP4Export.ExportProjectThread(self,projectDir=projectInfo['projectdirectory'],dbxml=dbxml,target=fileName,jobList=jobNumberList,inputFilesList=inputFilesList,directoriesList=directoriesList,)
    self.connect(self.exportThread,QtCore.SIGNAL('savingJobData'),self.updateSavingJobData)
    self.connect(self.exportThread,QtCore.SIGNAL('startSavingJobData'),self.progressSavingJobData)
    self.connect(self.exportThread,QtCore.SIGNAL('finished()'),functools.partial(self.doneSavingJobData,projectInfo['projectname'],fileName))
    self.exportThread.start()

    '''
    compressedFile,errReport = PROJECTSMANAGER().compressProject(projectId,fileName,after=after)
    if compressedFile is None:
      errReport.warningMessage('Exporting database','Error creating export compressed file')
    else:
      QtGui.QMessageBox.information(self,'Exporting project','Project saved as'+compressedFile)
    '''
    
        
  def progressSavingJobData(self,numberOfJobs):
    self.statusWidget.showMessage('Exporting project: Saving '+str(numberOfJobs)+' jobs to compressed file')
    self.statusWidget.showProgress(numberOfJobs)
    
  def updateSavingJobData(self,ret):
    jobNumber,jobsDone = ret    
    #print 'updateSavingJobData',jobNumber,jobsDone
    if jobNumber in ['IMPORT','DATABASE','PROJECT']:
      label = ['imported files','database file','project data'][ ['IMPORT','DATABASE','PROJECT'].index(jobNumber)]
      self.statusWidget.clear()
      if not jobsDone:
        self.statusWidget.showMessage('Exporting project: Saving '+label)
      else:
        self.statusWidget.showMessage('Exporting project: Finished saving '+label)
    else:
      self.statusWidget.setProgress(jobsDone)
    
  def doneSavingJobData(self,projectName,filename):
    if self.exportThread.errorReport.maxSeverity()>SEVERITY_WARNING:
      self.exportThread.errorReport.warningMessage('Saving job data','Error saving data files for export',parent=self)
    else:
      self.statusWidget.hideProgress()
      self.statusWidget.showMessage('Project '+str(projectName)+' saved to: '+str(filename))
    self.exportThread.deleteLater()
    self.exportThread = None
    
  def handleImportProject(self):

    import CCP4DbApi
    CCP4DbApi.CDbXml.updateInstances()
    if len(CCP4DbApi.CDbXml.Instances)>0:
      QtGui.QMessageBox.warning(self,'Import project','Another import is already in progress - please wait')
      return

    import CCP4FileBrowser,CCP4Export
    self.browser = CCP4FileBrowser.CFileDialog(self,
           title='Import project compressed file',
           filters = [ MIMETYPESHANDLER().getMimeTypeInfo("application/CCP4-compressed-db",'filter') ],
           defaultSuffix=  MIMETYPESHANDLER().getMimeTypeInfo("application/CCP4-compressed-db",'fileExtensions')[0],
           fileMode=QtGui.QFileDialog.ExistingFile  )
    self.connect(self.browser,QtCore.SIGNAL('selectFile'),self.handleImportProject1)
    self.browser.show()
    
  def handleImportProject1(self,compressedFile):
    import CCP4FileBrowser
    
    if hasattr(self,'browser'):
      try:
        self.browser.hide()
        self.browser.deleteLater()
        del self.browser
      except:
        pass

    self.statusWidget.showMessage('Checking contents of compressed file')

    try:
      xmlFile = PROJECTSMANAGER().extractDatabaseXml(compressedFile)
    except CException as e:
      e.warningMessage('Import project','Failed extracting database XML file from compressed file',parent=self)
      self.statusWidget.clear()
    except Exception as e:
      QtGui.QMessageBox.warning(self,'Import project','Error extracting database xml file from '+str(compressedFile))
      self.statusWidget.clear()
      return

    try:
      import CCP4DbApi
      self.dbImport = CCP4DbApi.CDbXml(db=PROJECTSMANAGER().db(),xmlFile=xmlFile)
      #self.dbImport.setDiagnostic(True)
      importProjectInfo = self.dbImport.loadProjectInfo()  
    except:
      QtGui.QMessageBox.warning(self,'Import project','Error attempting to read database file in\n'+str(compressedFile))
      return
    try:
      projectInfo =  PROJECTSMANAGER().db().getProjectInfo(projectId=self.dbImport.projectId)
    except:
      projectInfo = None
   
    #print 'handleImportProject1 importProjectInfo',importProjectInfo

    if projectInfo is None:
      # There is no matching projectId in db- query user to create new project
      if not hasattr(self,'importNewDialog'): self.importNewDialog = CImportNewProjectDialog(self)
      self.importNewDialog.reset(importProjectInfo=self.dbImport.headerInfo())
      self.connect(self.importNewDialog,QtCore.SIGNAL('import'),functools.partial(self.importProject0,compressedFile))
      self.importNewDialog.show()

    else:

      self.importExistingDialog=CImportExistingProjectDialog(self,projectName=projectInfo['projectname'])
      self.importExistingDialog.reset(importProjectInfo=self.dbImport.headerInfo())
      self.connect(self.importExistingDialog,QtCore.SIGNAL('import'),functools.partial(self.importProject1,compressedFile,projectInfo['projectdirectory'],self.dbImport.projectId))
      self.importExistingDialog.show()     
  
  def importProject0(self,compressedFile):
    # Close importNewDialog
    if not hasattr(self,'importNewDialog'): return
    self.importNewDialog.close()
    
    if self.importNewDialog.mode() == 1:
      # Create a new project
      dirName = self.importNewDialog.newDirectory()
      projectName = self.importNewDialog.projectName()
      self.importNewDialog.deleteLater()
      del self.importNewDialog
      self.importProject(compressedFile,dirName,projectName=projectName)
    else:
      # Merge into existing project despite incompatible projectId
      projectId = self.importNewDialog.mergeProject()
      self.importNewDialog.deleteLater()
      del self.importNewDialog
      projectInfo = PROJECTSMANAGER().db().getProjectInfo(projectId=projectId)
      self.dbImport.projectId = projectId
      self.dbImport.projectDirectory = projectInfo['projectdirectory']
      #self.dbImport.projectName = self.importNewDialog.projectName()
      self.importProject(compressedFile=compressedFile,dirName=self.dbImport.projectDirectory,existingProject=projectId,forceProjectId=True)
      
  def importProject1(self,compressedFile,dirName,existingProject=None):
    self.importExistingDialog.close()
    self.importExistingDialog.deleteLater()
    del self.importExistingDialog
    
    # Append to existing project
    self.importProject(compressedFile=compressedFile,dirName=dirName,existingProject=existingProject)

  def importProject(self,compressedFile,dirName,existingProject=None,forceProjectId=False,projectName=None):
    import os
    if DIAGNOSTIC: print 'CProjectManagerDialog.importProject',compressedFile,dirName,existingProject,projectName
    # Load the database.xml into temporary tables in db
    self.dbImport.projectDirectory = dirName
    if projectName is not None: self.dbImport.projectName = projectName
    if existingProject is None:
      ret = self.dbImport.createProject()
      if DIAGNOSTIC: print 'dbImport.createProject()',ret.report()
      if ret.maxSeverity()>SEVERITY_WARNING:
        print ret.report()
        ret.warningMessage(parent=self,windowTitle='Error creating project in database',ifStack=False)
        return

    
    self.dbImport.createTempTables()
    if forceProjectId:
      self.dbImport.loadTempTable(resetProjectId=existingProject)
    else:
      self.dbImport.loadTempTable()
    # If loading jobs to an existing project flag up jobs in temp tables that
    # are already in db
    if existingProject is not None:
      self.dbImport.setExclInTempTables()
    # Flag imported files to be imported (there is no checking yet that they exist)
    self.dbImport.setExclImportedFiles()

    if DIAGNOSTIC: print 'CProjectManagerDialog.importProject setting Temp Tables',self.dbImport.errReport.report()
    
    if self.dbImport.errReport.maxSeverity()>SEVERITY_WARNING:
      if DIAGNOSTIC: print 'Error report from the import process..'
      if DIAGNOSTIC: print self.dbImport.errReport.report()
      self.dbImport.errReport.warningMessage(parent=self,windowTitle='Error loading data from project export file',ifStack=False)
    
    # Make project directory if necessary
    if not os.path.exists(dirName):
      try:
        os.mkdir(dirName)
      except:
        QtGui.QMessageBox.warning(self,'Import project','Failed to create directory:'+dirName)
        return

    self.statusWidget.showMessage('Unpacking project files to '+dirName)
    import CCP4Export
    # Unpack project files from the tar file (possibly in separate thread) 
    # Pass import thread dbImport to enable query database and flagging loaded jobs/files
    if DIAGNOSTIC: print 'CProjectManagerDialog.importProject creating import thread'
    self.importThread = CCP4Export.ImportProjectThread(self,projectDir=dirName,compressedFile=compressedFile,
                                                       dbImport=self.dbImport,diagnostic=DIAGNOSTIC)
    #self.connect(self.importThread,QtCore.SIGNAL('extractingJobData'),self.handleImportProgress)
    self.connect(self.importThread,QtCore.SIGNAL('finished()'),self.doneImportProgress)
    errReport = self.importThread.run()
    if DIAGNOSTIC: print 'CProjectManagerDialog.importProject import thread running',errReport.report()
    
    self.dbImport.cleanupTempTables()
    #self.dbImport.listTempJobs('TempJobs after cleanup')
    #self.dbImport.listTempFiles('TempFiles after cleanup')
    #self.dbImport.listTempFileUses('TempFileUses after cleanup')
    stats = self.dbImport.importStats()
    if DIAGNOSTIC:
      for key,value in stats.items():
        if key == 'failedFiles':
          if len(value)>0: print 'Failed to import files..(probably already present)'
          for item in value:
              print 'Job_'+str(item[4]),item[2]
        else:
          print 'CProjectManagerDialog.importProject stats', key,value
    if errReport.maxSeverity()>SEVERITY_WARNING:
      self.dbImport.removeTempTables()
      text = 'ERRORS UNPACKING DATA FILES\n'
      for err in errReport: text = text + err['details'] + '\n'
      QtGui.QMessageBox.warning(self,'Import failed',text)
      self.statusWidget.showMessage('Error unpacking project files to '+dirName)
      return

    self.statusWidget.showMessage('Loading project data to database')
    self.dbImport.importTempTables()
    self.dbImport.removeTempTables()
    self.statusWidget.showMessage('Finished importing project')

    self.dbImport.db.emitSignal('projectReset',{'projectId':self.dbImport.projectId})
    
    if stats['jobsTotal']>stats['jobsImported'] or stats['filesTotal']>stats['filesImported']:
      text = 'Some of the jobs/files are already in the database in project '+str(self.dbImport.projectName)+'\n' + \
      'Imported '+str(stats['jobsImported'])+' new jobs from '+str(stats['jobsTotal'])+' in file \n' + \
     'and '+str(stats['filesImported'])+' new data files from '+str(stats['filesTotal'])+' in file\n'
    else:
      text = 'Successfully imported '+str(stats['jobsImported'])+' jobs and '+str(stats['filesImported'])+' data files'

    if stats.get('incrJobNumber',0) > 0:
        text = text +'\nImporting jobs '+str(stats['importMin'])+' to '+str(stats['importMax'])+' have been renumbered\n'+str(int(stats['importMin'])+int(stats['incrJobNumber']))+' to '+str(int(stats['importMax'])+int(stats['incrJobNumber'])) +' to avoid clash with existing jobs'
    if len(text)>0:  QtGui.QMessageBox.information(self,'Import complete',text)
    import copy
    projectId = copy.deepcopy(self.dbImport.projectId)
    openProject(projectId=projectId)

  def createImportProgress(self):
    pass

  def handleImportProgress(self,ret):
    #print 'handleImportProgress',ret
    jobNo,done = ret

  def doneImportProgress(self):
    pass

  def repairProject(self):
    pass

  def handleDeleteProject(self):
    projectId=self.projectsView.selectedProjectId()
    if projectId is None: return
    projectInfo =  PROJECTSMANAGER().db().getProjectInfo(projectId=projectId)

    self.deleteDialog = QtGui.QDialog(self)
    #self.deleteDialog.setModal(True)
    self.deleteDialog.setWindowTitle('Delete project?')
    self.deleteDialog.setLayout(QtGui.QVBoxLayout())
    self.deleteDialog.layout().addWidget(QtGui.QLabel('Delete project from database: '+projectInfo['projectname'],self))
    self.deleteDirectory = QtGui.QCheckBox('Delete directory: '+projectInfo['projectdirectory'],self)
    self.deleteDialog.layout().addWidget(self.deleteDirectory)
    butBox = QtGui.QDialogButtonBox(self)
    but = butBox.addButton('Delete project',QtGui.QDialogButtonBox.ApplyRole)
    but.setDefault(False)
    self.connect(but,QtCore.SIGNAL('released()'),functools.partial(self.deleteProject,projectId))
    but = butBox.addButton(QtGui.QDialogButtonBox.Cancel)
    self.connect(but,QtCore.SIGNAL('released()'),self.deleteDialog.close)
    self.deleteDialog.layout().addWidget(butBox)
    self.deleteDialog.show()
    self.deleteDialog.raise_()

  def deleteProject(self,projectId):
    deleteDirectory = self.deleteDirectory.isChecked()
    self.deleteDialog.close()
    if projectId is None: return
    e = PROJECTSMANAGER().deleteProject(projectId=projectId,deleteDirectory=deleteDirectory)
    if e.maxSeverity()>SEVERITY_WARNING:
       e.warningMessage('Deleting project',parent=self)

  def handleMoveProject(self):
    projectId=self.projectsView.selectedProjectId()
    #print 'handleMoveProject projectId',projectId,type(projectId)
    if projectId is None: return
    projectInfo =  PROJECTSMANAGER().db().getProjectInfo(projectId=projectId)

    self.moveProjectId = projectId
    self.moveDialog = QtGui.QDialog(self)
    self.moveDialog.setModal(True)
    self.moveDialog.setWindowTitle('Move project directory?')
    self.moveDialog.setLayout(QtGui.QVBoxLayout())
    self.moveDialog.layout().addWidget(QtGui.QLabel('Move the project directory for project: '+projectInfo['projectname'],self))
    self.moveDialog.layout().addWidget(QtGui.QLabel('Current directory: '+projectInfo['projectdirectory'],self))
    # Mode button group
    self.moveMode = -1
    self.moveModeGroup = QtGui.QButtonGroup(self)
    self.moveModeGroup.setExclusive(True)
    but =  QtGui.QRadioButton('Move the directory and register with database',self)
    self.moveModeGroup.addButton(but,1)
    self.moveDialog.layout().addWidget(but)
    but =  QtGui.QRadioButton('Directory is already moved - just register with database',self)
    self.moveModeGroup.addButton(but,0)
    self.moveDialog.layout().addWidget(but)
    self.connect(self.moveModeGroup,QtCore.SIGNAL('buttonReleased(int)'),self.handleMoveModeChange)

    import CCP4File,CCP4DataManager
    self.moveStack = QtGui.QStackedLayout()
    self.moveDialog.layout().addLayout(self.moveStack)

    self.moveStack.addWidget(QtGui.QLabel('Choose mode above',self))


    self.moveDirectory0 =  CCP4File.CDataFile(parent=self,qualifiers= {  'isDirectory' : True, 'mustExist' : True } )
    import CCP4FileBrowser
    self.moveDirectory0Widget = CCP4DataManager.DATAMANAGER().widget(model=self.moveDirectory0,parentWidget=self,qualifiers={'jobCombo': False, 'projectBrowser' : False } )
    self.moveDirectory0Widget.updateViewFromModel()
    self.moveDirectory0Widget.fileLineEdit.setCharWidth(60,mode='minimum')
    self.moveStack.addWidget(self.moveDirectory0Widget)
    
    self.moveDirectory1 =  CCP4File.CDataFile(parent=self,qualifiers= { 'isDirectory' : True,'mustExist' : False } )
    self.moveDirectory1Widget = CCP4DataManager.DATAMANAGER().widget(model=self.moveDirectory1,parentWidget=self,qualifiers={'jobCombo': False, 'projectBrowser' : False} )
    self.moveDirectory1Widget.updateViewFromModel()
    self.moveDirectory1Widget.fileLineEdit.setCharWidth(60,mode='minimum')
    self.moveStack.addWidget(self.moveDirectory1Widget)
    
    butBox = QtGui.QDialogButtonBox(self)
    but = butBox.addButton(QtGui.QDialogButtonBox.Apply)
    but.setDefault(False)
    self.connect(but,QtCore.SIGNAL('released()'),self.moveProject)
    but = butBox.addButton(QtGui.QDialogButtonBox.Cancel)
    self.connect(but,QtCore.SIGNAL('released()'),self.moveDialog.close)
    self.moveDialog.layout().addWidget(butBox)

    self.moveStack.setCurrentIndex(self.moveMode -1 )
    self.moveDialog.show()
    self.moveDialog.raise_()

  def handleMoveModeChange(self,mode):
    mode = int(mode)
    if mode != self.moveMode:
      self.moveMode = mode
      self.moveDirectory0Widget.closeBrowser()
      self.moveDirectory1Widget.closeBrowser()
      self.moveStack.setCurrentIndex(self.moveMode + 1 )
      #print 'handleMoveModeChange',self.moveMode,self.moveStack.currentIndex()

  def moveProject(self):
    import os
    errMess = None
    if self.moveMode < 0:
        errMess = 'Choose if moving the directory or just updating database'
    if self.moveMode == 0:
      if not self.moveDirectory0.isSet():
        errMess = 'Choose directory'
      else:
        newDir = str(self.moveDirectory0)
        if not os.path.exists(newDir) or not os.path.isdir(newDir):
          errMess = 'Directory does not exist or is not directory'
    else:
      if not self.moveDirectory1.isSet():
        errMess = 'Choose new directory path'
      else:
        newDir = str(self.moveDirectory1)
        if os.path.exists(newDir):
          errMess = 'New directory should not exist already'
        #else:
        #  if not os.path.exists(os.path.split(newDir)[0]):
        #    errMess = 'Parent directory for new directory must exist'
    if errMess is not None:
       QtGui.QMessageBox.warning(self,'Can not move directory',errMess)
       return
   
    #print 'moveProject',newDir,self.moveMode
    projectInfo =  PROJECTSMANAGER().db().getProjectInfo(projectId=self.moveProjectId)
    if self.moveMode == 1:
      import shutil
      try:
        shutil.copytree(projectInfo['projectdirectory'],newDir)
      except Exception as e:
         QtGui.QMessageBox.warning(self,'Error moving directory',str(e))
         return
    try:
      PROJECTSMANAGER().db().updateProject(self.moveProjectId,key='projectdirectory',value=newDir)
    except CException as e:
      e.warningMessage(windowTitle='Error updating database',parent=self)
      return
    except Exception as e:
      QtGui.QMessageBox.warning(self,'Error updating database',str(e))
      return

    self.moveDialog.close()

  def handleCleanup(self):
    projectId=self.projectsView.selectedProjectId()
    #print 'handleCleanup projectId',projectId
    if projectId is None:
      ret = QtGui.QMessageBox.question(self,self.windowTitle(),'Remove temporary files from all projects?',QtGui.QMessageBox.Ok|QtGui.QMessageBox.Cancel)
      if ret != QtGui.QMessageBox.Ok: return
      PROJECTSMANAGER().cleanupAllProjects(context='temporary')
    else:
      msgBox = QtGui.QMessageBox()
      msgBox.setWindowTitle('Cleanup files')
      msgBox.setText('Do you want to delete only temporary files or temporary files and intermediate data files from sub-jobs')
      b = msgBox.addButton(QtGui.QMessageBox.Cancel)
      b = msgBox.addButton('Temporary files only',QtGui.QMessageBox.ApplyRole)
      self.connect(b,QtCore.SIGNAL('released()'),functools.partial(self.handleCleanup2,projectId,'temporary'))
      b = msgBox.addButton('Temporary and intermediate files',QtGui.QMessageBox.ApplyRole)
      self.connect(b,QtCore.SIGNAL('released()'),functools.partial(self.handleCleanup2,projectId,'intermediate'))
      msgBox.exec_()

  def handleCleanup2(self,projectId,context):
      #print 'handleCleanup2',projectId,context
      import CCP4ProjectsManager
      cleanup = CCP4ProjectsManager.CPurgeProject(projectId = projectId)
      cleanup.purgeProject(context=context)



  def handleRerun(self):
    import CCP4FileBrowser
    projectId=self.projectsView.selectedProjectId()
    if projectId is None: return
    self.rerunProjectId = projectId
    filter_list = []

    self.rerunDialog = CCP4FileBrowser.CFileDialog(self,fileMode=QtGui.QFileDialog.Directory,
      title='Directory for rerun output')
    self.connect(self.rerunDialog, QtCore.SIGNAL('selectFile'), self.rerunProject)
    self.rerunDialog.show()

  def rerunProject(self,outputDirectory):
    import os
    import CCP4ProjectBasedTesting,CCP4TextViewer
    self.rerunDialog.close()
    if not os.path.exists(outputDirectory) or not os.path.isdir(outputDirectory):
      QtGui.QMessageBox.warning(self,'No suitable directory for project rerun','You must provide a directory in which the rerun project directory will be created')
      return
    self.projectBasedTest = CCP4ProjectBasedTesting.CProjectBasedTesting(sourceProjectList=[self.rerunProjectId],outputDirectory=outputDirectory,useCurrentDb=True)
    #self.projectBasedTest.start()
    self.projectBasedTest.runTests()
    logFile = self.projectBasedTest.logFiles[-1]
    print 'Re-running project log file:',logFile
    widget = WEBBROWSER().openFile(logFile)
    self.connect(self.projectBasedTest,QtCore.SIGNAL('reportUpdated'),WEBBROWSER().reloadFile)
    

  def handleRecover(self):
    # Attempt to recreate database from the contents of the project directory - no longer working
    ret = QtGui.QMessageBox.question(self,'Recover project',"This will attempt to restore a project to the database\n from the information in the project directory.\nAn intermediary file called my_project_dir.ccp4db.xml will be created and read automatically.\nThis file could also be edited and read with the 'Import project XML' tool.",QtGui.QMessageBox.Ok|QtGui.QMessageBox.Cancel)
    if ret == QtGui.QMessageBox.Cancel: return

    import CCP4FileBrowser
    self.browser = CCP4FileBrowser.CFileDialog(self,
           title='Recover database from the project directory',
           fileMode=QtGui.QFileDialog.Directory  )
    self.connect(self.browser,QtCore.SIGNAL('selectFile'),self.recoverProject)
    self.browser.show()

    '''
    import CCP4File,CCP4DataManager
    self.recoverDialog = QtGui.QDialog(self)
    self.recoverDialog.setModal(True)
    self.recoverDialog.setWindowTitle('Recover project database')
    self.recoverDialog.setLayout(QtGui.QVBoxLayout())
    self.recoverDialog.layout().addWidget(QtGui.QLabel('Recover database information from the project directory..'))
    self.recoverDirectory =  CCP4File.CDataFile(parent=self,qualifiers= {  'isDirectory' : True, 'mustExist' : True } )
    import CCP4FileBrowser
    self.recoverDirectoryWidget = CCP4DataManager.DATAMANAGER().widget(model=self.recoverDirectory,parentWidget=self,qualifiers={'jobCombo': False, 'projectBrowser' : False } )
    self.recoverDirectoryWidget.updateViewFromModel()
    self.recoverDirectoryWidget.fileLineEdit.setCharWidth(60,mode='minimum')
    self.recoverDialog.layout().addWidget(self.recoverDirectoryWidget)

    butBox = QtGui.QDialogButtonBox(self)
    but = butBox.addButton(QtGui.QDialogButtonBox.Apply)
    but.setDefault(False)
    self.connect(but,QtCore.SIGNAL('released()'),functools.partial(self.recoverProject,****))
    but = butBox.addButton(QtGui.QDialogButtonBox.Cancel)
    self.connect(but,QtCore.SIGNAL('released()'),self.recoverDialog.close)
    self.recoverDialog.layout().addWidget(butBox)
    self.recoverDialog.show()
    self.recoverDialog.raise_()
    '''

  def recoverProject(self,projectDir):
    #print 'recoverProject',projectDir
    import os
    if not os.path.exists(projectDir) or not os.path.isdir(projectDir):
      QtGui.QMessageBox.warning(self,'No project directory selected','You must select a project directory')
      return
    if not os.path.exists(os.path.join(projectDir,'CCP4_JOBS')):
      QtGui.QMessageBox.warning(self,'Not a project directory?','This does not appear to be a CCP4i2 project directory which should contain a CCP4_JOBS sub-directory')
      return

    import CCP4DbUtils
    self.makeDbXml = CCP4DbUtils.CMakeProjectDbXml(self,projectDir=projectDir,projectName=os.path.split(projectDir)[-1])
    self.connect(self.makeDbXml,QtCore.SIGNAL('jobLoaded'),self.statusWidget.setProgress)

    self.statusWidget.showMessage('Making database file from project directory')
    self.statusWidget.showProgress(self.makeDbXml.numberOfJobs())

    errReport = self.makeDbXml.loadProject()

    if errReport.maxSeverity()>SEVERITY_WARNING:
      errReport.warningMessage(parent=self,windowTitle='Error recovering project directory',
                               message='Some errors are reported',ifStack=False,minSeverity=SEVERITY_ERROR)
      return
  
    xmlFile = self.makeDbXml.saveXmlFile()
    if len(errReport)>0:
       errReport.warningMessage(parent=self,windowTitle='Project database information recovered',
         message='The project database information has been recovered and saved to '+xmlFile+ \
        '\nThere are some warnings which can probably be ignored.\nTo seee click Show Details' ,ifStack=False)

    self.statusWidget.showMessage('Loading database file to database')
    self.importXmlDatabase(xmlFile,projectDir=projectDir)
    self.statusWidget.clear()
    self.statusWidget.removeProgress()

  def handleImportXmlDatabase(self):
    import CCP4FileBrowser
    self.browser = CCP4FileBrowser.CFileDialog(self,
           title='Recover database from the project directory',
           filters= ['CCP4 Database XML (*.ccp4db.xml)'],
           defaultSuffix= 'xml',
           fileMode=QtGui.QFileDialog.ExistingFile  )                   
    self.connect(self.browser,QtCore.SIGNAL('selectFile'),self.handleRecoverDirSelected)
    self.browser.show()

  def handleRecoverDirSelected(self,xmlFile):
    self.browser.hide()
    self.importXmlDatabase(xmlFile)
    
  def importXmlDatabase(self,xmlFile,projectDir=None):
    import CCP4DbApi
    
    self.dbImport = CCP4DbApi.CDbXml(db=PROJECTSMANAGER().db(),xmlFile=xmlFile)
    projectInfo = self.dbImport.loadProjectInfo()
    # Check that we have a projectId and it is not already in DB
    #print 'importXmlDatabase projectInfo',projectInfo
    if self.dbImport.projectId is None:
      print 'Imported project XML file does not appear to have a projectId'
      return
    try:
      dbProjectInfo = PROJECTSMANAGER().db().getProjectInfo(projectId=self.dbImport.projectId,mode=['projectname','projectdirectory'])
    except:
      pass
    else:
      QtGui.QMessageBox.warning(self,'Error importing database XML','There is aready a project with the same projectID and project name '+str(dbProjectInfo.get('projectname','unknown'))+' and it has project directory '+str(dbProjectInfo.get('projectdirectory','unknown')))
      return
    if projectDir is not None: self.dbImport.projectDirectory = projectDir
    
    
    ret = self.dbImport.createProject()
    # Expect an error from createProject if the project is already in db
    #print 'CProjectManagerDialog.importProject from createProject',ret.report()
    self.dbImport.createTempTables()
    self.dbImport.loadTempTable()
    
    if self.dbImport.errReport.maxSeverity()>SEVERITY_WARNING:
      print 'Error report from the import process..'
      self.dbImport.errReport.warningMessage(windowTitle='Project manager- importing XML',
                                message='Failed importing database from XML file',ifStack=False,parent=self)
    else:
      self.dbImport.setAllToImport()
      #self.dbImport.listTempJobs('Temp jobs')
      #self.dbImport.listTempFiles('Temp files')
      self.dbImport.importTempTables()
      self.dbImport.removeTempTables()

      openProject(projectId=self.dbImport.projectId)

class CTagLineEdit( QtGui.QLineEdit ):

    def keyPressEvent(self,event):
      if event.key() in [QtCore.Qt.Key_Return,QtCore.Qt.Key_Enter]:
        self.emit(QtCore.SIGNAL('enterKeyPress'),event)
        event.accept()
      else:
        QtGui.QLineEdit.keyPressEvent(self,event)
      
class CProjectDescription(QtGui.QFrame):
  TAGSPERROW = 3
  TAGLABEL = 'Choose tag..'

  def __init__(self,parent,projectId,projectName=''):
    QtGui.QFrame.__init__(self,parent)
    self.projectId = projectId
    self.dbTags = PROJECTSMANAGER().db().getTagList()
    #print 'CProjectDescription self.dbTags',self.dbTags
    
    self.setLayout(QtGui.QGridLayout())
    MARGIN = 1
    self.layout().setSpacing(MARGIN)
    self.layout().setContentsMargins(MARGIN,MARGIN,MARGIN,MARGIN)
    self.layout().addWidget(QtGui.QLabel('Description of project',self),0,0,1,1)
    self.annotationWidget = QtGui.QTextEdit(self)
    self.annotationWidget.setToolTip('Enter description of project')
    self.layout().addWidget(self.annotationWidget,1,0,4,self.TAGSPERROW)

    #self.layout().addWidget(QtGui.QLabel('Tag the project..',self),5,0,1,self.TAGSPERROW)
    import CCP4GuiUtils
    icon = CCP4GuiUtils.createIcon('list_add_grey')
    self.moreTagsBut = QtGui.QToolButton(self)
    self.moreTagsBut.setIcon(icon)
    #self.moreTagsBut = QtGui.QPushButton('Add row of tags',self)
    self.layout().addWidget(self.moreTagsBut,5,0)
    self.connect(self.moreTagsBut,QtCore.SIGNAL('clicked()'),self.drawTags)

    # Edit frame
    self.newTagFrame = QtGui.QFrame(self)
    self.newTagFrame.setLayout(QtGui.QHBoxLayout())
    self.newTagFrame.layout().setSpacing(MARGIN)
    self.newTagFrame.layout().setContentsMargins(MARGIN,MARGIN,MARGIN,MARGIN)
    self.editTagLabel = QtGui.QLabel('New tag',self)
    self.newTagFrame.layout().addWidget(self.editTagLabel)
    self.newTagWidget = CTagLineEdit(self)
    self.newTagWidget.setToolTip('Enter new tag name')
    self.newTagFrame.layout().addWidget(self.newTagWidget)

    # Save/delete 
    newTagBut = QtGui.QPushButton('Save',self)
    self.newTagFrame.layout().addWidget(newTagBut)


    # Tool button
    icon =CCP4GuiUtils.createIcon(name='gears2')    
    but = QtGui.QToolButton(self)
    but.setIcon(icon)
    but.setToolTip('Tag handling tools')
    but.setPopupMode(QtGui.QToolButton.InstantPopup)
    but.setMenu(QtGui.QMenu(self))
    but.menu().addAction('Edit tag',self.editTag)
    but.menu().addAction('Delete tag',self.deleteTag)
    but.menu().addAction('Delete unused tags',self.deleteUnusedTags)
    self.newTagFrame.layout().addWidget(but)
     
    self.connect(self.newTagWidget,QtCore.SIGNAL('enterKeyPress'),self.saveNewTag)
    self.connect(newTagBut,QtCore.SIGNAL('clicked()'),self.saveNewTag)
    self.layout().addWidget(self.newTagFrame,6,0,1,self.TAGSPERROW)

    self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('tagCreated'),functools.partial(self.handleTagEditSignal,'tagCreated'))
    self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('tagUpdated'),functools.partial(self.handleTagEditSignal,'tagUpdated'))
    self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('tagDeleted'),functools.partial(self.handleTagEditSignal,'tagDeleted'))
    self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('unusedTagsDeleted'),functools.partial(self.handleTagEditSignal,'unusedTagsDeleted'))
    
    self.editTagCombo = None
    self.deleteTagCombo = None
    self.blockTagWidgetChanged = False
    self.nTagRows = 0
    self.tagWidgets = []
    self.commentId = None
    self.drawTags()
    self.load()

  def saveNewTag(self):
    text = str(self.newTagWidget.text()).strip()
    #print 'saveNewTag',text
    self.newTagWidget.clear()
    if len(text)==0:
      return
    try:
      tagId = PROJECTSMANAGER().db().newTag(self.projectId,text=text)
    except CException as e:
      e.warningMessage('Save project tag','Failed saving new project tag',self)
      return
    except Exception as e:
      print 'Failed creating tag',e
      return

  def handleTagEditSignal(self,mode,args={}):
    #print 'handleTagEditSignal',mode,args
    self.blockTagWidgetChanged = True
    if mode == 'tagCreated':
      tagId = args['tagId']
      text = args['text']
      # A tagsCreated signal so can just add new tag to combo boxes
      self.dbTags.append([tagId,None,text])
      # Load new tag to tagWidgets combos and set the first unset combo
      # to the new tag
      for tagWidget in self.tagWidgets:
        tagWidget.addItem(text,QtCore.QVariant(tagId))
      if self.editTagCombo is not None: self.editTagCombo.addItem(text,QtCore.QVariant(tagId))
      if self.deleteTagCombo is not None: self.deleteTagCombo.addItem(text,QtCore.QVariant(tagId))
      loaded = False
      for tagWidget in self.tagWidgets:
        if tagWidget.currentIndex() == 0:
          tagWidget.setCurrentIndex(tagWidget.count()-1)
          loaded = True
          break
      if not loaded:
        # No free tag combos - create another row and set first in the row
        self.drawTags()
        self.tagWidgets[-self.TAGSPERROW].setCurrentIndex(tagWidget.count()-1)
    elif mode in [ 'tagDeleted', 'tagUpdated','unusedTagsDeleted']:
      self.dbTags = PROJECTSMANAGER().db().getTagList()
      comboList = []
      comboList.extend(self.tagWidgets)
      if self.editTagCombo is not None:comboList.append(self.editTagCombo)
      if self.deleteTagCombo is not None:comboList.append(self.deleteTagCombo)
      for tagWidget in self.tagWidgets:
        currentTagId = tagWidget.itemData(tagWidget.currentIndex(),QtCore.Qt.UserRole).toString().__str__()
        tagWidget.clear()
        tagWidget.addItem('Choose tag..',QtCore.QVariant(0))
        for tid,parentTid,text in self.dbTags:
          tagWidget.addItem(text,QtCore.QVariant(tid))
        tagWidget.setCurrentIndex(max(0,tagWidget.findData(currentTagId,QtCore.Qt.UserRole)))
    self.blockTagWidgetChanged = False

  def drawTags(self):
    self.nTagRows += 1
    self.layout().addItem(self.layout().takeAt(self.layout().indexOf(self.newTagFrame)),6+self.nTagRows,0,1,self.TAGSPERROW)
    self.layout().addItem(self.layout().takeAt(self.layout().indexOf(self.moreTagsBut)),5+self.nTagRows,0)
    for iC in range(self.TAGSPERROW):
      self.tagWidgets.append(QtGui.QComboBox(self))
      self.tagWidgets[-1].setEditable(False)
      self.tagWidgets[-1].addItem(CProjectDescription.TAGLABEL,QtCore.QVariant(0))
      self.connect(self.tagWidgets[-1],QtCore.SIGNAL('currentIndexChanged(int)'),functools.partial(self.handleTagWidgetChanged,len(self.tagWidgets)-1))
      #for tid,parentTid,text,nUses in self.dbTags:
      #  print 'drawTags',tid,parentTid,text,nUses
      for tid,parentTid,text in self.dbTags:
        #print 'drawTags',tid,parentTid,text
        self.tagWidgets[-1].addItem(text,QtCore.QVariant(tid))
      self.layout().addWidget(self.tagWidgets[-1],4+self.nTagRows,iC)
      
  def handleTagWidgetChanged(self,widgetIndex,comboIndex):
    #print 'handleTagWidgetChanged',self.blockTagWidgetChanged,widgetIndex,comboIndex
    if self.blockTagWidgetChanged: return
    projectTagList = []
    for tW in self.tagWidgets:
      #if tW.currentIndex() != 0: tagList.append((str(tW.currentText()),str(tW.data(tW.currentIndex()).toString())  ))
      if tW.currentIndex() != 0:
        tid = str(tW.itemData(tW.currentIndex()).toString())
        if not tid in projectTagList: projectTagList.append(tid )
    #print 'CProjectDescription.handleTagWidgetChanged',projectTagList
    PROJECTSMANAGER().db().resetProjectTags(self.projectId,projectTagList)

  def load(self):
    if self.projectId is not None:
      commentInfo = PROJECTSMANAGER().db().getCommentInfo(projectId=self.projectId)
    else:
      commentInfo = {}
    #print 'CProjectDescription.load',commentInfo
    if len(commentInfo)==0:
      self.annotationWidget.setPlainText('')
      self.commentId = None
    else:
      self.annotationWidget.setPlainText(commentInfo['comment'])
      self.commentId = commentInfo['commentid']
    if self.projectId is not None:
      projectTagList = PROJECTSMANAGER().db().getProjectTags(projectId=self.projectId)
    else:
      projectTagList = []
    nR = (1 + ((len(projectTagList)-1)/self.TAGSPERROW)) - self.nTagRows
    for iR in range(nR): self.drawTags()
    for iT in range(len(projectTagList)):
      idx = self.tagWidgets[iT].findData(QtCore.QVariant(projectTagList[iT]))
      if idx>=0:
        self.tagWidgets[iT].setCurrentIndex(idx)
      else:
        self.tagWidgets[iT].setCurrentIndex(0)
    for iT in range(len(projectTagList),len(self.tagWidgets)):
      self.tagWidgets[iT].setCurrentIndex(0)
    
  def save(self,projectId=None):
    if projectId is not None: self.projectId = projectId
    annotation = str(self.annotationWidget.toPlainText())
    #print 'CProjectDescription.save',annotation
    if self.commentId is None:
      PROJECTSMANAGER().db().createComment(projectId=self.projectId,comment=annotation)
    else:
      PROJECTSMANAGER().db().updateComment(self.commentId,annotation)
    

      
  def deleteTag(self):
    if self.deleteTagCombo is None:
      win = QtGui.QDialog(self)
      win.setLayout(QtGui.QVBoxLayout())
      # Select tag to delete
      self.deleteTagCombo =  QtGui.QComboBox(self)
      win.layout().addWidget(self.deleteTagCombo)
      self.connect(self.deleteTagCombo,QtCore.SIGNAL('currentIndexChanged(int)'),self.selectDeleteTag)
      self.deleteTagWarning = QtGui.QLabel(self)
      win.layout().addWidget(self.deleteTagWarning)
      but = QtGui.QPushButton('Delete',self)
      win.layout().addWidget(but)
      self.connect(but,QtCore.SIGNAL('clicked()'),self.applyDeleteTag)

    self.deleteTagCombo.clear()
    self.deleteTagCombo.addItem('Delete tag..',QtCore.QVariant(0))
    for tid,parentTid,text in self.dbTags:
        self.deleteTagCombo.addItem(text,QtCore.QVariant(tid))
    self.deleteTagCombo.window().show()
    self.deleteTagCombo.window().raise_()
    
    

  def selectDeleteTag(self,indx):
    if indx is 0:
      self.deleteTagWarning.setText('')
      return
    tagId = self.deleteTagCombo.itemData(self.deleteTagCombo.currentIndex(),QtCore.Qt.UserRole).toString().__str__()
    taggedProjects = PROJECTSMANAGER().db().getProjectsWithTag(tagId)
    if len(taggedProjects)>0:
      text = 'Tag used: '+ taggedProjects[0][1]
      for tP in taggedProjects[1:]: text+= ' '+tP[1]
      self.deleteTagWarning.setText(text)
    else:
      self.deleteTagWarning.setText('Tag unused')

  def applyDeleteTag(self):
    if self.deleteTagCombo.currentIndex() == 0: return
    tagId = self.deleteTagCombo.itemData(self.deleteTagCombo.currentIndex(),QtCore.Qt.UserRole).toString().__str__()
    PROJECTSMANAGER().db().deleteTag(tagId)
    self.deleteTagCombo.window().hide()

  def deleteUnusedTags(self):
    PROJECTSMANAGER().db().deleteUnusedTags()

  def editTag(self):
    if self.editTagCombo is None:
      win = QtGui.QDialog(self)
      win.setLayout(QtGui.QVBoxLayout())
      # Select tag to edit
      self.editTagCombo =  QtGui.QComboBox(self)
      win.layout().addWidget(self.editTagCombo)
      self.connect(self.editTagCombo,QtCore.SIGNAL('currentIndexChanged(int)'),self.selectEditTag)
      line = QtGui.QHBoxLayout()
      line.addWidget(QtGui.QLabel('Change to',self))
      self.editTagLineEdit = QtGui.QLineEdit(self)
      line.addWidget(self.editTagLineEdit)
      but = QtGui.QPushButton('Save',self)
      line.addWidget(but)
      self.connect(but,QtCore.SIGNAL('clicked()'),self.saveEditTag)
      win.layout().addLayout(line)

    self.editTagCombo.clear()
    self.editTagCombo.addItem('Edit tag..',QtCore.QVariant(0))
    for tid,parentTid,text in self.dbTags:
        self.editTagCombo.addItem(text,QtCore.QVariant(tid))
    self.editTagLineEdit.clear()
    self.editTagCombo.window().show()
    self.editTagCombo.window().raise_()

  def selectEditTag(self,indx):
    if indx == 0:
      self.editTagLineEdit.setText('')
    else:
      self.editTagLineEdit.setText(self.editTagCombo.currentText())

  def saveEditTag(self):
    #print 'saveEditTag',self.editTagCombo.currentIndex()
    if self.editTagCombo.currentIndex() == 0: return
    tagId = self.editTagCombo.itemData(self.editTagCombo.currentIndex(),QtCore.Qt.UserRole).toString().__str__()
    new = self.editTagLineEdit.text().__str__()
    #print 'saveEditTag',new
    if len(new)==0 or new == self.editTagCombo.currentText(): return
    try:
      PROJECTSMANAGER().db().updateTag(tagId,'text',new)
    except Exception as e:
      print 'ERROR editing tag', e
    self.editTagCombo.window().hide()
   
    
class CProjectDescriptionDialog(QtGui.QDialog):

  def __init__(self,parent,projectId,projectName):
    QtGui.QDialog.__init__(self,parent)
    self.setWindowTitle('Edit annotation and tags for project:'+projectName)
    self.setLayout(QtGui.QVBoxLayout())
    self.widget = CProjectDescription(self,projectId)
    self.layout().addWidget(self.widget)

    buttonBox = QtGui.QDialogButtonBox(self)
    self.layout().addWidget(buttonBox)
    but = buttonBox.addButton(QtGui.QDialogButtonBox.Close)
    self.connect(but,QtCore.SIGNAL('clicked()'),self.save)

  def save(self):
    ret = self.widget.save()
    self.close()
    self.deleteLater()


class CSearchProjectDialog(QtGui.QDialog):

  def __init__(self,parent):
    QtGui.QDialog.__init__(self,parent)
    self.setWindowTitle('Search projects')
    self.setLayout(QtGui.QVBoxLayout())
    self.widget = CSearchProjectWidget(self)
    self.layout().addWidget(self.widget)

    line = QtGui.QHBoxLayout()
    line.addStretch(2)
    buttonBox = QtGui.QDialogButtonBox(self)
    button = buttonBox.addButton('Search',QtGui.QDialogButtonBox.ApplyRole)
    button.setDefault(True)
    self.connect(button,QtCore.SIGNAL('clicked()'),self.handleApply)
    button = buttonBox.addButton(QtGui.QDialogButtonBox.Close)
    self.connect(button,QtCore.SIGNAL('clicked()'),self.hide)
    button = buttonBox.addButton('Clear',QtGui.QDialogButtonBox.ActionRole)
    self.connect(button,QtCore.SIGNAL('clicked()'),self.handleClear)
    button = buttonBox.addButton('Help',QtGui.QDialogButtonBox.HelpRole)
    self.connect(button,QtCore.SIGNAL('clicked()'),self.showHelp)
    line.addWidget(buttonBox)
    line.addStretch(2)
    self.layout().addLayout(line)

  def handleApply(self):
    params = self.widget.get()
    retList = PROJECTSMANAGER().db().projectSearch(searchParams=params)
    #print 'CSearchProjectDialog.handleApply',retList
    pidList = []
    for pid,name in retList: pidList.append(pid)
    nHighlighted = self.parent().projectsView.setHighlights(None,pidList)
    #print 'CSearchProjectDialog.handleApply nHighlighted',nHighlighted
    
  def handleClear(self):
    pass

  def showHelp(self,target=None):
    WEBBROWSER().loadWebPage(helpFileName='searchTools',target="search_projects")

class CSearchProjectWidget(QtGui.QFrame):
  def __init__(self,parent):
    QtGui.QFrame.__init__(self,parent)
    import CCP4GuiUtils
    self.setFrameShape(QtGui.QFrame.Box)
    self.setLineWidth(2)
    self.setLayout(QtGui.QVBoxLayout())
    self.layout().setSpacing(2)
    self.layout().setContentsMargins(2,2,2,2)
    self.widgets = {}
    self.model = {}
    
    line = QtGui.QHBoxLayout()
    line.addWidget(QtGui.QLabel('Highlight  ',self))
    for item,label in [['name','name'],['annotation','description or'],['tags','tag']]:
      self.widgets[item] = QtGui.QCheckBox(label,self)
      self.widgets[item].setChecked(True)
      line.addWidget(self.widgets[item])
    self.widgets['textSearchMode'] = QtGui.QComboBox(self)
    self.widgets['textSearchMode'].setEditable(False)
    for lab in [ 'is','starts with','contains']:
      self.widgets['textSearchMode'].addItem(lab)
    self.widgets['textSearchMode'].setCurrentIndex(2)
    line.addWidget(self.widgets['textSearchMode'])
    self.widgets['text'] = CTagLineEdit(self)
    line.addWidget(self.widgets['text'])
    self.widgets['tagCombo'] =  QtGui.QComboBox(self)
    self.widgets['tagCombo'].setEditable(False)
    self.connect(self.widgets['tagCombo'],QtCore.SIGNAL('currentIndexChanged(int)'),self.handleTagCombo)
    self.loadTagCombo()
    line.addWidget(self.widgets['tagCombo'])

    self.simpleButtons = QtGui.QFrame(self)
    line.addWidget(self.simpleButtons)
    self.simpleButtons.setLayout(QtGui.QHBoxLayout())
    self.simpleButtons.layout().setSpacing(0)
    self.simpleButtons.layout().setContentsMargins(0,0,0,0)
    for text,icon,toolTip,action in [
         [ 'Search',None , 'Highlight matching projects', self.doSearch ],
         [ None ,'undo' , 'Unset highlighted projects', self.clear ],
         [ None ,'help', 'Show help pages' , self.showHelp] ,
         [ None ,'search-plus' , 'Show advanced search tools' ,functools.partial(self.toggleAdvanced,True) ] ]:
      but = QtGui.QToolButton(self)
      if text is not None:
        but.setText(text)
        but.setMinimumHeight(but.iconSize().height())
      else:
        icon =CCP4GuiUtils.createIcon(name=icon)    
        but.setIcon(icon)
      but.setToolTip(toolTip)
      self.connect(but,QtCore.SIGNAL('clicked()'),action)
      self.simpleButtons.layout().addWidget(but)
    
    self.layout().addLayout(line)
    self.connect(self.widgets['text'],QtCore.SIGNAL('enterKeyPress'),self.doSearch)

    self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('tagCreated'),self.loadTagCombo)
    self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('tagUpdated'),self.loadTagCombo)
    self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('tagDeleted'),self.loadTagCombo)

    self.adFrame = None

  def loadTagCombo(self):
    self.widgets['tagCombo'].clear()
    self.widgets['tagCombo'].addItem('Tag..',QtCore.QVariant(0))
    for tagId,parentId,text in PROJECTSMANAGER().db().getTagList():
      self.widgets['tagCombo'].addItem(text,QtCore.QVariant(tagId))

  def handleTagCombo(self,indx):
    if indx==0: return
    self.widgets['text'].setText(self.widgets['tagCombo'].currentText())

  def drawAdvancedFrame(self):
    import CCP4Annotation
    import CCP4AnnotationWidgets,CCP4GuiUtils
    self.adFrame = QtGui.QFrame(self)
    self.adFrame.setLayout(QtGui.QVBoxLayout())
    self.adFrame.layout().setSpacing(1)
    self.adFrame.layout().setContentsMargins(1,1,1,1)
    self.adFrame.hide()
    self.layout().addWidget(self.adFrame)

    line = QtGui.QHBoxLayout()
    line.setSpacing(1)
    '''
    Taken out alternative date search modes - just use active mode
    line.addWidget(QtGui.QLabel('and was',self))
    self.widgets['dateMode'] = QtGui.QButtonGroup(self)
    for item,bid in [['active',1],['created',2],['last accessed',3]]:
      but = QtGui.QRadioButton(item,self)
      line.addWidget(but)
      self.widgets['dateMode'].addButton(but,bid)
    self.widgets['dateMode'].button(1).setChecked(True)
    '''
    self.widgets['useDate'] = QtGui.QCheckBox('Project active',self)
    self.widgets['useDate'].setChecked(False)
    line.addWidget(self.widgets['useDate'])   
    self.model['dateRange']=CCP4Annotation.CDateRange(parent=self)
    self.widgets['dateRange'] = CCP4AnnotationWidgets.CDateRangeView(parent=self,model=self.model['dateRange'])
    self.widgets['dateRange'].layout().takeAt(0).widget().deleteLater()
    self.widgets['dateRange'].setStyleSheet("QFrame { border : 0px };")
    line.addWidget(self.widgets['dateRange'])
    line.addStretch(2)
    self.adFrame.layout().addLayout(line)

    line = QtGui.QHBoxLayout()
    line.addWidget(QtGui.QLabel('and is a sub-project of',self))
    import CCP4Widgets
    self.widgets['drop'] = QtGui.QToolButton(self)
    self.widgets['drop'].setIcon(QtGui.QIcon(CCP4Widgets.PIXMAPMANAGER().getPixmap('project')))
    line.addWidget(self.widgets['drop'])   
    self.widgets['parent'] = QtGui.QComboBox(self)
    self.widgets['parent'].setEditable(False)
    line.addWidget(self.widgets['parent'])
    line.setStretch(2,2)
    self.adFrame.layout().addLayout(line)

    line = QtGui.QHBoxLayout()
    line.addWidget(QtGui.QLabel('and uses',self))
    self.modeButtonGroup = QtGui.QButtonGroup(self)
    self.modeButtonGroup.setExclusive(True)
    butId = 0
    #for item in ['task and control parameters or','imported file or','directory']:
    for item in ['imported file or','directory']:
      but = QtGui.QRadioButton(item,self)
      butId += 1
      self.modeButtonGroup.addButton(but,butId)
      line.addWidget(but)
    line.addStretch(2)
    self.modeButtonGroup.button(1).setChecked(True)
    self.connect(self.modeButtonGroup,QtCore.SIGNAL('buttonClicked(int)'),self.handleModeChange)
    self.adFrame.layout().addLayout(line)

    self.modeStack = QtGui.QStackedLayout()
    self.adFrame.layout().addLayout(self.modeStack)

    frame = QtGui.QFrame(self)
    frame.setLayout(QtGui.QVBoxLayout())
    frame.layout().setSpacing(2)
    frame.layout().setContentsMargins(2,2,2,2)
    line = QtGui.QHBoxLayout()
    line.addWidget(QtGui.QLabel('Task'))

    '''
    #self.widgets['taskName'] = QtGui.QTreeView(self)
    self.widgets['taskName'] =CCP4Widgets.CTreeComboBox(self)
    self.taskMenu = CCP4Widgets.CTasksModel(self)
    self.widgets['taskName'].setModel(self.taskMenu)    
    line.addWidget(self.widgets['taskName'])
    frame.layout().addLayout(line)
    self.modeStack.addWidget(frame)
    '''

    frame = QtGui.QFrame(self)
    frame.setLayout(QtGui.QVBoxLayout())
    frame.layout().setSpacing(2)
    frame.layout().setContentsMargins(2,2,2,2)
    line = QtGui.QHBoxLayout()
    import CCP4File
    self.model['importedFile'] = CCP4File.CDataFile(parent=self)
    self.widgets['importedFile'] = CCP4Widgets.CDataFileView(self,model=self.model['importedFile'],
                qualifiers= { 'jobCombo':False, 'autoInfoOnFileImport':False, 'browseDb' : False } )
    self.widgets['importedFile'].layout().takeAt(0).widget().deleteLater()
    line.addWidget(QtGui.QLabel('File imported into project'))
    line.addWidget( self.widgets['importedFile'])
    line.setStretchFactor(self.widgets['importedFile'],2)
    frame.layout().addLayout(line)
    self.modeStack.addWidget(frame)

    frame = QtGui.QFrame(self)
    frame.setLayout(QtGui.QVBoxLayout())
    frame.layout().setSpacing(2)
    frame.layout().setContentsMargins(2,2,2,2)
    line = QtGui.QHBoxLayout()
    self.model['projectDir'] = CCP4File.CDataFile(parent=self,qualifiers={'isDirectory':True, 'mustExist' : True })
    self.widgets['projectDir'] = CCP4Widgets.CDataFileView(self,model=self.model['projectDir'],
                qualifiers= { 'jobCombo':False, 'autoInfoOnFileImport':False, 'browseDb' : False } )
    self.widgets['projectDir'].layout().takeAt(0).widget().deleteLater()
    line.addWidget(QtGui.QLabel('Uses project directory'))
    line.addWidget( self.widgets['projectDir'])
    line.setStretchFactor(self.widgets['projectDir'],2)
    frame.layout().addLayout(line)
    self.modeStack.addWidget(frame)

    line =QtGui.QHBoxLayout()
    line.addStretch(0.5)
    for icon,label,action in [ ['search','Search',self.doSearch],
                          ['help','Help',self.showHelp],
                          ['undo','Clear',self.clear],
                          ['search-plus','Close',functools.partial(self.toggleAdvanced,False)] ]:
      icon =CCP4GuiUtils.createIcon(name=icon)
      but = QtGui.QPushButton(icon,label,self)
      self.connect(but,QtCore.SIGNAL('clicked()'),action)
      line.addWidget(but)
      line.addStretch(0.1)
    line.addStretch(0.5)
    self.adFrame.layout().addLayout(line)

    self.load()

  def toggleAdvanced(self,advanced=None):
    #print 'toggleAdvanced',advanced,self.height()
    if not advanced:
      self.adFrame.hide()
      self.simpleButtons.show()
    else:
      # Save the heigth of the basic frame to reset when closing advanced
      if self.adFrame is None: self.drawAdvancedFrame()
      self.adFrame.show()
      self.simpleButtons.hide()

  def handleModeChange(self,mode):
    self.modeStack.setCurrentIndex(mode-1)

  def doSearch(self):
    params = self.get()
    retList = PROJECTSMANAGER().db().projectSearch(searchParams=params)
    #print 'CSearchProjectDialog.handleApply',retList
    pidList = []
    for pid,name in retList: pidList.append(pid)
    self.parent().projectsView.setHighlights(None,pidList)
    
  def clear(self):
    self.parent().projectsView.setHighlights(None,[])

  def showHelp(self,target=None):
    WEBBROWSER().loadWebPage(helpFileName='searchTools',target=target)
    
  def load(self):
    info = PROJECTSMANAGER().db().getProjectSearchInfo()
    #print 'load',info
    self.widgets['parent'].setEditable(True)
    self.widgets['parent'].clear()
    self.widgets['parent'].addItem('Not set',QtCore.QVariant(-1))
    for pid,name in info['parentProjects']:
      self.widgets['parent'].addItem(name,QtCore.QVariant(pid))
    self.widgets['parent'].setEditable(False)


  def get(self):
    ret = {}
    nSearch = 0
    for name in [ 'name','annotation','tags']:
      ret[name] = self.widgets[name].isChecked()
      if ret[name]: nSearch+=1

    ret['textSearchMode'] = self.widgets['textSearchMode'].currentIndex()

    if nSearch>0:
      ret['searchText'] = str(self.widgets['text'].text()).strip()
      if len(ret['searchText']) ==0: ret['searchText'] = None
    else:
      ret['searchText'] = None

    '''
    ret['dateMode'] = ['active','created','lastAccessed'][self.widgets['dateMode'].checkedId()-1]
    '''
    if self.adFrame is not None and self.adFrame.isVisible(): 
      ret['dateMode'] = 'active'
      ret['useDate'] = self.widgets['useDate'].isChecked()

      self.widgets['dateRange'].updateModelFromView()
      ret['minTime'],ret['maxTime'] = self.model['dateRange'].epochRange()

      if self.widgets['parent'].currentIndex() == 0:
        ret['parent'] = None
      else:
        ret['parent'] = str(self.widgets['parent'].itemData(self.widgets['parent'].currentIndex()).toString())
      
      if self.modeButtonGroup.checkedId() == 1:
        self.widgets['importedFile'].updateModelFromView()
        if self.model['importedFile'].isSet():
          ret['importedFile'] = str(self.model['importedFile'])
      elif self.modeButtonGroup.checkedId() == 2:
        self.widgets['projectDir'].updateModelFromView()
        if self.model['projectDir'].isSet():
          ret['projectDir'] = str(self.model['projectDir'])

    #print 'CSearchProjectWidget.get',ret
    return ret
