# -*- coding: utf-8 -*-
#  This software and supporting documentation are distributed by
#      Institut Federatif de Recherche 49
#      CEA/NeuroSpin, Batiment 145,
#      91191 Gif-sur-Yvette cedex
#      France
#
# This software is governed by the CeCILL license version 2 under
# French law and abiding by the rules of distribution of free software.
# You can  use, modify and/or redistribute the software under the
# terms of the CeCILL license version 2 as circulated by CEA, CNRS
# and INRIA at the following URL "http://www.cecill.info".
#
# As a counterpart to the access to the source code and  rights to copy,
# modify and redistribute granted by the license, users are provided only
# with a limited warranty  and the software's author,  the holder of the
# economic rights,  and the successive licensors  have only  limited
# liability.
#
# In this respect, the user's attention is drawn to the risks associated
# with loading,  using,  modifying and/or developing or reproducing the
# software by the user in light of its specific status of free software,
# that may mean  that it is complicated to manipulate,  and  that  also
# therefore means  that it is reserved for developers  and  experienced
# professionals having in-depth computer knowledge. Users are therefore
# encouraged to load and test the software's suitability as regards their
# requirements in conditions enabling the security of their systems and/or
# data to be ensured and,  more generally, to use and operate it in the
# same conditions as regards security.
#
# The fact that you are presently reading this means that you have had
# knowledge of the CeCILL license version 2 and that you accept its terms.
from __future__ import print_function
from __future__ import absolute_import
from brainvisa.processing.qtgui.backwardCompatibleQt \
    import QAction, QLineEdit, QPushButton, QToolButton, QComboBox, \
           Qt, QIcon, QWidget, QWidgetAction, QFileDialog, QVBoxLayout, \
           QListWidget, QHBoxLayout, QSpacerItem, QSizePolicy, QSize, QMenu, \
           QPalette, QColor, QItemSelectionModel, QLabel, \
           QListView, QTreeView, QAbstractItemView, QPixmap
from soma.wip.application.api import findIconFile
from soma.qtgui.api import largeIconSize
from soma.path import remove_query_string, parse_query_string, \
                      update_query_string, QueryStringParamUpdateMode
from brainvisa.data.qtgui.diskItemBrowser import DiskItemBrowser
from brainvisa.data.qtgui.neuroDataGUI import DataEditor, StringListEditor, buttonMargin, buttonIconSize
import brainvisa.processes
from brainvisa.processes import getProcessInstance
from brainvisa.processing.qt4gui import neuroProcessesGUI
from brainvisa.data.neuroDiskItems import DiskItem, Directory, aimsFileInfo, \
                                          getResolutionsFromItems
from brainvisa.data.qt4gui import history as historygui
from brainvisa.configuration import neuroConfig
from brainvisa.processing.neuroException import showException, HTMLMessage
from soma.qt_gui.qt_backend import QtCore, QtGui
import sys
import os
import six
import weakref
from six.moves import map
[docs]class ComboBoxAction(QWidgetAction):
    
    def __init__(self, parent, text = None, icon_file = None):
        super(ComboBoxAction, self).__init__(parent)
        
        widget = QWidget(parent)
        layout = QHBoxLayout()
        widget.setLayout(layout)
        self._icon = QLabel(widget)
        self._text = QLabel(widget)
        self._combo = QComboBox(widget)
        layout.addWidget(self._icon)
        layout.addWidget(self._text)
        layout.addWidget(self._combo)
                
        if (icon_file):
            self.setIconFile(icon_file)
        else:
            self._icon.setVisible(False)
        if (text):
            self.setText(text)
        else:
            self._text.setVisible(False)
        
        self._combo.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding,
                                              QSizePolicy.Fixed))
        self._combo.activated.connect(self.triggered)
        self.setDefaultWidget(widget)
        
[docs]    def triggered(self, value):
        self.parentWidget().setVisible(False) 
        
    def setIconFile(self, icon_file):
        if icon_file:
            self._icon.setPixmap(QPixmap(findIconFile(icon_file)))
            self._icon.setSizePolicy(QSizePolicy(QSizePolicy.Fixed,
                                                 QSizePolicy.Fixed))
            self._icon.setVisible(True)
            
        else:
            self.setVisible(False)
       
[docs]    def setText(self, text):
        if text:
            self._text.setText(text)
            self._text.setVisible(True)   
            
        else:
            self.setVisible(False) 
      
[docs]    def text(self):
        return self._text.text() 
    
    def comboBoxWidget(self):
        return self._combo
    
[docs]    def setVisible(self, visible):
        super(ComboBoxAction, self).setVisible(visible)
        self.defaultWidget().setVisible(visible)  
        
def addComboBoxMenu(parent, menu, title, icon_file, cmb, act, update_func):
    # Create ComboBoxAction widget
    a = ComboBoxAction(menu, title, icon_file)
    c = a.comboBoxWidget()
    
    setattr(parent, act, a)
    setattr(parent, cmb, c)
    
    # Update menu with the new action
    menu.addAction(a)
    menu.aboutToShow.connect(update_func)
#----------------------------------------------------------------------------
#----------------------------------------------------------------------------
[docs]class DiskItemEditor(QWidget, DataEditor):
    noDefault = QtCore.Signal(six.text_type)
    newValidValue = QtCore.Signal(six.text_type, object)
    def __init__(self, parameter, parent, name, write=False, context=None,
                 process=None):
        if getattr(DiskItemEditor, 'pixShow', None) is None:
            setattr(DiskItemEditor, 'pixShow',
                    QIcon(findIconFile('eye.png')))
            setattr(DiskItemEditor, 'pixEdit',
                    QIcon(findIconFile('pencil.png')))
            setattr(DiskItemEditor, 'pixDatabaseRead',
                    QIcon(findIconFile('database_read.png')))
            setattr(DiskItemEditor, 'pixDatabaseWrite',
                    QIcon(findIconFile('database_write.png')))
            setattr(DiskItemEditor, 'pixBrowseRead',
                    QIcon(findIconFile('browse_read.png')))
            setattr(DiskItemEditor, 'pixBrowseWrite',
                    QIcon(findIconFile('browse_write.png')))
            setattr(DiskItemEditor, 'pixHistory',
                    QIcon(findIconFile('history.png')))
        QWidget.__init__(self, parent)
        DataEditor.__init__(self)
        if name:
            self.setObjectName(name)
        hLayout = QHBoxLayout()
        self.setLayout(hLayout)
        if sys.platform == 'darwin' and QtCore.qVersion() == '4.6.2':
            # is this layout problem a bug in qt/Mac 4.6.2 ?
            hLayout.setSpacing(14)
        else:
            hLayout.setSpacing(2)
        hLayout.setContentsMargins(0, 0, 0, 0)
        self._write = write
        self.parameter = parameter
        self.led = QLineEdit()
        hLayout.addWidget(self.led)
        self.led.textChanged.connect(self.textChanged)
        self.led.returnPressed.connect(self.checkValue)
        self.setFocusProxy(self.led)
        self.diskItem = None
        self.forceDefault = False
        self._context = context
        if process is None:
            self.process = None
        else:
            if isinstance(process, weakref.ProxyType):
                self.process = process
            else:
                self.process = weakref.proxy(process)
        self.btnShow = RightClickablePushButton()
        hLayout.addWidget(self.btnShow)
        self.btnShow.setCheckable(True)
        self.btnShow.setIcon(self.pixShow)
        self.btnShow.setIconSize(buttonIconSize)
        self.btnShow.setFixedSize(buttonIconSize + buttonMargin)
        self.btnShow.setFocusPolicy(Qt.NoFocus)
        self.btnShow.setEnabled(False)
        # Sets default viewers list
        self._viewers = None
        self.actViewers = None
        self.cmbViewersSeparator = None
        self.cmbViewers = None
        self.newValidValue.connect(self.updateViewers)
        # Sets default data editors list
        self._editors = None
        self.actDataEditors = None
        self.cmbDataEditorsSeparator = None
        self.cmbDataEditors = None
        self.newValidValue.connect(self.updateDataEditors)
        # Sets default resolutions list
        self.actResolutions = None
        self.cmbResolutionsSeparator = None
        self.cmbResolutions = None
    
        self._view = None
        self.btnShow.clicked.connect(self.showPressed)
        self.btnShow.rightPressed.connect(self.openViewerPressed)
        self._edit = None
        self.btnEdit = RightClickablePushButton()
        hLayout.addWidget(self.btnEdit)
        self.btnEdit.setCheckable(True)
        self.btnEdit.setIcon(self.pixEdit)
        self.btnEdit.setIconSize(buttonIconSize)
        self.btnEdit.setFixedSize(buttonIconSize + buttonMargin)
        self.btnEdit.setFocusPolicy(Qt.NoFocus)
        self.btnEdit.setEnabled(0)
        self.btnEdit.clicked.connect(self.editPressed)
        self.btnEdit.rightPressed.connect(self.openEditorPressed)
        self.btnDatabase = QPushButton()
        hLayout.addWidget(self.btnDatabase)
        if write:
            self.btnDatabase.setIcon(self.pixDatabaseWrite)
            self.btnDatabase.setIconSize(buttonIconSize)
            self.btnDatabase.setToolTip(_t_("Browse the database (save mode)"))
        else:
            self.btnDatabase.setIcon(self.pixDatabaseRead)
            self.btnDatabase.setIconSize(buttonIconSize)
            self.btnDatabase.setToolTip(_t_("Browse the database (load mode)"))
        self.btnDatabase.setFixedSize(buttonIconSize + buttonMargin)
        self.btnDatabase.setFocusPolicy(Qt.NoFocus)
        self.customFileDialog = None
        if hasattr(parameter, 'fileDialog'):
            self.customFileDialog = parameter.fileDialog
        if hasattr(parameter, 'databaseUserLevel'):
            x = parameter.databaseUserLevel
            if x > neuroConfig.userLevel:
                self.btnDatabase.hide()
        self.btnDatabase.clicked.connect(self.databasePressed)
        self.databaseDialog = None
        self.btnBrowse = QPushButton()
        hLayout.addWidget(self.btnBrowse)
        if write:
            self.btnBrowse.setIcon(self.pixBrowseWrite)
            self.btnBrowse.setIconSize(buttonIconSize)
            self.btnBrowse.setToolTip(_t_("Browse the filesystem (save mode)"))
        else:
            self.btnBrowse.setIcon(self.pixBrowseRead)
            self.btnBrowse.setIconSize(buttonIconSize)
            self.btnBrowse.setToolTip(_t_("Browse the filesystem (load mode)"))
        self.btnBrowse.setFixedSize(buttonIconSize + buttonMargin)
        self.btnBrowse.setFocusPolicy(Qt.NoFocus)
        if hasattr(parameter, 'browseUserLevel'):
            x = parameter.browseUserLevel
            if x > neuroConfig.userLevel:
                self.btnBrowse.hide()
        self.btnBrowse.clicked.connect(self.browsePressed)
        self.browseDialog = None
        self._textChanged = False
    def isViewable(self):
        viewers = self.getViewers()
        return self.diskItem is not None and self.diskItem.isReadable() \
               
