text: from cells import events
  from cells.model import TrackTemplateModel
  from cells.observation import Observation
  from PySide2.QtCore import Qt
  from PySide2.QtWidgets import (QFrame, QHBoxLayout, QMessageBox, QScrollArea,
                                 QWidget, QLabel)
  
  from .code import CodeView
  from .dialogs import ConfirmationDialog
  from .theme import ScrollBar, Theme
  from .track import Track
  from .track_editor import TrackEditor
  
  
  class Editor(Observation, QScrollArea):
      def __init__(self, subject, powermode=False):
          Observation.__init__(self, subject)
          QScrollArea.__init__(self)
          self.powermode = powermode
  
          self.setFrameShape(QFrame.NoFrame)
          self.setMinimumSize(200, 300)
  
          self.codeView = CodeView(subject)
          self.trackEditor = TrackEditor(self.subject,
                                         self.powermode,
                                         confirmUpdate=False)
  
          self.selectedTrackIndex = -1
  
          self.innerLayout = QHBoxLayout()
  
          self.innerLayout.setSpacing(0)
          self.innerLayout.setContentsMargins(0, 0, 0, 0)
          self.innerLayout.setAlignment(Qt.AlignLeft)
  
          self.setAttribute(Qt.WA_StyledBackground)
          self.setStyleSheet(Theme.editor.style)
          self.setHorizontalScrollBar(ScrollBar())
          self.setVerticalScrollBar(ScrollBar())
  
          widget = QWidget()
          widget.setLayout(self.innerLayout)
          self.setWidget(widget)
          self.setWidgetResizable(True)
  
          self._initTip()
          self.setTipHidden(False)
  
          self.add_responder(events.document.Open, self.documentOpenResponder)
          self.add_responder(events.view.main.TrackNew, self.trackNewResponder)
          self.add_responder(events.view.browser.TrackNewFromTemplate,
                             self.trackNewFromTemplateResponder)
          self.add_responder(events.view.track.Clicked,
                             self.trackClickedResponder)
          self.add_responder(events.view.main.TrackSelectLeft,
                             self.trackSelectLeftResponder)
          self.add_responder(events.view.main.TrackSelectRight,
                             self.trackSelectRightResponder)
          self.add_responder(events.view.main.TrackMoveLeft,
                             self.trackMoveLeftResponder)
          self.add_responder(events.view.main.TrackMoveRight,
                             self.trackMoveRightResponder)
          self.add_responder(events.view.main.TrackRemove,
                             self.trackRemoveResponder)
          self.add_responder(events.view.main.RowRemove, self.rowRemoveResponder)
          self.add_responder(events.view.track.CellSelected,
                             self.cellSelectedResponder)
          self.add_responder(events.view.main.CellEvaluate,
                             self.cellEvaluateResponder)
          # we evaluate row here instead of in Track to keep left-to-right
          # evaluation order when we move tracks
          self.add_responder(events.view.main.RowEvaluate,
                             self.rowEvaluateResponder)
          self.add_responder(events.view.main.CellClear, self.cellClearResponder)
          self.add_responder(events.view.main.CellEdit, self.cellEditResponder)
          self.add_responder(events.view.main.TrackSetup,
                             self.trackSetupResponder)
          self.add_responder(events.view.main.TrackSaveAsTemplate,
                             self.trackSaveAsTemplateResponder)
          self.add_responder(events.track.TrackTemplateSaved,
                             self.trackTemplateSavedResponder)
          self.add_responder(events.view.main.TrackRestartBackend,
                             self.trackRestartBackendResponder)
          self.add_responder(events.view.main.BackendRestartAll,
                             self.backendRestartAllResponder)
          self.add_responder(events.view.track.BackendRestart,
                             self.trackConfirmationBackendRestartResponder)
  
      def _initTip(self):
          layout = QHBoxLayout()
          self.tip = QLabel("Add Track From Template")
          self.tip.setStyleSheet(Theme.editor.tip.style)
          self.tip.setFont(Theme.editor.tip.font)
          layout.addWidget(self.tip)
          layout.setAlignment(Qt.AlignCenter)
          layout.setSpacing(0)
          layout.setContentsMargins(0, 0, 0, 0)
          self.setLayout(layout)
  
      def setTipHidden(self, hidden):
          if self.tip.isHidden() == hidden:
              return
  
          self.tip.setHidden(hidden)
  
      def documentOpenResponder(self, e):
          self.clear()
  
          for (n, track) in enumerate(e.document.tracks):
              trackView = Track(self, self.subject, n, track.name,
                                track.template)
              self.innerLayout.addWidget(trackView)
              trackView.deserialize(track)
  
          if self.numOfTracks() > 1:
              self.setTipHidden(True)
  
      def trackClickedResponder(self, e):
          self.selectTrackAt(e.index)
  
      def trackSelectLeftResponder(self, e):
          if self.numOfTracks() < 1:
              return
  
          if not self.hasSelectedTrack():
              self.selectTrackAt(self.numOfTracks() - 1)
          else:
              self.selectTrackAt(self.selectedTrackIndex - 1)
  
      def trackSelectRightResponder(self, e):
          if self.numOfTracks() < 1:
              return
  
          if not self.hasSelectedTrack():
              self.selectTrackAt(0)
          else:
              self.selectTrackAt(self.selectedTrackIndex + 1)
  
      def trackNewResponder(self, e):
          self.newTrack(TrackTemplateModel())
  
      def trackNewFromTemplateResponder(self, e):
          self.newTrack(e.template)
  
          if self.numOfTracks() == 1:
              track = self.trackAt(0)
              track.addCell()
  
      def trackMoveLeftResponder(self, e):
          self.moveSelectedTrackTo(self.selectedTrackIndex - 1)
  
      def trackMoveRightResponder(self, e):
          self.moveSelectedTrackTo(self.selectedTrackIndex + 1)
  
      def moveSelectedTrackTo(self, index):
          if self.numOfTracks() < 2 or \
                  not self.hasSelectedTrack() or \
                  not index in range(self.numOfTracks()) or \
                  self.selectedTrackIndex == index:
  
              return
  
          track = self.innerLayout.takeAt(self.selectedTrackIndex)
          self.innerLayout.insertWidget(index, track.widget())
          track.widget().setIndex(index)
  
          previous = self.trackAt(self.selectedTrackIndex)
          previous.setIndex(self.selectedTrackIndex)
  
          self.notify(events.view.track.Move(self.selectedTrackIndex, index))
  
          self.selectTrackAt(index)
  
      def trackRemoveResponder(self, e):
          if not self.hasSelectedTrack():
              return
  
          track = self.trackAt(self.selectedTrackIndex)
          question = f'Do you really want to delete track {track.name()}?'
          confirmation = ConfirmationDialog("Delete Track", question)
  
          if confirmation.exec_() == ConfirmationDialog.No:
              return
  
          self.notify(
              events.view.track.Remove(self.selectedTrackIndex, track.template))
          track.delete()
          self.selectTrackAt(self.selectedTrackIndex - 1)
  
          for n in range(self.selectedTrackIndex + 1, self.numOfTracks()):
              track = self.trackAt(n)
              track.setIndex(track.index - 1)
  
          if self.numOfTracks() < 1:
              self.setTipHidden(False)
  
      def rowRemoveResponder(self, e):
          if not self.hasSelectedTrack():
              return
  
          track = self.trackAt(self.selectedTrackIndex)
  
          if not track.hasSelectedCell() or len(track.cells) < 1:
              return
  
          confirmation = ConfirmationDialog(
              "Delete Row", "Do you really want to delete selected row?")
  
          if confirmation.exec_() == ConfirmationDialog.No:
              return
  
          self.notify(events.view.track.RowRemove(track.selectedCellIndex))
  
      def cellSelectedResponder(self, e):
          self.ensureTrackVisible(self.selectedTrackIndex)
  
      def cellEvaluateResponder(self, e):
          if not self.hasSelectedTrack():
              return
  
          track = self.trackAt(self.selectedTrackIndex)
  
          if not track.hasSelectedCell():
              return
  
          track.cells[track.selectedCellIndex].evaluate()
  
      def rowEvaluateResponder(self, e):
          if not self.hasSelectedTrack():
              return
  
          for index in range(self.numOfTracks()):
              track = self.trackAt(index)
  
              if track.hasSelectedCell():
                  track.cells[track.selectedCellIndex].evaluate()
  
      def cellClearResponder(self, e):
          if not self.hasSelectedTrack():
              return
  
          track = self.trackAt(self.selectedTrackIndex)
  
          if not track.hasSelectedCell():
              return
  
          track.cells[track.selectedCellIndex].clear()
  
      def cellEditResponder(self, e):
          if not self.hasSelectedTrack():
              return
  
          track = self.trackAt(self.selectedTrackIndex)
  
          if not track.hasSelectedCell():
              return
  
          track.cells[track.selectedCellIndex].edit()
  
      def trackSetupResponder(self, e):
          if not self.hasSelectedTrack():
              return
  
          track = self.trackAt(self.selectedTrackIndex)
          track.edit()
  
      def trackSaveAsTemplateResponder(self, e):
          if not self.hasSelectedTrack():
              return
  
          self.trackAt(self.selectedTrackIndex).saveAsTemplate()
  
      def trackTemplateSavedResponder(self, e):
          msgBox = QMessageBox()
          msgBox.setText("Track Template Saved Succesfully")
          msgBox.setDetailedText(repr(e.template))
          msgBox.exec()
  
      def trackRestartBackendResponder(self, e):
          if not self.hasSelectedTrack():
              return
  
          track = self.trackAt(self.selectedTrackIndex)
          self.restartBackendForTrack(track)
  
      def backendRestartAllResponder(self, e):
          if self.numOfTracks() < 1:
              return
  
          templates = [
              self.trackAt(n).template for n in range(self.numOfTracks())
          ]
          self.notify(events.view.editor.BackendRestartAll(templates))
  
      def trackConfirmationBackendRestartResponder(self, e):
          self.restartBackendForTrack(self.trackAt(e.track_index))
  
      def restartBackendForTrack(self, track):
          backendName = track.template.backend_name
          templates = []
  
          for n in range(self.numOfTracks()):
              track = self.trackAt(n)
  
              if track.template.backend_name == backendName:
                  templates.append(track.template)
  
          self.notify(events.view.editor.TrackRestartBackend(templates))
  
      def newTrack(self, template):
          self.setTipHidden(True)
  
          length = self.innerLayout.count()
          name = "Track " + str(length + 1)
          track = Track(self, self.subject, length, name, template)
          self.innerLayout.addWidget(track)
          self.notify(events.view.track.New(name, template))
  
          if self.numOfTracks() > 1:
              firstTrack = self.trackAt(0)
  
              for _ in firstTrack.cells:
                  track.addCell()
              track.selectCellAt(firstTrack.selectedCellIndex)
  
              if not firstTrack.isPasteBufferEmpty():
                  track.fillPasteBuffer()
  
  
      def selectTrackAt(self, index):
          if self.selectedTrackIndex == index:
              return
  
          if self.hasSelectedTrack():
              track = self.trackAt(self.selectedTrackIndex)
              track.setSelected(False)
  
          if index in range(self.numOfTracks()):
              track = self.trackAt(index)
              track.setSelected(True)
              self.ensureTrackVisible(index)
  
          self.selectedTrackIndex = min(max(-1, index), self.numOfTracks())
          self.notify(events.view.track.Select(self.selectedTrackIndex))
  
      def ensureTrackVisible(self, index):
          if index not in range(self.numOfTracks()):
              return
  
          track = self.trackAt(index)
  
          if track.selectedCellIndex in range(len(track.cells)):
              cell = track.cells[track.selectedCellIndex]
              self.ensureWidgetVisible(cell, track.header.width(),
                                       track.header.height())
          else:
              self.ensureWidgetVisible(track, track.header.width(),
                                       track.header.height())
  
      def hasSelectedTrack(self):
          return self.selectedTrackIndex in range(self.numOfTracks())
  
      def numOfTracks(self):
          return self.innerLayout.count()
  
      def clear(self):
          for i in reversed(range(self.numOfTracks())):
              self.trackAt(i).setParent(None)
  
      def trackAt(self, index):
          return self.innerLayout.itemAt(index).widget()
  
      def closeEvent(self, e):
          self.codeView.delete()
          self.trackEditor.delete()
          self.unregister()
  
          return super().closeEvent(e)
  