class fullScreenEditor(QWidget):
    def __init__(self, index, parent=None):
        QWidget.__init__(self, parent)
        self._background = None
        self._index = index
        self._theme = findThemePath(settings.fullScreenTheme)
        self._themeDatas = loadThemeDatas(self._theme)
        self.setMouseTracking(True)
        self._geometries = {}

        # Text editor
        self.editor = MDEditView(self,
                                index=index,
                                spellcheck=settings.spellcheck,
                                highlighting=True,
                                dict=settings.dict)
        self.editor.setFrameStyle(QFrame.NoFrame)
        self.editor.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.editor.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.editor.installEventFilter(self)
        self.editor.setMouseTracking(True)
        self.editor.setVerticalScrollBar(myScrollBar())
        self.scrollBar = self.editor.verticalScrollBar()
        self.scrollBar.setParent(self)

        # Top Panel
        self.topPanel = myPanel(parent=self)
        # self.topPanel.layout().addStretch(1)

        # Spell checking
        if enchant:
            self.btnSpellCheck = QPushButton(self)
            self.btnSpellCheck.setFlat(True)
            self.btnSpellCheck.setIcon(QIcon.fromTheme("tools-check-spelling"))
            self.btnSpellCheck.setCheckable(True)
            self.btnSpellCheck.setChecked(self.editor.spellcheck)
            self.btnSpellCheck.toggled.connect(self.editor.toggleSpellcheck)
        else:
            self.btnSpellCheck = None

        # Navigation Buttons
        self.btnPrevious = QPushButton(self)
        self.btnPrevious.setFlat(True)
        self.btnPrevious.setIcon(QIcon.fromTheme("arrow-left"))
        self.btnPrevious.clicked.connect(self.switchPreviousItem)
        self.btnNext = QPushButton(self)
        self.btnNext.setFlat(True)
        self.btnNext.setIcon(QIcon.fromTheme("arrow-right"))
        self.btnNext.clicked.connect(self.switchNextItem)
        self.btnNew = QPushButton(self)
        self.btnNew.setFlat(True)
        self.btnNew.setIcon(QIcon.fromTheme("document-new"))
        self.btnNew.clicked.connect(self.createNewText)

        # Path and New Text Buttons
        self.wPath = myPath(self)

        # Close
        self.btnClose = QPushButton(self)
        self.btnClose.setIcon(qApp.style().standardIcon(QStyle.SP_DialogCloseButton))
        self.btnClose.clicked.connect(self.close)
        self.btnClose.setFlat(True)

        # Top panel Layout
        if self.btnSpellCheck:
            self.topPanel.layout().addWidget(self.btnSpellCheck)
        self.topPanel.layout().addSpacing(15)
        self.topPanel.layout().addWidget(self.btnPrevious)
        self.topPanel.layout().addWidget(self.btnNext)
        self.topPanel.layout().addWidget(self.btnNew)

        self.topPanel.layout().addStretch(1)
        self.topPanel.layout().addWidget(self.wPath)
        self.topPanel.layout().addStretch(1)

        self.topPanel.layout().addWidget(self.btnClose)
        self.updateTopBar()

        # Left Panel
        self._locked = False
        self.leftPanel = myPanel(vertical=True, parent=self)
        self.locker = locker(self)
        self.locker.lockChanged.connect(self.setLocked)
        self.leftPanel.layout().addWidget(self.locker)

        # Bottom Panel
        self.bottomPanel = myPanel(parent=self)

        self.bottomPanel.layout().addSpacing(24)
        self.lstThemes = QComboBox(self)
        self.lstThemes.setAttribute(Qt.WA_TranslucentBackground)
        paths = allPaths("resources/themes")
        for p in paths:
            lst = [i for i in os.listdir(p) if os.path.splitext(i)[1] == ".theme"]
            for t in lst:
                themeIni = os.path.join(p, t)
                name = loadThemeDatas(themeIni)["Name"]
                # self.lstThemes.addItem(os.path.splitext(t)[0])
                self.lstThemes.addItem(name)
                self.lstThemes.setItemData(self.lstThemes.count()-1, os.path.splitext(t)[0])

        self.lstThemes.setCurrentIndex(self.lstThemes.findData(settings.fullScreenTheme))
        # self.lstThemes.setCurrentText(settings.fullScreenTheme)
        self.lstThemes.currentTextChanged.connect(self.setTheme)
        self.lstThemes.setMaximumSize(QSize(300, QFontMetrics(qApp.font()).height()))
        themeLabel = QLabel(self.tr("Theme:"), self)
        self.bottomPanel.layout().addWidget(themeLabel)
        self.bottomPanel.layout().addWidget(self.lstThemes)
        self.bottomPanel.layout().addStretch(1)

        self.lblProgress = QLabel(self)
        self.lblProgress.setMaximumSize(QSize(200, 14))
        self.lblProgress.setMinimumSize(QSize(100, 14))
        self.lblWC = QLabel(self)
        self.lblClock = myClockLabel(self)
        self.bottomPanel.layout().addWidget(self.lblWC)
        self.bottomPanel.layout().addWidget(self.lblProgress)
        self.bottomPanel.layout().addSpacing(15)
        self.bottomPanel.layout().addWidget(self.lblClock)
        self.updateStatusBar()

        self.bottomPanel.layout().addSpacing(24)

        # Add Widget Settings
        if self.btnSpellCheck:
            self.topPanel.addWidgetSetting(self.tr("Spellcheck"), 'top-spellcheck', (self.btnSpellCheck, ))
        self.topPanel.addWidgetSetting(self.tr("Navigation"), 'top-navigation', (self.btnPrevious, self.btnNext))
        self.topPanel.addWidgetSetting(self.tr("New Text"), 'top-new-doc', (self.btnNew, ))
        self.topPanel.addWidgetSetting(self.tr("Title"), 'top-title', (self.wPath, ))
        self.topPanel.addSetting(self.tr("Title: Show Full Path"), 'title-show-full-path', True)
        self.topPanel.setSettingCallback('title-show-full-path', lambda var, val: self.updateTopBar())
        self.bottomPanel.addWidgetSetting(self.tr("Theme selector"), 'bottom-theme', (self.lstThemes, themeLabel))
        self.bottomPanel.addWidgetSetting(self.tr("Word count"), 'bottom-wc', (self.lblWC, ))
        self.bottomPanel.addWidgetSetting(self.tr("Progress"), 'bottom-progress', (self.lblProgress, ))
        self.bottomPanel.addSetting(self.tr("Progress: Auto Show/Hide"), 'progress-auto-show', True)
        self.bottomPanel.addWidgetSetting(self.tr("Clock"), 'bottom-clock', (self.lblClock, ))
        self.bottomPanel.addSetting(self.tr("Clock: Show Seconds"), 'clock-show-seconds', True)
        self.bottomPanel.setAutoHideVariable('autohide-bottom')
        self.topPanel.setAutoHideVariable('autohide-top')
        self.leftPanel.setAutoHideVariable('autohide-left')

        # Connection
        self._index.model().dataChanged.connect(self.dataChanged)

        # self.updateTheme()
        self.showFullScreen()
        # self.showMaximized()
        # self.show()

    def __del__(self):
        # print("Leaving fullScreenEditor via Destructor event", flush=True)
        self.showNormal()
        self.close()

    def setLocked(self, val):
        self._locked = val
        self.btnClose.setVisible(not val)

    def setTheme(self, themeName):
        themeName = self.lstThemes.currentData()
        settings.fullScreenTheme = themeName
        self._theme = findThemePath(themeName)
        self._themeDatas = loadThemeDatas(self._theme)
        self.updateTheme()

    def updateTheme(self):
        # Reinit stored geometries for hiding widgets
        self._geometries = {}
        rect = self.geometry()
        self._background = generateTheme(self._themeDatas, rect)

        setThemeEditorDatas(self.editor, self._themeDatas, self._background, rect)

        # Colors
        if self._themeDatas["Foreground/Color"] == self._themeDatas["Background/Color"] or \
                        self._themeDatas["Foreground/Opacity"] < 5:
            self._fgcolor = QColor(self._themeDatas["Text/Color"])
            self._bgcolor = QColor(self._themeDatas["Background/Color"])
        else:
            self._bgcolor = QColor(self._themeDatas["Foreground/Color"])
            self._bgcolor.setAlpha(self._themeDatas["Foreground/Opacity"] * 255 / 100)
            self._fgcolor = QColor(self._themeDatas["Text/Color"])
            if self._themeDatas["Text/Color"] == self._themeDatas["Foreground/Color"]:
                self._fgcolor = QColor(self._themeDatas["Background/Color"])

        # ScrollBar
        r = self.editor.geometry()
        w = qApp.style().pixelMetric(QStyle.PM_ScrollBarExtent)
        r.setWidth(w)
        r.moveRight(rect.right() - rect.left())
        self.scrollBar.setGeometry(r)
        # self.scrollBar.setVisible(False)
        self.hideWidget(self.scrollBar)
        p = self.scrollBar.palette()
        b = QBrush(self._background.copy(self.scrollBar.geometry()))
        p.setBrush(QPalette.Base, b)
        self.scrollBar.setPalette(p)

        self.scrollBar.setColor(self._bgcolor)

        # Left Panel
        r = self.locker.geometry()
        r.moveTopLeft(QPoint(
                0,
                self.geometry().height() / 2 - r.height() / 2
        ))
        self.leftPanel.setGeometry(r)
        self.hideWidget(self.leftPanel)
        self.leftPanel.setColor(self._bgcolor)

        # Top / Bottom Panels
        r = QRect(0, 0, 0, 24)
        r.setWidth(rect.width())
        # r.moveLeft(rect.center().x() - r.width() / 2)
        self.topPanel.setGeometry(r)
        # self.topPanel.setVisible(False)
        self.hideWidget(self.topPanel)
        r.moveBottom(rect.bottom() - rect.top())
        self.bottomPanel.setGeometry(r)
        # self.bottomPanel.setVisible(False)
        self.hideWidget(self.bottomPanel)
        self.topPanel.setColor(self._bgcolor)
        self.bottomPanel.setColor(self._bgcolor)

        # Lst theme
        # p = self.lstThemes.palette()
        p = self.palette()
        p.setBrush(QPalette.Button, self._bgcolor)
        p.setBrush(QPalette.ButtonText, self._fgcolor)
        p.setBrush(QPalette.WindowText, self._fgcolor)

        for panel in (self.bottomPanel, self.topPanel, self.leftPanel):
            for i in range(panel.layout().count()):
                item = panel.layout().itemAt(i)
                if item.widget():
                    item.widget().setPalette(p)
        # self.lstThemes.setPalette(p)
        # self.lblWC.setPalette(p)

        self.update()
        self.editor.centerCursor()

    def paintEvent(self, event):
        if self._background:
            painter = QPainter(self)
            painter.setClipRegion(event.region())
            painter.drawPixmap(event.rect(), self._background, event.rect())
            painter.end()

    def resizeEvent(self, event):
        self.updateTheme()

    def keyPressEvent(self, event):
        if event.key() in [Qt.Key_Escape, Qt.Key_F11] and \
                not self._locked:
            # print("Leaving fullScreenEditor via keyPressEvent", flush=True)
            self.showNormal()
            self.close()
        elif (event.modifiers() & Qt.AltModifier) and \
                event.key() in [Qt.Key_PageUp, Qt.Key_PageDown, Qt.Key_Left, Qt.Key_Right]:
            if event.key() in [Qt.Key_PageUp, Qt.Key_Left]:
                success = self.switchPreviousItem()
            if event.key() in [Qt.Key_PageDown, Qt.Key_Right]:
                success = self.switchNextItem()
            if not success:
                QWidget.keyPressEvent(self, event)
        else:
            QWidget.keyPressEvent(self, event)

    def mouseMoveEvent(self, event):
        r = self.geometry()

        for w in [self.scrollBar, self.topPanel,
                  self.bottomPanel, self.leftPanel]:
            # w.setVisible(w.geometry().contains(event.pos()))
            if self._geometries[w].contains(event.pos()):
                self.showWidget(w)
            else:
                self.hideWidget(w)

    def hideWidget(self, widget):
        if widget not in self._geometries:
            self._geometries[widget] = widget.geometry()

        if hasattr(widget, "_autoHide") and not widget._autoHide:
            return

        # Hides widget in the bottom right corner
        widget.move(self.geometry().bottomRight() + QPoint(1, 1))

    def showWidget(self, widget):
        if widget in self._geometries:
            widget.move(self._geometries[widget].topLeft())

    def eventFilter(self, obj, event):
        if obj == self.editor and event.type() == QEvent.Enter:
            for w in [self.scrollBar, self.topPanel,
                      self.bottomPanel, self.leftPanel]:
                # w.setVisible(False)
                self.hideWidget(w)
        return QWidget.eventFilter(self, obj, event)

    def dataChanged(self, topLeft, bottomRight):
        # This is called sometimes after self has been destroyed. Don't know why.
        if not self or not self._index:
            return
        if topLeft.row() <= self._index.row() <= bottomRight.row():
            self.updateStatusBar()

    def updateTopBar(self):
        item = self._index.internalPointer()
        previousItem = self.previousTextItem(item)
        nextItem = self.nextTextItem(item)
        self.btnPrevious.setEnabled(previousItem is not None)
        self.btnNext.setEnabled(nextItem is not None)
        self.wPath.setItem(item)

    def updateStatusBar(self):
        if self._index:
            item = self._index.internalPointer()

        wc = item.data(Outline.wordCount)
        goal = item.data(Outline.goal)
        pg = item.data(Outline.goalPercentage)

        if goal:
            if settings.fullscreenSettings.get("progress-auto-show", True):
                self.lblProgress.show()
            self.lblWC.setText(self.tr("{} words / {}").format(wc, goal))
        else:
            if settings.fullscreenSettings.get("progress-auto-show", True):
                self.lblProgress.hide()
            self.lblWC.setText(self.tr("{} words").format(wc))
            pg = 0
        rect = self.lblProgress.geometry()
        rect = QRect(QPoint(0, 0), rect.size())
        self.px = QPixmap(rect.size())
        self.px.fill(Qt.transparent)
        p = QPainter(self.px)
        drawProgress(p, rect, pg, 2)
        p.end()
        self.lblProgress.setPixmap(self.px)

        self.locker.setWordCount(wc)
        # If there's a goal, then we update the locker target's number of word accordingly
        # (also if there is a word count, we deduce it.
        if goal and not self.locker.isLocked():
            if wc and goal - wc > 0:
                self.locker.spnWordTarget.setValue(goal - wc)
            elif not wc:
                self.locker.spnWordTarget.setValue(goal)

    def setCurrentModelIndex(self, index):
        self._index = index
        self.editor.setCurrentModelIndex(index)
        self.updateTopBar()
        self.updateStatusBar()

    def switchPreviousItem(self):
        item = self._index.internalPointer()
        previousItem = self.previousTextItem(item)
        if previousItem:
            self.setCurrentModelIndex(previousItem.index())
            return True
        return False

    def switchNextItem(self):
        item = self._index.internalPointer()
        nextItem = self.nextTextItem(item)
        if nextItem:
            self.setCurrentModelIndex(nextItem.index())
            return True
        return False

    def switchToItem(self, item):
        item = self.firstTextItem(item)
        if item:
            self.setCurrentModelIndex(item.index())
        
    def createNewText(self):
        item = self._index.internalPointer()
        newItem = outlineItem(title=qApp.translate("outlineBasics", "New"), _type=settings.defaultTextType)
        self._index.model().insertItem(newItem, item.row() + 1, item.parent().index())
        self.setCurrentModelIndex(newItem.index())

    def previousModelItem(self, item):
        parent = item.parent()
        if not parent:
            # Root has no sibling
            return None

        row = parent.childItems.index(item)
        if row > 0:
            return parent.child(row - 1)
        return self.previousModelItem(parent)

    def nextModelItem(self, item):
        parent = item.parent()
        if not parent:
            # Root has no sibling
            return None

        row = parent.childItems.index(item)
        if row + 1 < parent.childCount():
            return parent.child(row + 1)
        return self.nextModelItem(parent)

    def previousTextItem(self, item):
        previous = self.previousModelItem(item)

        while previous:
            last = self.lastTextItem(previous)
            if last:
                return last
            previous = self.previousModelItem(previous)
        return None
        
    def nextTextItem(self, item):
        if item.isFolder() and item.childCount() > 0:
            next = item.child(0)
        else:
            next = self.nextModelItem(item)

        while next:
            first = self.firstTextItem(next)
            if first:
                return first
            next = self.nextModelItem(next)
        return None

    def firstTextItem(self, item):
        if item.isText():
            return item
        for child in item.children():
            first = self.firstTextItem(child)
            if first:
                return first
        return None

    def lastTextItem(self, item):
        if item.isText():
            return item
        for child in reversed(item.children()):
            last = self.lastTextItem(child)
            if last:
                return last
        return None