and len(viewers) > 0
    
    def isEditable(self):
        editors = self.getDataEditors()
        return self.diskItem is not None and not self.diskItem.isLockData() \
           
and self.diskItem.isWriteable() and len(editors) > 0
    def set_read_only(self, read_only):
        self.btnDatabase.setEnabled(not read_only)
        self.btnBrowse.setEnabled(not read_only)
        self.btnEdit.setEnabled(not read_only and self.isEditable())
        self.led.setReadOnly(read_only)
        self.led.setFrame(not read_only)
    def createPopupMenu(self, popup):
        if self.parameter.enableMultiResolution:
            self.cmbResolutionsSeparator = popup.addSeparator()
            addComboBoxMenu(self, popup, 
                            'resolution','resolution.png', 
                            'cmbResolutions', 'actResolutions', 
                            self.updateResolutions)
            self.cmbResolutions.currentIndexChanged[int].connect(
                self.setResolutionLevel)
        
        self.cmbViewersSeparator = popup.addSeparator()
        addComboBoxMenu(self, popup, 
                        'viewer', 'eye.png', 
                        'cmbViewers', 'actViewers',
                        self.updateViewersComboBox)
        self.cmbDataEditorsSeparator = popup.addSeparator()
        addComboBoxMenu(self, popup, 
                        'editor', 'pencil.png',
                        'cmbDataEditors', 'actDataEditors',
                        self.updateDataEditorsComboBox)
    def setResolutionLevel(self, resolution_level):
        v = self.getValue()
        if v is not None:
            v.setResolutionLevel(resolution_level)
            self.setValue(v)
    def updateResolutions(self):
        if self.parameter.enableMultiResolution:
            v = self.getValue()
            self.cmbResolutions.clear()
            
            if v is not None:
                res_infos = getResolutionsFromItems(v)
                
                if res_infos is not None and len(res_infos) > 1:           
                    
                    for level in six.moves.xrange(len(res_infos)):
                        self.cmbResolutions.addItem(res_infos[level], 
                                                    str(level))
                    resolution_level = v.resolutionLevel()                        
                        
                    if resolution_level is None:
                        resolution_level = len(res_infos) - 1
                        
                    self.cmbResolutions.setCurrentIndex(resolution_level)
                        
                    visible = True
                    
                else:
                    visible = False
                    
            else:
                visible = False
                
            # Display or hide the viewer action in popup menu
            self.cmbResolutionsSeparator.setVisible(visible)
            self.actResolutions.setVisible(visible)
      
    def setContext(self, newContext):
        oldContext = (self.btnShow.isChecked(), self._view,
                      self.btnEdit.isChecked(), self._edit)
        if newContext is None:
            self.btnShow.setChecked(False)
            self.btnEdit.setChecked(False)
            self._view = None
            self._edit = None
        else:
            if len(newContext) >= 4:
                o, v, z, e = newContext
            else:
                o, v = newContext
                z = e = 0
            self.btnShow.setChecked(o)
            self._view = v
            self.btnEdit.setChecked(z)
            self._edit = e
        return oldContext
    def getValue(self):
        return self.diskItem
    def setValue(self, value, default=0):
        self.forceDefault = default
        pal = QPalette()
        if self.diskItem != value or \
           
(not(value is None and self.led.text() == '') \
            
and (value != self.led.text())):
            self.diskItem = self.parameter.findValue(value)
            
            if self.diskItem is None:
                if value is None:
                    self.led.setText('')
                else:
                    # value is not None but is invalid: make it appear
                    pal.setColor(QPalette.Base, QColor(240, 150, 150))
                if self.btnShow:
                    self.btnShow.setEnabled(0)
                if self.btnEdit:
                    self.btnEdit.setEnabled(0)
                self.newValidValue.emit(
                    six.text_type(self.objectName()), self.diskItem)
            else:
                self.led.setText(self.diskItem.fullPath())
                self.checkReadable()
                self.newValidValue.emit(
                    six.text_type(self.objectName()), self.diskItem)
        self._textChanged = 0
        self.forceDefault = 0
        self.led.setPalette(pal)
        self.valuePropertiesChanged(default)
    def valuePropertiesChanged(self, isDefault):
        pal = self.led.palette()
        if not isDefault:
            pal.setColor(QPalette.Text, QColor(0, 0, 255))
        if self.diskItem is not None and self.diskItem.isLockData():
            pal.setColor(QPalette.Base, QColor(255, 230, 230))
        self.led.setPalette(pal)
    def lockChanged(self, locked):
        pal = self.led.palette()
        if self.diskItem is not None and self.diskItem.isLockData():
            pal.setColor(QPalette.Base, QColor(255, 230, 230))
        else:
            pal2 = QPalette()
            pal.setColor(QPalette.Base, pal2.color(QPalette.Base))
        self.led.setPalette(pal)
    def checkReadable(self):
        if self.btnShow:
            enabled = 0
            if self.diskItem:
                if len(self.getViewers()) > 0:
                    self.btnShow.show()
                else:
                    self.btnShow.hide()
                enabled = self.isViewable()
                
            self.btnShow.setEnabled(enabled)
            
        if self.btnEdit:
            enabled = 0
            if self.diskItem:
                if len(self.getDataEditors()) > 0:
                    self.btnEdit.show()
                else:
                    self.btnEdit.hide()
                enabled = self.isEditable()
            self.btnEdit.setEnabled(enabled)
    def textChanged(self):
        self._textChanged = 1
        if not self.forceDefault:
            self.noDefault.emit(six.text_type(self.objectName()))
    def checkValue(self):
        if self._textChanged:
            self.setValue(six.text_type(self.led.text()))
    def showPressed(self):
        if self.btnShow.isChecked():
            self.btnShow.setEnabled(0)
            v = self.getValue()
            viewerExists = False
            try:
                for viewer in self.viewersToTry():
                    try:
                        viewer = getProcessInstance(viewer)
                        viewerExists = True
                        if self.process is not None \
                                
