"""
     CCP4WebBrowser.py: CCP4 GUI Project
     Copyright (C) 2001-2008 University of York, CCLRC
     Copyright (C) 2009-2010 University of York
     Copyright (C) 2011-2014 Science & Technology Facilities Council

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

"""
   Liz Potterton Jan 2010 - Copied from MGWebBrowser with changes to make stand-alone and to enable
                            Qt plugins and custom mime types
                 Feb 2010 - Make menus/toolbars customisable, add CCP4ProjectManger as placeholder
"""

##@package CCP4WebBrowser (QtWebKit) The web browser framework

import sys,os
from PyQt4 import QtWebKit, QtGui, QtCore
import CCP4WebView
from CCP4ErrorHandling import *
import CCP4Modules
import CCP4UpdateManager

def setupWebkit():

	try:
		if sys.platform == 'win32':
     			homedir = os.environ.get('USERPROFILE')
		else:
			homedir = os.environ.get('HOME')
	except:
		pass
	if homedir and os.path.exists(homedir):
		webkit_db = os.path.join(homedir,'.qwebkit_idb')
		if os.path.exists(webkit_db):
        		QtWebKit.QWebSettings.setIconDatabasePath(webkit_db)
		else:
			try:
				os.mkdir(webkit_db)
        			QtWebKit.QWebSettings.setIconDatabasePath(webkit_db)
			except:
				# Favicons disabled
				pass



def exitBrowser():
  import sys
  import CCP4Modules
  #print '*CCP4WebBrowser exitBrowser'
  CMainWindow.queryClose=False
  rv = saveStatus()
  purgeStatusFiles(2)
  import CCP4ProjectViewer
  for win in CCP4ProjectViewer.CProjectViewer.Instances:
    win.Exit()
  CCP4Modules.PREFERENCES().save()
  #print 'purgeStatusFiles done'
  CCP4Modules.QTAPPLICATION().setActiveWindow(CCP4Modules.WEBBROWSER())
  CCP4Modules.QTAPPLICATION().closeAllWindows()


def saveStatus():
  import os
  import CCP4Utils
  if CMainWindow.STATUS_SAVED: return ''
  from lxml import etree
  import CCP4ProjectViewer
  body = etree.Element('body')
  root = etree.Element('windows')
  body.append(root)

  #print 'saveStatus',CBrowserWindow.Instances,CCP4ProjectViewer.CProjectViewer.Instances
  #import traceback
  #traceback.print_stack(limit=5)

  import CCP4I1Projects
  for win in CCP4I1Projects.CI1ProjectViewer.Instances:
    win.Exit()
  
  for win in CBrowserWindow.Instances:
    winEle = etree.Element('browser')
    try:
      size = (win.size().width(),win.size().height())
      sizeEle = etree.Element('windowSize')
      sizeEle.text = str(size[0])+','+str(size[1])
      winEle.append(sizeEle)
    except:
      pass
    #print 'CMainWindow.saveStatus browser',win.tab().count()
    for ii in range(win.tab().count()):
      tabEle = etree.Element('tab')
      winEle.append(tabEle)
      fileEle =  etree.Element('filename')
      try:
        url = win.tab().widget(ii).url()
      except:
        pass
      else:
        #filename = os.path.normpath(str(win.tab().widget(ii).fileName))
        #print 'CMainWindow.saveStatus',url.isLocalFile()
        if url.isLocalFile():
          filename = url.toLocalFile().__str__().strip('file:')
          if not os.path.exists(filename) and os.path.splitext(filename)[1] == '.htm':
            filename = filename +'l'
          if os.path.relpath(filename,CCP4Utils.getCCP4I2Dir()).startswith('docs'):
            fileEle.text = '$CCP4I2/'+ os.path.relpath(filename,CCP4Utils.getCCP4I2Dir())
          else:
            fileEle.text = filename
        else:
          fileEle.text = url.toString().__str__()
      #print 'CMainWindow.saveStatus browser tab file',win.tab().widget(ii).fileName
      tabEle.append(fileEle)
      title = win.tab().widget(ii).title()
      if title is not None:
        titleEle = etree.Element('title')
        titleEle.text =  title
      tabEle.append(titleEle)
    root.append(winEle)

  for win in CCP4ProjectViewer.CProjectViewer.Instances:
    size = None
    try:
      projectName = CCP4Modules.PROJECTSMANAGER().db().getProjectInfo(projectId=win.getProject(),mode='projectname')
      jobNumber = win.getOpenJobNumber()
      size = (win.size().width(),win.size().height())
    except:
      pass
    else:
      winEle = etree.Element('projectViewer')
      projectEle = etree.Element('project')
      #print 'CMainWindow.saveStatus projectName',projectName
      projectEle.text = projectName
      winEle.append(projectEle)
      if jobNumber is not None:
        jobEle = etree.Element('openJobNumber')
        jobEle.text = str(jobNumber)
        winEle.append(jobEle)
	if size is not None:
          sizeEle = etree.Element('windowSize')
  	  sizeEle.text = str(size[0])+','+str(size[1])
  	  winEle.append(sizeEle)
      # Issues with handing ByteArray - skip this for now
      #ele = etree.SubElement(projectEle,'headerState')
      #ele.text = win.projectWidget().getHeaderState()
      root.append(winEle)

  #print 'CCP4WebBrowser.saveStatus',etree.tostring(body,pretty_print=True)
    
  import CCP4Utils,CCP4File
  import os,time
  statusFile = os.path.join(CCP4Utils.getDotDirectory(),'status','status_'+str(int(time.time()))+'.ccp4i2_status.xml')
  f= CCP4File.CI2XmlDataFile(statusFile)
  f.header.function='STATUS'
  f.header.setCurrent()    
  f.saveFile(bodyEtree=body)
  CMainWindow.STATUS_SAVED = True
  print 'CCP4i2 status saved to',statusFile
  return statusFile

def restoreStatus():
  import CCP4ProjectViewer,CCP4CustomMimeTypes,CCP4Utils
  from lxml import etree
  # For now just try to restore projects
  statusEtree = retrieveStatus()
  if statusEtree is None: return
  nBrowserWindows = 0
  for winEle in statusEtree.iterchildren():
    #print 'CCP4WebBrowser.restoreStatus',winEle.tag
    if str(winEle.tag) == 'projectViewer':
      for  projectEle in winEle.iter('project'):
        projectName=projectEle.text
        try:
          projectId = CCP4Modules.PROJECTSMANAGER().db().getProjectId(projectName=projectName)
        except:
          projectId = None
          print 'Unable to open project - '+projectEle.text+'- not found in database'
        # 'restoreStatus ',projectEle.text,projectId
        if projectId is not None:
          # Do we know the open job?
          #print 'Opening project:',projectEle.text
          jobId= None
          jobEle = winEle.find('openJobNumber')
          if jobEle is not None:
            try:
              jobId = CCP4Modules.PROJECTSMANAGER().db().getJobId(jobNumber=str(jobEle.text),projectName=projectName)
            except CException as e:
              #print e.report()
              jobId = None
          try:
            print 'Opening Job:',jobEle.text,jobId
            proj = CCP4ProjectViewer.CProjectViewer(projectId=projectId,jobId=jobId)
            try:
              sizeStr = winEle.find('windowSize').text.split(',')
              size = (int(sizeStr[0]),int(sizeStr[1]))
            except:
              size = CCP4ProjectViewer.DEFAULT_WINDOW_SIZE	  
            proj.resize(size[0],size[1])
            # Issues with handing ByteArray - skip this for now
            #headerEle = winEle.find('headerState')
            #if headerEle is not None:
            #  proj.projectWidget().setHeaderState(str(headerEle.text()))
	      
            proj.menuBar().show()
            proj.show()
            proj.raise_()
          except:
            print "Failed to open project",projectId,jobId
    elif str(winEle.tag) == 'browser':
      if nBrowserWindows == 0:
        browser = CCP4Modules.WEBBROWSER()
      else:
        browser = CBrowserWindow()
        browser.show()
	browser.raise_()
      nBrowserWindows = nBrowserWindows + 1
      for tab in winEle.iterchildren():
        if tab.tag == 'windowSize':
          try:
  	        sizeStr = tab.text.split(',')
	        browser.resize(int(sizeStr[0]),int(sizeStr[1]))
          except:
           pass
        elif tab.tag == 'tab':
          fileName = None
          title = None
          for ele in tab.iterchildren():
            if ele.tag == 'filename':
              fileName = str(ele.text)
              #print 'restoreStatus fileName',fileName
              if fileName.startswith('$CCP4I2'):
                fileName = os.path.join(CCP4Utils.getCCP4I2Dir(),fileName[8:])
              elif ele.tag == 'title':
                title = str(ele.text)
              if fileName is not None:
                #print 'Opening file:',fileName
                if fileName.startswith('http'):
                  url = QtCore.QUrl(fileName)
                  browser.loadPage(url)
                else:
                  browser.openFile(fileName,title=title,internal=True)


def applyCommandLine(args):
	  	  
  # If there is a file/project on the com line..
  import CCP4ProjectViewer
  #print 'CCP4Browser.applyCommandLine',args
  iArg = 0
  while iArg < len(args):
    filepath = None
    if args[iArg][0:2] == '-t':      
      import CCP4Utils
      top_path = CCP4Utils.getCCP4I2Dir()
      filepath = os.path.join(top_path,'test','data','test_plugin.html')
    else:
      projectName = args[iArg]
      try:
        projectId = CCP4Modules.PROJECTSMANAGER().db().getProjectId(projectName=projectName)
      except:
        projectId = None
      #print 'CCP4WebBrowser.applyCommandLine projectId',projectId
      if projectId is None:
        filepath  = os.path.abspath(args[iArg])
	    #print 'CCP4Browser.applyCommandLine filepath',filepath
        if os.path.exists(filepath):
          #print 'Opening file:',filepath
          CCP4Modules.WEBBROWSER().openFile(filepath,internal=True)
        else:
          print args[iArg],'not recognised as project name or file'
      else:
        #print 'Opening project:',projectName
        proj = CCP4ProjectViewer.CProjectViewer(projectId=projectId)
	proj.resize(CCP4ProjectViewer.DEFAULT_WINDOW_SIZE[0],CCP4ProjectViewer.DEFAULT_WINDOW_SIZE[1])
  	proj.show()
	proj.raise_()
    iArg = iArg + 1

  if len(CBrowserWindow.Instances) + len(CCP4ProjectViewer.CProjectViewer.Instances) <=0:
    #print 'Opening default browser window'
    browser = CBrowserWindow()
    browser.show()
 
  
def retrieveStatus():
  import glob,os
  import CCP4File,CCP4Utils
  statusFileList = glob.glob(os.path.join(CCP4Utils.getDotDirectory(),'status','status_*.ccp4i2_status.xml'))
  if len(statusFileList)==0:
    return None
  else:
    statusFileList.sort()
    print 'Retrieving status file:'+statusFileList[-1]
    f = CCP4File.CI2XmlDataFile(statusFileList[-1])
    body = f.getBodyEtree()
    root = body.find('windows')
    return root

def purgeStatusFiles(leave=1):
  import glob,os
  import CCP4File,CCP4Utils
  #print 'purgeStatusFiles leave',leave
  statusFileList = glob.glob(os.path.join(CCP4Utils.getDotDirectory(),'status','status_*.ccp4i2_status.xml'))
  #print 'purgeStatusFiles',statusFileList
  if leave == 0:
    for sFile in statusFileList:
      os.remove(sFile)
  elif len(statusFileList)>leave:
    for sFile in statusFileList[0:-leave]:
      os.remove(sFile)

#-------------------------------------------------------------------
def OPENFILE(fileName=None,format=None,title=None,toFront=False):
#-------------------------------------------------------------------
    import CCP4Config,CCP4Modules
    
    mimeTypeHandler = CCP4Modules.MIMETYPESHANDLER()

    if os.path.isdir(fileName):
      format = 'dir'  
    if mimeTypeHandler is None:
      # No handler so just throw it at web browser and hope
      if format is None: format = 'text/html'
    else:
      if format is None:
        format = mimeTypeHandler.formatFromFileExt(fileName)
      if not mimeTypeHandler.isSupportedFormat(format):
        format = 'text/html'
   
    #print 'CCP4WebBrowser.OPENFILE format',fileName,format

    if mimeTypeHandler.useDesktopServices(format):
      abs_fileName = os.path.abspath(fileName)
      #print 'calling QDesktopServices',abs_fileName
      url = QtCore.QUrl.fromLocalFile(abs_fileName)
      rv = QtGui.QDesktopServices.openUrl(url)
      if not rv:
        QtGui.QMessageBox.warning(None,'CCP4i2 display '+fileName,'Attempting to display file '+os.path.split(abs_fileName)[-1]+'\nusing desktop services failed')
        return None

    widgetClassList = mimeTypeHandler.getViewers(format)
    if len(widgetClassList)>0:
      widgetClass = widgetClassList[0]
      #print 'CCP4WebBrowser.OPENFILE widgetClass',widgetClass,type(widgetClass)
      if isinstance(widgetClass,str):
        # This is a keyword for the launcher
	    CCP4Modules.LAUNCHER().openInViewer(viewer=widgetClass,fileName=fileName)
	    return None
        
    CCP4Modules.WEBBROWSER().openFile(fileName=fileName,format=format,title=title,toFront=toFront)
      
       
class CTabWidget(QtGui.QTabWidget):

  def __init__(self,parent=None,name=None,mini=False):
    QtGui.QTabWidget.__init__(self,parent)
    self.setObjectName(name)
    self.connect(self,QtCore.SIGNAL('currentChanged(int)'),self.handleCurrentChanged)
    self.currentOpen = -1
    #try:
    #  self.setTabsCloseable(True)
    #except:
    if mini:     
      self.setCornerWidget(QtGui.QPushButton(self.tr("Expand window")))
    else:
      self.setCornerWidget(QtGui.QPushButton(self.tr("Close tab")))

  def deleteTabWidget(self,idx=None,widget=None):
    if idx is None:
      for ii in range(self.count()):
        if self.widget(ii) == widget: 
          idx = ii
	  break
    if idx is None: return
    self.widget(idx).handleTabbedClosed()
    self.widget(idx).close()
    self.widget(idx).deleteLater()
    # We are keeping track of currentOpen to be sure to
    # call handleTabbedClosed() when necessary
    if self.currentOpen > idx:
      self.currentOpen = self.currentOpen - 1
    elif self.currentOpen == idx:
      self.currentOpen = -1
    self.removeTab(idx)
    

  def handleCurrentChanged(self,indx):
    
    #print 'CTabWidget.handleCurrentChanged',indx
    if self.currentOpen >= 0:
      self.widget(self.currentOpen).handleTabbedClosed()
      #print 'CTabWidget.handleCurrentChanged',self.currentOpen,self.widget(self.currentOpen)
    self.currentOpen = indx
    self.parent().parent().editSplitter.addressEdit.setText(self.widget(indx).title())
    self.widget(indx).handleTabbedOpen()
    self.parent().parent().updateActionEnabled()

  def closeTabWidget(self,obj):
    for ii in range(len(self.widget)):
      if self.widget[ii] == obj: 
        self.deleteTabWidget(ii)

class CEditSplitter(QtGui.QSplitter):
  def __init__(self,parent):
    QtGui.QSplitter.__init__(self,parent)
    self.addressEdit = QtGui.QLineEdit()
    self.searchEdit = QtGui.QLineEdit()
    searchStyle = """QLineEdit {
             		border: 1px solid gray;
             		border-radius: 6;
			border-style: inset;
             		padding: 0 3px;
         	}
	 	"""
    self.searchEdit.setStyleSheet(searchStyle)
    self.addWidget(self.addressEdit) 
    self.addWidget(self.searchEdit)
    self.setStretchFactor(0,2)


class CMenuBar(QtGui.QMenuBar):

  def __init__(self,parent):
    QtGui.QMenuBar. __init__(self,parent)
    self.menuDefinitions = {}
    for menuName,menuTitle in [['File','&File'],['Edit','&Edit'], ['History','H&istory'],['Utilities','&Utilities'],['Projects','&Projects'],['Help','Help']]:
      self.addMenu(menuName,menuTitle)
    self.menuDefinitions['File'] = ['open','open_browser','sep','save','print','sep','close_window','quit']
    #self.menuDefinitions['Edit'] = ['find','preferences','ccp4i2_config']
    self.menuDefinitions['Edit'] = ['find','preferences']
    self.menuDefinitions['History'] = []
    self.menuDefinitions['Utilities'] = ['listProcesses','manageImportFiles',['Program log','programPrintLog','programHTTPLog'],'clearStatusFiles','editScript','sendReport',['System administrator tools','update_core','serverSetup','import_task'],['Developer tools','redo_report','view_report_source','view_test_report','export_task','list_tasks','grab_task','compress_demo_data','auto_task_docs']]
    self.menuDefinitions['Customisation'] = ['manageWorkflows','patchComFile','customTask','importJob']
    self.menuDefinitions['Projects'] = ['manager_projects','new_project',['View old CCP4i projects','open_i1projects_default','open_i1projects_select']]
    self.menuDefinitions['Help'] = ['help_about','help_quickstart','help_quickexpert','help_youtube','task_docs','welcome_ccp4i2','help_ccp4i2','help_ccp4_home','help_updates','help_license']

    from CCP4Bazaar import bzrlib_exists
    if bzrlib_exists:
      self.menuDefinitions['Utilities'][6].insert(1, 'update_gui')

    # Beware need to define the 'quit' in File menu at startup for the slot to exitBrowser() to work
    # This is likely a mac-specific thing since the quit gets move to application menu
    import CCP4GuiUtils
    CCP4GuiUtils.populateMenu(self.parent(), self.menuWidget('Edit'),self.menuDefinition('Edit'),default_icon='')
    CCP4GuiUtils.populateMenu(self.parent(),self.menuWidget('File'),self.menuDefinition('File'),default_icon='')
   
  def menuWidget(self,menuName):
    return self.findChild(QtGui.QMenu,menuName)

  def updateMenu(self,menuName):
    import CCP4Utils,CCP4GuiUtils
    import functools
    menuWidget = self.menuWidget(menuName)
    if menuWidget is None:
      pass
    elif menuName == 'Projects':
      recentProjects = CCP4Modules.PROJECTSMANAGER().db().getRecentProjects(order='access',limit=11)
      menuDefn = ['manage_projects','new_project','import_project',['View old CCP4i projects','open_i1projects_default','open_i1projects_select'],'sep']
      for projectId,projectName,accessTime in recentProjects[:10]:
        if not self.parent().actionDefinitions.has_key('open_project_'+str(projectId)):
          self.parent().setActionDefinition('open_project_'+str(projectId), dict (
            text = projectName,
            tip = "Open this project",
            slot = functools.partial(self.parent().openProject,projectId,projectName),
            enabled = 1,
            checkable = 1,
            checked = functools.partial( self.isProjectOpen,projectId)
            ))
        menuDefn.append('open_project_'+str(projectId))
      menuDefn.append('more_projects')
      # Enable more_projects if >10 projects in db 
      self.parent().actionDefinitions['more_projects']['enabled'] = len(recentProjects)>10
      menuWidget.clear()
      CCP4GuiUtils.populateMenu(self.parent(),menuWidget,menuDefn,default_icon='')
      #self._redrawProjectMenu = False
    elif menuName == 'Edit':
      pass
    elif menuName == 'Utilities':
      menuWidget.clear()
      menuDefn = [ ['Copy demo data to project'] ]
      if self.parent().isProjectViewer():
        testDatasets = CCP4Modules.DEMODATAMANAGER().getTestDatasets()
        for dataset,label in testDatasets:
          self.parent().setActionDefinition('download_test_'+dataset, dict (
            text = label,
            tip = "Copy this data to project directory",
            slot = functools.partial(CCP4Modules.DEMODATAMANAGER().copyDemoDataToProject,self,self.parent().taskFrame.openJob.projectId,dataset),
            enabled = self.parent().isProjectViewer
            ) )
          menuDefn[0].append('download_test_'+dataset)
        menuDefn[0].append('sep')
      menuDefn[0].append('demo_data_info')
      menuDefn[0].append('download_demo_data')
      menuDefn.extend( self.menuDefinitions['Utilities'] )
      CCP4GuiUtils.populateMenu(self.parent(),menuWidget,menuDefn,default_icon='')			  
    else:
      menuWidget.clear()
      CCP4GuiUtils.populateMenu(self.parent(),menuWidget,self.menuDefinition(menuName),default_icon='')

  def isProjectOpen(self,projectId):
    import CCP4ProjectViewer
    for proj in CCP4ProjectViewer.CProjectViewer.Instances:
      if hasattr(proj,'openJob') and proj.openJob.projectId == projectId: return True
    return False


  def addMenu(self,menuName=None,menuTitle=None,updateFunction=None):
    if menuName is None or menuTitle is None:
      print 'ERROR in CMenuBar.addMenu; no menu name or no menu title given'
      return None
    widget = self.menuWidget(menuName)
    if widget is None:
      widget = QtGui.QMenuBar.addMenu(self,menuTitle)
      widget.setObjectName(menuName)
      import functools
      if updateFunction is None:
        self.connect(widget,QtCore.SIGNAL("aboutToShow()"),
                         functools.partial(self.updateMenu,menuName))
      else:
        self.connect(widget,QtCore.SIGNAL("aboutToShow()"),
                         functools.partial(updateFunction,menuName))
    return widget

  def removeMenu(self,menuName=''):
    # Save current menus, clear and add all menus again
    currentMenuList = []
    found = False
    currentMenuWidgets = self.findChildren(QtGui.QMenu)
    for w in currentMenuWidgets:
      name = str(w.objectName())
      if name == menuName:
        found = True
      else:
        currentMenuList.append([str(w.objectName()),str(w.title())])

    if found:
      self.clear()
      for menuName,menuTitle in currentMenuList:
        self.addMenu(menuName,menuTitle)

  def setMenuTitle(self,menuName='',menuTitle=''):
    widget = self.menuWidget(menuName)
    if widget is not None:
      widget.setTitle(menuTitle)

  def menuDefinition(self,menuName=''):
    return self.menuDefinitions.get(menuName,[])

  def setMenuDefinition(self,menuName='',definition=[]):
    self.menuDefinitions[menuName] = definition

  def appendToMenu(self,menuName='',menuItem=''):
    # menuItem should be the name of an action or 'sep' (separator)
    if not self.menuDefinitions.has_key(menuName):
      print 'ERROR in CMenuBar.appendToMenu: '+menuName+' menu does not exist'
    self.menuDefinitions[menuName].append(menuItem)

class CToolBar(QtGui.QToolBar):

  def __init__(self,parent,name='',title=''):
    QtGui.QToolBar.__init__(self,title,parent)
    self.setObjectName(name)
    if hasattr(CCP4Modules.PREFERENCES(),"TOOLBARBUTTONSSTYLE"):
        self.setToolButtonStyle(int(CCP4Modules.PREFERENCES().TOOLBARBUTTONSSTYLE))
    self.setIconSize(QtCore.QSize(24,24))
    self.definition = []
    
  def append(self,toolName=''):
    if not self.definition.count(toolName):
      self.definition.append(toolName)
      self.redraw()

  def extend(self,toolNameList=[]):
    for toolName in toolNameList:
      if not self.definition.count(toolName):
        self.definition.append(toolName)
    self.redraw()

  def remove(self,toolName=''):
    if self.definition.count(toolName):
      self.definition.remove(toolName)
      self.redraw()

  def redraw(self):
    self.clear()
    import CCP4GuiUtils
    CCP4GuiUtils.populateToolBar(parent=self.parent(),toolBarWidget=self,
			     definition=self.definition)

def isAlive(qobj):
  import sip
  try:
    sip.unwrapinstance(qobj)
  except RuntimeError:
    return False
  return True


def mainWindowIcon():
  if CMainWindow._MAINWINDOWICON is None:
    import CCP4Utils
    fileName = os.path.join(CCP4Utils.getCCP4I2Dir(),'qticons','ccp4.png')
    CMainWindow._MAINWINDOWICON = QtGui.QIcon(QtGui.QPixmap(fileName))
  return CMainWindow._MAINWINDOWICON
    

class CMainWindow(QtGui.QMainWindow):

  projectManagerDialog = None
  queryClose = False
  STATUS_SAVED = False
  _MAINWINDOWICON = None

  ERROR_CODES = { 201 : { 'description' : 'Error opening zip file for write' } ,
                  202 : { 'description' : 'Error saving directory to zip file' },
                  203 : { 'description' : 'Error closing zip file from read' },
                  204 : { 'description' : 'Error opening zip file to read' } ,
                  205 : { 'description' : 'Error extracting zip file from directory' },
                  206 : { 'description' : 'Compressed task file does not contain appropriately named task' },
                  207 : { 'description' : 'Can not import task as it requires overwriting exisiting task' },
                  208 : { 'description' : 'Error deleting existing task directory' },
                  208 : { 'description' : 'Selected inappropriate directory to save as compressed task file' }
                   }
  
  def __init__(self,parent=None):
    #print 'CMainWindow.__init__ parent',self,parent
    QtGui.QMainWindow.__init__(self,parent)
    self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
    self.setWindowIcon(mainWindowIcon())
    # Remove access to bzr as Andrey claims it causes access to repository that is acting badly possibly due to loading
    try:
      import CCP4Update
      self.version = CCP4Update.get_ccp4_str() + ' '
    except:
      self.version = 'CCP4i2'  
    
    self.preferenceWindow=None
    self.configWindow=None
    self.fileDialog = None
    self.findFrame= None
    self.initialiseActionDefinitions()
    self.setMenuBar(CMenuBar(self))
    CCP4UpdateManager.um.check()

  def closeEvent(self,event):
    import CCP4ProjectViewer,CCP4I1Projects
    if CBrowserWindow.Dummy is None:
      nOpen = 0
    else:
      nOpen = len(CBrowserWindow.Dummy.findChildren(QtGui.QDialog))
   # print 'closeEvent Dummy.findChildren',nOpen
    nOpen = nOpen + len(CBrowserWindow.Instances) + len(CCP4ProjectViewer.CProjectViewer.Instances) + len(CCP4I1Projects.CI1ProjectViewer.Instances)
    #print 'closeEvent Instances',nOpen
    #print 'closingDown',CCP4Modules.QTAPPLICATION().closingDown()
    if nOpen>1:
      self.close()
      event.accept()
    else:
      if CMainWindow.queryClose:
        rv = QtGui.QMessageBox.question(self,'Close CCP4Browser','Close CCP4 Browser?',
              QtGui.QMessageBox.Close|QtGui.QMessageBox.Cancel,QtGui.QMessageBox.Cancel)
      else:
        rv = QtGui.QMessageBox.Close
	
      if rv ==  QtGui.QMessageBox.Close:
        self.Exit()
        saveStatus()
        purgeStatusFiles(2)
        event.accept()
      else:
        event.ignore()

  def Exit(self):
    pass

  def windowTitle(self):
    return str(QtGui.QMainWindow.windowTitle(self))
        
  def initialiseActionDefinitions(self):
    import functools
    
    self.actionDefinitions = {}
    self.actionDefinitions['quit'] = dict ( 
        text = 'Quit CCP4i2',
        tip = 'Close all CCP4i2 windows',
  	    slot = exitBrowser )

    self.actionDefinitions['help_about'] = dict (
          text = "CCP4i2 info",
          tip = "CCP4i2 background",
          slot = self.showAbout,
	  icon = 'ccp4'
          )

    self.actionDefinitions['welcome_ccp4i2'] = dict (
          text = "Welcome - options to get started",
          tip = "Options to set up CCP4i2 Project",
          slot = functools.partial(self.showHelp,'ccp4i2'),
	  shortcut = self.tr('cmd+P'),
	  icon = 'ccp4'
          )
    self.actionDefinitions['help_quickstart'] = dict (
          text = "Quickstart - 10 minute intro",
          tip = "Quick introduction to using i2",
          slot = functools.partial(self.showHelp,'quickstart'),
	  shortcut = self.tr('cmd+Q'),
	  icon = 'ccp4'
          )
    self.actionDefinitions['help_quickexpert'] = dict (
          text = "Quick expert - more quick hints",
          tip = "Part 2 of quick introduction to i2",
          slot = functools.partial(self.showHelp,'quickexpert'),
	  icon = 'ccp4'
          )
    self.actionDefinitions['help_youtube'] = dict (
          text = "View YouTube video",
          tip = "Quick introduction iin youtube (6mins)",
          slot = functools.partial(self.showHelp,'youtube'),
	  icon = 'ccp4'
          )
    self.actionDefinitions['help_ccp4i2'] = dict (
          text = "CCP4i2 Help",
          tip = "Documentation and 'where to start'",
          slot = functools.partial(self.showHelp,'tutorial'),
	  shortcut = self.tr('cmd+H'),
	  icon = 'ccp4'
          )
    
    self.actionDefinitions['task_docs'] = dict (
          text = "Task documentation",
          tip = "Documentation for the individual tasks",
          slot = functools.partial(self.showHelp,'task_docs'),
	      icon = 'ccp4'
          )

    self.actionDefinitions['help_ccp4_home'] = dict (
          text = "CCP4 Home Page",
          tip = "For further information on CCP4",
          slot = functools.partial(self.showHelp,'ccp4_home'),
          icon = 'ccp4'
          )
    self.actionDefinitions['help_updates'] = dict (
          text = "CCP4 Updates",
          tip = "",
          slot = functools.partial(self.showHelp,'updates')
          )
    self.actionDefinitions['help_license'] = dict (
          text = "CCP4 Licence",
          tip = "",
          slot = functools.partial(self.showHelp,'license'),
          )
    
    self.actionDefinitions['open'] = dict (
          text = "Open file",
          tip = "Open html, text, image or log file",
          slot = self.openFileDialog,
          icon = 'fileopen',
	  shortcut = QtGui.QKeySequence.Open
          )
     

    self.actionDefinitions['close_window'] = dict (
          text = "Close window",
          tip = "Close this CCP4i2 window",
          slot = self.close,
          )

    self.actionDefinitions['save'] = dict (
          text = "Save",
          tip = "Save to file",
          slot = self.handleSave,
          enabled = self.widgetIsSaveable
          )
     
    self.actionDefinitions['print'] = dict (
          text = "Print",
          tip = "Print file",
          slot = self.handlePrint,
          enabled = self.widgetIsPrintable
          )
    
    self.actionDefinitions['run'] = dict (
          text = "Run",
          tip = "Run script",
          slot = self.handleRun,
          enabled = self.widgetIsRunable
          )

    
    self.actionDefinitions['find'] = dict (
          text = "Find",
          tip = "Find in file",
          slot = self.openFind,
	  icon = 'search',
          checkable = 1,
          checked = self.isFindFrameOpen,
          enabled = self.widgetIsSearchable
          )
    
    self.actionDefinitions['preferences'] = dict (
          text = "Preferences",
          slot = self.openPreferences,
          checkable = 1,
          checked = self.isPreferencesOpen,
          )
    
    self.actionDefinitions['ccp4i2_config'] = dict (
          text = "Configure CCP4i2",
          slot = self.openConfig,
          checkable = 1,
          checked = self.isConfigOpen,
          )
    
    self.actionDefinitions['close_tab'] = dict (
          text = "Close tab",
          tip = "Close the currently displayed file",
          slot = self.deleteTab,
          enabled = 1
          )

    self.actionDefinitions['open_browser'] = dict (
          text = "Open browser window",
          tip = "Open a new web browser window",
          slot = self.openBrowserWindow,
          enabled = 1
          )
    self.actionDefinitions['open_i1projects_default'] = dict (
          text = "View default CCP4i projects",
          tip = "Import and display projects from previous CCP4 interface",
          slot = self.openI1Projects,
          enabled = 1
          )
    self.actionDefinitions['open_i1projects_select'] = dict (
          text = "View selected CCP4i projects",
          tip = "Import and display projects from previous CCP4 interface",
          slot = functools.partial(self.openI1Projects,'select'),
          enabled = 1
          )
    
    self.actionDefinitions['back'] = dict (
          text = "Back",
          tip = "Go back one page",
          slot = self.historyBack,
          enabled = 1
          )
    
    self.actionDefinitions['forward'] = dict (
          text = "Forward",
          tip = "Go forward one page",
          slot = self.historyForward,
          enabled = 1
          )
    
    self.actionDefinitions['reload'] = dict (
          text = "Reload",
          tip = "Reload current page",
          slot = self.reloadPage,
	  shortcut = "Ctrl+R",
          enabled = 1
          )
    self.actionDefinitions['listProcesses'] = dict (
          text = "Running jobs and processes",
          tip = "Check jobs are still running",
          slot = self.listProcesses
	  )
    self.actionDefinitions['manageImportFiles'] = dict (
          text = "Manage imported files",
          tip = "Record provenance or remove imported files",
          slot = self.openManageImportFiles,
          enabled = self.isProjectViewer
	  )
    self.actionDefinitions['manageWorkflows'] = dict (
          text = "Workflows",
          tip = "Create & manage user defined work flows",
          slot = self.openWorkflow
	  )
    self.actionDefinitions['patchComFile'] = dict (
          text = "Task parameters and patches",
          tip = "Save/restore task parameters or customise a program command file",
          slot = self.openPatchComFile
	  )
    self.actionDefinitions['customTask'] = dict (
          text = "Custom tasks",
          tip = "Define a task",
          slot = self.openCustomTasks
	  )
    self.actionDefinitions['importJob'] = dict (
          text = "Import job",
          tip = "Report job run outside CCP4i2",
          slot = self.openImportJobs
	  )
    
    self.actionDefinitions['programPrintLog'] = dict (
          text = "Program print log",
          tip = "Show printed diagnostic for this run of the program",
          slot = functools.partial(self.openApplication,'programPrintLog'),
          enabled = 1
          )
    self.actionDefinitions['programHTTPLog'] = dict (
          text = "HTTP server log",
          tip = "Show log from internal HTTP server",
          slot = functools.partial(self.openApplication,'programHTTPLog'),
          enabled = 1
          )
    self.actionDefinitions['editScript'] = dict (
          text = "Edit/run script",
          tip = "Edit/run Python script within CCP4i2",
          slot = functools.partial(self.openApplication,'editScript'),
          enabled = 1
          )
    self.actionDefinitions['sendReport'] = dict (
          text = "Send error report",
          tip = "Send error report to CCP4",
          slot = self.openSendReport,
          enabled = self.isProjectViewer
          )
    self.actionDefinitions['update_core'] = dict (
          text = "Manage CCP4 updates",
          tip = "Examine, apply or remove CCP4 updates",
          slot = CCP4UpdateManager.um.manage,
          enabled = CCP4UpdateManager.um.is_unlocked
          )
    self.actionDefinitions['update_gui'] = dict (
          text = "Update CCP4I2",
          tip = "Fast track update for CCP4 interface",
          slot = self.openUpdate,
          enabled = CCP4Modules.PREFERENCES().guiUpdateEnabled
          )
    self.actionDefinitions['import_task'] = dict (
          text = "Import task code",
          tip = "Load new task from compressed file",
          slot = self.openImportTask,
          enabled = 1
          )
    self.actionDefinitions['export_task'] = dict (
          text = "Export task",
          tip = "Save task to compressed file",
          slot = self.openExportTask,
          enabled = 1
          )
    self.actionDefinitions['list_tasks'] = dict (
          text = "List tasks",
          tip = "List tasks currently available in CCP4i2",
          slot = self.listTasks,
          enabled = 1
          )
    self.actionDefinitions['grab_task'] = dict (
          text = "Grab task widget/report",
          tip = "Make screenshot of current task widget or report",
          slot = self.grabWidget,
          enabled = self.isProjectViewer
          )
    self.setActionDefinition('compress_demo_data', dict (
          text = "Compress demo data",
          tip = "Create zip file to place on demo data download site",
          slot = self.makeDemoData,
          enabled = 1
          ))
    self.setActionDefinition('general_project', dict (
          text = "Create a General project",
          tip = "Create a project for odds and ends",
          slot = self.makeGeneralProject,
          enabled = 1
          ))
    self.setActionDefinition('new_project', dict (
          text = "New project",
          tip = "Create new CCP4 project",
          slot = functools.partial(self.handleProjectMenu,'new_project'),
          enabled = 1
          ))
    self.setActionDefinition('more_projects', dict (
          text = "More projects..",
          tip = "Show all projects",
          slot = functools.partial(self.handleProjectMenu,'more_projects'),
          enabled = 1
          ))
    self.setActionDefinition('manage_projects',dict (
          text = "Manage/open projects",
          tip = "Review, manage and open CCP4 projects",
          slot = functools.partial(self.handleProjectMenu,'manage_projects'),
          enabled = 1
          ))
    self.setActionDefinition('import_project',dict (
          text = "Import project",
          tip = "Load project from a compressed file",
          slot = functools.partial(self.handleProjectMenu,'import_project'),
          enabled = 1
          ))
    self.setActionDefinition('serverSetup',dict (
          text = "Configure servers for 'remote' run jobs",
          tip = "Specify host and mechanism to run remote jobs",
          slot = self.openServerSetup,
          checked = self.isServerSetupOpen
          ))
    self.setActionDefinition('redo_report',dict (
          text = "Remake report",
          tip = "Remake the task report",
          slot = functools.partial(self.handleDeveloperTools,'redo_report'),
          enabled = self.reportAvailable
          ))
    self.setActionDefinition('view_report_source',dict (
          text = "View report source",
          tip = "Display the report in a text viewer",
          slot = functools.partial(self.handleDeveloperTools,'view_report_source'),
          enabled = self.reportAvailable
          ))
    
    self.setActionDefinition('view_test_report',dict (
          text = "Show project test report",
          tip = "Display the report from re-running the project",
          slot = functools.partial(self.handleDeveloperTools,'view_test_report'),
          enabled = self.testReportAvailable
          ))

    self.setActionDefinition('make_test_suite',dict (
          text = "Convert project to test suite",
          tip = "Add unittest template files",
          slot = functools.partial(self.handleDeveloperTools,'make_test_suite'),
          enabled = self.isProjectViewer
          ))
    
    self.setActionDefinition('demo_data_info', dict (
          text = "More info",
          tip = "Description of downloadable demo data",
          slot = functools.partial(self.showDemoDataInfo)
          #enabled = self.isProjectViewer
          ))
    self.setActionDefinition('download_demo_data', dict (
          text = "Download data",
          tip = "Download data from internet",
          slot = functools.partial(self.downloadDemoData)
          #enabled = self.isProjectViewer
          ))
    self.setActionDefinition('auto_task_docs', dict (
          text = "Recreate task docs index",
          tip = "Auto generate docs/tasks/index.html",
          slot = functools.partial(self.handleDeveloperTools,'auto_task_docs')
          #enabled = self.isProjectViewer
          ))
    
    self.setActionDefinition('clearStatusFiles', dict (
          text = "Clear shutdown/restart status",
          tip = "Remove possibly corrupt status files",
          slot = functools.partial(purgeStatusFiles,0)
          ))
    
  def updateActionEnabled(self):
    for actionName in ['print','run','find','save']:
      ifEnabled = self.actionDefinitions[actionName]['enabled']()
      action = self.findChild(QtGui.QAction,actionName)      
      #print 'updateActionEnabled',actionName,ifEnabled,action
      if action is not None: action.setEnabled(ifEnabled)
      
  def actionDefinition(self,actionName=''):
    return self.actionDefinitions.get(actionName,{})

  def setActionDefinition(self,actionName='',actionDefinition={}):
    self.actionDefinitions[actionName] = actionDefinition

  def getActionDef(self,name):
    return self.actionDefinitions.get(name,dict(text=name))

  def openBrowserWindow(self):
    b = CBrowserWindow()
    #print 'openBrowserWindow',b,len(CBrowserWindow.Instances)
    b.show()
    #print 'openBrowserWindow raise_'
    b.raise_()

  def openI1Projects(self,mode='default'):
    import CCP4I1Projects
    if mode == 'default':
     self.openI1Projects1()
    else:
      import CCP4FileBrowser
      self.I1FileBrowser = CCP4FileBrowser.CFileDialog(self,
           title='Open old CCP4 project(s) from directories.def or database.def',
           filters= [ 'Old CCP4 directory.def or database.def (*.def)' ],
           defaultSuffix='.def',
           fileMode=QtGui.QFileDialog.ExistingFile  )
      self.I1FileBrowser.widget.fileDialog.setFilter(QtCore.QDir.AllEntries | QtCore.QDir.Hidden | QtCore.QDir.NoDotAndDotDot )
      self.connect(self.I1FileBrowser,QtCore.SIGNAL('selectFile'),self.openI1Projects1)
      self.I1FileBrowser.show()

  def openI1Projects1(self,fileName=None):
    import CCP4I1Projects,CCP4Utils
    if fileName is None:
      if sys.platform == "win32":
        fileName = os.path.normpath(os.path.join(CCP4Utils.getHOME(),'CCP4','windows','directories.def'))
      else:
        fileName = os.path.normpath(os.path.join(CCP4Utils.getHOME(),'.CCP4','unix','directories.def'))
    for pV in CCP4I1Projects.CI1ProjectViewer.Instances:
      if pV.model().sourceFile == fileName:
        pV.show()
        pV.raise_()
        return
    
    pV = CCP4I1Projects.CI1ProjectViewer(fileName=fileName)
    CCP4I1Projects.CI1ProjectViewer.Instances[-1].show()
    CCP4I1Projects.CI1ProjectViewer.Instances[-1].raise_()
    
  def toolBar(self,name):
    return self.findChild(QtGui.QToolBar,name)

  
  def openProject(self,projectId,projectName=None):
    #print 'CMainWindow.openProject',projectId,projectName
    import CCP4ProjectViewer
    for window in CCP4ProjectViewer.CProjectViewer.Instances:
      #print 'currently open',window,window.taskFrame.openJob.projectId,type(window.taskFrame.openJob.projectId)
      try:
        if window.taskFrame.openJob.projectId == projectId:
          window.show()
          window.raise_()
          return
      except Exception as e:
        #print 'CMainWindow.openProject error',str(e)
        pass

    projectDir = CCP4Modules.PROJECTSMANAGER().db().getProjectInfo(projectId=projectId,mode='projectdirectory')
    if not os.path.exists(projectDir):
       QtGui.QMessageBox.warning(self,'','Error in opening project '+str(projectName)+'\nProject directory does not exist:\n'+str(projectDir))
       return
    if not os.path.exists(os.path.join(projectDir,'CCP4_JOBS')):
      QtGui.QMessageBox.warning(self,'','Error in opening project '+str(projectName)+'\nProject directory does not exist:\n'+os.path.join(projectDir,'CCP4_JOBS'))
      return

    p = CCP4ProjectViewer.CProjectViewer(projectId=projectId)
    p.resize(CCP4ProjectViewer.DEFAULT_WINDOW_SIZE[0],CCP4ProjectViewer.DEFAULT_WINDOW_SIZE[1])
    p.show()

  def openWorkflow(self):
    import CCP4WorkflowManagerGui
    CCP4WorkflowManagerGui.openWorkflowManagerGui()

  def openPatchComFile(self):
    import CCP4ComFilePatchManagerGui
    CCP4ComFilePatchManagerGui.openGui()
    
  def openCustomTasks(self):
    import CCP4CustomTaskManagerGui
    CCP4CustomTaskManagerGui.openGui()
    
  def openImportJobs(self):
    import CCP4ImportedJobManagerGui
    CCP4ImportedJobManagerGui.openGui()
    
  def handleProjectMenu(self,mode=''):
    #print 'handleprojectMenu',mode
    import os

    if mode == 'new_project':
      import CCP4ProjectManagerGui
      if CCP4ProjectManagerGui.CNewProjectGui.insts is None:
        CCP4ProjectManagerGui.CNewProjectGui.insts = CCP4ProjectManagerGui.CNewProjectGui()
      CCP4ProjectManagerGui.CNewProjectGui.insts.clear()
      CCP4ProjectManagerGui.CNewProjectGui.insts.show()

    elif mode == 'manage_projects':
      if CMainWindow.projectManagerDialog is None:
        import CCP4ProjectManagerGui
        CMainWindow.projectManagerDialog = CCP4ProjectManagerGui.CProjectManagerDialog()
      CMainWindow.projectManagerDialog.setMode('all')
      CMainWindow.projectManagerDialog.show()
      CMainWindow.projectManagerDialog.raise_()

    elif mode == 'more_projects':
      if CMainWindow.projectManagerDialog is None:
        import CCP4ProjectManagerGui
        CMainWindow.projectManagerDialog = CCP4ProjectManagerGui.CProjectManagerDialog()
      CMainWindow.projectManagerDialog.setMode('open')
      CMainWindow.projectManagerDialog.show()
      CMainWindow.projectManagerDialog.raise_()

    elif mode == 'import_project':
      if CMainWindow.projectManagerDialog is None:
        import CCP4ProjectManagerGui
        CMainWindow.projectManagerDialog = CCP4ProjectManagerGui.CProjectManagerDialog()
        CMainWindow.projectManagerDialog.hide()
      CMainWindow.projectManagerDialog.handleImportProject()

    elif mode == 'general_project':
      self.makeGeneralProject()
     

  def makeGeneralProject(self):
    import CCP4Utils,os
    name = 'General'
    status = CCP4Modules.PROJECTSMANAGER().projectStatus(name)
    if status == 0:
      QtGui.QMessageBox.information(self,'General project exists','General project already exists - use Project menu to open it')
      return
    elif status != 1:
      # Its in the database but broken in some way..
      QtGui.QMessageBox.warning(self,'General project exists','General project exists but may be corrupted - use Manage Projects tool from the Projects menu')
      return
    else:
      directory = os.path.join(CCP4Utils.getHOME(),'CCP4_General_Project')
      print 'Making General Project in directory:',directory
      try:
        projectId = CCP4Modules.PROJECTSMANAGER().createProject(projectName=name,projectPath=directory)    
      except CException as e:
        e.warningMessage()
        return
      except:
        QtGui.QMessageBox.warning(self,'Error creating project','Unknown error creating project')
        return
      else:
        QtGui.QMessageBox.information(self,'General project created','General project created in directory\n'+directory)				    
        self.openProject(projectName='General')

  def openPreferences(self):
    if self.preferenceWindow is None:
      import CCP4PreferencesGui
      self.preferenceWindow = CCP4PreferencesGui.CPreferencesWindow(self)
    #print 'CMainWindow.openPreferences',self.preferenceWindow
    self.preferenceWindow.show()
    self.preferenceWindow.raise_()

  def isPreferencesOpen(self):
    if self.preferenceWindow is not None and self.preferenceWindow.isVisible():
      return True
    else:
      return False
  
  def openServerSetup(self):
    if getattr(self,'serverSetupWindow',None) is None:
      import CCP4JobControlGui
      self.serverSetupWindow = CCP4JobControlGui.CServerSetupWindow(self)
    #print 'CMainWindow.openPreferences',self.preferenceWindow
    self.serverSetupWindow.show()
    self.serverSetupWindow.raise_()

  def isServerSetupOpen(self):
    if getattr(self,'serverSetupWindow',None) is not None and self.serverSetupWindow.isVisible():
      return True
    else:
      return False

  def openConfig(self):
    if self.configWindow is None:
      import CCP4ConfigGui
      self.configWindow = CCP4ConfigGui.CConfigWindow(self)
    #print 'CMainWindow.openConfig',self.configWindow
    self.configWindow.show()
    self.configWindow.raise_()

  def isConfigOpen(self):
    if self.configWindow is not None and self.configWindow.isVisible():
      return True
    else:
      return False
	

  def openFileDialog(self):
    import CCP4FileBrowser
    
    filter_list = []
    mimeTypeHandler = CCP4Modules.MIMETYPESHANDLER()

    if self.fileDialog is None:
      for mime_type in mimeTypeHandler.getMimeTypes():
        filter_text = mimeTypeHandler.getMimeTypeInfo(mime_type,'description') + ' ('
        for item in  mimeTypeHandler.getMimeTypeInfo(mime_type,'fileExtensions'):
          filter_text = filter_text + ('*.'+item+' ')
        filter_text = filter_text[0:-1] + ')'
        filter_list.append(filter_text)
      # addAll=False - don't add an 'All files (....)' list with all file extensions - its too long
      self.fileDialog = CCP4FileBrowser.CFileDialog(self,
	 title='Open file',filters=filter_list,addAll=False)
      self.connect(self.fileDialog, QtCore.SIGNAL('selectFile'), self.handleOpenFileSelection)
    self.fileDialog.show()

  def handleOpenFileSelection(self,fileName=''):
    #print 'CCP4BrowserWindow.handleOpenFileSelection',fileName
    if isinstance(self,CBrowserWindow):
      self.openFile(fileName,internal=True)
    else:
      OPENFILE(fileName)

  def handleDeveloperTools(self,mode):
    #print 'handleDeveloperTools',mode
    if mode == 'redo_report':
      self.redoReport()
    elif mode == 'view_report_source':
      try:
        openJob = self.taskFrame.openJob
      except:
        return
      fileName = CCP4Modules.PROJECTSMANAGER().makeFileName(jobId=openJob.jobId,mode='REPORT')
      if os.path.exists(fileName):
        CCP4Modules.WEBBROWSER().openFile(fileName=fileName,format='text/plain')
    elif mode == 'view_test_report':
      import glob
      testReportList = glob.glob(os.path.join(CCP4Modules.PROJECTSMANAGER().db().getProjectInfo(projectId=self.openJob.projectId,mode='projectdirectory'),'CCP4_test*.log'))
      if len(testReportList)>0:
         CCP4Modules.WEBBROWSER().openFile(fileName=testReportList[0])
      return
    elif mode == 'make_test_suite':
      import CCP4ProjectBasedTesting
      testBuilder = CCP4ProjectBasedTesting.BuildTestSuite(projectId=self.getProject())  
      rv = testBuilder.run()
      if rv.maxSeverity()>SEVERITY_WARNING:
        rv.warningMessage(parent=self,windowTitle=self.windowTitle(),message='Error converting project to test suite')
      else:
        import CCP4ProjectBasedTesting
	builder = CCP4ProjectBasedTesting.BuildTestSuite(projectId=self.getProject())
	try:
	  err = builder.run()
	except:
	  err.warningMessage()
	else:
	  projDir = CCP4Modules.PROJECTSMANAGER().db().getProjectDirectory(projectId=self.getProject())
	  self.testSuiteDialog = QtGui.QDialog(self)
	  self.testSuiteDialog.setWindowTitle('Creating a test suite')
	  self.testSuiteDialog.setLayout(QtGui.QVBoxLayout())
	  label = QtGui.QLabel( \
		"""A directory for test definitions has been created at\n""" +
		str(projDir)+'/CCP4_TEST_SYSTEM\n' +
		"""You should edit in your tests and then 'Export' this project from 'Manage Projects'\n""" +
		"""You can then run the test with 'testi2sys exported_file_path'""", self)
	
          self.testSuiteDialog.layout().addWidget(label)
	  self.testSuiteDialog.show()
	  self.testSuiteDialog.raise_()
    elif mode == 'auto_task_docs':
      import CCP4TaskManager
      rv = CCP4TaskManager.CMakeDocsIndex().run()
      if rv.maxSeverity()>SEVERITY_WARNING:
        rv.warningMessage(parent=self,windowTitle=self.windowTitle(),message='Error recreating task documentation index page')

  def listTasks(self):
    import tempfile
    import CCP4TaskManager,CCP4Utils
    text = CCP4TaskManager.LISTTASKS(ifPrint=False)
    fileName = tempfile.mktemp(suffix='.txt')
    CCP4Utils.saveFile(fileName,text)
    widget = CCP4Modules.WEBBROWSER().openFile(fileName)
    widget.setFont(style='fixed_width')



  def openExportTask(self):
    import CCP4Utils
    import os,zipfile
    dirPath = QtGui.QFileDialog.getExistingDirectory(self,'Select pipeline directory to save as task compressed file',os.path.join(CCP4Utils.getCCP4I2Dir(),'pipelines')).__str__()
    if not os.path.isdir(dirPath): return
    if not os.path.relpath(dirPath,CCP4Utils.getCCP4I2Dir()).startswith('pipelines'):
      err = CException(self.__class__,209,dirPath)
      err.warningMessage(parent=self,windowTitle='Error creating compressed task file',message='Selected directory is not a pipeline in the currently running cccp4i2')
      return     

    zipPath = dirPath + '.ccp4i2task.zip'
    if os.path.exists(zipPath):
      query = QtGui.QMessageBox.question(self,'Overwrite task compressed file?','Overwrite existing'+zipPath+'?',QtGui.QMessageBox.Cancel|QtGui.QMessageBox.Yes)
      if query == QtGui.QMessageBox.Cancel:
        return
      else:
        os.remove(zipPath)
    try:
      zip = zipfile.ZipFile(zipPath,mode='w')
    except:
      err = CException(self.__class__,201,zipPath)
      err.warningMessage(parent=self,windowTitle='Error creating compressed task file',message='Creating'+str(zipPath))
      return

    try:
      CCP4Utils.zipDirectory(zip,dirPath,rootRelPath=CCP4Utils.getCCP4I2Dir())
    except:
      err = CException(self.__class__,202,'Saving',dirPath,'to',zipPath)
      err.warningMessage(parent=self,windowTitle='Error creating compressed task file',message='Saving'+str(dirPath))
      return

    try:
      zip.close()
    except:
      return CErrorReport(self.__class__,203,zipPath)
      err.warningMessage(parent=self,windowTitle='Error creating compressed task file',message='Closing'+str(zipPath))
      return

    info = QtGui.QMessageBox.information(self,'Saved compressed task file','Task saved to'+str(zipPath))
    

  def openImportTask(self):
    filePath = QtGui.QFileDialog.getOpenFileName(self,'Select compressed file containing task','',"Compressed task (*.ccp4i2task.zip)").__str__()

    import os,shutil,zipfile
    if not os.path.isfile(filePath): return
    try:
      zip = zipfile.ZipFile(filePath,mode='r')
    except:
      err = CException(self.__class__,204,filePath)
      err.warningMessage(parent=self,windowTitle='Error reading compressed task file',message='Reading '+str(filePath))
      return

    import CCP4Utils
    targetPath = CCP4Utils.getCCP4I2Dir()
    err= CException()
    for zinfo in zip.infolist():
      if not zinfo.filename.startswith('pipelines'):
        err.append(self.__class__,206,str(zinfo.filename))
      else:
        if os.path.exists(os.path.join(targetPath,zinfo.filename)):
          overwrite = QtGui.QMessageBox.question(self,'Overwrite existing task?','Overwrite existing '+str(zinfo.filename)+'?',QtGui.QMessageBox.Cancel|QtGui.QMessageBox.Yes)
          if overwrite == QtGui.QMessageBox.Cancel:
             err.append(self.__class__,207,zinfo.filename)
             break
          else:
            try:
               shutil.rmtree(os.path.join(targetPath,zinfo.filename))
            except:
              err.append(self.__class__,208,os.path.join(targetPath,zinfo.filename))
              break
        try:
          zip.extract(zinfo,targetPath)
        except:
          err.append(self.__class__,205,filePath)

    zip.close()
    if len(err)>0:     
      err.warningMessage(parent=self,windowTitle='Error reading compressed task file',message='Extracting from '+str(filePath)+' to '+str(targetPath))
      return                         
    
    info = QtGui.QMessageBox.information(self,'New task installed','New '+ zinfo.filename+ ' task installed - Please restart CCP4i2')
	

  def showAbout(self):
    if not hasattr(self,'aboutDialog'):
      import CCP4Utils
      self.aboutDialog = QtGui.QDialog(self)
      self.aboutDialog.setLayout(QtGui.QGridLayout())
      self.aboutDialog.layout().setContentsMargins(1,1,1,1)
      self.aboutDialog.layout().setSpacing(1)
      self.aboutDialog.setWindowTitle('About CCP4i2')
      
      label =  QtGui.QLabel(self)
      fileName = os.path.normpath(os.path.join(CCP4Utils.getCCP4I2Dir(),'qticons','ccp4_white.png'))
      label.setPixmap(QtGui.QPixmap(fileName))
      self.aboutDialog.layout().addWidget(label,0,0,3,1)
      
      label = QtGui.QLabel('CCP4i2',self)
      label.setStyleSheet("QLabel { font-size: 24px; font-weight: bold }")
      self.aboutDialog.layout().addWidget(label,0,1)

      version = CCP4Utils.getProgramVersion('ccp4i2')
      date = CCP4Utils.getProgramVersion('ccp4i2',mode='date')
      label = QtGui.QLabel('Version '+version+' built on '+date,self)
      label.setStyleSheet("QLabel { font-size: 14px; font-style: italic; font-weight: bold }")
      self.aboutDialog.layout().addWidget(label,1,1)

      label = QtGui.QLabel(self)
      label.setText(
      'User interface to CCP4 Program Suite version '+ CCP4Utils.getProgramVersion('ccp4') + '\n\n' +
      'Copyright (C) 2001-2008 University of York, CCLRC\n' +
      'Copyright (C) 2009-2010 University of York\n' +
      'Copyright (C) 2011-2015 Science & Technology Facilities Council')
      self.aboutDialog.layout().addWidget(label,2,1)

      
    self.aboutDialog.show()
    self.aboutDialog.raise_()

  def showDemoDataInfo(self):
    import CCP4Utils
    CCP4Modules.WEBBROWSER().loadWebPage(CCP4Modules.DEMODATAMANAGER().getOverviewPage())

  def downloadDemoData(self):
    CCP4Modules.DEMODATAMANAGER().downloadDemoData(self)

  def getProject(self):
    return None

  def isProjectViewer(self):
    return (self.getProject() is not None)

  def reportAvailable(self):
    return False

  def testReportAvailable(self):
    return False

  def grabWidget(self):
    # Dummy method reimplemented in CProjectViewer
    pass

  def makeDemoData(self):
    CCP4Modules.DEMODATAMANAGER().makeDemoData(parentWidget=self)

  def listProcesses(self):
    CCP4Modules.JOBCONTROLLER().listLocalProcesses()
    import CCP4JobControlGui
    if getattr(self,'listProcessesWindow',None) is None:
      self.listProcessesWindow = CCP4JobControlGui.CListProcesses(self)
    self.listProcessesWindow.load()
    self.listProcessesWindow.show()
    self.listProcessesWindow.raise_()
      
      
class CBrowserWindow(CMainWindow):

  ERROR_CODES = { 100 : { 'description' : 'Invalid file name' }
		  }

  Instances = set()
  Dummy = None
	
  def __init__(self,parent=None,welcome=True):
    self.stack = None
    CMainWindow.__init__(self,parent)
    self.setWindowTitle(self.version)
    CBrowserWindow.Instances.add(self)
    self.stack = QtGui.QStackedWidget(self)
    #print 'CBrowserWindow.__init__ parent',self,parent,CBrowserWindow.Instances
    self.setCentralWidget(self.stack)

    self.connect(self,QtCore.SIGNAL("destroyed(QObject*)"),CBrowserWindow.updateInstances)
    self.factory = None
		
    QtWebKit.QWebSettings.globalSettings().setAttribute(QtWebKit.QWebSettings.PluginsEnabled, True)

    # Create menu and tool bar and add the 'general' actions
    self.mainToolBar = CToolBar(self,'main','Main toolbar')
    self.fileToolBar = CToolBar(self,'file','File')
    self.addToolBar(self.mainToolBar)
    self.addToolBar(self.fileToolBar)
    
    if self.mainToolBar is not None:
      self.mainToolBar.extend(['help_ccp4i2','open','back','forward','reload','find','run'])

    self.mini = False
    self.setTab('nocontext')		

    self.editSplitter = CEditSplitter(self)
    self.connect(self.editSplitter.addressEdit,QtCore.SIGNAL("returnPressed()"),self.addressEdited)
    self.connect(self.editSplitter.searchEdit,QtCore.SIGNAL("returnPressed()"),self.searchEdited)
    self.toolBar('file').addWidget(self.editSplitter)

    self.setHistoryActionAvailability()
    self.connect(self.tab(),QtCore.SIGNAL("currentChanged(int)"),self.setTabTitles)
    sb = self.statusBar()
    self.searchEngineString = "http://www.google.com/search?q="
    self.drawFindTool()

    self.defaultSizePolicy = [self.sizePolicy().horizontalPolicy(),self.sizePolicy().verticalPolicy()]

    import CCP4Utils
    if welcome: self.loadWebPage(helpFileName='welcome.html',newTab=True)
    self.resize(800,800)

  
  @staticmethod
  def updateInstances(qobj):
    CBrowserWindow.Instances = set([window for window in CBrowserWindow.Instances if isAlive(window)])
    #print 'webbrowser.updateInstances',CBrowserWindow.Instances

  def close(self):
    #Close any widgets in tabs - mostly out of concern for watched files
    for indx in range(self.tab().count()): self.tab().widget(indx).close()
    CMainWindow.close(self)

  def setMini(self,mode=True):
    self.mini = mode
    tabs = self.findChildren(CTabWidget)
    if self.mini:
      self.mainToolBar.hide()
      self.fileToolBar.hide()
      self.resize(500,300)
      self.layout().setContentsMargins(0,0,0,0)
      self.layout().setSpacing(0)
      for tab in tabs:
        tab.cornerWidget().setText('Expand window')
      
    else:
      self.mainToolBar.show()
      self.fileToolBar.show()
      self.resize(800,800)
      for tab in tabs:
        tab.cornerWidget().setText('Close tab')

  def positionOver(self,widget):
    geom = widget.geometry()
    globalPos = widget.mapToGlobal(QtCore.QPoint(geom.x(),geom.y()))
    self.move(globalPos)
    

  def downloadTestData(self,**kw):
    pass

  def drawFindTool(self):
    self.findFrame = CFindFrame(self)
    self.findDock = QtGui.QDockWidget('Find',self)
    self.findDock.setWidget(self.findFrame)
    self.findDock.setAllowedAreas(QtCore.Qt.TopDockWidgetArea|QtCore.Qt.BottomDockWidgetArea)
    self.findDock.setTitleBarWidget(None)
    self.addDockWidget(QtCore.Qt.BottomDockWidgetArea,self.findDock,QtCore.Qt.Horizontal)
    self.findDock.close()
    self.connect(self.findFrame,QtCore.SIGNAL('findNext'),self.handleFindNext)
    self.connect(self.findFrame,QtCore.SIGNAL('findPrevious'),self.handleFindPrevious)
    self.connect(self.findFrame,QtCore.SIGNAL('highlightAll'),self.handleFindHighlightAll)

  def StatusBarMessage(self,message=None):
    sb = self.statusBar()
    sb.showMessage(message)
    
  def NewWindowRequested(self,view=None):
    if view:
      self.tab().addTab(view,view.title())
      view.connect(view,QtCore.SIGNAL("CustomMimeTypeRequested"),self.CustomMimeTypeRequested)
      self.connect(view,QtCore.SIGNAL("NewWindowRequested"),self.NewWindowRequested)
      self.connect(view,QtCore.SIGNAL("StatusBarMessage"),self.StatusBarMessage)
      self.connect(view,QtCore.SIGNAL("titleChanged(const QString &)"),self.setTabTitles)
      self.connect(view,QtCore.SIGNAL("IconReady"),self.IconReady)

  def setTabTitles(self,title=None):
    #print 'setTabTitles',title
    for i in range(self.tab().count()):
      widget = self.tab().widget(i)
      if hasattr(widget,"title") and callable(widget.title):
        self.tab().setTabText(i,widget.title())
      self.setWindowTitle(self.version+self.tab().tabText(self.tab().currentIndex()))
      widget = self.tab().widget(self.tab().currentIndex())
      if hasattr(widget,"url") and callable(widget.url):
        if hasattr(widget.url(),"toString") and callable(widget.url().toString):
	  self.editSplitter.addressEdit.setText(widget.url().toString())
		
    self.setHistoryActionAvailability()

  def tab(self):
    # Returns the CTabWidget for the current context (ie project)
    if self.stack is None: return None
    return self.stack.currentWidget()

  def contextTab(self,context):
    for indx in range(self.stack.count()):
      if str(self.stack.widget(indx).objectName()) == context:
        return self.stack.widget(indx)
    return None

  def setTab(self,context):
    tab = self.stack.currentWidget()
    if tab is not None and str(tab.objectName()) == context:
      return

    for indx in range(self.stack.count()):
      if str(self.stack.widget(indx).objectName()) == context:
        self.stack.setCurrentIndex(indx)
        return

    tab = CTabWidget(self,name=context,mini=self.mini)
    self.connect(tab.cornerWidget(),QtCore.SIGNAL("clicked(bool)"),self.handleTabCornerWidget)    
    indx = self.stack.addWidget(tab)
    self.stack.setCurrentIndex(indx)
    #print 'setTab new',indx,tab,context
      
#-------------------------------------------------------------------
  def newTab(self,widget=None,title=None,context='nocontext',
                    toolTip=None,icon=None,open=True):
#-------------------------------------------------------------------
    if title is None: title = widget.title()
    #print 'CWebBrowser.newTab title',widget,title
    if icon is not None:
      idx = self.tab().addTab(widget,icon,title)
    else:
      idx = self.tab().addTab(widget,QtCore.QString(title))
    if toolTip is not None: self.tab().setTabToolTip(idx,toolTip)
    #if colour: self.tab().setTabTextColor(QtGui.QColor(colour))
    if open:
      self.setTabIndex(idx)
      self.editSplitter.addressEdit.setText(title)

    self.tab().repaint()
    return idx    

  def handleTabCornerWidget(self,clickBool = None):
    if self.mini:
      self.setMini(False)
    else:
      self.deleteTab()
      
#-------------------------------------------------------------------
  def deleteTab(self,ok=False):
#-------------------------------------------------------------------
    if self.tab().count()>1:
      indx = self.tab().currentIndex()
      self.tab().deleteTabWidget(indx)
      
    self.setHistoryActionAvailability()

  def tabWidgets(self):
    widgets = []
    for idx in range(0,self.tab().count()):
      widgets.append(self.tab().widget(idx))
    return widgets

#-------------------------------------------------------------------
  def setTabIndex(self,index=-1,fileName=''):
#-------------------------------------------------------------------
      if fileName and index<0:
        index = self.fileOpenInTab(fileName)
      if index>=0 and index<=self.tab().count():
        self.tab().setCurrentIndex(index)
        self.setWindowTitle(self.version+self.tab().widget(index).objectName())

#-------------------------------------------------------------------
  def fileOpenInTab(self,fileName):
#-------------------------------------------------------------------
    for idx in range(0,self.tab().count()):
      if str(self.tab().tabText(idx)) == fileName:
        return idx

    fileName = os.path.abspath(str(fileName))
    for idx in range(0,self.tab().count()):
      if self.tab().widget(idx).fileName == fileName:
        return idx
    return -1

  def CustomMimeTypeRequested(self,url=None):
    #print 'CWebBrowser.CustomMimeTypeRequested',url.path(),url.scheme()
    if url is not None and url.scheme()=='file':
       path = str(url.path())
       if os.path.exists(path):
         self.openFile(path,internal=True)
       elif os.path.splitext(path)[1]=='.i2com':
	 com = os.path.split(os.path.splitext(path)[0])[1]
         #print 'CustomMimeTypeRequested i2com',com
	 action = self.findChild(QtGui.QAction,com)
	 if action is not None:
	   action.trigger()
	 elif self.actionDefinitions.has_key(com) and self.actionDefinitions[com].has_key('slot'):
	   #print 'CustomMimeTypeRequested try',self.actionDefinitions[com]['slot']
	   try:
	     self.actionDefinitions[com]['slot']()
	   except:
	     pass
	 
#-------------------------------------------------------------------
  def openApplication(self,application=None):
#-------------------------------------------------------------------
    widget = None
    application = str(application)
    idx = self.fileOpenInTab(application)
    #print 'openApplication',application,idx
    if idx>=0:
      self.tab().setCurrentIndex(idx)
      #self.tab().widget(idx).open(self.tab().widget(idx).filename)
      return self.tab().widget(idx)

    if application == 'programPrintLog':
      import CCP4ErrorReportViewer
      widget = CCP4ErrorReportViewer.CPrintLogViewer(self)
      #print 'CWebBrowser.openApplication',widget
      widget.openThread(thread='main_thread')
      title = 'Program print log'
    elif application == 'programHTTPLog':
      import CCP4ErrorReportViewer
      widget = CCP4ErrorReportViewer.CPrintLogViewer(self)
      #print 'CWebBrowser.openApplication',widget
      widget.openThread(thread='HTTPServer')
      title = 'Program print log'

    elif  application == 'editScript':
      import CCP4TextViewer
      widget = CCP4TextViewer.CScriptViewer(self)
      widget.open()
      title= 'Edit script'

    if widget is not None:
      widget.setObjectName(title)
      self.newTab(widget,title)
      widget.show()
    return widget



#-------------------------------------------------------------------
  def openFile(self,fileName=None,format=None,title=None,toFront=False,internal=False):
#-------------------------------------------------------------------
    import CCP4Config,CCP4Modules
    # Is the file already displayed - force a redraw
    if fileName is None or fileName=='None': return None
    fileName = str(fileName)
    idx = self.fileOpenInTab(fileName)
    if idx>=0:
      if self.tab().widget(idx).isFileModified(): self.tab().widget(idx).reload()
      self.tab().setCurrentIndex(idx)
      if toFront: self.raise_()
      return self.tab().widget(idx)
    mimeTypeHandler = CCP4Modules.MIMETYPESHANDLER()

    #print "CCP4WebBrowser.open fileName",fileName,format,mimeTypeHandler,title
    if not os.path.exists(fileName):
      QtGui.QMessageBox.warning(self,self.windowTitle(),'File does not exist \n'+fileName)
      return None

    if os.path.isdir(fileName):
      format = 'dir'

    
    # If format not recognised try if file extension is recognised
    if mimeTypeHandler is None and format is None:
      # No handler so just throw it at web browser and hope
      format = 'text/html'
    else:
      if format is not None and not mimeTypeHandler.isSupportedFormat(format):
        format = None
      if format is None:
        format = mimeTypeHandler.formatFromFileExt(fileName)
      if format is None:
        format = 'text/html'
    

    #print 'CCP4WebBrowser.openFile format',format

    # If its a plugin format launch the plugin
    if ['text/html'].count(format):
      widget = self.loadWebPage(fileName,newTab=True)
    elif mimeTypeHandler.useDesktopServices(format):
      abs_fileName = os.path.abspath(fileName)
      #print 'calling QDesktopServices',abs_fileName
      url = QtCore.QUrl.fromLocalFile(abs_fileName)
      rv = QtGui.QDesktopServices.openUrl(url)
      if not rv:
        QtGui.QMessageBox.warning(None,self.windowTitle(),'Attempting to display file '+os.path.split(abs_fileName)[-1]+'\nusing desktop services failed')
      # .. and close this window if nothing else is displayed
      if not internal and self.stack.count() == 1:
        self.close()
      return None
    else:
      widgetClassList = mimeTypeHandler.getViewers(format)
      if len(widgetClassList)==0:
        QtGui.QMessageBox.warning(None,self.windowTitle(),'Sorry no way to display file type: '+format)
        return None
      else:
        widgetClass = widgetClassList[0]

      #print 'CCP4WebBrowser.openFile widgetClass',widgetClass,type(widgetClass)
      if isinstance(widgetClass,str):
        # This is a keyword for the launcher
	    CCP4Modules.LAUNCHER().openInViewer(viewer=widgetClass,fileName=fileName)
	    return None

      if CCP4Config.DEVELOPER():
         widget = widgetClass(self)
      else:
        try:
          widget = widgetClass(self)
        except:
          QtGui.QMessageBox.warning(None,self.windowTitle(),'Error opening display for file type: '+format)
   	  return None
      #print 'CWebBrowser.openFile',widget
      
      if CCP4Config.DEVELOPER():
        rv = widget.open(fileName=fileName)
      else:
        try:
          rv = widget.open(fileName=fileName)
        except CException as e:
            widget.close()
	    e.warningMessage(windowTitle=self.windowTitle())
        except:
            widget.close()
            QtGui.QMessageBox.warning(None,self.windowTitle(),'Attempting to display file '+os.path.split(fileName)[-1]+'\n as '+format+' failed.\n')
            return None
      
      if title is None:  title = widget.title()
      self.newTab(widget,title)
      widget.show()
      #print 'openFile size',widget.height(),widget.width()
      if toFront: self.raise_()
      return widget

  def reloadFile(self,fileName):
    # Beware this is used as slot by CCP4ProjectManagerGui so arguments are fixed
    #print 'WEBBROWSER.reloadFile',fileName
    if fileName is None or fileName=='None': return None
    fileName = str(fileName)
    idx = self.fileOpenInTab(fileName)
    if idx>=0:
      if self.tab().widget(idx).isFileModified(): self.tab().widget(idx).reload()
      self.tab().setCurrentIndex(idx)
      #if toFront: self.raise_()
      return self.tab().widget(idx)
    else:
      return None

  def showHelp(self,mode='ccp4i2'):
    #print 'showHelp',mode
    import CCP4Utils
    if mode == 'ccp4i2':
      self.loadWebPage(helpFileName='welcome.html',newTab=True)
    elif mode == 'quickstart':
      self.loadWebPage(helpFileName='quickstart/index.html',newTab=True)
    elif mode == 'quickexpert':
      self.loadWebPage(helpFileName='quickexpert/index.html',newTab=True)
    elif mode == 'youtube':
      rv = QtGui.QDesktopServices.openUrl(QtCore.QUrl('https://www.youtube.com/watch?v=fB7BRVzBURg'))
    elif mode == 'task_docs':
       self.loadWebPage(helpFileName=os.path.join(CCP4Utils.getCCP4I2Dir(),'docs','tasks','index.html'),newTab=True)
    elif mode == 'tutorial':
      self.loadWebPage(helpFileName='tutorial.html',newTab=True)
    elif mode == 'ccp4_home':
      url =  QtCore.QUrl("http://www.ccp4.ac.uk") 
      self.loadPage(url,newTab=True)
    elif mode == 'updates':
      url =  QtCore.QUrl("http://www.ccp4.ac.uk/updates") 
      self.loadPage(url,newTab=True)
    elif mode == 'license':
      self.loadWebPage(helpFileName='general/license.html',newTab=True)
	  
  def loadWebPage(self,fileName=None,helpFileName=None,target=None,newTab=0):
    #print 'loadWebPage',fileName,helpFileName,target
    if fileName is not None:
      if target is None and fileName.count('#'): fileName,target = fileName.split('#',1)
    elif helpFileName is not None:
      if target is None and helpFileName.count('#') : helpFileName,target = helpFileName.split('#',1)
      if os.path.splitext(helpFileName)[1] == '': helpFileName = helpFileName + '.html'
      if os.path.exists(helpFileName):
        fileName = helpFileName
      else:
        import CCP4Utils
        docDir = os.path.join(CCP4Utils.getCCP4I2Dir(),'docs')
        fileName = os.path.join(docDir,helpFileName)
        idx = 0
        subDirList =  ['general','developers','tasks']
        while not os.path.exists(fileName) and idx<len(subDirList):
          fileName =  os.path.join(docDir,subDirList[idx],helpFileName)
          idx += 1
        if idx>len(subDirList): return None

    if target is not None:
        if target[0] != '#': target = '#' + target
    else:
        target = ''
    if os.path.exists(fileName):
      url = QtCore.QUrl.fromLocalFile(os.path.abspath(fileName))
      if len(target)>1: url.setFragment(target[1:])
      #url = QtCore.QUrl(fileName+target)
    elif not fileName[0:4] == 'http':
      url =  QtCore.QUrl("http://"+fileName)
    else:
      #url = QtCore.QUrl(file)
      url = file
    
    return self.loadPage(url,newTab=newTab)
	   
	   
  def loadPage(self,url,newTab=0):
    #print 'CWebBrowser.loadPage', url.scheme(), url.path().toAscii()
    target = ''
    if url.scheme()=='file':
      if str(url.path().toAscii()).find('#')>0:
        urlpath,target=str(url.path().toAscii()).split('#',1)
        url = QtCore.QUrl.fromLocalFile(urlpath)
        #print 'loadPage file',target
    
    if not url.isValid():
      err = CException(self.__class__,100,url.path().toAscii())
      err.warningMessage()
      return
      #print "CWebBrowser.loadPage trying",url.path(),target
    if self.tab().count()>0:      
      widget = self.tab().currentWidget()
    else:
      widget= None
    #print 'CWebBrowser.loadPage',url.isValid(),widget,newTab
    if not newTab and isinstance(widget,CCP4WebView.CWebView):
      try:
        widget.setTarget(target)
        widget.load(url)
        #print 'CWebBrowser.loadPage widget.loaded'
      except:
        exc_type,exc_value,exc_tb = sys.exc_info()[:3]
        print exc_type
        print exc_value
        import traceback
        traceback.print_tb(exc_tb)
        return None
      return widget
    else:
      view = CCP4WebView.CWebView()
      view.connect(view,QtCore.SIGNAL("CustomMimeTypeRequested"),self.CustomMimeTypeRequested)
      self.connect(view,QtCore.SIGNAL("NewWindowRequested"),self.NewWindowRequested)
      self.connect(view,QtCore.SIGNAL("StatusBarMessage"),self.StatusBarMessage)
      self.connect(view,QtCore.SIGNAL("titleChanged(const QString &)"),self.setTabTitles)
      self.connect(view,QtCore.SIGNAL("IconReady"),self.IconReady)
      view.load(url)
      view.setTarget(target)
      #self.connect(view,QtCore.SIGNAL("linkClicked(const QUrl &)"),self.handleLinkClick)
      '''
      # This is now done in CWebView
      if self.factory is None:
        #Create Qt web plugin factory
        try:
          import CCP4WebPluginFactory
          self.factory = CCP4WebPluginFactory.CWebPluginFactory(view)
	except:
	  print 'CCP4WebBrowser Error creating web plugin factory'

      if self.factory is not None:
        view.page().setPluginFactory(self.factory)
      '''
   
      self.newTab(view,str(view.title()))
      return view

  def loadViaHTTP(self,fileName=None,target=None):
    import CCP4Utils
    relPath = os.path.relpath(fileName,os.path.join(CCP4Utils.getCCP4I2Dir(),'docs'))
    #print 'loadViaHTTP',relPath
    url =  QtCore.QUrl('http://127.0.0.1:43434/'+relPath+'#'+target)
    #print 'loadViaHTTP fragment',url.fragment()
    self.loadPage(url,True)


  def handleLinkClick(self,url):
    #print 'CCP4BrowserWindow.handleLinkClick',url
    pass
		
  def IconReady(self,args=None):
    icon,view = args
    for i in range(self.tab().count()):
      widget = self.tab().widget(i)
      if widget is view:
        self.tab().setTabIcon(i,icon)


  def searchEdited(self):
    text =  self.editSplitter.searchEdit.text().replace(QtCore.QRegExp("\\s+"),QtCore.QString("+"))
    #print 'QWebBrowser.searchEdited',text
    self.loadPage(QtCore.QUrl(self.searchEngineString+str(text)))

  def addressEdited(self):
    text = str(self.editSplitter.addressEdit.text())
    if os.path.exists(text.split('#')[0]):
      self.openFile(fileName=text,internal=True)
    else:
      self.loadPage(QtCore.QUrl(self.editSplitter.addressEdit.text()))

  def setHistoryActionAvailability(self):
    widget = self.tab().currentWidget()
    forwardAction = self.findChild(QtGui.QAction,'forward')
    backAction = self.findChild(QtGui.QAction,'back')
    if hasattr(widget,"history") and callable(widget.history):
      history = widget.history()
      #print 'setHistoryActionAvailability',backAction,history.canGoBack()
      if forwardAction:
        if history.canGoForward():
          forwardAction.setEnabled(True)
        else:
          forwardAction.setEnabled(False)
      if backAction:
        if history.canGoBack():
          backAction.setEnabled(True)
        else:
          backAction.setEnabled(False)
    else:
      # No notion of history so set actions disabled
      if forwardAction: forwardAction.setEnabled(False)
      if backAction: backAction.setEnabled(False)


  def historyForward(self):
    widget = self.tab().currentWidget()
    if widget and hasattr(widget,"history") and callable(widget.history):
      history = widget.history()
      if history.canGoForward():
        history.goToItem(history.forwardItem())
    self.setHistoryActionAvailability()

  def historyBack(self):
    widget = self.tab().currentWidget()
    if widget and hasattr(widget,"history") and callable(widget.history):
      history = widget.history()
      if history.canGoBack():
        history.goToItem(history.backItem())
    self.setHistoryActionAvailability()

  def reloadPage(self):
    widget = self.tab().currentWidget()
    if widget is None: return

    if hasattr(widget,"history") and callable(widget.history):
      #print 'QWebBrowser.reloadPage'
      widget.history().goToItem(widget.history().currentItem())

#-------------------------------------------------------------------
  def currentWidget(self):
#-------------------------------------------------------------------
    if self.tab() is None: return None
    idx = self.tab().currentIndex()
    if idx<0: return None
    return self.tab().widget(idx)


#-------------------------------------------------------------------
  def widgetIsRunable(self):
#-------------------------------------------------------------------
    w = self.currentWidget()
    if w is not None:
      return w.isRunable()
    else:
      return False

#-------------------------------------------------------------------
  def widgetIsSaveable(self):
#-------------------------------------------------------------------
    w = self.currentWidget()
    if w is not None:
      return w.isSaveable()
    else:
      return False
#-------------------------------------------------------------------
  def widgetIsPrintable(self):
#-------------------------------------------------------------------
    w = self.currentWidget()
    if w is not None:
      return w.isPrintable()
    else:
      return False
#-------------------------------------------------------------------
  def widgetIsSearchable(self):
#-------------------------------------------------------------------
    w = self.currentWidget()
    #print 'widgetIsSearchable',w
    if w is not None:
      return w.isSearchable()
    else:
      return False
#-------------------------------------------------------------------
  def widgetIsScaleable(self):
#-------------------------------------------------------------------
    w = self.currentWidget()
    if w is not None:
      return w.isScaleable()
    else:
      return None

    

#-------------------------------------------------------------------
  def isCurrentWidget(self,mode='text'):
#-------------------------------------------------------------------
    widget = self.currentWidget()
    if widget is None: return 0
    if mode=='text':
       import CCP4TextViewer
       if isinstance(widget,CCP4TextViewer.CTextViewer):
          return 1
       else:
          return 0
    if mode=='image':
       import CCP4ImageViewer
       if isinstance(widget,CCP4ImageViewer.CImageViewer):
          return 1
       else:
          return 0
    else:
        return 0




#-------------------------------------------------------------------
  def handlePrint(self):
#-------------------------------------------------------------------
      idx = self.tab().currentIndex()
      if idx<0: return
      
      printer = QtGui.QPrinter()
      pd = QtGui.QPrintDialog(printer)
      pdret = pd.exec_()
      if pdret == QtGui.QDialog.Accepted:
          painter = QtGui.QPainter()
          painter.begin(printer)
          self.tab().widget(idx).Print(painter)
          painter.end()

#-------------------------------------------------------------------
  def handleSave(self):
#-------------------------------------------------------------------
    widget = self.currentWidget()
    if widget is None: return
    import CCP4FileBrowser
    defaultSuffix = ''
    filter_list = []
    ext_list =  widget.getFileExt()
    if ext_list: defaultSuffix = ext_list[0]
    ext_text = ''
    for ext in widget.getFileExt() : ext_text = ext_text+'*.'+ext+' '
    if ext_text: filter_list.append(widget.getLabel()+' ('+ext_text+')')    
    self.saveWidget = CCP4FileBrowser.CFileDialog(self,title='Save '+widget.objectName(),filters=filter_list,defaultSuffix=defaultSuffix,fileMode=QtGui.QFileDialog.AnyFile)
    self.connect(self.saveWidget,QtCore.SIGNAL('selectFile'),self.currentWidget().Save)
    self.saveWidget.show()

  def handleRun(self):
    widget = self.currentWidget()
    if widget is None: return
    widget.run()

#-------------------------------------------------------------------
  def openFind(self):
#-------------------------------------------------------------------
    if self.findDock.isVisible():
      self.findDock.setVisible(0)
    else:
      self.findDock.setVisible(1)
      
#-------------------------------------------------------------------
  def isFindFrameOpen(self):
#-------------------------------------------------------------------
    if self.findFrame is not None and self.findFrame.isVisible(): return 1
    return 0

#-------------------------------------------------------------------
  def handleFind(self,direction=1):
#-------------------------------------------------------------------
    widget=self.currentWidget()
    if widget is None or not widget.isSearchable(): return
    text = self.findFrame.text()
    #print 'CCP4WebBrowser.handleFind',text,direction
    rv = widget.findText(subString=text,direction=direction,
			 caseSensitive=self.findFrame.caseSensitive(),
			 wrapAroundDocument=self.findFrame.wrapAroundDocument() )
	  
#-------------------------------------------------------------------
  def handleFindNext(self):
#-------------------------------------------------------------------
    self.handleFind(1)
    
#-------------------------------------------------------------------
  def handleFindPrevious(self):
#-------------------------------------------------------------------
    self.handleFind(-1)

#-------------------------------------------------------------------
  def handleFindHighlightAll(self):
#-------------------------------------------------------------------
    widget=self.currentWidget()
    if widget is None or not widget.isSearchable(): return
    text = self.findFrame.text()
    rv = widget.findText(subString=text,highlightAll=1)


  def setFixedWidth(self,width=0):
    #print 'CWebBrowser.setFixedWidth',width
    if width<= 0:
      self.setMaximumWidth(16777215)
      self.setMinimumWidth(0)
    else:
      self.setMaximumWidth(width+10)
      self.setMinimumWidth(width+10)
      
  def moveEvent(self,event):
    delta = event.pos()-event.oldPos()
    #print 'CWebBrowser.moveEvent',event.pos().x(),event.pos().y(),event.oldPos().x(),event.oldPos().y(),'*',delta.x(),delta.y()
    self.emit(QtCore.SIGNAL('windowMoved'),delta)
    event.ignore()
    
  def openSendReport(self):
    import CCP4ErrorReportViewer
    widget = CCP4ErrorReportViewer.CSendJobError(self,projectId=self.getProject())
    widget.show()

  def openUpdate(self):
    import CCP4UpdateDialog
    widget = CCP4UpdateDialog.CUpdateDialog(self)
    widget.show()


  def openManageImportFiles(self):
    pass
    
class CFindFrame( QtGui.QFrame):
	
  def __init__(self,parent):
      QtGui.QFrame.__init__(self,parent)
      import functools
      find_layout = QtGui.QHBoxLayout()
      margin = 2
      find_layout.setContentsMargins(margin,margin,margin,margin)
      find_layout.setSpacing(margin)
      #widget = QtGui.QPushButton('Close',self)
      #find_layout.addWidget(widget)
      #self.connect(widget,QtCore.SIGNAL('clicked(bool)'),self.closeFind)
      find_layout.addWidget(QtGui.QLabel('Find:'))
      self.findTextWidget = QtGui.QLineEdit(self)
      find_layout.addWidget(self.findTextWidget)
      self.connect(self.findTextWidget,QtCore.SIGNAL('returnPressed()'),functools.partial(self.handleClicked,'findNext'))
      self.next = QtGui.QPushButton('Next',self)
      find_layout.addWidget(self.next)
      self.connect(self.next,QtCore.SIGNAL('clicked(bool)'),functools.partial(self.handleClicked,'findNext'))
      self.previous= QtGui.QPushButton('Previous',self)
      find_layout.addWidget(self.previous)
      self.connect(self.previous,QtCore.SIGNAL('clicked(bool)'),functools.partial(self.handleClicked,'findPrevious'))

      '''
      self.all= QtGui.QPushButton('Highlight all',self)
      find_layout.addWidget(self.all)
      self.connect(self.all,QtCore.SIGNAL('clicked(bool)'),functools.partial(self.emit,QtCore.SIGNAL('highlightAll')))
      '''

      self.findCase = QtGui.QCheckBox('Match case',self)
      find_layout.addWidget(self.findCase)
      find_layout.addStretch(5)
      
      self.wrap = QtGui.QCheckBox('Wrap',self)
      find_layout.addWidget(self.wrap)
      self.wrap.setChecked(1)
            
      find_layout.addStretch(5)
      
      self.setLayout(find_layout)

  def handleClicked(self,mode,clickBool=False):
    #print 'CFindFrame.handleClicked',mode,clickBool
    self.emit(QtCore.SIGNAL(mode))

  def text(self):
    return str(self.findTextWidget.text())

  def caseSensitive(self):
    return self.findCase.isChecked()

  def wrapAroundDocument(self):
    return self.wrap.isChecked()

class CUpdaterProxy(QtCore.QObject):
  def exit(self):
    exitBrowser()

updaterProxyInstance = CUpdaterProxy()
CCP4UpdateManager.um.restart_requested.connect(updaterProxyInstance.exit)