class fullScreenEditor(QWidget):
    exited = pyqtSignal()

    def __init__(self, index, parent=None, screenNumber=None):
        QWidget.__init__(self, parent)
        self.setAttribute(Qt.WA_DeleteOnClose, True)
        self._background = None
        self._index = index
        self._theme = findThemePath(settings.fullScreenTheme)
        self._themeDatas = loadThemeDatas(self._theme)
        self.setMouseTracking(True)
        self._geometries = {}

        # Text editor
        self.editor = MDEditView(self,
                                 index=index,
                                 spellcheck=settings.spellcheck,
                                 highlighting=True,
                                 dict=settings.dict)
        self.editor.setFrameStyle(QFrame.NoFrame)
        self.editor.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.editor.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.editor.installEventFilter(self)
        self.editor.setMouseTracking(True)
        self.editor.setVerticalScrollBar(myScrollBar())
        self.scrollBar = self.editor.verticalScrollBar()
        self.scrollBar.setParent(self)

        # Top Panel
        self.topPanel = myPanel(parent=self)
        # self.topPanel.layout().addStretch(1)

        # Spell checking
        if Spellchecker.isInstalled():
            self.btnSpellCheck = QPushButton(self)
            self.btnSpellCheck.setFlat(True)
            self.btnSpellCheck.setIcon(QIcon.fromTheme("tools-check-spelling"))
            self.btnSpellCheck.setCheckable(True)
            self.btnSpellCheck.setChecked(self.editor.spellcheck)
            self.btnSpellCheck.toggled.connect(self.editor.toggleSpellcheck)
        else:
            self.btnSpellCheck = None

        # Navigation Buttons
        self.btnPrevious = QPushButton(self)
        self.btnPrevious.setFlat(True)
        self.btnPrevious.setIcon(QIcon.fromTheme("arrow-left"))
        self.btnPrevious.clicked.connect(self.switchPreviousItem)
        self.btnNext = QPushButton(self)
        self.btnNext.setFlat(True)
        self.btnNext.setIcon(QIcon.fromTheme("arrow-right"))
        self.btnNext.clicked.connect(self.switchNextItem)
        self.btnNew = QPushButton(self)
        self.btnNew.setFlat(True)
        self.btnNew.setIcon(QIcon.fromTheme("document-new"))
        self.btnNew.clicked.connect(self.createNewText)

        # Path and New Text Buttons
        self.wPath = myPath(self)

        # Close
        self.btnClose = QPushButton(self)
        self.btnClose.setIcon(qApp.style().standardIcon(
            QStyle.SP_DialogCloseButton))
        self.btnClose.clicked.connect(self.leaveFullscreen)
        self.btnClose.setFlat(True)

        # Top panel Layout
        if self.btnSpellCheck:
            self.topPanel.layout().addWidget(self.btnSpellCheck)
        self.topPanel.layout().addSpacing(15)
        self.topPanel.layout().addWidget(self.btnPrevious)
        self.topPanel.layout().addWidget(self.btnNext)
        self.topPanel.layout().addWidget(self.btnNew)

        self.topPanel.layout().addStretch(1)
        self.topPanel.layout().addWidget(self.wPath)
        self.topPanel.layout().addStretch(1)

        self.topPanel.layout().addWidget(self.btnClose)
        self.updateTopBar()

        # Left Panel
        self._locked = False
        self.leftPanel = myPanel(vertical=True, parent=self)
        self.locker = locker(self)
        self.locker.lockChanged.connect(self.setLocked)
        self.leftPanel.layout().addWidget(self.locker)

        # Bottom Panel
        self.bottomPanel = myPanel(parent=self)

        self.bottomPanel.layout().addSpacing(24)
        self.lstThemes = QComboBox(self)
        self.lstThemes.setAttribute(Qt.WA_TranslucentBackground)
        paths = allPaths("resources/themes")
        for p in paths:
            lst = [
                i for i in os.listdir(p) if os.path.splitext(i)[1] == ".theme"
            ]
            for t in lst:
                themeIni = os.path.join(p, t)
                name = loadThemeDatas(themeIni)["Name"]
                # self.lstThemes.addItem(os.path.splitext(t)[0])
                self.lstThemes.addItem(name)
                self.lstThemes.setItemData(self.lstThemes.count() - 1,
                                           os.path.splitext(t)[0])

        self.lstThemes.setCurrentIndex(
            self.lstThemes.findData(settings.fullScreenTheme))
        # self.lstThemes.setCurrentText(settings.fullScreenTheme)
        self.lstThemes.currentTextChanged.connect(self.setTheme)
        self.lstThemes.setMaximumSize(
            QSize(300,
                  QFontMetrics(qApp.font()).height()))
        themeLabel = QLabel(self.tr("Theme:"), self)
        self.bottomPanel.layout().addWidget(themeLabel)
        self.bottomPanel.layout().addWidget(self.lstThemes)
        self.bottomPanel.layout().addStretch(1)

        self.lblProgress = QLabel(self)
        self.lblProgress.setMaximumSize(QSize(200, 14))
        self.lblProgress.setMinimumSize(QSize(100, 14))
        self.lblWC = QLabel(self)
        self.lblClock = myClockLabel(self)
        self.bottomPanel.layout().addWidget(self.lblWC)
        self.bottomPanel.layout().addWidget(self.lblProgress)
        self.bottomPanel.layout().addSpacing(15)
        self.bottomPanel.layout().addWidget(self.lblClock)
        self.updateStatusBar()

        self.bottomPanel.layout().addSpacing(24)

        # Add Widget Settings
        if self.btnSpellCheck:
            self.topPanel.addWidgetSetting(self.tr("Spellcheck"),
                                           'top-spellcheck',
                                           (self.btnSpellCheck, ))
        self.topPanel.addWidgetSetting(self.tr("Navigation"), 'top-navigation',
                                       (self.btnPrevious, self.btnNext))
        self.topPanel.addWidgetSetting(self.tr("New Text"), 'top-new-doc',
                                       (self.btnNew, ))
        self.topPanel.addWidgetSetting(self.tr("Title"), 'top-title',
                                       (self.wPath, ))
        self.topPanel.addSetting(self.tr("Title: Show Full Path"),
                                 'title-show-full-path', True)
        self.topPanel.setSettingCallback('title-show-full-path',
                                         lambda var, val: self.updateTopBar())
        self.bottomPanel.addWidgetSetting(self.tr("Theme selector"),
                                          'bottom-theme',
                                          (self.lstThemes, themeLabel))
        self.bottomPanel.addWidgetSetting(self.tr("Word count"), 'bottom-wc',
                                          (self.lblWC, ))
        self.bottomPanel.addWidgetSetting(self.tr("Progress"),
                                          'bottom-progress',
                                          (self.lblProgress, ))
        self.bottomPanel.addSetting(self.tr("Progress: Auto Show/Hide"),
                                    'progress-auto-show', True)
        self.bottomPanel.addWidgetSetting(self.tr("Clock"), 'bottom-clock',
                                          (self.lblClock, ))
        self.bottomPanel.addSetting(self.tr("Clock: Show Seconds"),
                                    'clock-show-seconds', True)
        self.bottomPanel.setAutoHideVariable('autohide-bottom')
        self.topPanel.setAutoHideVariable('autohide-top')
        self.leftPanel.setAutoHideVariable('autohide-left')

        # Set the screen to the same screen as the main window
        if screenNumber is not None:
            screenres = QDesktopWidget().screenGeometry(screenNumber)
            self.move(QPoint(screenres.x(), screenres.y()))
            self.resize(screenres.width(), screenres.height())

        # Connection
        self._index.model().dataChanged.connect(self.dataChanged)

        # self.updateTheme()
        self.showFullScreen()
        # self.showMaximized()
        # self.show()

    def leaveFullscreen(self):
        self.__exit__("Leaving fullScreenEditor via leaveFullScreen.")

    def __exit__(self, message):
        LOGGER.debug(message)
        self.showNormal()
        self.exited.emit()
        self.close()

    def setLocked(self, val):
        self._locked = val
        self.btnClose.setVisible(not val)

    def setTheme(self, themeName):
        themeName = self.lstThemes.currentData()
        settings.fullScreenTheme = themeName
        self._theme = findThemePath(themeName)
        self._themeDatas = loadThemeDatas(self._theme)
        self.updateTheme()

    def updateTheme(self):
        # Reinit stored geometries for hiding widgets
        self._geometries = {}
        rect = self.geometry()
        self._background = generateTheme(self._themeDatas, rect)

        setThemeEditorDatas(self.editor, self._themeDatas, self._background,
                            rect)

        # Colors
        if self._themeDatas["Foreground/Color"] == self._themeDatas["Background/Color"] or \
                        self._themeDatas["Foreground/Opacity"] < 5:
            self._fgcolor = QColor(self._themeDatas["Text/Color"])
            self._bgcolor = QColor(self._themeDatas["Background/Color"])
        else:
            self._bgcolor = QColor(self._themeDatas["Foreground/Color"])
            self._bgcolor.setAlpha(self._themeDatas["Foreground/Opacity"] *
                                   255 / 100)
            self._fgcolor = QColor(self._themeDatas["Text/Color"])
            if self._themeDatas["Text/Color"] == self._themeDatas[
                    "Foreground/Color"]:
                self._fgcolor = QColor(self._themeDatas["Background/Color"])

        # ScrollBar
        r = self.editor.geometry()
        w = qApp.style().pixelMetric(QStyle.PM_ScrollBarExtent)
        r.setWidth(w)
        r.moveRight(rect.right() - rect.left())
        self.scrollBar.setGeometry(r)
        # self.scrollBar.setVisible(False)
        self.hideWidget(self.scrollBar)
        p = self.scrollBar.palette()
        b = QBrush(self._background.copy(self.scrollBar.geometry()))
        p.setBrush(QPalette.Base, b)
        self.scrollBar.setPalette(p)

        self.scrollBar.setColor(self._bgcolor)

        # Left Panel
        r = self.locker.geometry()
        r.moveTopLeft(QPoint(0, self.geometry().height() / 2 - r.height() / 2))
        self.leftPanel.setGeometry(r)
        self.hideWidget(self.leftPanel)
        self.leftPanel.setColor(self._bgcolor)

        # Top / Bottom Panels
        r = QRect(0, 0, 0, 24)
        r.setWidth(rect.width())
        # r.moveLeft(rect.center().x() - r.width() / 2)
        self.topPanel.setGeometry(r)
        # self.topPanel.setVisible(False)
        self.hideWidget(self.topPanel)
        r.moveBottom(rect.bottom() - rect.top())
        self.bottomPanel.setGeometry(r)
        # self.bottomPanel.setVisible(False)
        self.hideWidget(self.bottomPanel)
        self.topPanel.setColor(self._bgcolor)
        self.bottomPanel.setColor(self._bgcolor)

        # Lst theme
        # p = self.lstThemes.palette()
        p = self.palette()
        p.setBrush(QPalette.Button, self._bgcolor)
        p.setBrush(QPalette.ButtonText, self._fgcolor)
        p.setBrush(QPalette.WindowText, self._fgcolor)

        for panel in (self.bottomPanel, self.topPanel, self.leftPanel):
            for i in range(panel.layout().count()):
                item = panel.layout().itemAt(i)
                if item.widget():
                    item.widget().setPalette(p)
        # self.lstThemes.setPalette(p)
        # self.lblWC.setPalette(p)

        self.update()
        self.editor.centerCursor()

    def paintEvent(self, event):
        if self._background:
            painter = QPainter(self)
            painter.setClipRegion(event.region())
            painter.drawPixmap(event.rect(), self._background, event.rect())
            painter.end()

    def resizeEvent(self, event):
        self.updateTheme()

    def keyPressEvent(self, event):
        if event.key() in [Qt.Key_Escape, Qt.Key_F11] and \
                not self._locked:
            self.__exit__("Leaving fullScreenEditor via keyPressEvent.")
        elif (event.modifiers() & Qt.AltModifier) and \
                event.key() in [Qt.Key_PageUp, Qt.Key_PageDown, Qt.Key_Left, Qt.Key_Right]:
            if event.key() in [Qt.Key_PageUp, Qt.Key_Left]:
                success = self.switchPreviousItem()
            if event.key() in [Qt.Key_PageDown, Qt.Key_Right]:
                success = self.switchNextItem()
            if not success:
                QWidget.keyPressEvent(self, event)
        else:
            QWidget.keyPressEvent(self, event)

    def mouseMoveEvent(self, event):
        r = self.geometry()

        for w in [
                self.scrollBar, self.topPanel, self.bottomPanel, self.leftPanel
        ]:
            # w.setVisible(w.geometry().contains(event.pos()))
            if self._geometries[w].contains(event.pos()):
                self.showWidget(w)
            else:
                self.hideWidget(w)

    def hideWidget(self, widget):
        if widget not in self._geometries:
            self._geometries[widget] = widget.geometry()

        if hasattr(widget, "_autoHide") and not widget._autoHide:
            return

        # Hides widget in the bottom right corner
        widget.move(self.geometry().bottomRight() + QPoint(1, 1))

    def showWidget(self, widget):
        if widget in self._geometries:
            widget.move(self._geometries[widget].topLeft())

    def eventFilter(self, obj, event):
        if obj == self.editor and event.type() == QEvent.Enter:
            for w in [
                    self.scrollBar, self.topPanel, self.bottomPanel,
                    self.leftPanel
            ]:
                # w.setVisible(False)
                self.hideWidget(w)
        return QWidget.eventFilter(self, obj, event)

    def dataChanged(self, topLeft, bottomRight):
        # This is called sometimes after self has been destroyed. Don't know why.
        if not self or not self._index:
            return
        if topLeft.row() <= self._index.row() <= bottomRight.row():
            self.updateStatusBar()

    def updateTopBar(self):
        item = self._index.internalPointer()
        previousItem = self.previousTextItem(item)
        nextItem = self.nextTextItem(item)
        self.btnPrevious.setEnabled(previousItem != None)
        self.btnNext.setEnabled(nextItem != None)
        self.wPath.setItem(item)

    def updateStatusBar(self):
        if self._index:
            item = self._index.internalPointer()

        wc = item.data(Outline.wordCount)
        goal = item.data(Outline.goal)
        pg = item.data(Outline.goalPercentage)

        if goal:
            if settings.fullscreenSettings.get("progress-auto-show", True):
                self.lblProgress.show()
            self.lblWC.setText(self.tr("{} words / {}").format(wc, goal))
        else:
            if settings.fullscreenSettings.get("progress-auto-show", True):
                self.lblProgress.hide()
            self.lblWC.setText(self.tr("{} words").format(wc))
            pg = 0
        rect = self.lblProgress.geometry()
        rect = QRect(QPoint(0, 0), rect.size())
        self.px = QPixmap(rect.size())
        self.px.fill(Qt.transparent)
        p = QPainter(self.px)
        drawProgress(p, rect, pg, 2)
        p.end()
        self.lblProgress.setPixmap(self.px)

        self.locker.setWordCount(wc)
        # If there's a goal, then we update the locker target's number of word accordingly
        # (also if there is a word count, we deduce it.
        if goal and not self.locker.isLocked():
            if wc and goal - wc > 0:
                self.locker.spnWordTarget.setValue(goal - wc)
            elif not wc:
                self.locker.spnWordTarget.setValue(goal)

    def setCurrentModelIndex(self, index):
        self._index = index
        self.editor.setCurrentModelIndex(index)
        self.updateTopBar()
        self.updateStatusBar()

    def switchPreviousItem(self):
        item = self._index.internalPointer()
        previousItem = self.previousTextItem(item)
        if previousItem:
            self.setCurrentModelIndex(previousItem.index())
            return True
        return False

    def switchNextItem(self):
        item = self._index.internalPointer()
        nextItem = self.nextTextItem(item)
        if nextItem:
            self.setCurrentModelIndex(nextItem.index())
            return True
        return False

    def switchToItem(self, item):
        item = self.firstTextItem(item)
        if item:
            self.setCurrentModelIndex(item.index())

    def createNewText(self):
        item = self._index.internalPointer()
        newItem = outlineItem(title=qApp.translate("outlineBasics", "New"),
                              _type=settings.defaultTextType)
        self._index.model().insertItem(newItem,
                                       item.row() + 1,
                                       item.parent().index())
        self.setCurrentModelIndex(newItem.index())

    def previousModelItem(self, item):
        parent = item.parent()
        if not parent:
            # Root has no sibling
            return None

        row = parent.childItems.index(item)
        if row > 0:
            return parent.child(row - 1)
        return self.previousModelItem(parent)

    def nextModelItem(self, item):
        parent = item.parent()
        if not parent:
            # Root has no sibling
            return None

        row = parent.childItems.index(item)
        if row + 1 < parent.childCount():
            return parent.child(row + 1)
        return self.nextModelItem(parent)

    def previousTextItem(self, item):
        previous = self.previousModelItem(item)

        while previous:
            last = self.lastTextItem(previous)
            if last:
                return last
            previous = self.previousModelItem(previous)
        return None

    def nextTextItem(self, item):
        if item.isFolder() and item.childCount() > 0:
            next = item.child(0)
        else:
            next = self.nextModelItem(item)

        while next:
            first = self.firstTextItem(next)
            if first:
                return first
            next = self.nextModelItem(next)
        return None

    def firstTextItem(self, item):
        if item.isText():
            return item
        for child in item.children():
            first = self.firstTextItem(child)
            if first:
                return first
        return None

    def lastTextItem(self, item):
        if item.isText():
            return item
        for child in reversed(item.children()):
            last = self.lastTextItem(child)
            if last:
                return last
        return None