and hasattr(viewer, 'allowed_processes'):
                            viewer.reference_process = self.process
                        brainvisa.processes.defaultContext().runInteractiveProcess(
                            self._viewerExited, viewer, v)
                        self.btnShow.setEnabled(True)
                        return
                    except Exception:
                        # Try another viewer if possible
                        brainvisa.processes.defaultContext().log(
                            _t_('Warning for %s') % _t_(viewer.name),
                          html=_t_('Viewer aborted for type=<em>%s</em> and '
                                   'format=<em>%s</em> value=<em>%s</em> '
                                   '(try using it interactively by '
                                   'right-clicking on the eye icon)')
                          % (six.text_type(v.type), six.text_type(v.format), six.text_type(v)),
                          icon='eye.png')
                        continue
                # if a viewer exists, still keep the button enabled to allow using the
                # right-click options
                self.btnShow.setEnabled(viewerExists)
                self.btnShow.setChecked(False)
                raise RuntimeError(
                    HTMLMessage(_t_('No viewer could be found for type=<em>%s</em> and format=<em>%s</em>') % (six.text_type(v.type), six.text_type(v.format))))
            except Exception as error:
                    # transform the exception into a print message, and return.
                    # We are in a Qt slot here, raising an exception results in
                    # undefined behaviour, which happens to have changed between PyQt 5.4
                    # and PyQt 5.5
                print(error)
                import traceback
                traceback.print_exc()
        else:
            self._view = None
    def _viewerExited(self, result):
        if isinstance(result, Exception):
            showException(parent=self)
        else:
            self._view = result
        neuroProcessesGUI.mainThreadActions().push(
            self.btnShow.setEnabled, True)
        if result is None:
            neuroProcessesGUI.mainThreadActions().push(
                self.btnShow.setChecked, False)
    def close_viewer(self):
        if self._view is not None:
            self._view = None
            neuroProcessesGUI.mainThreadActions().push(
                self.btnShow.setChecked, False)
            neuroProcessesGUI.mainThreadActions().push(
                self.btnShow.setEnabled, True)
    def openViewerPressed(self, pos):
        v = self.getValue()
        if v.get('lastHistoricalEvent'):
            popup = QMenu(self)
            op = popup.addAction(DiskItemEditor.pixShow, 'open viewer')
            sh = popup.addAction(DiskItemEditor.pixHistory, 'show history')
            ac = popup.exec(pos)
            if ac is not None:
                if ac is sh:
                    self.openHistory()
                else:
                    self.openViewer()
        else:
            self.openViewer()
    def openViewer(self):
        # Normally it is not possible to try to open viewer if none is
        # available
        try:
            viewer = getProcessInstance(self.viewersToTry()[0])
            v = self.getValue()
            if self.process is not None \
                    
and hasattr(viewer, 'allowed_processes'):
                viewer.reference_process = self.process
            neuroProcessesGUI.showProcess(viewer, v)
        except Exception as e:
            print('openViewer failed:', e)
            import traceback
            traceback.print_exc()
    def getViewers(self, update = False):
        if self._viewers is None or update:
            self.updateViewers()
        
        return self._viewers
    def selectedViewer(self):
        # Current index is shifted in Combo box due to the 'Default value' item
        if self.cmbViewers is not None:
            index = self.cmbViewers.currentIndex()
            if index != -1 and index > 0 and index <= len(self._viewers):
                return self._viewers[index - 1]
        return None
    def updateViewers(self):
        if self.diskItem is None:
            format = None
            if len(self.parameter.formats) != 0:
                format = self.parameter.formats[0]
            source = (self.parameter.type, format)
        else:
            source = self.diskItem
        self._viewers = brainvisa.processes.getViewers(
            source, 1, checkUpdate=False, process=self.process,
                                check_values=True)
    def updateViewersComboBox(self):
        if self.cmbViewers is not None:
            v = self.selectedViewer()
            # print('selected viewer:', v)
            # Update viewers in combo box
            self.cmbViewers.clear()
            self.cmbViewers.addItem(_t_('Default'), None)
            self.getViewers() # to update if needed
            for viewer in self._viewers:
                self.cmbViewers.addItem(_t_(viewer.name), viewer)
            if v is not None and v in self._viewers:
                i = self._viewers.index(v)
                self.cmbViewers.setCurrentIndex(i + 1)
            # else:
                # print('Selecting default item with index', self.cmbViewers.findText(_t_('Default')))
                # Select default value
                # self.cmbViewers.setCurrentIndex(0)
            # Display or hide the viewer action in popup menu
            visible = len(self._viewers) > 1
            self.cmbViewersSeparator.setVisible(visible)
            self.actViewers.setVisible(visible)
        if len(self._viewers) == 0:
            self.btnShow.hide()
    def viewersToTry(self):
        viewer = getProcessInstance(self.selectedViewer())
        if viewer is None:
            return self.getViewers()
        else:
            return [viewer]
    def openHistory(self):
        v = self.getValue()
        bvproc_uuid = v.get("lastHistoricalEvent", None)
        if bvproc_uuid is not None:
            try:
                history_window = historygui.DataHistoryWindow(v, bvproc_uuid,
                                                              parent=self)
                history_window.setAttribute(Qt.WA_DeleteOnClose)
                history_window.show()
            except Exception:
                showException()
    def getDataEditors(self, update = False):
        if self._editors is None or update:
            self.updateDataEditors()
        return self._editors
    def selectedDataEditor(self):
        # Current index is shifted in Combo box due to the 'Default value' item
        self.getDataEditors() # trigger update if needed
        if self.cmbDataEditors is not None:
            index = self.cmbDataEditors.currentIndex()
            if index != -1 and index > 0 and index <= len(self._editors):
                return self._editors[index - 1]
        return None
    def editPressed(self):
        if self.btnEdit.isChecked():
            self.btnEdit.setEnabled(0)
            v = self.getValue()
            editorExists = False
            try:
                for editor in self.dataEditorsToTry():
                    # print('try editor:', editor.name)
                    try:
                        editor = getProcessInstance(editor())
                        editorExists = True
                        if self.process is not None \
                                
and hasattr(editor, 'allowed_processes'):
                            editor.reference_process = self.process
                        brainvisa.processes.defaultContext().runInteractiveProcess(
                            self._editorExited, editor, v)
                        self.btnEdit.setEnabled(True)
                        return
                    except Exception:
                        # Log an error then try another editor if possible
                        brainvisa.processes.defaultContext().log(
                            _t_('Warning for %s') % _t_(editor.name),
                          html=_t_('Editor aborted for type=<em>%s</em> and '
                                   'format=<em>%s</em> value=<em>%s</em> '
                                   '(try using it interactively by '
                                   'right-clicking on the pencil icon)')
                          % (six.text_type(v.type), six.text_type(v.format), six.text_type(v)),
                          icon='pencil.png')
                        continue
                self.btnEdit.setEnabled(False)
                self.btnEdit.setChecked(False)
                raise RuntimeError(HTMLMessage(_t_('No editor could be found for '
                                                   'type=<em>%s</em> and format=<em>%s</em>')
                                               % (six.text_type(v.type), six.text_type(v.format))))
            except Exception as error:
                    # transform the exception into a print message, and return.
                    # We are in a Qt slot here, raising an exception results in
                    # undefined behaviour, which happens to have changed between PyQt 5.4
                    # and PyQt 5.5
                print(error)
                import traceback
                traceback.print_stack()
        else:
            self._edit = None
    def _editorExited(self, result):
        if isinstance(result, Exception):
            showException(parent=self)
        else:
            self._view = result
        neuroProcessesGUI.mainThreadActions().push(
            self.btnEdit.setEnabled, True)
        if result is None:
            neuroProcessesGUI.mainThreadActions().push(
                self.btnEdit.setChecked, False)
    def close_editor(self):
        if self._edit is not None:
            self._edit = None
            neuroProcessesGUI.mainThreadActions().push(
                self.btnShow.setChecked, False)
            neuroProcessesGUI.mainThreadActions().push(
                self.btnShow.setEnabled, True)
    def openEditorPressed(self):
        editor = getProcessInstance(self.dataEditorsToTry()[0]())
        v = self.getValue()
        if self.process is not None \
                
