mirror of
https://github.com/olivierkes/manuskript.git
synced 2024-09-30 01:07:24 +13:00
59662921a7
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
410 lines
16 KiB
Python
410 lines
16 KiB
Python
#!/usr/bin/env python
|
|
# --!-- coding: utf8 --!--
|
|
import locale
|
|
|
|
from PyQt5.QtCore import QModelIndex, QRect, QPoint
|
|
from PyQt5.QtCore import Qt
|
|
from PyQt5.QtGui import QPixmap, QPainter, QIcon
|
|
from PyQt5.QtWidgets import QWidget, qApp, QDesktopWidget
|
|
|
|
from manuskript import settings
|
|
from manuskript.enums import Outline
|
|
from manuskript.functions import AUC, mainWindow, drawProgress, appPath, uiParse
|
|
from manuskript.ui_qt import style
|
|
from manuskript.ui_qt.editors.editorWidget import editorWidget
|
|
from manuskript.ui_qt.editors.fullScreenEditor import fullScreenEditor
|
|
from manuskript.ui_qt.editors.mainEditor_ui import Ui_mainEditor
|
|
|
|
try:
|
|
locale.setlocale(locale.LC_ALL, '')
|
|
except:
|
|
pass
|
|
|
|
import logging
|
|
LOGGER = logging.getLogger(__name__)
|
|
|
|
class mainEditor(QWidget, Ui_mainEditor):
|
|
"""
|
|
`mainEditor` is responsible for opening `outlineItem`s and offering information
|
|
and commands about those `outlineItem`s to the used.
|
|
|
|
It contains two main elements:
|
|
|
|
1. A `tabSplitter`, which can open any number of `outlineItem`s either in tabs
|
|
(in `QTabWidget`) and/or in split views (children `tabSplitter`s).
|
|
2. An horizontal layout contain a number of buttons and information:
|
|
|
|
- Go up button
|
|
- Select folder view: either "text", "cork" or "outline" (see `editorWidget`)
|
|
- Zoom slider for "cork" view
|
|
- Label showing stats about displayed `outlineItem`
|
|
- Fullscreen button
|
|
|
|
`mainEditor` is responsible for opening indexes, propagating event to relevant
|
|
views, opening and closing tabs, etc.
|
|
|
|
+---------------------------| mainEditor |--------------------------------+
|
|
| |
|
|
| +--------| tabSplitter |----------------------------------------------+ |
|
|
| | +----------| tabSplitter |---------+ | |
|
|
| | | | | |
|
|
| | +-----| editorWidget |----+ | +-------| editorWidget |-----+ | | |
|
|
| | | | | | | | | |
|
|
| | +-------------------------+ | +----------------------------+ | | |
|
|
| | | | | |
|
|
| | +-----| editorWidget |----+ | +-------| editorWidget |-----+ | | |
|
|
| | | | | | | | | |
|
|
| | +-------------------------+ | +----------------------------+ | | |
|
|
| | +----------------------------------+ | |
|
|
| +---------------------------------------------------------------------+ |
|
|
| |
|
|
+-------------------------------------------------------------------------+
|
|
| ## ## ## ## toolbar ## ## |
|
|
+-------------------------------------------------------------------------+
|
|
"""
|
|
|
|
def __init__(self, parent=None):
|
|
QWidget.__init__(self, parent)
|
|
self.setupUi(self)
|
|
self._updating = False
|
|
self._fullScreen = None
|
|
|
|
self.mw = mainWindow()
|
|
|
|
# Connections --------------------------------------------------------
|
|
|
|
self.btnGoUp.clicked.connect(self.goToParentItem)
|
|
self.sldCorkSizeFactor.valueChanged.connect(
|
|
self.setCorkSizeFactor, AUC)
|
|
self.btnRedacFolderCork.toggled.connect(
|
|
self.sldCorkSizeFactor.setVisible, AUC
|
|
)
|
|
self.btnRedacFolderText.clicked.connect(
|
|
lambda v: self.setFolderView("text"), AUC)
|
|
self.btnRedacFolderCork.clicked.connect(
|
|
lambda v: self.setFolderView("cork"), AUC)
|
|
self.btnRedacFolderOutline.clicked.connect(
|
|
lambda v: self.setFolderView("outline"), AUC)
|
|
|
|
self.btnRedacFullscreen.clicked.connect(
|
|
self.showFullScreen, AUC)
|
|
|
|
# self.tab.setDocumentMode(False)
|
|
|
|
# Bug in Qt < 5.5: doesn't always load icons from custom theme.
|
|
# Cf. https://github.com/qtproject/qtbase/commit/a8621a3f85e64f1252a80ae81a6e22554f7b3f44
|
|
# Since those are important, we provide fallback.
|
|
self.btnRedacFolderCork.setIcon(QIcon.fromTheme("view-cards",
|
|
QIcon(appPath("icons/NumixMsk/256x256/actions/view-cards.svg"))))
|
|
self.btnRedacFolderOutline.setIcon(QIcon.fromTheme("view-outline",
|
|
QIcon(appPath("icons/NumixMsk/256x256/actions/view-outline.svg"))))
|
|
self.btnRedacFolderText.setIcon(QIcon.fromTheme("view-text",
|
|
QIcon(appPath("icons/NumixMsk/256x256/actions/view-text.svg"))))
|
|
|
|
for btn in [self.btnRedacFolderCork, self.btnRedacFolderText, self.btnRedacFolderOutline]:
|
|
btn.setToolTip(btn.text())
|
|
btn.setText("")
|
|
|
|
###############################################################################
|
|
# TABS
|
|
###############################################################################
|
|
|
|
def currentTabWidget(self):
|
|
"""Returns the tabSplitter that has focus."""
|
|
ts = self.tabSplitter
|
|
while ts:
|
|
if ts.focusTab == 1:
|
|
return ts.tab
|
|
else:
|
|
ts = ts.secondTab
|
|
|
|
# No tabSplitter has focus, something is strange.
|
|
# But probably not important.
|
|
# Let's return self.tabSplitter.tab anyway.
|
|
return self.tabSplitter.tab
|
|
|
|
def currentEditor(self, tabWidget=None):
|
|
if tabWidget == None:
|
|
tabWidget = self.currentTabWidget()
|
|
return tabWidget.currentWidget()
|
|
# return self.tab.currentWidget()
|
|
|
|
def tabChanged(self, index=QModelIndex()):
|
|
if self.currentEditor():
|
|
index = self.currentEditor().currentIndex
|
|
view = self.currentEditor().folderView
|
|
self.updateFolderViewButtons(view)
|
|
else:
|
|
index = QModelIndex()
|
|
|
|
self.updateMainTreeView(index)
|
|
|
|
self.updateStats()
|
|
self.updateThingsVisible(index)
|
|
|
|
def updateMainTreeView(self, index):
|
|
self._updating = True
|
|
self.mw.treeRedacOutline.setCurrentIndex(index)
|
|
self._updating = False
|
|
|
|
def closeAllTabs(self):
|
|
for ts in self.allTabSplitters():
|
|
while(ts.tab.count()):
|
|
ts.closeTab(0)
|
|
|
|
for ts in reversed(self.allTabSplitters()):
|
|
ts.closeSplit()
|
|
|
|
def close(self):
|
|
if self._fullScreen is not None:
|
|
self._fullScreen.leaveFullscreen()
|
|
|
|
def allTabs(self, tabWidget=None):
|
|
"""Returns all the tabs from the given tabWidget. If tabWidget is None, from the current tabWidget."""
|
|
if tabWidget == None:
|
|
tabWidget = self.currentTabWidget()
|
|
return [tabWidget.widget(i) for i in range(tabWidget.count())]
|
|
|
|
def allAllTabs(self):
|
|
"""Returns a list of all tabs, from all tabWidgets."""
|
|
r = []
|
|
for ts in self.allTabSplitters():
|
|
r.extend(self.allTabs(ts.tab))
|
|
return r
|
|
|
|
def allTabSplitters(self):
|
|
r = []
|
|
ts = self.tabSplitter
|
|
while ts:
|
|
r.append(ts)
|
|
ts = ts.secondTab
|
|
return r
|
|
|
|
###############################################################################
|
|
# SELECTION AND UPDATES
|
|
###############################################################################
|
|
|
|
def selectionChanged(self):
|
|
if self._updating:
|
|
return
|
|
|
|
# This might be called during a drag n drop operation, or while deleting
|
|
# items. If so, we don't want to do anything.
|
|
if not self.mw.mdlOutline._removingRows:
|
|
if len(self.mw.treeRedacOutline.selectionModel().
|
|
selection().indexes()) == 0:
|
|
idx = QModelIndex()
|
|
else:
|
|
idx = self.mw.treeRedacOutline.currentIndex()
|
|
|
|
self.setCurrentModelIndex(idx)
|
|
self.updateThingsVisible(idx)
|
|
|
|
def openIndexes(self, indexes, newTab=False):
|
|
for i in indexes:
|
|
self.setCurrentModelIndex(i, newTab)
|
|
|
|
def goToParentItem(self):
|
|
if self.currentEditor():
|
|
idx = self.currentEditor().currentIndex
|
|
self.mw.treeRedacOutline.setCurrentIndex(idx.parent())
|
|
|
|
def setCurrentModelIndex(self, index, newTab=False, tabWidget=None):
|
|
|
|
title = self.getIndexTitle(index)
|
|
|
|
if tabWidget == None:
|
|
tabWidget = self.currentTabWidget()
|
|
|
|
# Checking if tab is already opened
|
|
for w in self.allTabs(tabWidget):
|
|
if w.currentIndex == index:
|
|
tabWidget.setCurrentWidget(w)
|
|
return
|
|
|
|
if qApp.keyboardModifiers() & Qt.ControlModifier:
|
|
newTab = True
|
|
|
|
if newTab or not tabWidget.count():
|
|
editor = editorWidget(self)
|
|
editor.setCurrentModelIndex(index)
|
|
editor._tabWidget = tabWidget
|
|
i = tabWidget.addTab(editor, editor.ellidedTitle(title))
|
|
tabWidget.setTabToolTip(i, title)
|
|
tabWidget.setCurrentIndex(tabWidget.count() - 1)
|
|
else:
|
|
self.currentEditor(tabWidget).setCurrentModelIndex(index)
|
|
#tabWidget.setTabText(tabWidget.currentIndex(), title)
|
|
|
|
def updateTargets(self):
|
|
"""Updates all tabSplitter that are targets. This is called from editorWidget."""
|
|
index = self.sender().currentIndex()
|
|
|
|
for ts in self.allTabSplitters():
|
|
if ts.isTarget:
|
|
self.updateMainTreeView(index)
|
|
self.setCurrentModelIndex(index, tabWidget=ts.tab)
|
|
self.updateThingsVisible(index)
|
|
|
|
def getIndexTitle(self, index):
|
|
if not index.isValid():
|
|
title = self.tr("Root")
|
|
else:
|
|
title = index.internalPointer().title()
|
|
|
|
return title
|
|
|
|
###############################################################################
|
|
# FUNCTIONS FOR MENU ACCESS
|
|
###############################################################################
|
|
|
|
def copy(self): self.currentEditor().copy()
|
|
def cut(self): self.currentEditor().cut()
|
|
def paste(self): self.currentEditor().paste()
|
|
def rename(self): self.currentEditor().rename()
|
|
def duplicate(self): self.currentEditor().duplicate()
|
|
def delete(self): self.currentEditor().delete()
|
|
def moveUp(self): self.currentEditor().moveUp()
|
|
def moveDown(self): self.currentEditor().moveDown()
|
|
def splitDialog(self): self.currentEditor().splitDialog()
|
|
def splitCursor(self): self.currentEditor().splitCursor()
|
|
def merge(self): self.currentEditor().merge()
|
|
|
|
###############################################################################
|
|
# UI
|
|
###############################################################################
|
|
|
|
def updateThingsVisible(self, index):
|
|
if index.isValid():
|
|
visible = index.internalPointer().isFolder()
|
|
else:
|
|
visible = True
|
|
|
|
self.btnRedacFolderText.setVisible(visible)
|
|
self.btnRedacFolderCork.setVisible(visible)
|
|
self.btnRedacFolderOutline.setVisible(visible)
|
|
self.sldCorkSizeFactor.setVisible(visible and self.btnRedacFolderCork.isChecked())
|
|
self.btnRedacFullscreen.setVisible(not visible)
|
|
|
|
def updateFolderViewButtons(self, view):
|
|
if view == "text":
|
|
self.btnRedacFolderText.setChecked(True)
|
|
elif view == "cork":
|
|
self.btnRedacFolderCork.setChecked(True)
|
|
elif view == "outline":
|
|
self.btnRedacFolderOutline.setChecked(True)
|
|
|
|
def updateStats(self):
|
|
|
|
if not self.currentEditor():
|
|
return
|
|
|
|
index = self.currentEditor().currentIndex
|
|
|
|
if index.isValid():
|
|
item = index.internalPointer()
|
|
else:
|
|
item = self.mw.mdlOutline.rootItem
|
|
|
|
if not item:
|
|
item = self.mw.mdlOutline.rootItem
|
|
|
|
cc = item.data(Outline.charCount)
|
|
wc = item.data(Outline.wordCount)
|
|
goal = item.data(Outline.goal)
|
|
chars = item.data(Outline.charCount) # len(item.data(Outline.text))
|
|
progress = item.data(Outline.goalPercentage)
|
|
|
|
goal = uiParse(goal, None, int, lambda x: x>=0)
|
|
progress = uiParse(progress, 0.0, float)
|
|
|
|
if not cc:
|
|
cc = 0
|
|
|
|
if not wc:
|
|
wc = 0
|
|
|
|
if goal:
|
|
self.lblRedacProgress.show()
|
|
rect = self.lblRedacProgress.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, progress, 2)
|
|
del p
|
|
self.lblRedacProgress.setPixmap(self.px)
|
|
|
|
if settings.progressChars:
|
|
self.lblRedacWC.setText(self.tr("({} chars) {} words / {} ").format(
|
|
locale.format("%d", cc, grouping=True),
|
|
locale.format("%d", wc, grouping=True),
|
|
locale.format("%d", goal, grouping=True)))
|
|
self.lblRedacWC.setToolTip("")
|
|
else:
|
|
self.lblRedacWC.setText(self.tr("{} words / {} ").format(
|
|
locale.format("%d", wc, grouping=True),
|
|
locale.format("%d", goal, grouping=True)))
|
|
self.lblRedacWC.setToolTip(self.tr("{} chars").format(
|
|
locale.format("%d", cc, grouping=True)))
|
|
else:
|
|
self.lblRedacProgress.hide()
|
|
|
|
if settings.progressChars:
|
|
self.lblRedacWC.setText(self.tr("{} chars ").format(
|
|
locale.format("%d", cc, grouping=True)))
|
|
self.lblRedacWC.setToolTip("")
|
|
else:
|
|
self.lblRedacWC.setText(self.tr("{} words ").format(
|
|
locale.format("%d", wc, grouping=True)))
|
|
self.lblRedacWC.setToolTip(self.tr("{} chars").format(
|
|
locale.format("%d", cc, grouping=True)))
|
|
|
|
###############################################################################
|
|
# VIEWS
|
|
###############################################################################
|
|
|
|
def setFolderView(self, view):
|
|
if self.currentEditor():
|
|
self.currentEditor().setFolderView(view)
|
|
|
|
def setCorkSizeFactor(self, val):
|
|
for w in self.allAllTabs():
|
|
w.setCorkSizeFactor(val)
|
|
settings.corkSizeFactor = val
|
|
|
|
def updateCorkView(self):
|
|
for w in self.allAllTabs():
|
|
w.corkView.viewport().update()
|
|
|
|
def updateCorkBackground(self):
|
|
for w in self.allAllTabs():
|
|
w.corkView.updateBackground()
|
|
|
|
def updateTreeView(self):
|
|
for w in self.allAllTabs():
|
|
w.outlineView.viewport().update()
|
|
|
|
def showFullScreen(self):
|
|
if self.currentEditor():
|
|
currentScreenNumber = QDesktopWidget().screenNumber(widget=self)
|
|
self._fullScreen = fullScreenEditor(
|
|
self.currentEditor().currentIndex,
|
|
screenNumber=currentScreenNumber)
|
|
# Clean the variable when closing fullscreen prevent errors
|
|
self._fullScreen.exited.connect(self.clearFullScreen)
|
|
|
|
def clearFullScreen(self):
|
|
self._fullScreen = None
|
|
|
|
###############################################################################
|
|
# DICT AND STUFF LIKE THAT
|
|
###############################################################################
|
|
|
|
def setDict(self, dict):
|
|
for w in self.allAllTabs():
|
|
w.setDict(dict)
|
|
|
|
def toggleSpellcheck(self, val):
|
|
for w in self.allAllTabs():
|
|
w.toggleSpellcheck(val)
|