Exemple #3
0
class fullScreenEditor(QWidget):
    def __init__(self, index, parent=None):
        QWidget.__init__(self, parent)
        self._background = None
        self._index = index
        self._theme = findThemePath(settings.fullScreenTheme)
        self._themeDatas = loadThemeDatas(self._theme)
        self.setMouseTracking(True)
        self._geometries = {}

        # Text editor
        self.editor = MDEditView(self,
                                index=index,
                                spellcheck=settings.spellcheck,
                                highlighting=True,
                                dict=settings.dict)
        self.editor.setFrameStyle(QFrame.NoFrame)
        self.editor.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.editor.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.editor.installEventFilter(self)
        self.editor.setMouseTracking(True)
        self.editor.setVerticalScrollBar(myScrollBar())
        self.scrollBar = self.editor.verticalScrollBar()
        self.scrollBar.setParent(self)

        # Top Panel
        self.topPanel = myPanel(parent=self)
        # self.topPanel.layout().addStretch(1)

        # Spell checking
        if enchant:
            self.btnSpellCheck = QPushButton(self)
            self.btnSpellCheck.setFlat(True)
            self.btnSpellCheck.setIcon(QIcon.fromTheme("tools-check-spelling"))
            self.btnSpellCheck.setCheckable(True)
            self.btnSpellCheck.setChecked(self.editor.spellcheck)
            self.btnSpellCheck.toggled.connect(self.editor.toggleSpellcheck)
            self.topPanel.layout().addWidget(self.btnSpellCheck)

        self.topPanel.layout().addStretch(1)

        # Close
        self.btnClose = QPushButton(self)
        self.btnClose.setIcon(qApp.style().standardIcon(QStyle.SP_DialogCloseButton))
        self.btnClose.clicked.connect(self.close)
        self.btnClose.setFlat(True)
        self.topPanel.layout().addWidget(self.btnClose)

        # Left Panel
        self._locked = False
        self.leftPanel = myPanel(vertical=True, parent=self)
        self.locker = locker(self)
        self.locker.lockChanged.connect(self.setLocked)
        self.leftPanel.layout().addWidget(self.locker)

        # Bottom Panel
        self.bottomPanel = myPanel(parent=self)

        self.bottomPanel.layout().addSpacing(24)
        self.lstThemes = QComboBox(self)
        self.lstThemes.setAttribute(Qt.WA_TranslucentBackground)
        paths = allPaths("resources/themes")
        for p in paths:
            lst = [i for i in os.listdir(p) if os.path.splitext(i)[1] == ".theme"]
            for t in lst:
                themeIni = os.path.join(p, t)
                name = loadThemeDatas(themeIni)["Name"]
                # self.lstThemes.addItem(os.path.splitext(t)[0])
                self.lstThemes.addItem(name)
                self.lstThemes.setItemData(self.lstThemes.count()-1, os.path.splitext(t)[0])

        self.lstThemes.setCurrentIndex(self.lstThemes.findData(settings.fullScreenTheme))
        # self.lstThemes.setCurrentText(settings.fullScreenTheme)
        self.lstThemes.currentTextChanged.connect(self.setTheme)
        self.lstThemes.setMaximumSize(QSize(300, QFontMetrics(qApp.font()).height()))
        self.bottomPanel.layout().addWidget(QLabel(self.tr("Theme:"), self))
        self.bottomPanel.layout().addWidget(self.lstThemes)
        self.bottomPanel.layout().addStretch(1)

        self.lblProgress = QLabel(self)
        self.lblProgress.setMaximumSize(QSize(200, 14))
        self.lblProgress.setMinimumSize(QSize(100, 14))
        self.lblWC = QLabel(self)
        self.bottomPanel.layout().addWidget(self.lblWC)
        self.bottomPanel.layout().addWidget(self.lblProgress)
        self.updateStatusBar()

        self.bottomPanel.layout().addSpacing(24)

        # Connection
        self._index.model().dataChanged.connect(self.dataChanged)

        # self.updateTheme()
        self.showFullScreen()
        # self.showMaximized()
        # self.show()

    def setLocked(self, val):
        self._locked = val
        self.btnClose.setVisible(not val)

    def setTheme(self, themeName):
        themeName = self.lstThemes.currentData()
        settings.fullScreenTheme = themeName
        self._theme = findThemePath(themeName)
        self._themeDatas = loadThemeDatas(self._theme)
        self.updateTheme()

    def updateTheme(self):
        # Reinit stored geometries for hiding widgets
        self._geometries = {}
        rect = self.geometry()
        self._background = generateTheme(self._themeDatas, rect)

        setThemeEditorDatas(self.editor, self._themeDatas, self._background, rect)

        # Colors
        if self._themeDatas["Foreground/Color"] == self._themeDatas["Background/Color"] or \
                        self._themeDatas["Foreground/Opacity"] < 5:
            self._bgcolor = QColor(self._themeDatas["Text/Color"])
            self._fgcolor = QColor(self._themeDatas["Background/Color"])
        else:
            self._bgcolor = QColor(self._themeDatas["Foreground/Color"])
            self._bgcolor.setAlpha(self._themeDatas["Foreground/Opacity"] * 255 / 100)
            self._fgcolor = QColor(self._themeDatas["Text/Color"])
            if self._themeDatas["Text/Color"] == self._themeDatas["Foreground/Color"]:
                self._fgcolor = QColor(self._themeDatas["Background/Color"])

        # ScrollBar
        r = self.editor.geometry()
        w = qApp.style().pixelMetric(QStyle.PM_ScrollBarExtent)
        r.setWidth(w)
        r.moveRight(rect.right() - rect.left())
        self.scrollBar.setGeometry(r)
        # self.scrollBar.setVisible(False)
        self.hideWidget(self.scrollBar)
        p = self.scrollBar.palette()
        b = QBrush(self._background.copy(self.scrollBar.geometry()))
        p.setBrush(QPalette.Base, b)
        self.scrollBar.setPalette(p)

        self.scrollBar.setColor(self._bgcolor)

        # Left Panel
        r = self.locker.geometry()
        r.moveTopLeft(QPoint(
                0,
                self.geometry().height() / 2 - r.height() / 2
        ))
        self.leftPanel.setGeometry(r)
        self.hideWidget(self.leftPanel)
        self.leftPanel.setColor(self._bgcolor)

        # Top / Bottom Panels
        r = QRect(0, 0, 0, 24)
        r.setWidth(rect.width())
        # r.moveLeft(rect.center().x() - r.width() / 2)
        self.topPanel.setGeometry(r)
        # self.topPanel.setVisible(False)
        self.hideWidget(self.topPanel)
        r.moveBottom(rect.bottom() - rect.top())
        self.bottomPanel.setGeometry(r)
        # self.bottomPanel.setVisible(False)
        self.hideWidget(self.bottomPanel)
        self.topPanel.setColor(self._bgcolor)
        self.bottomPanel.setColor(self._bgcolor)

        # Lst theme
        # p = self.lstThemes.palette()
        p = self.palette()
        p.setBrush(QPalette.Button, self._bgcolor)
        p.setBrush(QPalette.ButtonText, self._fgcolor)
        p.setBrush(QPalette.WindowText, self._fgcolor)

        for panel in (self.bottomPanel, self.topPanel):
            for i in range(panel.layout().count()):
                item = panel.layout().itemAt(i)
                if item.widget():
                    item.widget().setPalette(p)
        # self.lstThemes.setPalette(p)
        # self.lblWC.setPalette(p)

        self.update()
        self.editor.centerCursor()

    def paintEvent(self, event):
        if self._background:
            painter = QPainter(self)
            painter.setClipRegion(event.region())
            painter.drawPixmap(event.rect(), self._background, event.rect())
            painter.end()

    def resizeEvent(self, event):
        self.updateTheme()

    def keyPressEvent(self, event):
        if event.key() in [Qt.Key_Escape, Qt.Key_F11] and \
                not self._locked:
            self.close()
        else:
            QWidget.keyPressEvent(self, event)

    def mouseMoveEvent(self, event):
        r = self.geometry()

        for w in [self.scrollBar, self.topPanel,
                  self.bottomPanel, self.leftPanel]:
            # w.setVisible(w.geometry().contains(event.pos()))
            if self._geometries[w].contains(event.pos()):
                self.showWidget(w)
            else:
                self.hideWidget(w)

    def hideWidget(self, widget):
        if widget not in self._geometries:
            self._geometries[widget] = widget.geometry()

        if hasattr(widget, "_autoHide") and not widget._autoHide:
            return

        # Hides widget in the bottom right corner
        widget.move(self.geometry().bottomRight() + QPoint(1, 1))

    def showWidget(self, widget):
        if widget in self._geometries:
            widget.move(self._geometries[widget].topLeft())

    def eventFilter(self, obj, event):
        if obj == self.editor and event.type() == QEvent.Enter:
            for w in [self.scrollBar, self.topPanel,
                      self.bottomPanel, self.leftPanel]:
                # w.setVisible(False)
                self.hideWidget(w)
        return QWidget.eventFilter(self, obj, event)

    def dataChanged(self, topLeft, bottomRight):
        # This is called sometimes after self has been destroyed. Don't know why.
        if not self or not self._index:
            return
        if topLeft.row() <= self._index.row() <= bottomRight.row():
            self.updateStatusBar()

    def updateStatusBar(self):
        if self._index:
            item = self._index.internalPointer()

        wc = item.data(Outline.wordCount)
        goal = item.data(Outline.goal)
        pg = item.data(Outline.goalPercentage)

        if goal:
            rect = self.lblProgress.geometry()
            rect = QRect(QPoint(0, 0), rect.size())
            self.px = QPixmap(rect.size())
            self.px.fill(Qt.transparent)
            p = QPainter(self.px)
            drawProgress(p, rect, pg, 2)
            p.end()
            self.lblProgress.setPixmap(self.px)
            self.lblWC.setText(self.tr("{} words / {}").format(wc, goal))
        else:
            self.lblProgress.hide()
            self.lblWC.setText(self.tr("{} words").format(wc))

        self.locker.setWordCount(wc)
        # If there's a goal, then we update the locker target's number of word accordingly
        # (also if there is a word count, we deduce it.
        if goal and not self.locker.isLocked():
            if wc and goal - wc > 0:
                self.locker.spnWordTarget.setValue(goal - wc)
            elif not wc:
                self.locker.spnWordTarget.setValue(goal)