# Source code for taurus.qt.qtgui.model.qbasemodel

    #!/usr/bin/env python

    # ###########################################################################
    #
    # This file is part of Taurus
    #
    # http://taurus-scada.org
    #
    # Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
    #
    # Taurus is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Lesser General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.
    #
    # Taurus 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.
    #
    # You should have received a copy of the GNU Lesser General Public License
    # along with Taurus.  If not, see <http://www.gnu.org/licenses/>.
    #
    # ###########################################################################

    """This module provides base widget that displays a Qt view widget envolved
    by a toolbar and a status bar (optional)
    """

    __docformat__ = "restructuredtext"

    from taurus.external.qt import Qt, compat

    from taurus.qt.qtgui.util import ActionFactory
    from taurus.qt.qtgui.base import TaurusBaseWidget

    class BaseToolBar(Qt.QToolBar):
        def __init__(self, name=None, view=None, parent=None, designMode=False):
            if name is None:
                name = "Base toolbar"
            self._viewWidget = view or parent
            Qt.QToolBar.__init__(self, name, parent)
            self.setIconSize(Qt.QSize(16, 16))
            self.setFloatable(False)
            self.setMovable(False)

        def viewWidget(self):
            return self._viewWidget

    class FilterToolBar(BaseToolBar):
        """Internal widget providing quick filter to be placed in a _QToolArea"""

        filterEdited = Qt.pyqtSignal("const QString &")
        filterChanged = Qt.pyqtSignal("const QString &")
        clearFilterTriggered = Qt.pyqtSignal()

        def __init__(self, view=None, parent=None, designMode=False):
            BaseToolBar.__init__(
                self,
                name="Taurus filter toolbar",
                view=view,
                parent=parent,
                designMode=designMode,
            )
            filterLineEdit = self._filterLineEdit = Qt.QLineEdit(self)
            filterLineEdit.setSizePolicy(
                Qt.QSizePolicy(Qt.QSizePolicy.Preferred, Qt.QSizePolicy.Preferred)
            )
            filterLineEdit.setToolTip("Quick filter")
            filterLineEdit.textChanged.connect(self.onFilterChanged)
            filterLineEdit.textEdited.connect(self.onFilterEdited)
            self.addWidget(filterLineEdit)

            af = ActionFactory()
            self._clearFilterAction = af.createAction(
                self,
                "Clear",
                icon=Qt.QIcon.fromTheme("edit-clear"),
                tip="Clears the filter",
                triggered=self.onClearFilter,
            )
            self.addAction(self._clearFilterAction)

        def getFilterLineEdit(self):
            return self._filterLineEdit

        def onClearFilter(self):
            self.getFilterLineEdit().setText("")
            self.clearFilterTriggered.emit()

        def onFilterChanged(self, text=None):
            text = text or self.getFilterLineEdit().text()
            self.filterChanged.emit(text)

        def onFilterEdited(self, text=None):
            text = text or self.getFilterLineEdit().text()
            self.filterEdited.emit(text)

        def setFilterText(self, text):
            self.getFilterLineEdit().setText(text)

    class EditorToolBar(BaseToolBar):
        """Internal widget to be placed in a _QToolArea providing buttons for
        moving, adding and removing items from a view based widget
        """

        addTriggered = Qt.pyqtSignal()
        removeTriggered = Qt.pyqtSignal()
        moveTopTriggered = Qt.pyqtSignal()
        moveDownTriggered = Qt.pyqtSignal()
        moveUpTriggered = Qt.pyqtSignal()
        moveBottomTriggered = Qt.pyqtSignal()

        def __init__(self, view=None, parent=None, designMode=False):
            BaseToolBar.__init__(
                self,
                name="Taurus editor toolbar",
                view=view,
                parent=parent,
                designMode=designMode,
            )

            af = ActionFactory()
            self._addAction = af.createAction(
                self,
                "New item",
                icon=Qt.QIcon.fromTheme("list-add"),
                tip="Add new item",
                triggered=self.onAdd,
            )
            self._removeAction = af.createAction(
                self,
                "Remove item",
                icon=Qt.QIcon.fromTheme("list-remove"),
                tip="Remove item",
                triggered=self.onRemove,
            )
            self._moveTopAction = af.createAction(
                self,
                "To top",
                icon=Qt.QIcon.fromTheme("go-top"),
                tip="Move selected item to top",
                triggered=self.onMoveTop,
            )
            self._moveUpAction = af.createAction(
                self,
                "Move up",
                icon=Qt.QIcon.fromTheme("go-up"),
                tip="Move selected item up one level",
                triggered=self.onMoveUp,
            )
            self._moveDownAction = af.createAction(
                self,
                "Move down",
                icon=Qt.QIcon.fromTheme("go-down"),
                tip="Move selected item down one level",
                triggered=self.onMoveDown,
            )
            self._moveBottomAction = af.createAction(
                self,
                "To bottom",
                icon=Qt.QIcon.fromTheme("go-bottom"),
                tip="Move selected item to bottom",
                triggered=self.onMoveBottom,
            )
            self.addAction(self._addAction)
            self.addAction(self._removeAction)
            self.addAction(self._moveTopAction)
            self.addAction(self._moveUpAction)
            self.addAction(self._moveDownAction)
            self.addAction(self._moveBottomAction)
            # self.setStyleSheet("QWidget {background : red; }")

        def onAdd(self):
            self.addTriggered.emit()

        def onRemove(self):
            self.removeTriggered.emit()

        def onMoveTop(self):
            self.moveTopTriggered.emit()

        def onMoveUp(self):
            self.moveUpTriggered.emit()

        def onMoveDown(self):
            self.moveDownTriggered.emit()

        def onMoveBottom(self):
            self.moveBottomTriggered.emit()

    class SelectionToolBar(BaseToolBar):

        selectAllTriggered = Qt.pyqtSignal()
        clearSelectionTriggered = Qt.pyqtSignal()

        def __init__(self, view=None, parent=None, designMode=False):
            BaseToolBar.__init__(
                self,
                name="Taurus selection toolbar",
                view=view,
                parent=parent,
                designMode=designMode,
            )

            af = ActionFactory()
            self._selectAllAction = af.createAction(
                self,
                "Select All",
                icon=Qt.QIcon.fromTheme("edit-select-all"),
                tip="Select all items",
                triggered=self.onSelectAll,
            )
            self._clearSelectionAction = af.createAction(
                self,
                "Clear selection",
                icon=Qt.QIcon.fromTheme("edit-clear"),
                tip="Clears current selection",
                triggered=self.onclearSelection,
            )

            self.addAction(self._selectAllAction)
            self.addAction(self._clearSelectionAction)

        def onSelectAll(self):
            self.selectAllTriggered.emit()

        def onclearSelection(self):
            self.clearSelectionTriggered.emit()

    class RefreshToolBar(BaseToolBar):

        refreshTriggered = Qt.pyqtSignal()

        def __init__(self, view=None, parent=None, designMode=False):
            BaseToolBar.__init__(
                self,
                name="Taurus refresh toolbar",
                view=view,
                parent=parent,
                designMode=designMode,
            )

            af = ActionFactory()
            self._refreshAction = af.createAction(
                self,
                "Refresh",
                icon=Qt.QIcon.fromTheme("view-refresh"),
                tip="Refresh view",
                triggered=self.onRefresh,
            )
            self.addAction(self._refreshAction)

        def onRefresh(self):
            self.refreshTriggered.emit()

    class PerspectiveToolBar(BaseToolBar):

        perspectiveChanged = Qt.pyqtSignal(compat.PY_OBJECT)

        def __init__(self, perspective, view=None, parent=None, designMode=False):
            BaseToolBar.__init__(
                self,
                name="Taurus refresh toolbar",
                view=view,
                parent=parent,
                designMode=designMode,
            )
            self._perspective = perspective
            view = self.viewWidget()
            b = self._perspective_button = Qt.QToolButton(self)
            b.setToolTip("Perspective selection")
            b.setPopupMode(Qt.QToolButton.InstantPopup)
            b.setToolButtonStyle(Qt.Qt.ToolButtonTextBesideIcon)

            menu = Qt.QMenu("Perspective", b)
            b.setMenu(menu)
            af = ActionFactory()
            for persp, persp_data in view.KnownPerspectives.items():
                label = persp_data["label"]
                icon = Qt.QIcon.fromTheme(persp_data["icon"])
                tip = persp_data["tooltip"]
                action = af.createAction(
                    self,
                    label,
                    icon=icon,
                    tip=tip,
                    triggered=self.onSwitchPerspective,
                )
                action.perspective = persp
                menu.addAction(action)
                if persp == perspective:
                    b.setDefaultAction(action)

            self._perspectiveAction = self.addWidget(b)

        def switchPerspectiveButton(self):
            """Returns the QToolButton that handles the switch perspective.

            :return: the switch perspective tool button
            :rtype: PyQt5.Qt.QToolButton
            """
            return self._perspective_button

        def onSwitchPerspective(self):
            action = self.sender()
            self._perspective = action.perspective
            self._perspective_button.setDefaultAction(action)
            self.perspectiveChanged.emit(action.perspective)

        def perspective(self):
            return self._perspective

    class QBaseModelWidget(Qt.QMainWindow):
        """A pure Qt widget designed to display a Qt view widget (QTreeView for
        example), envolved by optional toolbar and statusbar.
        The Qt model associated with the internal Qt view widget should be a
        :class:`taurus.qt.qtcore.model.TaurusBaseModel`
        """

        KnownPerspectives = {}
        DftPerspective = None

        itemClicked = Qt.pyqtSignal(compat.PY_OBJECT, int)
        itemDoubleClicked = Qt.pyqtSignal(compat.PY_OBJECT, int)
        itemSelectionChanged = Qt.pyqtSignal()
        currentItemChanged = Qt.pyqtSignal(compat.PY_OBJECT, compat.PY_OBJECT)

        def __init__(
            self,
            parent=None,
            designMode=False,
            with_filter_widget=True,
            with_selection_widget=True,
            with_refresh_widget=True,
            perspective=None,
            proxy=None,
        ):
            Qt.QMainWindow.__init__(self, parent)
            self.setWindowFlags(Qt.Qt.Widget)
            self._baseQModel = None
            self._toolBars = []

            if with_filter_widget:
                if isinstance(with_filter_widget, (bool, int)):
                    self._with_filter_widget = FilterToolBar
                else:
                    self._with_filter_widget = with_filter_widget
            else:
                self._with_filter_widget = None

            if with_selection_widget:
                if isinstance(with_selection_widget, (bool, int)):
                    self._with_selection_widget = SelectionToolBar
                else:
                    self._with_selection_widget = with_selection_widget
            else:
                self._with_selection_widget = None

            if with_refresh_widget:
                if isinstance(with_refresh_widget, (bool, int)):
                    self._with_refresh_widget = RefreshToolBar
                else:
                    self._with_refresh_widget = with_refresh_widget
            else:
                self._with_refresh_widget = None

            self._proxyModel = proxy

            toolBars = self.createToolArea()
            self._viewWidget = self.createViewWidget()
            statusbar = self.createStatusBar()

            for toolBar in toolBars:
                # toolBar.addSeparator()
                self.addToolBar(toolBar)
            self.setContentsMargins(0, 0, 0, 0)
            self.setCentralWidget(self._viewWidget)
            self.setStatusBar(statusbar)

            if perspective is None:
                perspective = self.DftPerspective

            if len(self.KnownPerspectives) > 1:
                p_bar = self._perspectiveBar = PerspectiveToolBar(
                    perspective, view=self, parent=self
                )
                p_bar.perspectiveChanged.connect(self.onSwitchPerspective)
                self.addToolBar(p_bar)
            else:
                self._perspectiveBar = None
            self._setPerspective(perspective)

        def createViewWidget(self, klass=None):
            raise NotImplementedError

        def createStatusBar(self):
            sb = Qt.QStatusBar()
            sb.setSizeGripEnabled(False)
            return sb

        def createToolArea(self):
            tb = []  # tb = self._toolArea = QToolArea(self)
            if self._with_filter_widget:
                f_bar = self._filterBar = self._with_filter_widget(
                    view=self, parent=self
                )
                f_bar.filterChanged.connect(self.onFilterChanged)
                tb.append(f_bar)
            else:
                self._filterBar = None

            if self._with_selection_widget:
                s_bar = self._selectionBar = self._with_selection_widget(
                    view=self, parent=self
                )
                s_bar.selectAllTriggered.connect(self.onSelectAll)
                s_bar.clearSelectionTriggered.connect(self.onClearSelection)
                tb.append(s_bar)
            else:
                self._selectionBar = None

            if self._with_refresh_widget:
                r_bar = self._refreshBar = self._with_refresh_widget(
                    view=self, parent=self
                )
                r_bar.refreshTriggered.connect(self.onRefreshModel)
                tb.append(r_bar)
            else:
                self._refreshBar = None

            return tb

        def getPerspectiveBar(self):
            return self._perspectiveBar

        def getFilterBar(self):
            return self._filterBar

        def getSelectionBar(self):
            return self._selectionBar

        def getRefreshBar(self):
            return self._refreshBar

        def onRefreshModel(self):
            self.getQModel().refresh(True)

        def onSelectAll(self):
            view = self.viewWidget()
            view.selectAll()

        def onClearSelection(self):
            view = self.viewWidget()
            view.clearSelection()

        def _onClicked(self, index):
            """Emits an "itemClicked" signal with with the clicked item and column
            as arguments
            """
            item = self._mapToSource(index).internalPointer()
            self.itemClicked.emit(item, index.column())

        def _onDoubleClicked(self, index):
            """Emits an "itemDoubleClicked" signal with the clicked item and column
            as arguments
            """
            item = self._mapToSource(index).internalPointer()
            self.itemDoubleClicked.emit(item, index.column())

        def viewWidget(self):
            return self._viewWidget

        def getQModel(self):
            return self.viewWidget().model()

        def getBaseQModel(self):
            return self._baseQModel

        def usesProxyQModel(self):
            return isinstance(self.getQModel(), Qt.QAbstractProxyModel)

        def _mapToSource(self, index):
            if not self.usesProxyQModel():
                return index
            model = self.getQModel()
            while isinstance(model, Qt.QAbstractProxyModel):
                index = model.mapToSource(index)
                model = model.sourceModel()
            return index

        def setQModel(self, qmodel):

            self._baseQModel = qmodel
            while isinstance(self._baseQModel, Qt.QAbstractProxyModel):
                self._baseQModel = self._baseQModel.sourceModel()

            view = self.viewWidget()
            old_selection_model = view.selectionModel()
            if old_selection_model is not None:
                old_selection_model.currentChanged.disconnect(
                    self.viewCurrentIndexChanged
                )
                old_selection_model.selectionChanged.disconnect(
                    self.viewSelectionChanged
                )
            view.setModel(qmodel)
            new_selection_model = view.selectionModel()
            if new_selection_model is not None:
                new_selection_model.currentChanged.connect(
                    self.viewCurrentIndexChanged
                )
                new_selection_model.selectionChanged.connect(
                    self.viewSelectionChanged
                )
            view.setCurrentIndex(view.rootIndex())
            self._updateToolBar()

        def viewSelectionChanged(self, selected, deselected):
            self.itemSelectionChanged.emit()

        def viewCurrentIndexChanged(self, current, previous):
            # if there is a proxy model we have to translate the selection
            base_current = self._mapToSource(current)
            base_previous = self._mapToSource(previous)

            self._updateToolBar(current)

            if base_current.isValid():
                currentTaurusTreeItem = base_current.internalPointer()
            else:
                currentTaurusTreeItem = None

            if base_previous.isValid():
                previousTaurusTreeItem = base_previous.internalPointer()
            else:
                previousTaurusTreeItem = None
            self.currentItemChanged.emit(
                currentTaurusTreeItem, previousTaurusTreeItem
            )

        def _updateToolBar(self, current=None, previous=None):
            pass

        def selectedItems(self):
            """Returns a list of all selected non-hidden items

            :return:
            :rtype: list<TaurusTreeItem>
            """
            view = self.viewWidget()
            return [
                self._mapToSource(index).internalPointer()
                for index in view.selectedIndexes()
            ]

        def onFilterChanged(self, filter):
            if not self.usesProxyQModel():
                return
            proxy_model = self.getQModel()
            if len(filter) > 0 and filter[0] != "^":
                filter = "^" + filter
            proxy_model.setFilterRegExp(filter)

            # proxy_model.setFilterFixedString(filter)
            # proxy_model.setFilterWildcard(filter)
            # self.update()

        def refresh(self):
            self.getQModel().refresh()

        # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
        # Perspective handling
        # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~

        def perspective(self):
            return self._perspectiveBar.perspective()

        def onSwitchPerspective(self, perspective):
            old_qmodel = self.getQModel()
            self._setPerspective(perspective)

            # set the selectables as they where in the previous model
            if hasattr(old_qmodel, "selectables"):
                self.getQModel().setSelectables(old_qmodel.selectables())

            # set the taurus model (if any) to the qmodel
            if hasattr(self, "getModelObj"):
                taurusModel = self.getModelObj()
                if taurusModel is not None:
                    self.getQModel().setDataSource(taurusModel)

        def _setPerspective(self, perspective):
            qmodel_classes = self.KnownPerspectives[perspective]["model"]
            # reversed
            qmodel_class, qmodel_proxy_classes = (
                qmodel_classes[-1],
                qmodel_classes[-2::-1],
            )
            qmodel = qmodel_class(parent=self)
            qmodel_source = qmodel
            if self._proxyModel is None:  # applies the chain of proxies
                for qmodel_proxy_class in qmodel_proxy_classes:
                    qproxy = qmodel_proxy_class(self)
                    qproxy.setSourceModel(qmodel_source)
                    qmodel_source = qproxy
            else:
                self._proxyModel.setSourceModel(qmodel_source)
                qmodel_source = self._proxyModel
            self.setQModel(qmodel_source)

        # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
        # QMainWindow overwriting
        # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~

        def addToolBar(self, toolbar):
            Qt.QMainWindow.addToolBar(self, toolbar)
            self._toolBars.append(toolbar)

        def insertToolBar(self, before, toolbar):
            if isinstance(before, Qt.QToolBar):
                index = self._toolBars.index(before)
            else:
                index = before
                before = self._toolBars[before]
            Qt.QMainWindow.insertToolBar(self, before, toolbar)
            self._toolBars.insert(index, toolbar)

    class TaurusBaseModelWidget(TaurusBaseWidget):
        """A class:`taurus.qt.qtgui.base.TaurusBaseWidget` that connects to a
        taurus model. It must be used together with
        class:`~taurus.qt.qtgui.base.QBaseModelWidget`
        """

        def __init__(self, designMode=False):  # , perspective=None, proxy=None):
            name = self.__class__.__name__
            self.call__init__(TaurusBaseWidget, name, designMode=designMode)

        # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
        # TaurusBaseWidget overwriting
        # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~

        def setModel(self, m, **kwargs):
            TaurusBaseWidget.setModel(self, m, **kwargs)

            view, modelObj = self.viewWidget(), self.getModelObj(**kwargs)
            model = view.model()
            if model is None:
                return
            model.setDataSource(modelObj)

        # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
        # QT property definition
        # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~

        #: This property holds the unique URI string representing the model name
        #: with which this widget will get its data from. The convention used for
        #: the string can be found :ref:`here <model-concept>`.
        #:
        #: **Access functions:**
        #:
        #:     * :meth:`TaurusBaseWidget.getModel`
        #:     * :meth:`TaurusBaseWidget.setModel`
        #:     * :meth:`TaurusBaseWidget.resetModel`
        #:
        #: .. seealso:: :ref:`model-concept`
        model = Qt.pyqtProperty(
            "QString",
            TaurusBaseWidget.getModel,
            setModel,
            TaurusBaseWidget.resetModel,
        )