mirror of
https://github.com/olivierkes/manuskript.git
synced 2024-05-18 20:02:32 +12:00
6d607e8804
Add "Help -> About" menu entry that invokes an "About" dialog window for Manuskript. Show details such as logo, version, website, copyright, license, and software versions in use (Python, PyQT, and Qt).
1197 lines
47 KiB
Python
1197 lines
47 KiB
Python
#!/usr/bin/env python
|
|
# --!-- coding: utf8 --!--
|
|
import imp
|
|
import os
|
|
|
|
from PyQt5.QtCore import pyqtSignal, QSignalMapper, QTimer, QSettings, Qt, QRegExp, QUrl, QSize
|
|
from PyQt5.QtGui import QStandardItemModel, QIcon, QColor
|
|
from PyQt5.QtWidgets import QMainWindow, QHeaderView, qApp, QMenu, QActionGroup, QAction, QStyle, QListWidgetItem, \
|
|
QLabel
|
|
|
|
from manuskript import settings
|
|
from manuskript.enums import Character, PlotStep, Plot, World, Outline
|
|
from manuskript.functions import AUC, wordCount, appPath, findWidgetsOfClass
|
|
from manuskript import loadSave
|
|
from manuskript.models.characterModel import characterModel
|
|
from manuskript.models.outlineModel import outlineModel
|
|
from manuskript.models.plotModel import plotModel
|
|
from manuskript.models.worldModel import worldModel
|
|
from manuskript.settingsWindow import settingsWindow
|
|
from manuskript.ui import style
|
|
from manuskript.ui.about import aboutDialog
|
|
from manuskript.ui.collapsibleDockWidgets import collapsibleDockWidgets
|
|
from manuskript.ui.exporters.exporter import exporterDialog
|
|
from manuskript.ui.helpLabel import helpLabel
|
|
from manuskript.ui.mainWindow import Ui_MainWindow
|
|
from manuskript.ui.tools.frequencyAnalyzer import frequencyAnalyzer
|
|
from manuskript.ui.views.outlineDelegates import outlineCharacterDelegate
|
|
from manuskript.ui.views.plotDelegate import plotDelegate
|
|
|
|
# Spellcheck support
|
|
from manuskript.ui.views.textEditView import textEditView
|
|
|
|
try:
|
|
import enchant
|
|
except ImportError:
|
|
enchant = None
|
|
|
|
|
|
class MainWindow(QMainWindow, Ui_MainWindow):
|
|
dictChanged = pyqtSignal(str)
|
|
|
|
# Tab indexes
|
|
TabInfos = 0
|
|
TabSummary = 1
|
|
TabPersos = 2
|
|
TabPlots = 3
|
|
TabWorld = 4
|
|
TabOutline = 5
|
|
TabRedac = 6
|
|
|
|
def __init__(self):
|
|
QMainWindow.__init__(self)
|
|
self.setupUi(self)
|
|
self.currentProject = None
|
|
|
|
self.readSettings()
|
|
|
|
# UI
|
|
self.setupMoreUi()
|
|
|
|
# Welcome
|
|
self.welcome.updateValues()
|
|
self.stack.setCurrentIndex(0)
|
|
|
|
# Word count
|
|
self.mprWordCount = QSignalMapper(self)
|
|
for t, i in [
|
|
(self.txtSummarySentence, 0),
|
|
(self.txtSummaryPara, 1),
|
|
(self.txtSummaryPage, 2),
|
|
(self.txtSummaryFull, 3)
|
|
]:
|
|
t.textChanged.connect(self.mprWordCount.map)
|
|
self.mprWordCount.setMapping(t, i)
|
|
self.mprWordCount.mapped.connect(self.wordCount)
|
|
|
|
# Snowflake Method Cycle
|
|
self.mapperCycle = QSignalMapper(self)
|
|
for t, i in [
|
|
(self.btnStepTwo, 0),
|
|
(self.btnStepThree, 1),
|
|
(self.btnStepFour, 2),
|
|
(self.btnStepFive, 3),
|
|
(self.btnStepSix, 4),
|
|
(self.btnStepSeven, 5),
|
|
(self.btnStepEight, 6)
|
|
]:
|
|
t.clicked.connect(self.mapperCycle.map)
|
|
self.mapperCycle.setMapping(t, i)
|
|
|
|
self.mapperCycle.mapped.connect(self.clickCycle)
|
|
self.cmbSummary.currentIndexChanged.connect(self.summaryPageChanged)
|
|
self.cmbSummary.setCurrentIndex(0)
|
|
self.cmbSummary.currentIndexChanged.emit(0)
|
|
|
|
# Main Menu
|
|
for i in [self.actSave, self.actSaveAs, self.actCloseProject,
|
|
self.menuEdit, self.menuView, self.menuTools, self.menuHelp]:
|
|
i.setEnabled(False)
|
|
|
|
self.actOpen.triggered.connect(self.welcome.openFile)
|
|
self.actSave.triggered.connect(self.saveDatas)
|
|
self.actSaveAs.triggered.connect(self.welcome.saveAsFile)
|
|
self.actCompile.triggered.connect(self.doCompile)
|
|
self.actLabels.triggered.connect(self.settingsLabel)
|
|
self.actStatus.triggered.connect(self.settingsStatus)
|
|
self.actSettings.triggered.connect(self.settingsWindow)
|
|
self.actCloseProject.triggered.connect(self.closeProject)
|
|
self.actQuit.triggered.connect(self.close)
|
|
self.actToolFrequency.triggered.connect(self.frequencyAnalyzer)
|
|
self.actAbout.triggered.connect(self.about)
|
|
self.generateViewMenu()
|
|
|
|
self.actModeGroup = QActionGroup(self)
|
|
self.actModeSimple.setActionGroup(self.actModeGroup)
|
|
self.actModeFiction.setActionGroup(self.actModeGroup)
|
|
self.actModeSnowflake.setActionGroup(self.actModeGroup)
|
|
self.actModeSimple.triggered.connect(self.setViewModeSimple)
|
|
self.actModeFiction.triggered.connect(self.setViewModeFiction)
|
|
self.actModeSnowflake.setEnabled(False)
|
|
|
|
self.makeUIConnections()
|
|
|
|
# self.loadProject(os.path.join(appPath(), "test_project.zip"))
|
|
|
|
###############################################################################
|
|
# SUMMARY
|
|
###############################################################################
|
|
|
|
def summaryPageChanged(self, index):
|
|
fractalButtons = [
|
|
self.btnStepTwo,
|
|
self.btnStepThree,
|
|
self.btnStepFive,
|
|
self.btnStepSeven,
|
|
]
|
|
for b in fractalButtons:
|
|
b.setVisible(fractalButtons.index(b) == index)
|
|
|
|
###############################################################################
|
|
# OUTLINE
|
|
###############################################################################
|
|
|
|
def outlineRemoveItemsRedac(self):
|
|
self.treeRedacOutline.delete()
|
|
|
|
def outlineRemoveItemsOutline(self):
|
|
self.treeOutlineOutline.delete()
|
|
|
|
###############################################################################
|
|
# CHARACTERS
|
|
###############################################################################
|
|
|
|
def changeCurrentCharacter(self, trash=None):
|
|
"""
|
|
|
|
@return:
|
|
"""
|
|
c = self.lstCharacters.currentCharacter()
|
|
if not c:
|
|
self.tabPersos.setEnabled(False)
|
|
return
|
|
|
|
self.tabPersos.setEnabled(True)
|
|
index = c.index()
|
|
|
|
for w in [
|
|
self.txtPersoName,
|
|
self.sldPersoImportance,
|
|
self.txtPersoMotivation,
|
|
self.txtPersoGoal,
|
|
self.txtPersoConflict,
|
|
self.txtPersoEpiphany,
|
|
self.txtPersoSummarySentence,
|
|
self.txtPersoSummaryPara,
|
|
self.txtPersoSummaryFull,
|
|
self.txtPersoNotes,
|
|
]:
|
|
w.setCurrentModelIndex(index)
|
|
|
|
# Button color
|
|
self.updateCharacterColor(c.ID())
|
|
|
|
# Slider importance
|
|
self.updateCharacterImportance(c.ID())
|
|
|
|
# Character Infos
|
|
self.tblPersoInfos.setRootIndex(index)
|
|
|
|
if self.mdlCharacter.rowCount(index):
|
|
self.updatePersoInfoView()
|
|
|
|
def updatePersoInfoView(self):
|
|
self.tblPersoInfos.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
|
|
self.tblPersoInfos.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
|
|
self.tblPersoInfos.verticalHeader().hide()
|
|
|
|
def updateCharacterColor(self, ID):
|
|
c = self.mdlCharacter.getCharacterByID(ID)
|
|
color = c.color().name()
|
|
self.btnPersoColor.setStyleSheet("background:{};".format(color))
|
|
|
|
def updateCharacterImportance(self, ID):
|
|
c = self.mdlCharacter.getCharacterByID(ID)
|
|
self.sldPersoImportance.setValue(int(c.importance()))
|
|
|
|
###############################################################################
|
|
# PLOTS
|
|
###############################################################################
|
|
|
|
def changeCurrentPlot(self):
|
|
index = self.lstPlots.currentPlotIndex()
|
|
|
|
if not index.isValid():
|
|
self.tabPlot.setEnabled(False)
|
|
return
|
|
|
|
self.tabPlot.setEnabled(True)
|
|
self.txtPlotName.setCurrentModelIndex(index)
|
|
self.txtPlotDescription.setCurrentModelIndex(index)
|
|
self.txtPlotResult.setCurrentModelIndex(index)
|
|
self.sldPlotImportance.setCurrentModelIndex(index)
|
|
self.lstPlotPerso.setRootIndex(index.sibling(index.row(),
|
|
Plot.characters.value))
|
|
|
|
# Slider importance
|
|
self.updatePlotImportance(index.row())
|
|
|
|
subplotindex = index.sibling(index.row(), Plot.steps.value)
|
|
self.lstSubPlots.setRootIndex(subplotindex)
|
|
if self.mdlPlots.rowCount(subplotindex):
|
|
self.updateSubPlotView()
|
|
|
|
# self.txtSubPlotSummary.setCurrentModelIndex(QModelIndex())
|
|
self.txtSubPlotSummary.setEnabled(False)
|
|
self._updatingSubPlot = True
|
|
self.txtSubPlotSummary.setPlainText("")
|
|
self._updatingSubPlot = False
|
|
self.lstPlotPerso.selectionModel().clear()
|
|
|
|
def updateSubPlotView(self):
|
|
# Hide columns
|
|
for i in range(self.mdlPlots.columnCount()):
|
|
self.lstSubPlots.hideColumn(i)
|
|
self.lstSubPlots.showColumn(PlotStep.name.value)
|
|
self.lstSubPlots.showColumn(PlotStep.meta.value)
|
|
|
|
self.lstSubPlots.horizontalHeader().setSectionResizeMode(
|
|
PlotStep.name.value, QHeaderView.Stretch)
|
|
self.lstSubPlots.horizontalHeader().setSectionResizeMode(
|
|
PlotStep.meta.value, QHeaderView.ResizeToContents)
|
|
self.lstSubPlots.verticalHeader().hide()
|
|
|
|
def updatePlotImportance(self, ID):
|
|
imp = self.mdlPlots.getPlotImportanceByID(ID)
|
|
self.sldPlotImportance.setValue(int(imp))
|
|
|
|
def changeCurrentSubPlot(self, index):
|
|
# Got segfaults when using textEditView model system, so ad hoc stuff.
|
|
index = index.sibling(index.row(), PlotStep.summary.value)
|
|
item = self.mdlPlots.itemFromIndex(index)
|
|
if not item:
|
|
self.txtSubPlotSummary.setEnabled(False)
|
|
return
|
|
self.txtSubPlotSummary.setEnabled(True)
|
|
txt = item.text()
|
|
self._updatingSubPlot = True
|
|
self.txtSubPlotSummary.setPlainText(txt)
|
|
self._updatingSubPlot = False
|
|
|
|
def updateSubPlotSummary(self):
|
|
if self._updatingSubPlot:
|
|
return
|
|
|
|
index = self.lstSubPlots.currentIndex()
|
|
if not index.isValid():
|
|
return
|
|
index = index.sibling(index.row(), PlotStep.summary.value)
|
|
item = self.mdlPlots.itemFromIndex(index)
|
|
|
|
self._updatingSubPlot = True
|
|
item.setText(self.txtSubPlotSummary.toPlainText())
|
|
self._updatingSubPlot = False
|
|
|
|
def plotPersoSelectionChanged(self):
|
|
"Enables or disables remove plot perso button."
|
|
self.btnRmPlotPerso.setEnabled(
|
|
len(self.lstPlotPerso.selectedIndexes()) != 0)
|
|
|
|
###############################################################################
|
|
# WORLD
|
|
###############################################################################
|
|
|
|
def changeCurrentWorld(self):
|
|
index = self.mdlWorld.selectedIndex()
|
|
|
|
if not index.isValid():
|
|
self.tabWorld.setEnabled(False)
|
|
return
|
|
|
|
self.tabWorld.setEnabled(True)
|
|
self.txtWorldName.setCurrentModelIndex(index)
|
|
self.txtWorldDescription.setCurrentModelIndex(index)
|
|
self.txtWorldPassion.setCurrentModelIndex(index)
|
|
self.txtWorldConflict.setCurrentModelIndex(index)
|
|
|
|
# ###############################################################################
|
|
# # LOAD AND SAVE
|
|
# ###############################################################################
|
|
|
|
def loadProject(self, project, loadFromFile=True):
|
|
"""Loads the project ``project``.
|
|
|
|
If ``loadFromFile`` is False, then it does not load datas from file.
|
|
It assumes that the datas have been populated in a different way."""
|
|
if loadFromFile and not os.path.exists(project):
|
|
print(self.tr("The file {} does not exist. Try again.").format(project))
|
|
self.statusBar().showMessage(
|
|
self.tr("The file {} does not exist. Try again.").format(project),
|
|
5000)
|
|
return
|
|
|
|
if loadFromFile:
|
|
# Load empty settings
|
|
imp.reload(settings)
|
|
|
|
# Load data
|
|
self.loadEmptyDatas()
|
|
self.loadDatas(project)
|
|
|
|
self.makeConnections()
|
|
|
|
# Load settings
|
|
if settings.openIndexes:
|
|
self.mainEditor.tabSplitter.restoreOpenIndexes(settings.openIndexes)
|
|
self.generateViewMenu()
|
|
self.mainEditor.sldCorkSizeFactor.setValue(settings.corkSizeFactor)
|
|
self.actSpellcheck.setChecked(settings.spellcheck)
|
|
self.toggleSpellcheck(settings.spellcheck)
|
|
self.updateMenuDict()
|
|
self.setDictionary()
|
|
|
|
self.mainEditor.setFolderView(settings.folderView)
|
|
self.mainEditor.updateFolderViewButtons(settings.folderView)
|
|
self.mainEditor.tabSplitter.updateStyleSheet()
|
|
self.tabMain.setCurrentIndex(settings.lastTab)
|
|
# We force to emit even if it opens on the current tab
|
|
self.tabMain.currentChanged.emit(settings.lastTab)
|
|
self.mainEditor.updateCorkBackground()
|
|
if settings.viewMode == "simple":
|
|
self.setViewModeSimple()
|
|
else:
|
|
self.setViewModeFiction()
|
|
|
|
# Set autosave
|
|
self.saveTimer = QTimer()
|
|
self.saveTimer.setInterval(settings.autoSaveDelay * 60 * 1000)
|
|
self.saveTimer.setSingleShot(False)
|
|
self.saveTimer.timeout.connect(self.saveDatas)
|
|
if settings.autoSave:
|
|
self.saveTimer.start()
|
|
|
|
# Set autosave if no changes
|
|
self.saveTimerNoChanges = QTimer()
|
|
self.saveTimerNoChanges.setInterval(settings.autoSaveNoChangesDelay * 1000)
|
|
self.saveTimerNoChanges.setSingleShot(True)
|
|
self.mdlFlatData.dataChanged.connect(self.startTimerNoChanges)
|
|
self.mdlOutline.dataChanged.connect(self.startTimerNoChanges)
|
|
self.mdlCharacter.dataChanged.connect(self.startTimerNoChanges)
|
|
self.mdlPlots.dataChanged.connect(self.startTimerNoChanges)
|
|
self.mdlWorld.dataChanged.connect(self.startTimerNoChanges)
|
|
# self.mdlPersosInfos.dataChanged.connect(self.startTimerNoChanges)
|
|
self.mdlStatus.dataChanged.connect(self.startTimerNoChanges)
|
|
self.mdlLabels.dataChanged.connect(self.startTimerNoChanges)
|
|
|
|
self.saveTimerNoChanges.timeout.connect(self.saveDatas)
|
|
self.saveTimerNoChanges.stop()
|
|
|
|
# UI
|
|
for i in [self.actOpen, self.menuRecents]:
|
|
i.setEnabled(False)
|
|
for i in [self.actSave, self.actSaveAs, self.actCloseProject,
|
|
self.menuEdit, self.menuView, self.menuTools, self.menuHelp]:
|
|
i.setEnabled(True)
|
|
|
|
# Add project name to Window's name
|
|
pName = os.path.split(project)[1]
|
|
if pName.endswith('.msk'):
|
|
pName=pName[:-4]
|
|
self.setWindowTitle(pName + " - " + self.tr("Manuskript"))
|
|
|
|
# Stuff
|
|
# self.checkPersosID() # Should'n be necessary any longer
|
|
|
|
self.currentProject = project
|
|
QSettings().setValue("lastProject", project)
|
|
|
|
# Show main Window
|
|
self.stack.setCurrentIndex(1)
|
|
|
|
def closeProject(self):
|
|
|
|
if not self.currentProject:
|
|
return
|
|
|
|
# Close open tabs in editor
|
|
self.mainEditor.closeAllTabs()
|
|
|
|
# Save datas
|
|
self.saveDatas()
|
|
|
|
self.currentProject = None
|
|
QSettings().setValue("lastProject", "")
|
|
|
|
# Clear datas
|
|
self.loadEmptyDatas()
|
|
self.saveTimer.stop()
|
|
loadSave.clearSaveCache()
|
|
|
|
self.breakConnections()
|
|
|
|
# UI
|
|
for i in [self.actOpen, self.menuRecents]:
|
|
i.setEnabled(True)
|
|
for i in [self.actSave, self.actSaveAs, self.actCloseProject,
|
|
self.menuEdit, self.menuView, self.menuTools, self.menuHelp]:
|
|
i.setEnabled(False)
|
|
|
|
# Set Window's name - no project loaded
|
|
self.setWindowTitle(self.tr("Manuskript"))
|
|
|
|
# Reload recent files
|
|
self.welcome.updateValues()
|
|
|
|
# Show welcome dialog
|
|
self.stack.setCurrentIndex(0)
|
|
|
|
def readSettings(self):
|
|
# Load State and geometry
|
|
sttgns = QSettings(qApp.organizationName(), qApp.applicationName())
|
|
if sttgns.contains("geometry"):
|
|
self.restoreGeometry(sttgns.value("geometry"))
|
|
if sttgns.contains("windowState"):
|
|
self.restoreState(sttgns.value("windowState"))
|
|
else:
|
|
self.dckCheatSheet.hide()
|
|
self.dckSearch.hide()
|
|
if sttgns.contains("metadataState"):
|
|
state = [False if v == "false" else True for v in sttgns.value("metadataState")]
|
|
self.redacMetadata.restoreState(state)
|
|
if sttgns.contains("revisionsState"):
|
|
state = [False if v == "false" else True for v in sttgns.value("revisionsState")]
|
|
self.redacMetadata.revisions.restoreState(state)
|
|
if sttgns.contains("splitterRedacH"):
|
|
self.splitterRedacH.restoreState(sttgns.value("splitterRedacH"))
|
|
if sttgns.contains("splitterRedacV"):
|
|
self.splitterRedacV.restoreState(sttgns.value("splitterRedacV"))
|
|
if sttgns.contains("toolbar"):
|
|
# self.toolbar is not initialized yet, so we just store balue
|
|
self._toolbarState = sttgns.value("toolbar")
|
|
else:
|
|
self._toolbarState = ""
|
|
|
|
|
|
def closeEvent(self, event):
|
|
# Save State and geometry and other things
|
|
sttgns = QSettings(qApp.organizationName(), qApp.applicationName())
|
|
sttgns.setValue("geometry", self.saveGeometry())
|
|
sttgns.setValue("windowState", self.saveState())
|
|
sttgns.setValue("metadataState", self.redacMetadata.saveState())
|
|
sttgns.setValue("revisionsState", self.redacMetadata.revisions.saveState())
|
|
sttgns.setValue("splitterRedacH", self.splitterRedacH.saveState())
|
|
sttgns.setValue("splitterRedacV", self.splitterRedacV.saveState())
|
|
sttgns.setValue("toolbar", self.toolbar.saveState())
|
|
|
|
# Specific settings to save before quitting
|
|
settings.lastTab = self.tabMain.currentIndex()
|
|
|
|
if self.currentProject:
|
|
# Remembering the current items (stores outlineItem's ID)
|
|
settings.openIndexes = self.mainEditor.tabSplitter.openIndexes()
|
|
|
|
# Save data from models
|
|
if self.currentProject and settings.saveOnQuit:
|
|
self.saveDatas()
|
|
|
|
# closeEvent
|
|
# QMainWindow.closeEvent(self, event) # Causin segfaults?
|
|
|
|
def startTimerNoChanges(self):
|
|
if settings.autoSaveNoChanges:
|
|
self.saveTimerNoChanges.start()
|
|
|
|
def saveDatas(self, projectName=None):
|
|
"""Saves the current project (in self.currentProject).
|
|
|
|
If ``projectName`` is given, currentProject becomes projectName.
|
|
In other words, it "saves as...".
|
|
"""
|
|
|
|
if projectName:
|
|
self.currentProject = projectName
|
|
QSettings().setValue("lastProject", projectName)
|
|
|
|
loadSave.saveProject() # version=0
|
|
self.saveTimerNoChanges.stop()
|
|
|
|
# Giving some feedback
|
|
print(self.tr("Project {} saved.").format(self.currentProject))
|
|
self.statusBar().showMessage(
|
|
self.tr("Project {} saved.").format(self.currentProject), 5000)
|
|
|
|
def loadEmptyDatas(self):
|
|
self.mdlFlatData = QStandardItemModel(self)
|
|
self.mdlCharacter = characterModel(self)
|
|
# self.mdlPersosProxy = persosProxyModel(self)
|
|
# self.mdlPersosInfos = QStandardItemModel(self)
|
|
self.mdlLabels = QStandardItemModel(self)
|
|
self.mdlStatus = QStandardItemModel(self)
|
|
self.mdlPlots = plotModel(self)
|
|
self.mdlOutline = outlineModel(self)
|
|
self.mdlWorld = worldModel(self)
|
|
|
|
def loadDatas(self, project):
|
|
|
|
errors = loadSave.loadProject(project)
|
|
|
|
# Giving some feedback
|
|
if not errors:
|
|
print(self.tr("Project {} loaded.").format(project))
|
|
self.statusBar().showMessage(
|
|
self.tr("Project {} loaded.").format(project), 5000)
|
|
else:
|
|
print(self.tr("Project {} loaded with some errors:").format(project))
|
|
for e in errors:
|
|
print(self.tr(" * {} wasn't found in project file.").format(e))
|
|
self.statusBar().showMessage(
|
|
self.tr("Project {} loaded with some errors.").format(project), 5000)
|
|
|
|
###############################################################################
|
|
# MAIN CONNECTIONS
|
|
###############################################################################
|
|
|
|
def makeUIConnections(self):
|
|
"Connections that have to be made once only, even when a new project is loaded."
|
|
self.lstCharacters.currentItemChanged.connect(self.changeCurrentCharacter, AUC)
|
|
|
|
self.txtPlotFilter.textChanged.connect(self.lstPlots.setFilter, AUC)
|
|
self.lstPlots.currentItemChanged.connect(self.changeCurrentPlot, AUC)
|
|
self.txtSubPlotSummary.document().contentsChanged.connect(
|
|
self.updateSubPlotSummary, AUC)
|
|
self.lstSubPlots.clicked.connect(self.changeCurrentSubPlot, AUC)
|
|
|
|
self.btnRedacAddFolder.clicked.connect(self.treeRedacOutline.addFolder, AUC)
|
|
self.btnOutlineAddFolder.clicked.connect(self.treeOutlineOutline.addFolder, AUC)
|
|
self.btnRedacAddText.clicked.connect(self.treeRedacOutline.addText, AUC)
|
|
self.btnOutlineAddText.clicked.connect(self.treeOutlineOutline.addText, AUC)
|
|
self.btnRedacRemoveItem.clicked.connect(self.outlineRemoveItemsRedac, AUC)
|
|
self.btnOutlineRemoveItem.clicked.connect(self.outlineRemoveItemsOutline, AUC)
|
|
|
|
self.tabMain.currentChanged.connect(self.toolbar.setCurrentGroup)
|
|
|
|
def makeConnections(self):
|
|
|
|
# Flat datas (Summary and general infos)
|
|
for widget, col in [
|
|
(self.txtSummarySituation, 0),
|
|
(self.txtSummarySentence, 1),
|
|
(self.txtSummarySentence_2, 1),
|
|
(self.txtSummaryPara, 2),
|
|
(self.txtSummaryPara_2, 2),
|
|
(self.txtPlotSummaryPara, 2),
|
|
(self.txtSummaryPage, 3),
|
|
(self.txtSummaryPage_2, 3),
|
|
(self.txtPlotSummaryPage, 3),
|
|
(self.txtSummaryFull, 4),
|
|
(self.txtPlotSummaryFull, 4),
|
|
]:
|
|
widget.setModel(self.mdlFlatData)
|
|
widget.setColumn(col)
|
|
widget.setCurrentModelIndex(self.mdlFlatData.index(1, col))
|
|
|
|
for widget, col in [
|
|
(self.txtGeneralTitle, 0),
|
|
(self.txtGeneralSubtitle, 1),
|
|
(self.txtGeneralSerie, 2),
|
|
(self.txtGeneralVolume, 3),
|
|
(self.txtGeneralGenre, 4),
|
|
(self.txtGeneralLicense, 5),
|
|
(self.txtGeneralAuthor, 6),
|
|
(self.txtGeneralEmail, 7),
|
|
]:
|
|
widget.setModel(self.mdlFlatData)
|
|
widget.setColumn(col)
|
|
widget.setCurrentModelIndex(self.mdlFlatData.index(0, col))
|
|
|
|
# Characters
|
|
self.lstCharacters.setCharactersModel(self.mdlCharacter)
|
|
self.tblPersoInfos.setModel(self.mdlCharacter)
|
|
|
|
self.btnAddPerso.clicked.connect(self.mdlCharacter.addCharacter, AUC)
|
|
try:
|
|
self.btnRmPerso.clicked.connect(self.lstCharacters.removeCharacter, AUC)
|
|
self.btnPersoColor.clicked.connect(self.lstCharacters.choseCharacterColor, AUC)
|
|
self.btnPersoAddInfo.clicked.connect(self.lstCharacters.addCharacterInfo, AUC)
|
|
self.btnPersoRmInfo.clicked.connect(self.lstCharacters.removeCharacterInfo, AUC)
|
|
except TypeError:
|
|
# Connection has already been made
|
|
pass
|
|
|
|
for w, c in [
|
|
(self.txtPersoName, Character.name.value),
|
|
(self.sldPersoImportance, Character.importance.value),
|
|
(self.txtPersoMotivation, Character.motivation.value),
|
|
(self.txtPersoGoal, Character.goal.value),
|
|
(self.txtPersoConflict, Character.conflict.value),
|
|
(self.txtPersoEpiphany, Character.epiphany.value),
|
|
(self.txtPersoSummarySentence, Character.summarySentence.value),
|
|
(self.txtPersoSummaryPara, Character.summaryPara.value),
|
|
(self.txtPersoSummaryFull, Character.summaryFull.value),
|
|
(self.txtPersoNotes, Character.notes.value)
|
|
]:
|
|
w.setModel(self.mdlCharacter)
|
|
w.setColumn(c)
|
|
self.tabPersos.setEnabled(False)
|
|
|
|
# Plots
|
|
self.lstPlots.setPlotModel(self.mdlPlots)
|
|
self.lstPlotPerso.setModel(self.mdlPlots)
|
|
self.lstSubPlots.setModel(self.mdlPlots)
|
|
self._updatingSubPlot = False
|
|
self.btnAddPlot.clicked.connect(self.mdlPlots.addPlot, AUC)
|
|
self.btnRmPlot.clicked.connect(lambda:
|
|
self.mdlPlots.removePlot(self.lstPlots.currentPlotIndex()), AUC)
|
|
self.btnAddSubPlot.clicked.connect(self.mdlPlots.addSubPlot, AUC)
|
|
self.btnAddSubPlot.clicked.connect(self.updateSubPlotView, AUC)
|
|
self.btnRmSubPlot.clicked.connect(self.mdlPlots.removeSubPlot, AUC)
|
|
self.lstPlotPerso.selectionModel().selectionChanged.connect(self.plotPersoSelectionChanged)
|
|
self.btnRmPlotPerso.clicked.connect(self.mdlPlots.removePlotPerso, AUC)
|
|
|
|
for w, c in [
|
|
(self.txtPlotName, Plot.name.value),
|
|
(self.txtPlotDescription, Plot.description.value),
|
|
(self.txtPlotResult, Plot.result.value),
|
|
(self.sldPlotImportance, Plot.importance.value),
|
|
]:
|
|
w.setModel(self.mdlPlots)
|
|
w.setColumn(c)
|
|
|
|
self.tabPlot.setEnabled(False)
|
|
self.mdlPlots.updatePlotPersoButton()
|
|
self.mdlCharacter.dataChanged.connect(self.mdlPlots.updatePlotPersoButton)
|
|
self.lstOutlinePlots.setPlotModel(self.mdlPlots)
|
|
self.lstOutlinePlots.setShowSubPlot(True)
|
|
self.plotCharacterDelegate = outlineCharacterDelegate(self.mdlCharacter, self)
|
|
self.lstPlotPerso.setItemDelegate(self.plotCharacterDelegate)
|
|
self.plotDelegate = plotDelegate(self)
|
|
self.lstSubPlots.setItemDelegateForColumn(PlotStep.meta.value, self.plotDelegate)
|
|
|
|
# World
|
|
self.treeWorld.setModel(self.mdlWorld)
|
|
for i in range(self.mdlWorld.columnCount()):
|
|
self.treeWorld.hideColumn(i)
|
|
self.treeWorld.showColumn(0)
|
|
self.btnWorldEmptyData.setMenu(self.mdlWorld.emptyDataMenu())
|
|
self.treeWorld.selectionModel().selectionChanged.connect(self.changeCurrentWorld, AUC)
|
|
self.btnAddWorld.clicked.connect(self.mdlWorld.addItem, AUC)
|
|
self.btnRmWorld.clicked.connect(self.mdlWorld.removeItem, AUC)
|
|
for w, c in [
|
|
(self.txtWorldName, World.name.value),
|
|
(self.txtWorldDescription, World.description.value),
|
|
(self.txtWorldPassion, World.passion.value),
|
|
(self.txtWorldConflict, World.conflict.value),
|
|
]:
|
|
w.setModel(self.mdlWorld)
|
|
w.setColumn(c)
|
|
self.tabWorld.setEnabled(False)
|
|
self.treeWorld.expandAll()
|
|
|
|
# Outline
|
|
self.treeRedacOutline.setModel(self.mdlOutline)
|
|
self.treeOutlineOutline.setModelCharacters(self.mdlCharacter)
|
|
self.treeOutlineOutline.setModelLabels(self.mdlLabels)
|
|
self.treeOutlineOutline.setModelStatus(self.mdlStatus)
|
|
|
|
self.redacMetadata.setModels(self.mdlOutline, self.mdlCharacter,
|
|
self.mdlLabels, self.mdlStatus)
|
|
self.outlineItemEditor.setModels(self.mdlOutline, self.mdlCharacter,
|
|
self.mdlLabels, self.mdlStatus)
|
|
|
|
self.treeOutlineOutline.setModel(self.mdlOutline)
|
|
# self.redacEditor.setModel(self.mdlOutline)
|
|
self.storylineView.setModels(self.mdlOutline, self.mdlCharacter, self.mdlPlots)
|
|
|
|
self.treeOutlineOutline.selectionModel().selectionChanged.connect(self.outlineItemEditor.selectionChanged, AUC)
|
|
self.treeOutlineOutline.clicked.connect(self.outlineItemEditor.selectionChanged, AUC)
|
|
|
|
# Sync selection
|
|
self.treeRedacOutline.selectionModel().selectionChanged.connect(self.redacMetadata.selectionChanged, AUC)
|
|
self.treeRedacOutline.clicked.connect(self.redacMetadata.selectionChanged, AUC)
|
|
|
|
self.treeRedacOutline.selectionModel().selectionChanged.connect(self.mainEditor.selectionChanged, AUC)
|
|
|
|
# Cheat Sheet
|
|
self.cheatSheet.setModels()
|
|
|
|
# Debug
|
|
self.mdlFlatData.setVerticalHeaderLabels(["Infos générales", "Summary"])
|
|
self.tblDebugFlatData.setModel(self.mdlFlatData)
|
|
self.tblDebugPersos.setModel(self.mdlCharacter)
|
|
self.tblDebugPersosInfos.setModel(self.mdlCharacter)
|
|
self.tblDebugPersos.selectionModel().currentChanged.connect(
|
|
lambda: self.tblDebugPersosInfos.setRootIndex(self.mdlCharacter.index(
|
|
self.tblDebugPersos.selectionModel().currentIndex().row(),
|
|
Character.name.value)), AUC)
|
|
|
|
self.tblDebugPlots.setModel(self.mdlPlots)
|
|
self.tblDebugPlotsPersos.setModel(self.mdlPlots)
|
|
self.tblDebugSubPlots.setModel(self.mdlPlots)
|
|
self.tblDebugPlots.selectionModel().currentChanged.connect(
|
|
lambda: self.tblDebugPlotsPersos.setRootIndex(self.mdlPlots.index(
|
|
self.tblDebugPlots.selectionModel().currentIndex().row(),
|
|
Plot.characters.value)), AUC)
|
|
self.tblDebugPlots.selectionModel().currentChanged.connect(
|
|
lambda: self.tblDebugSubPlots.setRootIndex(self.mdlPlots.index(
|
|
self.tblDebugPlots.selectionModel().currentIndex().row(),
|
|
Plot.steps.value)), AUC)
|
|
self.treeDebugWorld.setModel(self.mdlWorld)
|
|
self.treeDebugOutline.setModel(self.mdlOutline)
|
|
self.lstDebugLabels.setModel(self.mdlLabels)
|
|
self.lstDebugStatus.setModel(self.mdlStatus)
|
|
|
|
def disconnectAll(self, signal, oldHandler=None):
|
|
# Disconnect all "oldHandler" slot connections for a signal
|
|
#
|
|
# Ref: PyQt Widget connect() and disconnect()
|
|
# https://stackoverflow.com/questions/21586643/pyqt-widget-connect-and-disconnect
|
|
#
|
|
# The loop is needed for safely disconnecting a specific handler,
|
|
# because it may have been connected multiple times, and
|
|
# disconnect only removes one connection at a time.
|
|
while True:
|
|
try:
|
|
if oldHandler is not None:
|
|
signal.disconnect(oldHandler)
|
|
else:
|
|
signal.disconnect()
|
|
except TypeError:
|
|
break
|
|
|
|
def breakConnections(self):
|
|
# Break connections for UI elements that were connected in makeConnections()
|
|
|
|
# Characters
|
|
self.disconnectAll(self.btnAddPerso.clicked, self.mdlCharacter.addCharacter)
|
|
self.disconnectAll(self.btnRmPerso.clicked, self.lstCharacters.removeCharacter)
|
|
self.disconnectAll(self.btnPersoColor.clicked, self.lstCharacters.choseCharacterColor)
|
|
self.disconnectAll(self.btnPersoAddInfo.clicked, self.lstCharacters.addCharacterInfo)
|
|
self.disconnectAll(self.btnPersoRmInfo.clicked, self.lstCharacters.removeCharacterInfo)
|
|
|
|
# Plots
|
|
self._updatingSubPlot = False
|
|
self.disconnectAll(self.btnAddPlot.clicked, self.mdlPlots.addPlot)
|
|
self.disconnectAll(self.btnRmPlot.clicked, lambda:
|
|
self.mdlPlots.removePlot(self.lstPlots.currentPlotIndex()))
|
|
self.disconnectAll(self.btnAddSubPlot.clicked, self.mdlPlots.addSubPlot)
|
|
self.disconnectAll(self.btnAddSubPlot.clicked, self.updateSubPlotView)
|
|
self.disconnectAll(self.btnRmSubPlot.clicked, self.mdlPlots.removeSubPlot)
|
|
self.disconnectAll(self.lstPlotPerso.selectionModel().selectionChanged, self.plotPersoSelectionChanged)
|
|
self.disconnectAll(self.btnRmPlotPerso.clicked, self.mdlPlots.removePlotPerso)
|
|
|
|
self.disconnectAll(self.mdlCharacter.dataChanged, self.mdlPlots.updatePlotPersoButton)
|
|
|
|
# World
|
|
self.disconnectAll(self.treeWorld.selectionModel().selectionChanged, self.changeCurrentWorld)
|
|
self.disconnectAll(self.btnAddWorld.clicked, self.mdlWorld.addItem)
|
|
self.disconnectAll(self.btnRmWorld.clicked, self.mdlWorld.removeItem)
|
|
|
|
# Outline
|
|
self.disconnectAll(self.treeOutlineOutline.selectionModel().selectionChanged, self.outlineItemEditor.selectionChanged)
|
|
self.disconnectAll(self.treeOutlineOutline.clicked, self.outlineItemEditor.selectionChanged)
|
|
|
|
# Sync selection
|
|
self.disconnectAll(self.treeRedacOutline.selectionModel().selectionChanged, self.redacMetadata.selectionChanged)
|
|
self.disconnectAll(self.treeRedacOutline.clicked, self.redacMetadata.selectionChanged)
|
|
|
|
self.disconnectAll(self.treeRedacOutline.selectionModel().selectionChanged, self.mainEditor.selectionChanged)
|
|
|
|
# Debug
|
|
self.disconnectAll(self.tblDebugPersos.selectionModel().currentChanged,
|
|
lambda: self.tblDebugPersosInfos.setRootIndex(self.mdlCharacter.index(
|
|
self.tblDebugPersos.selectionModel().currentIndex().row(),
|
|
Character.name.value)))
|
|
self.disconnectAll(self.tblDebugPlots.selectionModel().currentChanged,
|
|
lambda: self.tblDebugPlotsPersos.setRootIndex(self.mdlPlots.index(
|
|
self.tblDebugPlots.selectionModel().currentIndex().row(),
|
|
Plot.characters.value)))
|
|
self.disconnectAll(self.tblDebugPlots.selectionModel().currentChanged,
|
|
lambda: self.tblDebugSubPlots.setRootIndex(self.mdlPlots.index(
|
|
self.tblDebugPlots.selectionModel().currentIndex().row(),
|
|
Plot.steps.value)))
|
|
|
|
###############################################################################
|
|
# HELP
|
|
###############################################################################
|
|
|
|
def about(self):
|
|
self.dialog = aboutDialog(mw=self)
|
|
self.dialog.setFixedSize(self.dialog.size())
|
|
self.dialog.show()
|
|
# Center about dialog
|
|
r = self.dialog.geometry()
|
|
r2 = self.geometry()
|
|
self.dialog.move(r2.center() - r.center())
|
|
|
|
###############################################################################
|
|
# GENERAL AKA UNSORTED
|
|
###############################################################################
|
|
|
|
def clickCycle(self, i):
|
|
if i == 0: # step 2 - paragraph summary
|
|
self.tabMain.setCurrentIndex(self.TabSummary)
|
|
self.tabSummary.setCurrentIndex(1)
|
|
if i == 1: # step 3 - characters summary
|
|
self.tabMain.setCurrentIndex(self.TabPersos)
|
|
self.tabPersos.setCurrentIndex(0)
|
|
if i == 2: # step 4 - page summary
|
|
self.tabMain.setCurrentIndex(self.TabSummary)
|
|
self.tabSummary.setCurrentIndex(2)
|
|
if i == 3: # step 5 - characters description
|
|
self.tabMain.setCurrentIndex(self.TabPersos)
|
|
self.tabPersos.setCurrentIndex(1)
|
|
if i == 4: # step 6 - four page synopsis
|
|
self.tabMain.setCurrentIndex(self.TabSummary)
|
|
self.tabSummary.setCurrentIndex(3)
|
|
if i == 5: # step 7 - full character charts
|
|
self.tabMain.setCurrentIndex(self.TabPersos)
|
|
self.tabPersos.setCurrentIndex(2)
|
|
if i == 6: # step 8 - scene list
|
|
self.tabMain.setCurrentIndex(self.TabPlots)
|
|
|
|
def wordCount(self, i):
|
|
|
|
src = {
|
|
0: self.txtSummarySentence,
|
|
1: self.txtSummaryPara,
|
|
2: self.txtSummaryPage,
|
|
3: self.txtSummaryFull
|
|
}[i]
|
|
|
|
lbl = {
|
|
0: self.lblSummaryWCSentence,
|
|
1: self.lblSummaryWCPara,
|
|
2: self.lblSummaryWCPage,
|
|
3: self.lblSummaryWCFull
|
|
}[i]
|
|
|
|
wc = wordCount(src.toPlainText())
|
|
if i in [2, 3]:
|
|
pages = self.tr(" (~{} pages)").format(int(wc / 25) / 10.)
|
|
else:
|
|
pages = ""
|
|
lbl.setText(self.tr("Words: {}{}").format(wc, pages))
|
|
|
|
def setupMoreUi(self):
|
|
|
|
style.styleMainWindow(self)
|
|
|
|
# Tool bar on the right
|
|
self.toolbar = collapsibleDockWidgets(Qt.RightDockWidgetArea, self)
|
|
self.toolbar.addCustomWidget(self.tr("Book summary"), self.grpPlotSummary, self.TabPlots)
|
|
self.toolbar.addCustomWidget(self.tr("Project tree"), self.treeRedacWidget, self.TabRedac)
|
|
self.toolbar.addCustomWidget(self.tr("Metadata"), self.redacMetadata, self.TabRedac)
|
|
self.toolbar.addCustomWidget(self.tr("Story line"), self.storylineView, self.TabRedac)
|
|
if self._toolbarState:
|
|
self.toolbar.restoreState(self._toolbarState)
|
|
|
|
# Custom "tab" bar on the left
|
|
self.lstTabs.setIconSize(QSize(48, 48))
|
|
for i in range(self.tabMain.count()):
|
|
icons = ["general-128px.png",
|
|
"summary-128px.png",
|
|
"characters-128px.png",
|
|
"plot-128px.png",
|
|
"world-128px.png",
|
|
"outline-128px.png",
|
|
"redaction-128px.png",
|
|
""
|
|
]
|
|
self.tabMain.setTabIcon(i, QIcon(appPath("icons/Custom/Tabs/{}".format(icons[i]))))
|
|
item = QListWidgetItem(self.tabMain.tabIcon(i),
|
|
self.tabMain.tabText(i))
|
|
item.setSizeHint(QSize(item.sizeHint().width(), 64))
|
|
item.setTextAlignment(Qt.AlignCenter)
|
|
self.lstTabs.addItem(item)
|
|
self.tabMain.tabBar().hide()
|
|
self.lstTabs.currentRowChanged.connect(self.tabMain.setCurrentIndex)
|
|
self.tabMain.currentChanged.connect(self.lstTabs.setCurrentRow)
|
|
|
|
# Splitters
|
|
self.splitterPersos.setStretchFactor(0, 25)
|
|
self.splitterPersos.setStretchFactor(1, 75)
|
|
|
|
self.splitterPlot.setStretchFactor(0, 20)
|
|
self.splitterPlot.setStretchFactor(1, 60)
|
|
self.splitterPlot.setStretchFactor(2, 30)
|
|
|
|
self.splitterWorld.setStretchFactor(0, 25)
|
|
self.splitterWorld.setStretchFactor(1, 75)
|
|
|
|
self.splitterOutlineH.setStretchFactor(0, 25)
|
|
self.splitterOutlineH.setStretchFactor(1, 75)
|
|
self.splitterOutlineV.setStretchFactor(0, 75)
|
|
self.splitterOutlineV.setStretchFactor(1, 25)
|
|
|
|
self.splitterRedacV.setStretchFactor(0, 75)
|
|
self.splitterRedacV.setStretchFactor(1, 25)
|
|
|
|
self.splitterRedacH.setStretchFactor(0, 30)
|
|
self.splitterRedacH.setStretchFactor(1, 40)
|
|
self.splitterRedacH.setStretchFactor(2, 30)
|
|
|
|
# QFormLayout stretch
|
|
for w in [self.txtWorldDescription, self.txtWorldPassion, self.txtWorldConflict]:
|
|
s = w.sizePolicy()
|
|
s.setVerticalStretch(1)
|
|
w.setSizePolicy(s)
|
|
|
|
# Help box
|
|
references = [
|
|
(self.lytTabOverview,
|
|
self.tr("Enter infos about your book, and yourself."),
|
|
0),
|
|
(self.lytSituation,
|
|
self.tr(
|
|
"""The basic situation, in the form of a 'What if...?' question. Ex: 'What if the most dangerous
|
|
evil wizard could wasn't abled to kill a baby?' (Harry Potter)"""),
|
|
1),
|
|
(self.lytSummary,
|
|
self.tr(
|
|
"""Take time to think about a one sentence (~50 words) summary of your book. Then expand it to
|
|
a paragraph, then to a page, then to a full summary."""),
|
|
1),
|
|
(self.lytTabPersos,
|
|
self.tr("Create your characters."),
|
|
0),
|
|
(self.lytTabPlot,
|
|
self.tr("Develop plots."),
|
|
0),
|
|
(self.lytTabOutline,
|
|
self.tr("Create the outline of your masterpiece."),
|
|
0),
|
|
(self.lytTabRedac,
|
|
self.tr("Write."),
|
|
0),
|
|
(self.lytTabDebug,
|
|
self.tr("Debug infos. Sometimes useful."),
|
|
0)
|
|
]
|
|
|
|
for widget, text, pos in references:
|
|
label = helpLabel(text, self)
|
|
self.actShowHelp.toggled.connect(label.setVisible, AUC)
|
|
widget.layout().insertWidget(pos, label)
|
|
|
|
self.actShowHelp.setChecked(False)
|
|
|
|
# Spellcheck
|
|
if enchant:
|
|
self.menuDict = QMenu(self.tr("Dictionary"))
|
|
self.menuDictGroup = QActionGroup(self)
|
|
self.updateMenuDict()
|
|
self.menuTools.addMenu(self.menuDict)
|
|
|
|
self.actSpellcheck.toggled.connect(self.toggleSpellcheck, AUC)
|
|
self.dictChanged.connect(self.mainEditor.setDict, AUC)
|
|
self.dictChanged.connect(self.redacMetadata.setDict, AUC)
|
|
self.dictChanged.connect(self.outlineItemEditor.setDict, AUC)
|
|
|
|
else:
|
|
# No Spell check support
|
|
self.actSpellcheck.setVisible(False)
|
|
a = QAction(self.tr("Install PyEnchant to use spellcheck"), self)
|
|
a.setIcon(self.style().standardIcon(QStyle.SP_MessageBoxWarning))
|
|
a.triggered.connect(self.openPyEnchantWebPage, AUC)
|
|
self.menuTools.addAction(a)
|
|
|
|
###############################################################################
|
|
# SPELLCHECK
|
|
###############################################################################
|
|
|
|
def updateMenuDict(self):
|
|
|
|
if not enchant:
|
|
return
|
|
|
|
self.menuDict.clear()
|
|
for i in enchant.list_dicts():
|
|
a = QAction(str(i[0]), self)
|
|
a.setCheckable(True)
|
|
if settings.dict is None:
|
|
settings.dict = enchant.get_default_language()
|
|
if str(i[0]) == settings.dict:
|
|
a.setChecked(True)
|
|
a.triggered.connect(self.setDictionary, AUC)
|
|
self.menuDictGroup.addAction(a)
|
|
self.menuDict.addAction(a)
|
|
|
|
def setDictionary(self):
|
|
if not enchant:
|
|
return
|
|
|
|
for i in self.menuDictGroup.actions():
|
|
if i.isChecked():
|
|
# self.dictChanged.emit(i.text().replace("&", ""))
|
|
settings.dict = i.text().replace("&", "")
|
|
|
|
# Find all textEditView from self, and toggle spellcheck
|
|
for w in self.findChildren(textEditView, QRegExp(".*"),
|
|
Qt.FindChildrenRecursively):
|
|
w.setDict(settings.dict)
|
|
|
|
def openPyEnchantWebPage(self):
|
|
from PyQt5.QtGui import QDesktopServices
|
|
QDesktopServices.openUrl(QUrl("http://pythonhosted.org/pyenchant/"))
|
|
|
|
def toggleSpellcheck(self, val):
|
|
settings.spellcheck = val
|
|
|
|
# Find all textEditView from self, and toggle spellcheck
|
|
for w in self.findChildren(textEditView, QRegExp(".*"),
|
|
Qt.FindChildrenRecursively):
|
|
w.toggleSpellcheck(val)
|
|
|
|
###############################################################################
|
|
# SETTINGS
|
|
###############################################################################
|
|
|
|
def settingsLabel(self):
|
|
self.settingsWindow(3)
|
|
|
|
def settingsStatus(self):
|
|
self.settingsWindow(4)
|
|
|
|
def settingsWindow(self, tab=None):
|
|
self.sw = settingsWindow(self)
|
|
self.sw.hide()
|
|
self.sw.setWindowModality(Qt.ApplicationModal)
|
|
self.sw.setWindowFlags(Qt.Dialog)
|
|
r = self.sw.geometry()
|
|
r2 = self.geometry()
|
|
self.sw.move(r2.center() - r.center())
|
|
if tab:
|
|
self.sw.setTab(tab)
|
|
self.sw.show()
|
|
|
|
###############################################################################
|
|
# TOOLS
|
|
###############################################################################
|
|
|
|
def frequencyAnalyzer(self):
|
|
self.fw = frequencyAnalyzer(self)
|
|
self.fw.show()
|
|
|
|
###############################################################################
|
|
# VIEW MENU
|
|
###############################################################################
|
|
|
|
def generateViewMenu(self):
|
|
|
|
values = [
|
|
(self.tr("Nothing"), "Nothing"),
|
|
(self.tr("POV"), "POV"),
|
|
(self.tr("Label"), "Label"),
|
|
(self.tr("Progress"), "Progress"),
|
|
(self.tr("Compile"), "Compile"),
|
|
]
|
|
|
|
menus = [
|
|
(self.tr("Tree"), "Tree"),
|
|
(self.tr("Index cards"), "Cork"),
|
|
(self.tr("Outline"), "Outline")
|
|
]
|
|
|
|
submenus = {
|
|
"Tree": [
|
|
(self.tr("Icon color"), "Icon"),
|
|
(self.tr("Text color"), "Text"),
|
|
(self.tr("Background color"), "Background"),
|
|
],
|
|
"Cork": [
|
|
(self.tr("Icon"), "Icon"),
|
|
(self.tr("Text"), "Text"),
|
|
(self.tr("Background"), "Background"),
|
|
(self.tr("Border"), "Border"),
|
|
(self.tr("Corner"), "Corner"),
|
|
],
|
|
"Outline": [
|
|
(self.tr("Icon color"), "Icon"),
|
|
(self.tr("Text color"), "Text"),
|
|
(self.tr("Background color"), "Background"),
|
|
],
|
|
}
|
|
|
|
self.menuView.clear()
|
|
self.menuView.addMenu(self.menuMode)
|
|
self.menuView.addSeparator()
|
|
|
|
# print("Generating menus with", settings.viewSettings)
|
|
|
|
for mnu, mnud in menus:
|
|
m = QMenu(mnu, self.menuView)
|
|
for s, sd in submenus[mnud]:
|
|
m2 = QMenu(s, m)
|
|
agp = QActionGroup(m2)
|
|
for v, vd in values:
|
|
a = QAction(v, m)
|
|
a.setCheckable(True)
|
|
a.setData("{},{},{}".format(mnud, sd, vd))
|
|
if settings.viewSettings[mnud][sd] == vd:
|
|
a.setChecked(True)
|
|
a.triggered.connect(self.setViewSettingsAction, AUC)
|
|
agp.addAction(a)
|
|
m2.addAction(a)
|
|
m.addMenu(m2)
|
|
self.menuView.addMenu(m)
|
|
|
|
def setViewSettingsAction(self):
|
|
action = self.sender()
|
|
item, part, element = action.data().split(",")
|
|
self.setViewSettings(item, part, element)
|
|
|
|
def setViewSettings(self, item, part, element):
|
|
settings.viewSettings[item][part] = element
|
|
if item == "Cork":
|
|
self.mainEditor.updateCorkView()
|
|
if item == "Outline":
|
|
self.mainEditor.updateTreeView()
|
|
self.treeOutlineOutline.viewport().update()
|
|
if item == "Tree":
|
|
self.treeRedacOutline.viewport().update()
|
|
|
|
###############################################################################
|
|
# VIEW MODES
|
|
###############################################################################
|
|
|
|
def setViewModeSimple(self):
|
|
settings.viewMode = "simple"
|
|
self.tabMain.setCurrentIndex(self.TabRedac)
|
|
self.viewModeFictionVisibilitySwitch(False)
|
|
self.actModeSimple.setChecked(True)
|
|
|
|
def setViewModeFiction(self):
|
|
settings.viewMode = "fiction"
|
|
self.viewModeFictionVisibilitySwitch(True)
|
|
self.actModeFiction.setChecked(True)
|
|
|
|
def viewModeFictionVisibilitySwitch(self, val):
|
|
"""
|
|
Swtiches the visibility of some UI components useful for fiction only
|
|
@param val: sets visibility to val
|
|
"""
|
|
|
|
# Menu navigation & boutton in toolbar
|
|
self.toolbar.setDockVisibility(self.dckNavigation, val)
|
|
|
|
# POV in metadatas
|
|
from manuskript.ui.views.propertiesView import propertiesView
|
|
for w in findWidgetsOfClass(propertiesView):
|
|
w.lblPOV.setVisible(val)
|
|
w.cmbPOV.setVisible(val)
|
|
|
|
# POV in outline view
|
|
if Outline.POV.value in settings.outlineViewColumns:
|
|
settings.outlineViewColumns.remove(Outline.POV.value)
|
|
|
|
from manuskript.ui.views.outlineView import outlineView
|
|
for w in findWidgetsOfClass(outlineView):
|
|
w.hideColumns()
|
|
|
|
# TODO: clean up all other fiction things in non-fiction view mode
|
|
# Character in search widget
|
|
# POV in settings / views
|
|
|
|
###############################################################################
|
|
# COMPILE
|
|
###############################################################################
|
|
|
|
def doCompile(self):
|
|
self.dialog = exporterDialog(mw=self)
|
|
self.dialog.show()
|
|
|
|
r = self.dialog.geometry()
|
|
r2 = self.geometry()
|
|
self.dialog.move(r2.center() - r.center())
|
|
|