and hasattr(editor, 'allowed_processes'):
            editor.reference_process = self.process
        neuroProcessesGUI.showProcess(editor, v)
    def updateDataEditors(self):
        if self.diskItem is None:
            format = None
            if len(self.parameter.formats) != 0:
                format = self.parameter.formats[0]
            source = (self.parameter.type, format)
        else:
            source = self.diskItem
        try:
            self._editors = brainvisa.processes.getDataEditors(
                source, 0, checkUpdate=False, process=self.process)
        except Exception:
            self._editors = None
    def updateDataEditorsComboBox(self):
        if self.cmbDataEditors is not None:
            e = self.selectedDataEditor()
            # print('selected data editor:', v)
            # Update editors in combo box
            self.cmbDataEditors.clear()
            self.cmbDataEditors.addItem(_t_('Default'), None)
            self.getDataEditors() # update if needed
            for editor in self._editors:
                self.cmbDataEditors.addItem(_t_(editor.name), editor)
            if e is not None and e in self._editors:
                i = self._editors.index(e)
                self.cmbDataEditors.setCurrentIndex(i + 1)
            # else:
                # print('Selecting default item with index', self.cmbDataEditors.findText(_t_('Default')))
                # Select default value
                # self.cmbDataEditors.setCurrentIndex(0)
            # Display or hide the data editor action in popup menu
            visible = len(self._editors) > 1
            self.cmbDataEditorsSeparator.setVisible(visible)
            self.actDataEditors.setVisible(visible)
        if len(self._editors) == 0:
            self.btnEdit.hide()
    def dataEditorsToTry(self):
        editor = getProcessInstance(self.selectedDataEditor())
        if editor is None:
            return self.getDataEditors()
        else:
            return [editor]
    def databasePressed(self):
        try:
            if self.databaseDialog is None or self.parameter._modified:
                self.parameter._modified = 0
                if self.diskItem:  # this parameter has already a value, use it to initialize the browser
                    selection = self.diskItem.hierarchyAttributes()
                    if self.diskItem.type is None:
                        selection['_type'] = None
                    else:
                        selection['_type'] = self.diskItem.type.name
                    if self.diskItem.format is None:
                        selection['_format'] = None
                    else:
                        selection['_format'] = self.diskItem.format.name
                    self.databaseDialog = DiskItemBrowser(
                        self.parameter.database, selection=selection, required=self.parameter.requiredAttributes, parent=self, write=self._write,
                        enableConversion=self.parameter.enableConversion, exactType=self.parameter.exactType)
                else:  # if there is no value, we could have some selected attributes from a linked value, use it to initialize the browser
                    self.databaseDialog = DiskItemBrowser(
                        self.parameter.database, selection=self.parameter._selectedAttributes, required=self.parameter.requiredAttributes,
                                                          parent=self, write=self._write, enableConversion=self.parameter.enableConversion, exactType=self.parameter.exactType)
                self.databaseDialog.setWindowTitle(_t_(self.parameter.type.name))
                self.databaseDialog.accepted.connect(self.databaseAccepted)
            else:
                self.databaseDialog.resetSelectedAttributes(
                    self.diskItem, self.parameter._selectedAttributes)
            self.databaseDialog.show()
        except Exception:
            showException(parent=self)
    def databaseAccepted(self):
        values = self.databaseDialog.getValues()
        if values:
            self.setValue(values[0])
    def browsePressed(self):
        if self.browseDialog is None or self.parameter._modified:
            self.parameter._modified = False
            if self.customFileDialog:
                self.browseDialog = self.customFileDialog(self)
            else:
                self.browseDialog = QFileDialog(self)
            if self._write:
                mode = QFileDialog.AnyFile
            else:
                mode = QFileDialog.ExistingFile
            filters = []
            allPatterns = {}
            dirOnly = True
            formats = set(self.parameter.formats)
            if self.parameter.enableConversion:
                for t in [self.parameter.type] + self.parameter.type.parents():
                    for f in self.parameter.formats:
                        conv = brainvisa.processes.getConvertersTo((t, f))
                        for t2, f2 in six.iterkeys(conv):
                            formats.add(f2)
            for f in formats:
                if f.fileOrDirectory() is not Directory:
                    dirOnly = False
                flt = f.getPatterns().unmatch(
                    {}, {'filename_variable': '*'})[0]
                allPatterns[flt] = 1
                filters.append(_t_(f.name) + ' (' + flt + ')')
            filters.insert(0, _t_('Recognized formats') + ' ('
                           + ' '.join(list(allPatterns.keys())) + ')')
            filters.append(_t_('All files') + ' (*)')
            if dirOnly:
                mode = QFileDialog.Directory
            self.browseDialog.setFileMode(mode)
            self.browseDialog.setNameFilters(filters)
            if self.customFileDialog and self.customFileDialog.customFilter != "":
                self.browseDialog.selectFilter(
                    self.customFileDialog.customFilter)
            self.browseDialog.accepted.connect(self.browseAccepted)
        # set current directory
        parent = self._context
        if hasattr(parent, '_currentDirectory') and parent._currentDirectory:
            self.browseDialog.setDirectory(parent._currentDirectory)
        else:
            self.browseDialog.setDirectory(os.getcwd())
        self.browseDialog.show()
    def browseAccepted(self):
        value = self.browseDialog.selectedFiles()
        if (len(value) > 0):
            value = six.text_type(value[0])
        else:
            value = None
        parent = self._context
        if hasattr(parent, '_currentDirectory'):
            parent._currentDirectory = six.text_type(
                self.browseDialog.directory().path())
        self.setValue(value)
[docs]    def releaseCallbacks(self):
        self._view = None
        self._edit = None  
#----------------------------------------------------------------------------
[docs]class DiskItemListEditor(QWidget, DataEditor):
    noDefault = QtCore.Signal(six.text_type)
    newValidValue = QtCore.Signal(six.text_type, object)
[docs]    class DiskItemListSelect(QWidget):  # Ex QSemiModal
        accepted = QtCore.Signal((six.text_type, ), (list,))
        # accepted = QtCore.Signal(list)
        def __init__(self, dilEditor, name, write, context=None,
                     databaseUserLevel=0, browseUserLevel=0):
            self._context = context
            if getattr(DiskItemListEditor.DiskItemListSelect, 'pixUp', None) is None:
                setattr(DiskItemListEditor.DiskItemListSelect, 'pixUp',
                        QIcon(findIconFile('up.png')))
                setattr(DiskItemListEditor.DiskItemListSelect, 'pixDown',
                        QIcon(findIconFile('down.png')))
                setattr(DiskItemListEditor.DiskItemListSelect, 'pixFindRead',
                        QIcon(findIconFile('database_read.png')))
                setattr(DiskItemListEditor.DiskItemListSelect, 'pixFindWrite',
                        QIcon(findIconFile('database_write.png')))
                setattr(DiskItemListEditor.DiskItemListSelect, 'pixBrowseRead',
                        QIcon(findIconFile('browse_read.png')))
                setattr(
                    DiskItemListEditor.DiskItemListSelect, 'pixBrowseWrite',
                  QIcon(findIconFile('browse_write.png')))
                setattr(
                    DiskItemListEditor.DiskItemListSelect, 'pixBrowseUpdate',
                  QIcon(findIconFile('browse_update.png')))
            QWidget.__init__(
                self, dilEditor.window(), Qt.Dialog | Qt.WindowStaysOnTopHint)
            if name:
                self.setObjectName(name)
            self.setAttribute(Qt.WA_DeleteOnClose, True)
            self.setWindowModality(Qt.WindowModal)
            layout = QVBoxLayout()
            layout.setContentsMargins(10, 10, 10, 10)
            layout.setSpacing(5)
            self.setLayout(layout)
            self.dilEditor = dilEditor
            self.parameter = dilEditor.parameter
            self.values = []
            self.browseDialog = None
            self.findDialog = None
            self.lbxValues = QListWidget()
            self.lbxValues.setSelectionMode(QListWidget.ExtendedSelection)
            # self.lbxValues.currentRowChanged.connect(self._currentChanged)
            self.lbxValues.itemSelectionChanged.connect(self._selectionChanged)
            layout.addWidget(self.lbxValues)
            self.textLine = QLabel()
            layout.addWidget(self.textLine)
            hb = QHBoxLayout()
            hb.setSpacing(6)
            self.btnAdd = QPushButton(_t_('Add'))
            self.btnAdd.setEnabled(0)
            self.btnAdd.clicked.connect(self._add)
            hb.addWidget(self.btnAdd)
            self.btnRemove = QPushButton(_t_('Remove'))
            self.btnRemove.setEnabled(0)
            self.btnRemove.clicked.connect(self._remove)
            hb.addWidget(self.btnRemove)
            self.btnUp = QPushButton()
            self.btnUp.setIcon(self.pixUp)
            self.btnUp.setIconSize(buttonIconSize)
            self.btnUp.setEnabled(0)
            self.btnUp.clicked.connect(self._up)
            hb.addWidget(self.btnUp)
            self.btnDown = QPushButton()
            self.btnDown.setIcon(self.pixDown)
            self.btnDown.setIconSize(buttonIconSize)
            self.btnDown.setEnabled(0)
            self.btnDown.clicked.connect(self._down)
            hb.addWidget(self.btnDown)
            if write:
                # Add a button to change output directories of selected items
                self.btnSetDirectory = QPushButton()
                self.btnSetDirectory.setIcon(self.pixBrowseUpdate)
                self.btnSetDirectory.setIconSize(buttonIconSize)
                self.btnSetDirectory.setEnabled(0)
                self.btnSetDirectory.clicked.connect(self._setDirectory)
                hb.addWidget(self.btnSetDirectory)
            spacer = QSpacerItem(
                10, 10, QSizePolicy.Expanding, QSizePolicy.Minimum)
            hb.addItem(spacer)
            layout.addLayout(hb)
            hb = QHBoxLayout()
            hb.setSpacing(6)
            self.sle = StringListEditor(None, six.text_type(self.objectName()))
            self.sle.newValidValue.connect(self.checkUI)
            hb.addWidget(self.sle)
            btn = QPushButton()
            if write:
                btn.setIcon(self.pixFindWrite)
            else:
                btn.setIcon(self.pixFindRead)
            btn.setIconSize(buttonIconSize)
            if databaseUserLevel > neuroConfig.userLevel:
                btn.hide()
            btn.clicked.connect(self.findPressed)
            hb.addWidget(btn)
            btn = QPushButton()
            if write:
                btn.setIcon(self.pixBrowseWrite)
            else:
                btn.setIcon(self.pixBrowseRead)
            btn.setIconSize(buttonIconSize)
            if browseUserLevel > neuroConfig.userLevel:
                btn.hide()
            btn.clicked.connect(self.browsePressed)
            hb.addWidget(btn)
            layout.addLayout(hb)
#      self.editor = self.parameter.editor( self, self.name() )
#      layout.addWidget( self.editor )
            hb = QHBoxLayout()
            hb.setSpacing(6)
            hb.setContentsMargins(6, 6, 6, 6)
            spacer = QSpacerItem(
                20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
            hb.addItem(spacer)
            btn = QPushButton(_t_('Ok'))
            hb.addWidget(btn)
            btn.clicked.connect(self._ok)
            btn = QPushButton(_t_('Cancel'))
            hb.addWidget(btn)
            btn.clicked.connect(self._cancel)
            layout.addLayout(hb)
            neuroConfig.registerObject(self)
[docs]        def closeEvent(self, event):
            neuroConfig.unregisterObject(self)
            QWidget.closeEvent(self, event) 
        def checkUI(self):
            # Check that user interface buttons are coherent with values
            sindexes = [i.row() for i in self.lbxValues.selectedIndexes()]
            sindexes.sort()
            if len(sindexes) > 0:
                self.btnRemove.setEnabled(1)
                if hasattr(self, 'btnSetDirectory'):
                    self.btnSetDirectory.setEnabled(1)
                if (sindexes[0] > 0) or (len(sindexes) > 1):
                    self.btnUp.setEnabled(1)
                else:
                    self.btnUp.setEnabled(0)
                if (sindexes[-1] < (len(self.values) - 1)) or (len(sindexes) > 1):
                    self.btnDown.setEnabled(1)
                else:
                    self.btnDown.setEnabled(0)
            else:
                self.btnRemove.setEnabled(0)
                if hasattr(self, 'btnSetDirectory'):
                    self.btnSetDirectory.setEnabled(0)
                self.btnUp.setEnabled(0)
                self.btnDown.setEnabled(0)
            self.btnAdd.setEnabled(self.sle.getValue() is not None)
            return None
        # def _currentChanged( self, index ):
            # if index >= 0 and index < len( self.values ):
                # if self.values[ index ] :
                    # self.sle.setValue( [ self.values[ index ].fullPath() ] )
                # else :
                    # self.sle.setValue( None )
            # else:
                # self.sle.setValue( None )
        def updateEditorValue(self):
            if len(self.lbxValues.selectedIndexes()) > 0:
                v = [self.values[s.row()]
                     for s in self.lbxValues.selectedIndexes()]
                self.sle.setValue(v)
            else:
                self.sle.setValue(None)
        def _selectionChanged(self):
            self.checkUI()
            self.updateEditorValue()
            self.textLine.setText('items: %d, selected: %d'
                                  % (self.lbxValues.count(), len(self.lbxValues.selectedIndexes())))
        def _add(self):
            try:
                for v in map(self.parameter.findValue, self.sle.getValue()):
                    self.values.append(v)
                    if v is None:
                        self.lbxValues.addItem('<' + _t_('None') + '>')
                    else:
                        self.lbxValues.addItem(v.fileName())
                self.lbxValues.clearSelection()
                self.lbxValues.setCurrentRow(len(self.values) - 1,
                                             QItemSelectionModel.SelectCurrent)
            except Exception:
                showException(parent=self)
        def _remove(self):
            indexes = [i.row() for i in self.lbxValues.selectedIndexes()]
            rindexes = list(indexes)
            rindexes.sort()
            lindex = rindexes.index(indexes[-1])
            rindexes.reverse()
            for index in rindexes:
                del self.values[index]
                self.lbxValues.takeItem(index)
            # Select the item preceding the last deleted item
            if (indexes[-1] - lindex) <= 0:
                c = 0
            elif (indexes[-1] - lindex) >= (len(self.values) - 1):
                c = len(self.values) - 1
            else:
                c = indexes[-1] - lindex
            self.lbxValues.setCurrentRow(c,
                                         QItemSelectionModel.SelectCurrent)
            # if (c == indexes[-1]) :
                # Artificially ensure that value was changed
                # self.updateEditorValues()
        def _up(self):
            indexes = [i.row() for i in self.lbxValues.selectedIndexes()]
            sindexes = list(indexes)
            sindexes.sort()
            for index in sindexes:
                if index > 0:
                    tmp = self.values[index]
                    self.values[index] = self.values[index - 1]
                    self.values[index - 1] = tmp
                    item = self.lbxValues.takeItem(index)
                    self.lbxValues.insertItem(index - 1, item)
                    item.setSelected(1)
        def _down(self):
            indexes = [i.row() for i in self.lbxValues.selectedIndexes()]
            rindexes = list(indexes)
            rindexes.sort()
            rindexes.reverse()
            for index in rindexes:
                if (index + 1) < len(self.values):
                    tmp = self.values[index]
                    self.values[index] = self.values[index + 1]
                    self.values[index + 1] = tmp
                    item = self.lbxValues.takeItem(index)
                    self.lbxValues.insertItem(index + 1, item)
                    item.setSelected(1)
        def _setDirectory(self):
            self.browseDirectoryDialog = QFileDialog(self.window())
            self.browseDirectoryDialog.accepted.connect(
                self._setDirectoryAccepted)
            self.browseDirectoryDialog.setFileMode(QFileDialog.Directory)
            self.browseDirectoryDialog.setOption(
                QFileDialog.ShowDirsOnly, True)
            parent = self._context
            if hasattr(parent, '_currentDirectory') and parent._currentDirectory:
                self.browseDirectoryDialog.setDirectory(
                    parent._currentDirectory)
            else:
                self.browseDirectoryDialog.setDirectory(os.getcwd())
            self.browseDirectoryDialog.show()
        def _setDirectoryAccepted(self):
            parent = self._context
            # Get the selected directory
            directory = six.text_type(self.browseDirectoryDialog.selectedFiles()[0])
            if hasattr(parent, '_currentDirectory'):
                parent._currentDirectory = directory
            if self.isVisible():
                indexes = [i.row() for i in self.lbxValues.selectedIndexes()]
                for index in indexes:
                    # Replaces the disk item with a new one
                    self.values[index] = brainvisa.data.neuroDiskItems.File(
                        os.path.join(directory,
                                     os.path.basename(
                                     self.values[index].fullPath(
                                     ))),
                        None)
                    self.lbxValues.item(index).setText(
                        self.values[index].fullPath())
                # Updates current editor value
                self.updateEditorValue()
            else:
                self.accepted.emit(six.text_type(directory))
        def setValue(self, value):
            if isinstance(value, (list, tuple)):
                self.values = []
                self.lbxValues.clear()
                for v in value:
                    self.values.append(v)
                    if v is None:
                        self.lbxValues.addItem('<' + _t_('None') + '>')
                    else:
                        self.lbxValues.addItem(v.fileName())
        def _ok(self):
            self.dilEditor._newValue(self.values)
            self.close()
        def _cancel(self):
            self.close()
        def findPressed(self):
            if self.findDialog is None:
                self.findDialog = DiskItemBrowser(self.parameter.database,
                                                  required=self.parameter.requiredAttributes,
                                                  parent=self,
                                                  write=self.parameter._write,
                                                  enableConversion=self.parameter.enableConversion,
                                                  multiple=True,
                                                  exactType=self.parameter.exactType)
                self.findDialog.accepted[()].connect(self.findAccepted)
            else:
                self.findDialog.rescan()
            self.findDialog.show()
        def findAccepted(self):
            value = [x.fullPath() for x in self.findDialog.getValues()]
            if self.isVisible():
                self.sle.setValue(value)
                self._add()
            else:
                self.accepted[list].emit(value)
        def browsePressed(self):
            if self.browseDialog is None:
                self.browseDialog = QFileDialog(self.window())
                if not self.parameter._write:
                    self.browseDialog.setFileMode(QFileDialog.ExistingFiles)
                else:
                    self.browseDialog.setFileMode(QFileDialog.AnyFile)
                filters = []
                allPatterns = {}
                dirOnly = 1
                formats = set(self.parameter.formats)
                if self.parameter.enableConversion:
                    for t in [self.parameter.type] + self.parameter.type.parents():
                        for f in self.parameter.formats:
                            conv = brainvisa.processes.getConvertersTo(
                                (t, f))
                            for t2, f2 in six.iterkeys(conv):
                                formats.add(f2)
                for f in formats:
                    if f.fileOrDirectory() is not Directory:
                        dirOnly = 0
                    flt = f.getPatterns().unmatch(
                        {}, {'filename_variable': '*'})[0]
                    allPatterns[flt] = 1
                    filters.append(_t_(f.name) + ' (' + flt + ')')
                filters.insert(0, _t_('Recognized formats') + ' ('
                               + ' '.join(list(allPatterns.keys())) + ')')
                filters.append(_t_('All files') + ' (*)')
                self.browseDialog.setNameFilters(filters)
                # self.browseDialog.fileSelected.connect(self.browseAccepted)
                self.browseDialog.accepted.connect(self.browseAccepted)
                if dirOnly:
                    self.browseDialog.setFileMode(QFileDialog.Directory)
                    self.browseDialog.setOption(QFileDialog.ShowDirsOnly)
                parent = self._context
                if hasattr(parent, '_currentDirectory') and parent._currentDirectory:
                    self.browseDialog.setDirectory(parent._currentDirectory)
                else:
                    self.browseDialog.setDirectory(os.getcwd())
                # Set multiselection even for Directory and AnyFile modes
                self.browseDialog.setOption(
                    QFileDialog.DontUseNativeDialog, True)
                l = self.browseDialog.findChild(QListView, "listView")
                if l:
                    l.setSelectionMode(QAbstractItemView.ExtendedSelection)
                t = self.browseDialog.findChild(QTreeView)
                if t:
                    t.setSelectionMode(QAbstractItemView.ExtendedSelection)
                e = self.browseDialog.findChild(QLineEdit)
                if e:
                    # Increases QLineEdit maximum length wich default was to 32767
                    e.setMaxLength(2**20)
            self.browseDialog.show()
        def browseAccepted(self):
            parent = self._context
            if hasattr(parent, '_currentDirectory'):
                parent._currentDirectory = six.text_type(
                    self.browseDialog.directory().path())
            l = [str(i) for i in self.browseDialog.selectedFiles()]
            if self.isVisible():
                self.sle.setValue(l)
                self._add()
            else:
                self.accepted[list].emit(l) 
    def __init__(self, parameter, parent, name, write=0, context=None,
                 process=None):
        if getattr(DiskItemListEditor, 'pixFindRead', None) is None:
            setattr(DiskItemListEditor, 'pixShow',
                    QIcon(findIconFile('eye.png')))
            setattr(DiskItemListEditor, 'pixEdit',
                    QIcon(findIconFile('pencil.png')))
            setattr(DiskItemListEditor, 'pixFindRead',
                    QIcon(findIconFile('database_read.png')))
            setattr(DiskItemListEditor, 'pixFindWrite',
                    QIcon(findIconFile('database_write.png')))
            setattr(DiskItemListEditor, 'pixBrowseRead',
                    QIcon(findIconFile('browse_read.png')))
            setattr(DiskItemListEditor, 'pixBrowseWrite',
                    QIcon(findIconFile('browse_write.png')))
        QWidget.__init__(self, parent)
        if name:
            self.setObjectName(name)
        hb = QHBoxLayout()
        self.setLayout(hb)
        hb.setContentsMargins(0, 0, 0, 0)
        hb.setSpacing(2)
        self._context = context
        self.parameter = parameter
        self.write = write
        self.sle = StringListEditor(None, name)
        hb.addWidget(self.sle)
        self._value = None
        self.sle.newValidValue.connect(self._newTextValue)
        self.btnShow = RightClickablePushButton()
        hb.addWidget(self.btnShow)
        self.btnShow.setCheckable(True)
        self.btnShow.setIcon(self.pixShow)
        self.btnShow.setIconSize(buttonIconSize)
        self.btnShow.setFixedSize(buttonIconSize + buttonMargin)
        self.btnShow.setFocusPolicy(Qt.NoFocus)
        self.btnShow.setEnabled(False)
        if process is None:
            self.process = None
        else:
            if isinstance(process, weakref.ProxyType):
                self.process = process
            else:
                self.process = weakref.proxy(process)
        # Sets default viewers list
        self._viewers = None
        self.actViewers = None
        self.cmbViewersSeparator = None
        self.cmbViewers = None
        self.newValidValue.connect(self.updateViewers)
        self._view = None
        self.btnShow.clicked.connect(self.showPressed)
        self.btnShow.rightPressed.connect(self.openViewerPressed)
        self._edit = None
        self.btnEdit = RightClickablePushButton()
        hb.addWidget(self.btnEdit)
        self.btnEdit.setCheckable(True)
        self.btnEdit.setIcon(self.pixEdit)
        self.btnEdit.setIconSize(buttonIconSize)
        self.btnEdit.setFixedSize(buttonIconSize + buttonMargin)
        self.btnEdit.setFocusPolicy(Qt.NoFocus)
        self.btnEdit.setEnabled(0)
        # if not brainvisa.processes.getDataEditor( (self.parameter.type, self.parameter.formats ), checkUpdate=False, listof=True ):
            # self.btnEdit.hide()
        self.btnEdit.clicked.connect(self.editPressed)
        self.btnEdit.rightPressed.connect(self.openEditorPressed)
        # Sets default data editors list
        self._editors = None
        self.actDataEditors = None
        self.cmbDataEditorsSeparator = None
        self.cmbDataEditors = None
        self.newValidValue.connect(self.updateDataEditors)
        
        # Sets default resolutions list
        self.actResolutions = None
        self.cmbResolutionsSeparator = None
        self.cmbResolutions = None
        self._res_infos = None
        self.btnFind = RightClickablePushButton()
        hb.addWidget(self.btnFind)
        if write:
            self.btnFind.setIcon(self.pixFindWrite)
        else:
            self.btnFind.setIcon(self.pixFindRead)
        self.btnFind.setIconSize(buttonIconSize)
        self.btnFind.setFixedSize(buttonIconSize + buttonMargin)
        self.btnFind.setFocusPolicy(Qt.NoFocus)
        if hasattr(parameter, 'databaseUserLevel'):
            x = parameter.databaseUserLevel
            if x > neuroConfig.userLevel:
                self.btnFind.hide()
        self.btnFind.clicked.connect(self.findPressed)
        self.btnFind.rightPressed.connect(self.findRightPressed)
        self.btnBrowse = QPushButton()
        hb.addWidget(self.btnBrowse)
        if write:
            self.btnBrowse.setIcon(self.pixBrowseWrite)
        else:
            self.btnBrowse.setIcon(self.pixBrowseRead)
        self.btnBrowse.setIconSize(buttonIconSize)
        self.btnBrowse.setFixedSize(buttonIconSize + buttonMargin)
        self.btnBrowse.setFocusPolicy(Qt.NoFocus)
        if hasattr(parameter, 'browseUserLevel'):
            x = parameter.browseUserLevel
            if x > neuroConfig.userLevel:
                self.btnBrowse.hide()
        # only one click on the browse button : always open the diskItemListSelect widget
        # as we often need to select files in the filesystem in several steps
        # when the files are not all in the same directory.
        self.btnBrowse.clicked.connect(self.browsePressed)
        self.setValue(None, 1)
    def createPopupMenu(self, popup):
        if self.parameter.enableMultiResolution:
            self.cmbResolutionsSeparator = popup.addSeparator()
            addComboBoxMenu(self, popup, 
                            'resolution','resolution.png', 
                            'cmbResolutions', 'actResolutions', 
                            self.updateResolutions)
            self.cmbResolutions.currentIndexChanged[int].connect(
                self.setResolutionLevel)
        
        self.cmbViewersSeparator = popup.addSeparator()
        addComboBoxMenu(self, popup, 
                        'viewer', 'eye.png', 
                        'cmbViewers', 'actViewers',
                        self.updateViewersComboBox)
        self.cmbDataEditorsSeparator = popup.addSeparator()
        addComboBoxMenu(self, popup, 
                        'editor', 'pencil.png',
                        'cmbDataEditors', 'actDataEditors',
                         self.updateDataEditorsComboBox)
    def setResolutionLevel(self, resolution_level):
        v = self.getValue()
        if v is not None:
            for i in v:
                if i is not None:
                    i.setResolutionLevel(resolution_level)
            self.setValue(v)
    def updateResolutions(self):
        v = self.getValue()
        visible = False
        
        if v is not None and None not in v:
            res_infos = getResolutionsFromItems(v)
            
            if self._res_infos != res_infos:
                self._res_infos = res_infos
                if res_infos is not None and len(res_infos) > 1:
                    self.cmbResolutions.clear()
                    for level in six.moves.xrange(len(res_infos)):
                        self.cmbResolutions.addItem(res_infos[level], 
                                                    str(level))
                    
                    resolution_level = None
                    for i in six.moves.xrange(len(v)):
                        if i == 0:
                            resolution_level = v[i].resolutionLevel()
                            
                        else:
                            r = v[i].resolutionLevel()
                            
                            # If 2 items do not have the same resolution level
                            # select default (this choice can be discussed)
                            if resolution_level != r:
                                resolution_level = None
                                break
                            
                    if resolution_level is None:
                        resolution_level = len(res_infos) - 1
                        
                    self.cmbResolutions.setCurrentIndex(resolution_level)
                    
                    visible = True
            
            else:
                visible = res_infos is not None and len(res_infos) > 1
                
        # Display or hide the viewer action in popup menu
        self.cmbResolutionsSeparator.setVisible(visible)
        self.actResolutions.setVisible(visible)
    def getValue(self):
        return self._value
    def _setValue(self, value):
        self._value = value
        if isinstance(value, (list, tuple)):
            r = []
            for v in value:
                if v is None:
                    r.append('')
                else:
                    r.append(str(v))
            value = r
        self.sle._setValue(value)
    def setValue(self, value, default=0):
        self.forceDefault = default
        self._value = value
        if isinstance(value, (list, tuple)):
            r = []
            for v in value:
                if v is None:
                    r.append('')
                else:
                    r.append(str(v))
            value = r
        if value:
            self.checkReadable()
        else:
            if self.btnShow:
                self.btnShow.setEnabled(0)
            if self.btnEdit:
                self.btnEdit.setEnabled(0)
        
        self.sle.setValue(value, default)
        self.forceDefault = 0
    def isViewable(self):
        if self._value:
            viewable = True
            viewers = self.getViewers()
            for v in self._value:
                if not (v and isinstance(v, DiskItem) and v.isReadable()):
                    viewable = False
                    break
                
            return viewable and len(viewers) > 0
        
        return False
    
    def isEditable(self):
        if self._value:
            editable = True
            editors = self.getDataEditors()
            for v in self._value:
                if v is None or not isinstance(v, DiskItem) or v.isLockData() \
                   
or not v.isWriteable():
                    editable = False
                    break
                
            return editable and len(editors) > 0
        
        return False
    def set_read_only(self, read_only):
        self.btnFind.setEnabled(not read_only)
        self.btnBrowse.setEnabled(not read_only)
        self.btnEdit.setEnabled(not read_only and self.isEditable())
        self.sle.setReadOnly(read_only)
        self.sle.setFrame(not read_only)
        
    def checkReadable(self):
        if self.btnShow:
            enabled = True
            
            if len(self.getViewers()) > 0:
                self.btnShow.show()
            else:
                self.btnShow.hide()
                
            self.btnShow.setEnabled(self.isViewable())
            
        if self.btnEdit:
            enabled = True
            
            if len(self.getDataEditors()) > 0:
                self.btnEdit.show()
            else:
                self.btnEdit.hide()
            self.btnEdit.setEnabled(self.isEditable())
    def showPressed(self):
        if self.btnShow.isChecked():
            self.btnShow.setEnabled(0)
            v = self.getValue()
            viewerExists = False
            try:
                for viewer in self.viewersToTry():
                    try:
                        viewer = getProcessInstance(viewer)
                        viewerExists = True
                        if self.process is not None \
                                
and hasattr(viewer, 'allowed_processes'):
                            viewer.reference_process = self.process
                        brainvisa.processes.defaultContext().runInteractiveProcess(
                            self._viewerExited, viewer, v)
                        self.btnShow.setEnabled(True)
                        return
                    except Exception:
                        # Log an error then try another viewer if possible
                        brainvisa.processes.defaultContext().log(
                            _t_('Warning for %s') % _t_(viewer.name),
                          html=_t_('Viewer aborted for type=<em>ListOf(%s)</em> and '
                                   'format=<em>%s</em> value=<em>%s</em> '
                                   '(try using it interactively by '
                                   'right-clicking on the eye icon)')
                          % (six.text_type(self.parameter.type),
                             six.text_type(self.parameter.formats), six.text_type(v)),
                          icon='eye.png')
                        continue
                self.btnShow.setEnabled(False)
                self.btnShow.setChecked(False)
                raise RuntimeError(HTMLMessage(_t_('No viewer could be found for '
                                                   'type=<em>%s</em> and format=<em>%s</em>')
                                               % (six.text_type(self.parameter.type),
                                                  six.text_type(self.parameter.formats))))
            except Exception as error:
                    # transform the exception into a print message, and return.
                    # We are in a Qt slot here, raising an exception results in
                    # undefined behaviour, which happens to have changed between PyQt 5.4
                    # and PyQt 5.5
                print(error)
                import traceback
                traceback.print_exc()
        else:
            self._view = None
    def _viewerExited(self, result):
        if isinstance(result, Exception):
            showException(parent=self)
        else:
            self._view = result
        neuroProcessesGUI.mainThreadActions().push(
            self.btnShow.setEnabled, True)
        if result is None:
            neuroProcessesGUI.mainThreadActions().push(
                self.btnShow.setChecked, False)
    def close_viewer(self):
        if self._view is not None:
            self._view = None
            neuroProcessesGUI.mainThreadActions().push(
                self.btnShow.setChecked, False)
            neuroProcessesGUI.mainThreadActions().push(
                self.btnShow.setEnabled, True)
    def openViewerPressed(self):
        # Normally it is not possible to try to open viewer if none is
        # available
        viewer = self.viewersToTry()[0]
        viewer = getProcessInstance(viewer)
        v = self.getValue()
        if self.process is not None \
                
and hasattr(viewer, 'allowed_processes'):
            viewer.reference_process = self.process
        neuroProcessesGUI.showProcess(viewer, v)
    def getViewers(self, update = False):
        if self._viewers is None or update:
            self.updateViewers()
        
        return self._viewers
    
    def selectedViewer(self):
        # Current index is shifted in Combo box due to the 'Default value' item
        if self.cmbViewers is not None:
            index = self.cmbViewers.currentIndex()
            if index != -1 and index > 0 and index <= len(self._viewers):
                return self._viewers[index - 1]
        return None
    def updateViewers(self):
        v = self.getValue()
        if v is None or len(v) == 0:
            format = None
            if len(self.parameter.formats) != 0:
                format = self.parameter.formats[0]
            source = (self.parameter.type, format)
        else:
            source = v
        try:
            self._viewers = brainvisa.processes.getViewers(
                source, 1, checkUpdate=False, listof=True,
                                  process=self.process, check_values=True)
        except Exception:
            self._viewers = []
            
    def updateViewersComboBox(self):
        if self.cmbViewers is not None:
            v = self.selectedViewer()
            # print('selected viewer:', v)
            # Update viewers in combo box
            self.cmbViewers.clear()
            self.cmbViewers.addItem(_t_('Default'), None)
            for viewer in self._viewers:
                self.cmbViewers.addItem(_t_(viewer.name), viewer)
            if v is not None and v in self._viewers:
                i = self._viewers.index(v)
                self.cmbViewers.setCurrentIndex(i + 1)
            # else:
                # print('Selecting default item with index', self.cmbViewers.findText(_t_('Default')))
                # Select default value
                # self.cmbViewers.setCurrentIndex(0)
            # Display or hide the viewer action in popup menu
            visible = len(self._viewers) > 1
            self.cmbViewersSeparator.setVisible(visible)
            self.actViewers.setVisible(visible)
        if len(self._viewers) == 0:
            self.btnShow.hide()
    def viewersToTry(self):
        viewer = getProcessInstance(self.selectedViewer())
        if viewer is None:
            return self._viewers
        else:
            return [viewer]
    def getDataEditors(self, update = False):
        if self._editors is None or update:
            self.updateDataEditors()
        
        return self._editors
    
    def selectedDataEditor(self):
        # Current index is shifted in Combo box due to the 'Default value' item
        if self.cmbDataEditors is not None:
            index = self.cmbDataEditors.currentIndex()
            if index != -1 and index > 0 and index <= len(self._editors):
                return self._editors[index - 1]
        return None
    def editPressed(self):
        if self.btnEdit.isChecked():
            self.btnEdit.setEnabled(0)
            v = self.getValue()
            editorExists = False
            try:
                for editor in self.dataEditorsToTry():
                    try:
                        editor = getProcessInstance(editor())
                        editorExists = True
                        if self.process is not None \
                                
and hasattr(editor, 'allowed_processes'):
                            editor.reference_process = self.process
                        brainvisa.processes.defaultContext().runInteractiveProcess(
                            self._editorExited, editor, v)
                        self.btnEdit.setEnabled(True)
                        return
                    except Exception:
                        # Log an error then try another editor if possible
                        brainvisa.processes.defaultContext().log(
                            _t_('Warning for %s') % _t_(editor.name),
                          html=_t_('Editor aborted for type=<em>%s</em> and '
                                   'format=<em>%s</em> value=<em>%s</em> '
                                   '(try using it interactively by '
                                   'right-clicking on the pencil icon)')
                          % (six.text_type(v.type), six.text_type(v.format), six.text_type(v)),
                          icon='pencil.png')
                        continue
                self.btnEdit.setEnabled(False)
                self.btnEdit.setChecked(False)
                raise RuntimeError(HTMLMessage(_t_('No editor could be found for '
                                                   'type=<em>%s</em> and format=<em>%s</em>')
                                               % (six.text_type(v.type), six.text_type(v.format))))
            except Exception as error:
                    # transform the exception into a print message, and return.
                    # We are in a Qt slot here, raising an exception results in
                    # undefined behaviour, which happens to have changed between PyQt 5.4
                    # and PyQt 5.5
                print(error)
                import traceback
                traceback.print_stack()
        else:
            self._edit = None
    def _editorExited(self, result):
        if isinstance(result, Exception):
            showException(parent=self)
        else:
            self._view = result
        neuroProcessesGUI.mainThreadActions().push(
            self.btnEdit.setEnabled, True)
        if result is None:
            neuroProcessesGUI.mainThreadActions().push(
                self.btnEdit.setChecked, False)
    def close_editor(self):
        if self._edit is not None:
            self._edit = None
            neuroProcessesGUI.mainThreadActions().push(
                self.btnShow.setChecked, False)
            neuroProcessesGUI.mainThreadActions().push(
                self.btnShow.setEnabled, True)
    def openEditorPressed(self):
        editor = getProcessInstance(self.dataEditorsToTry()[0]())
        v = self.getValue()
        if self.process is not None \
                
and hasattr(editor, 'allowed_processes'):
            editor.reference_process = self.process
        neuroProcessesGUI.showProcess(editor, v)
    def updateDataEditors(self):
        v = self.getValue()
        if v is None or len(v) == 0:
            format = None
            if len(self.parameter.formats) != 0:
                format = self.parameter.formats[0]
            source = (self.parameter.type, format)
        else:
            source = v
        try:
            proc = self.process
            if proc is not None:
                proc = proc()
            self._editors = brainvisa.processes.getDataEditors(
                source, 0, checkUpdate=False, listof=True,
                                process=proc)
        except Exception:
            self._editors = []
    def updateDataEditorsComboBox(self):
        if self.cmbDataEditors is not None:
            e = self.selectedDataEditor()
            # print('selected data editor:', v)
            # Update editors in combo box
            self.cmbDataEditors.clear()
            self.cmbDataEditors.addItem(_t_('Default'), None)
            for editor in self._editors:
                self.cmbDataEditors.addItem(_t_(editor.name), editor)
            if e is not None and e in self._editors:
                i = self._editors.index(e)
                self.cmbDataEditors.setCurrentIndex(i + 1)
            # else:
                # print('Selecting default item with index', self.cmbDataEditors.findText(_t_('Default')))
                # Select default value
                # self.cmbDataEditors.setCurrentIndex(0)
            # Display or hide the data editor action in popup menu
            visible = len(self._editors) > 1
            self.cmbDataEditorsSeparator.setVisible(visible)
            self.actDataEditors.setVisible(visible)
        if len(self._editors) == 0:
            self.btnEdit.hide()
    def dataEditorsToTry(self):
        editor = getProcessInstance(self.selectedDataEditor())
        if editor is None:
            return self._editors
        else:
            return [editor]
    def findPressed(self):
        dul = 0
        bul = 0
        if hasattr(self.parameter, 'databaseUserLevel'):
            dul = self.parameter.databaseUserLevel
        if hasattr(self.parameter, 'browseUserLevel'):
            bul = self.parameter.browseUserLevel
        w = self.DiskItemListSelect(
            self, six.text_type(self.objectName()), self.write,
          databaseUserLevel=dul, browseUserLevel=bul)
        try:
            w.setValue(self.getValue())
        except Exception:
            showException(parent=self)
        w.accepted[list].connect(self._newValue)
        w.findPressed()
    def findRightPressed(self):
        dul = 0
        bul = 0
        if hasattr(self.parameter, 'databaseUserLevel'):
            dul = self.parameter.databaseUserLevel
        if hasattr(self.parameter, 'browseUserLevel'):
            bul = self.parameter.browseUserLevel
        w = self.DiskItemListSelect(
            self, six.text_type(self.objectName()), self.write,
          databaseUserLevel=dul, browseUserLevel=bul)
        try:
            w.setValue(self.getValue())
        except Exception:
            showException(parent=self)
        w.show()
        w.findPressed()
    # def browsePressed( self ):
        # dul = 0
        # bul = 0
        # if hasattr( self.parameter, 'databaseUserLevel' ):
            # dul = self.parameter.databaseUserLevel
        # if hasattr( self.parameter, 'browseUserLevel' ):
            # bul = self.parameter.browseUserLevel
        # w = self.DiskItemListSelect( self, unicode(self.objectName()), self.write,
            # context = self._context, databaseUserLevel=dul, browseUserLevel=bul )
        # try:
            # w.setValue( self.getValue() )
        # except Exception:
            # showException( parent = self )
        # w.accepted.connect(self._newValue)
        # w.browsePressed()
    def browsePressed(self):
        dul = 0
        bul = 0
        if hasattr(self.parameter, 'databaseUserLevel'):
            dul = self.parameter.databaseUserLevel
        if hasattr(self.parameter, 'browseUserLevel'):
            bul = self.parameter.browseUserLevel
        w = self.DiskItemListSelect(
            self, six.text_type(self.objectName()), self.write,
          context=self._context, databaseUserLevel=dul, browseUserLevel=bul)
        try:
            w.setValue(self.getValue())
        except Exception:
            showException(parent=self)
        w.show()
        if len(self.sle.getValue()) == 0:
            # Only opens browser when no values were selected
            w.browsePressed()
    def _newTextValue(self):
        textValues = self.sle.getValue()
        if textValues is not None:
            self._newValue([self.parameter.findValue(x) for x in textValues])
        else:
            self._newValue(None)
        return None
    def _newValue(self, v):
        self._setValue(v)
        self.newValidValue.emit(six.text_type(self.objectName()), v)
        if not self.forceDefault:
            self.noDefault.emit(six.text_type(self.objectName()))
    def checkValue(self):
        self.sle.checkValue()