Clean up imports
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Main Window Elements
|
||||
from nw.gui.mainmenu import GuiMainMenu
|
||||
from nw.gui.statusbar import GuiMainStatus
|
||||
|
||||
# Dialogs
|
||||
from nw.gui.dialogs.configeditor import GuiConfigEditor
|
||||
from nw.gui.dialogs.export import GuiExport
|
||||
from nw.gui.dialogs.itemeditor import GuiItemEditor
|
||||
from nw.gui.dialogs.projecteditor import GuiProjectEditor
|
||||
from nw.gui.dialogs.sessionlog import GuiSessionLogView
|
||||
from nw.gui.dialogs.timelineview import GuiTimeLineView
|
||||
|
||||
# GUI Elements
|
||||
from nw.gui.elements.docdetails import GuiDocDetails
|
||||
from nw.gui.elements.doceditor import GuiDocEditor
|
||||
from nw.gui.elements.doctree import GuiDocTree
|
||||
from nw.gui.elements.docviewer import GuiDocViewer
|
||||
from nw.gui.elements.noticebar import GuiNoticeBar
|
||||
from nw.gui.elements.searchbar import GuiSearchBar
|
||||
from nw.gui.elements.viewdetails import GuiDocViewDetails
|
||||
|
||||
# Tools
|
||||
from nw.gui.tools.dochighlight import GuiDocHighlighter
|
||||
from nw.gui.tools.wordcounter import WordCounter
|
||||
|
||||
__all__ = [
|
||||
GuiMainMenu,
|
||||
GuiMainStatus,
|
||||
GuiConfigEditor,
|
||||
GuiExport,
|
||||
GuiItemEditor,
|
||||
GuiProjectEditor,
|
||||
GuiSessionLogView,
|
||||
GuiTimeLineView,
|
||||
GuiDocDetails,
|
||||
GuiDocEditor,
|
||||
GuiDocTree,
|
||||
GuiDocViewer,
|
||||
GuiNoticeBar,
|
||||
GuiSearchBar,
|
||||
GuiDocViewDetails,
|
||||
GuiDocHighlighter,
|
||||
WordCounter,
|
||||
]
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from nw.gui.dialogs.configeditor import GuiConfigEditor
|
||||
from nw.gui.dialogs.export import GuiExport
|
||||
from nw.gui.dialogs.itemeditor import GuiItemEditor
|
||||
from nw.gui.dialogs.projecteditor import GuiProjectEditor
|
||||
from nw.gui.dialogs.sessionlog import GuiSessionLogView
|
||||
from nw.gui.dialogs.timelineview import GuiTimeLineView
|
||||
|
||||
__all__ = [
|
||||
GuiConfigEditor,
|
||||
GuiExport,
|
||||
GuiItemEditor,
|
||||
GuiProjectEditor,
|
||||
GuiSessionLogView,
|
||||
GuiTimeLineView,
|
||||
]
|
||||
|
||||
@@ -19,10 +19,12 @@ from PyQt5.QtCore import Qt, QSize
|
||||
from PyQt5.QtGui import QIcon, QPixmap, QColor, QBrush, QStandardItemModel, QFont
|
||||
from PyQt5.QtSvg import QSvgWidget
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QHBoxLayout, QVBoxLayout, QFormLayout, QLineEdit, QPlainTextEdit, QLabel,
|
||||
QWidget, QTabWidget, QDialogButtonBox, QSpinBox, QGroupBox, QComboBox, QMessageBox,
|
||||
QCheckBox, QGridLayout, QFontComboBox, QPushButton, QFileDialog
|
||||
QDialog, QHBoxLayout, QVBoxLayout, QFormLayout, QLineEdit, QPlainTextEdit,
|
||||
QLabel, QWidget, QTabWidget, QDialogButtonBox, QSpinBox, QGroupBox,
|
||||
QComboBox, QMessageBox, QCheckBox, QGridLayout, QFontComboBox, QPushButton,
|
||||
QFileDialog
|
||||
)
|
||||
|
||||
from nw.enum import nwAlert
|
||||
from nw.constants import nwQuotes
|
||||
|
||||
|
||||
@@ -24,16 +24,11 @@ from PyQt5.QtWidgets import (
|
||||
QFileDialog, QProgressBar, QSpinBox, QMessageBox
|
||||
)
|
||||
|
||||
from nw.project.document import NWDoc
|
||||
from nw.tools.translate import numberToWord
|
||||
from nw.tools.optlaststate import OptLastState
|
||||
from nw.convert.file.text import TextFile
|
||||
from nw.convert.file.html import HtmlFile
|
||||
from nw.convert.file.markdown import MarkdownFile
|
||||
from nw.convert.file.latex import LaTeXFile
|
||||
from nw.convert.file.concat import ConcatFile
|
||||
from nw.common import packageRefURL
|
||||
from nw.convert import TextFile, HtmlFile, MarkdownFile, LaTeXFile, ConcatFile
|
||||
from nw.project import NWDoc
|
||||
from nw.tools import numberToWord, OptLastState
|
||||
from nw.constants import nwFiles
|
||||
from nw.common import packageRefURL
|
||||
from nw.enum import nwItemType, nwAlert
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -24,6 +24,7 @@ from PyQt5.QtWidgets import (
|
||||
QListWidgetItem, QPushButton, QColorDialog, QAbstractItemView, QTreeWidget,
|
||||
QTreeWidgetItem, QCheckBox
|
||||
)
|
||||
|
||||
from nw.enum import nwAlert
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -24,8 +24,8 @@ from PyQt5.QtWidgets import (
|
||||
QCheckBox
|
||||
)
|
||||
|
||||
from nw.tools.optlaststate import OptLastState
|
||||
from nw.constants import nwConst, nwFiles
|
||||
from nw.tools import OptLastState
|
||||
from nw.enum import nwAlert
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -23,8 +23,8 @@ from PyQt5.QtWidgets import (
|
||||
QGroupBox, QCheckBox
|
||||
)
|
||||
|
||||
from nw.tools.optlaststate import OptLastState
|
||||
from nw.constants import nwFiles
|
||||
from nw.tools import OptLastState
|
||||
from nw.enum import nwItemClass
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from nw.gui.elements.docdetails import GuiDocDetails
|
||||
from nw.gui.elements.doceditor import GuiDocEditor
|
||||
from nw.gui.elements.doctree import GuiDocTree
|
||||
from nw.gui.elements.docviewer import GuiDocViewer
|
||||
from nw.gui.elements.noticebar import GuiNoticeBar
|
||||
from nw.gui.elements.searchbar import GuiSearchBar
|
||||
from nw.gui.elements.viewdetails import GuiDocViewDetails
|
||||
|
||||
__all__ = [
|
||||
GuiDocDetails,
|
||||
GuiDocEditor,
|
||||
GuiDocTree,
|
||||
GuiDocViewer,
|
||||
GuiNoticeBar,
|
||||
GuiSearchBar,
|
||||
GuiDocViewDetails,
|
||||
]
|
||||
|
||||
@@ -24,10 +24,9 @@ from PyQt5.QtGui import (
|
||||
QPalette, QTextDocument,
|
||||
)
|
||||
|
||||
from nw.project.document import NWDoc
|
||||
from nw.gui.tools.dochighlight import GuiDocHighlighter
|
||||
from nw.gui.tools.wordcounter import WordCounter
|
||||
from nw.tools.spellcheck import NWSpellCheck
|
||||
from nw.project import NWDoc
|
||||
from nw.gui.tools import GuiDocHighlighter, WordCounter
|
||||
from nw.tools import NWSpellCheck
|
||||
from nw.constants import nwFiles, nwUnicode
|
||||
from nw.enum import nwDocAction, nwAlert
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ from PyQt5.QtWidgets import (
|
||||
QTreeWidget, QTreeWidgetItem, QAbstractItemView, QApplication
|
||||
)
|
||||
|
||||
from nw.project.item import NWItem
|
||||
from nw.project import NWItem
|
||||
from nw.constants import nwLabels
|
||||
from nw.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert
|
||||
|
||||
|
||||
@@ -17,8 +17,7 @@ from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QTextBrowser
|
||||
from PyQt5.QtGui import QTextOption, QFont, QPalette, QColor
|
||||
|
||||
from nw.convert.tokenizer import Tokenizer
|
||||
from nw.convert.text.tohtml import ToHtml
|
||||
from nw.convert import Tokenizer, ToHtml
|
||||
from nw.enum import nwAlert, nwItemType
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from nw.gui.tools.dochighlight import GuiDocHighlighter
|
||||
from nw.gui.tools.wordcounter import WordCounter
|
||||
|
||||
__all__ = [
|
||||
GuiDocHighlighter,
|
||||
WordCounter,
|
||||
]
|
||||
|
||||
@@ -1,833 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""novelWriter GUI Main Window
|
||||
|
||||
novelWriter – GUI Main Window
|
||||
===============================
|
||||
Class holding the main window
|
||||
|
||||
File History:
|
||||
Created: 2018-09-22 [0.0.1]
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
import nw
|
||||
|
||||
from os import path
|
||||
|
||||
from PyQt5.QtCore import Qt, QTimer
|
||||
from PyQt5.QtGui import QIcon, QPixmap, QColor
|
||||
from PyQt5.QtWidgets import (
|
||||
qApp, QWidget, QMainWindow, QVBoxLayout, QFrame, QSplitter, QFileDialog,
|
||||
QShortcut, QMessageBox, QProgressDialog, QDialog
|
||||
)
|
||||
|
||||
from nw.gui.mainmenu import GuiMainMenu
|
||||
from nw.gui.statusbar import GuiMainStatus
|
||||
from nw.gui.elements.doctree import GuiDocTree
|
||||
from nw.gui.elements.doceditor import GuiDocEditor
|
||||
from nw.gui.elements.docviewer import GuiDocViewer
|
||||
from nw.gui.elements.docdetails import GuiDocDetails
|
||||
from nw.gui.elements.searchbar import GuiSearchBar
|
||||
from nw.gui.elements.noticebar import GuiNoticeBar
|
||||
from nw.gui.elements.viewdetails import GuiDocViewDetails
|
||||
from nw.gui.dialogs.configeditor import GuiConfigEditor
|
||||
from nw.gui.dialogs.projecteditor import GuiProjectEditor
|
||||
from nw.gui.dialogs.export import GuiExport
|
||||
from nw.gui.dialogs.itemeditor import GuiItemEditor
|
||||
from nw.gui.dialogs.timelineview import GuiTimeLineView
|
||||
from nw.gui.dialogs.sessionlog import GuiSessionLogView
|
||||
from nw.project.project import NWProject
|
||||
from nw.project.document import NWDoc
|
||||
from nw.project.item import NWItem
|
||||
from nw.project.index import NWIndex
|
||||
from nw.project.backup import NWBackup
|
||||
from nw.tools.wordcount import countWords
|
||||
from nw.theme import Theme
|
||||
from nw.enum import nwItemType, nwAlert
|
||||
from nw.constants import nwFiles
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiMain(QMainWindow):
|
||||
|
||||
def __init__(self):
|
||||
QWidget.__init__(self)
|
||||
|
||||
logger.info("Starting %s" % nw.__package__)
|
||||
logger.debug("Initialising GUI ...")
|
||||
self.mainConf = nw.CONFIG
|
||||
self.theTheme = Theme(self)
|
||||
self.theProject = NWProject(self)
|
||||
self.theIndex = NWIndex(self.theProject, self)
|
||||
self.hasProject = False
|
||||
|
||||
logger.info("OS: %s" % (
|
||||
self.mainConf.osType)
|
||||
)
|
||||
logger.info("Qt5 Version: %s (%d)" % (
|
||||
self.mainConf.verQtString, self.mainConf.verQtValue)
|
||||
)
|
||||
logger.info("PyQt5 Version: %s (%d)" % (
|
||||
self.mainConf.verPyQtString, self.mainConf.verPyQtValue)
|
||||
)
|
||||
logger.info("Python Version: %s (0x%x)" % (
|
||||
self.mainConf.verPyString, self.mainConf.verPyHexVal)
|
||||
)
|
||||
|
||||
self.resize(*self.mainConf.winGeometry)
|
||||
self._setWindowTitle()
|
||||
self.setWindowIcon(QIcon(path.join(self.mainConf.appIcon)))
|
||||
|
||||
# Main GUI Elements
|
||||
self.statusBar = GuiMainStatus(self)
|
||||
self.noticeBar = GuiNoticeBar(self)
|
||||
self.docEditor = GuiDocEditor(self, self.theProject)
|
||||
self.docViewer = GuiDocViewer(self, self.theProject)
|
||||
self.viewMeta = GuiDocViewDetails(self, self.theProject)
|
||||
self.searchBar = GuiSearchBar(self)
|
||||
self.treeMeta = GuiDocDetails(self, self.theProject)
|
||||
self.treeView = GuiDocTree(self, self.theProject)
|
||||
self.mainMenu = GuiMainMenu(self, self.theProject)
|
||||
|
||||
# Minor Gui Elements
|
||||
self.statusIcons = []
|
||||
self.importIcons = []
|
||||
|
||||
# Assemble Main Window
|
||||
self.treePane = QFrame()
|
||||
self.treeBox = QVBoxLayout()
|
||||
self.treeBox.setContentsMargins(0,0,0,0)
|
||||
self.treeBox.addWidget(self.treeView)
|
||||
self.treeBox.addWidget(self.treeMeta)
|
||||
self.treePane.setLayout(self.treeBox)
|
||||
|
||||
self.editPane = QFrame()
|
||||
self.docEdit = QVBoxLayout()
|
||||
self.docEdit.setContentsMargins(0,0,0,0)
|
||||
self.docEdit.addWidget(self.searchBar)
|
||||
self.docEdit.addWidget(self.noticeBar)
|
||||
self.docEdit.addWidget(self.docEditor)
|
||||
self.editPane.setLayout(self.docEdit)
|
||||
|
||||
self.viewPane = QFrame()
|
||||
self.docView = QVBoxLayout()
|
||||
self.docView.setContentsMargins(0,0,0,0)
|
||||
self.docView.addWidget(self.docViewer)
|
||||
self.docView.addWidget(self.viewMeta)
|
||||
self.docView.setStretch(0, 1)
|
||||
self.viewPane.setLayout(self.docView)
|
||||
|
||||
self.splitView = QSplitter(Qt.Horizontal)
|
||||
self.splitView.addWidget(self.editPane)
|
||||
self.splitView.addWidget(self.viewPane)
|
||||
self.splitView.splitterMoved.connect(self._splitViewMove)
|
||||
|
||||
self.splitMain = QSplitter(Qt.Horizontal)
|
||||
self.splitMain.setContentsMargins(4,4,4,4)
|
||||
self.splitMain.addWidget(self.treePane)
|
||||
self.splitMain.addWidget(self.splitView)
|
||||
self.splitMain.setSizes(self.mainConf.mainPanePos)
|
||||
self.splitMain.splitterMoved.connect(self._splitMainMove)
|
||||
|
||||
self.setCentralWidget(self.splitMain)
|
||||
|
||||
self.idxTree = self.splitMain.indexOf(self.treePane)
|
||||
self.idxMain = self.splitMain.indexOf(self.splitView)
|
||||
self.idxEditor = self.splitView.indexOf(self.editPane)
|
||||
self.idxViewer = self.splitView.indexOf(self.viewPane)
|
||||
|
||||
self.splitMain.setCollapsible(self.idxTree, False)
|
||||
self.splitMain.setCollapsible(self.idxMain, False)
|
||||
self.splitView.setCollapsible(self.idxEditor, False)
|
||||
self.splitView.setCollapsible(self.idxViewer, True)
|
||||
|
||||
self.viewPane.setVisible(False)
|
||||
self.searchBar.setVisible(False)
|
||||
|
||||
# Build The Tree View
|
||||
self.treeView.itemSelectionChanged.connect(self._treeSingleClick)
|
||||
self.treeView.itemDoubleClicked.connect(self._treeDoubleClick)
|
||||
self.rebuildTree()
|
||||
|
||||
# Set Main Window Elements
|
||||
self.setMenuBar(self.mainMenu)
|
||||
self.setStatusBar(self.statusBar)
|
||||
self.statusBar.setStatus("Ready")
|
||||
|
||||
# Set Up Autosaving Project Timer
|
||||
self.asProjTimer = QTimer()
|
||||
self.asProjTimer.timeout.connect(self._autoSaveProject)
|
||||
|
||||
# Set Up Autosaving Document Timer
|
||||
self.asDocTimer = QTimer()
|
||||
self.asDocTimer.timeout.connect(self._autoSaveDocument)
|
||||
|
||||
# Keyboard Shortcuts
|
||||
QShortcut(
|
||||
Qt.Key_Return,
|
||||
self.treeView,
|
||||
context=Qt.WidgetShortcut,
|
||||
activated=self._treeKeyPressReturn
|
||||
)
|
||||
QShortcut(
|
||||
Qt.Key_Escape,
|
||||
self,
|
||||
activated=self._keyPressEscape
|
||||
)
|
||||
|
||||
# Forward Functions
|
||||
self.setStatus = self.statusBar.setStatus
|
||||
self.setProjectStatus = self.statusBar.setProjectStatus
|
||||
|
||||
if self.mainConf.showGUI:
|
||||
self.show()
|
||||
|
||||
self.initMain()
|
||||
self.asProjTimer.start()
|
||||
self.asDocTimer.start()
|
||||
self.statusBar.clearStatus()
|
||||
|
||||
logger.debug("GUI initialisation complete")
|
||||
|
||||
return
|
||||
|
||||
def clearGUI(self):
|
||||
self.treeView.clearTree()
|
||||
self.docEditor.clearEditor()
|
||||
self.closeDocViewer()
|
||||
self.statusBar.clearStatus()
|
||||
return True
|
||||
|
||||
def initMain(self):
|
||||
self.asProjTimer.setInterval(int(self.mainConf.autoSaveProj*1000))
|
||||
self.asDocTimer.setInterval(int(self.mainConf.autoSaveDoc*1000))
|
||||
return True
|
||||
|
||||
##
|
||||
# Project Actions
|
||||
##
|
||||
|
||||
def newProject(self, projPath=None, forceNew=False):
|
||||
|
||||
if self.hasProject:
|
||||
msgBox = QMessageBox()
|
||||
msgRes = msgBox.warning(
|
||||
self, "New Project",
|
||||
"Please close the current project<br>before making a new one."
|
||||
)
|
||||
return False
|
||||
|
||||
if projPath is None:
|
||||
projPath = self.newProjectDialog()
|
||||
if projPath is None:
|
||||
return False
|
||||
|
||||
if path.isfile(path.join(projPath,self.theProject.projFile)) and not forceNew:
|
||||
msgBox = QMessageBox()
|
||||
msgRes = msgBox.critical(
|
||||
self, "New Project",
|
||||
"A project already exists in that location.<br>Please choose another folder."
|
||||
)
|
||||
return False
|
||||
|
||||
logger.info("Creating new project")
|
||||
self.theProject.newProject()
|
||||
self.theProject.setProjectPath(projPath)
|
||||
self.rebuildTree()
|
||||
self.saveProject()
|
||||
self.hasProject = True
|
||||
self.statusBar.setRefTime(self.theProject.projOpened)
|
||||
|
||||
return True
|
||||
|
||||
def closeProject(self, isYes=False):
|
||||
"""Closes the project if one is open.
|
||||
isYes is passed on from the close application event so the user
|
||||
doesn't get prompted twice.
|
||||
"""
|
||||
if not self.hasProject:
|
||||
# There is no project loaded, everything OK
|
||||
return True
|
||||
|
||||
if self.mainConf.showGUI and not isYes:
|
||||
msgBox = QMessageBox()
|
||||
msgRes = msgBox.question(
|
||||
self, "Close Project", "Save changes and close current project?"
|
||||
)
|
||||
if msgRes != QMessageBox.Yes:
|
||||
return False
|
||||
|
||||
if self.docEditor.docChanged:
|
||||
self.saveDocument()
|
||||
|
||||
if self.theProject.projAltered:
|
||||
saveOK = self.saveProject()
|
||||
doBackup = False
|
||||
if self.theProject.doBackup and self.mainConf.backupOnClose:
|
||||
doBackup = True
|
||||
if self.mainConf.showGUI and self.mainConf.askBeforeBackup:
|
||||
msgBox = QMessageBox()
|
||||
msgRes = msgBox.question(
|
||||
self, "Backup Project", "Backup current project?"
|
||||
)
|
||||
if msgRes != QMessageBox.Yes:
|
||||
doBackup = False
|
||||
if doBackup:
|
||||
self.backupProject()
|
||||
else:
|
||||
saveOK = True
|
||||
|
||||
if saveOK:
|
||||
self.closeDocument()
|
||||
self.theProject.closeProject()
|
||||
self.theIndex.clearIndex()
|
||||
self.clearGUI()
|
||||
self.hasProject = False
|
||||
|
||||
return saveOK
|
||||
|
||||
def openProject(self, projFile=None):
|
||||
"""Open a project. The parameter projFile is passed from the
|
||||
open recent projects menu, so can be set. If not, we pop the
|
||||
dialog.
|
||||
"""
|
||||
if projFile is None:
|
||||
projFile = self.openProjectDialog()
|
||||
if projFile is None:
|
||||
return False
|
||||
|
||||
# Make sure any open project is cleared out first before we load
|
||||
# another one
|
||||
if not self.closeProject():
|
||||
return False
|
||||
|
||||
# Try to open the project
|
||||
if not self.theProject.openProject(projFile):
|
||||
return False
|
||||
|
||||
# project is loaded
|
||||
self.hasProject = True
|
||||
|
||||
# Load the tag index
|
||||
self.theIndex.loadIndex()
|
||||
|
||||
# Update GUI
|
||||
self._setWindowTitle(self.theProject.projName)
|
||||
self.rebuildTree()
|
||||
self.docEditor.setDictionaries()
|
||||
self.docEditor.setSpellCheck(self.theProject.spellCheck)
|
||||
self.statusBar.setRefTime(self.theProject.projOpened)
|
||||
self.mainMenu.updateMenu()
|
||||
|
||||
# Restore previously open documents, if any
|
||||
if self.theProject.lastEdited is not None:
|
||||
self.openDocument(self.theProject.lastEdited)
|
||||
if self.theProject.lastViewed is not None:
|
||||
self.viewDocument(self.theProject.lastViewed)
|
||||
|
||||
# Check if we need to rebuild the index
|
||||
if self.theIndex.indexBroken:
|
||||
self.rebuildIndex()
|
||||
|
||||
return True
|
||||
|
||||
def saveProject(self, isAuto=False):
|
||||
"""Save the current project.
|
||||
"""
|
||||
if not self.hasProject:
|
||||
return False
|
||||
|
||||
# If the project is new, it may not have a path, so we need one
|
||||
if self.theProject.projPath is None:
|
||||
projPath = self.saveProjectDialog()
|
||||
self.theProject.setProjectPath(projPath)
|
||||
if self.theProject.projPath is None:
|
||||
return False
|
||||
|
||||
self.treeView.saveTreeOrder()
|
||||
self.theProject.saveProject(isAuto)
|
||||
self.theIndex.saveIndex()
|
||||
self.mainMenu.updateRecentProjects()
|
||||
|
||||
return True
|
||||
|
||||
def backupProject(self):
|
||||
theBackup = NWBackup(self, self.theProject)
|
||||
theBackup.zipIt()
|
||||
return True
|
||||
|
||||
##
|
||||
# Document Actions
|
||||
##
|
||||
|
||||
def closeDocument(self):
|
||||
if self.hasProject:
|
||||
if self.docEditor.docChanged:
|
||||
self.saveDocument()
|
||||
self.docEditor.clearEditor()
|
||||
return True
|
||||
|
||||
def openDocument(self, tHandle):
|
||||
if self.hasProject:
|
||||
self.closeDocument()
|
||||
if self.docEditor.loadText(tHandle):
|
||||
self.docEditor.setFocus()
|
||||
self.docEditor.changeWidth()
|
||||
self.theProject.setLastEdited(tHandle)
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
def saveDocument(self):
|
||||
if self.hasProject:
|
||||
self.docEditor.saveText()
|
||||
return True
|
||||
|
||||
def viewDocument(self, tHandle=None):
|
||||
|
||||
if tHandle is None:
|
||||
tHandle = self.treeView.getSelectedHandle()
|
||||
if tHandle is None:
|
||||
logger.debug("No document selected, trying editor document")
|
||||
tHandle = self.docEditor.theHandle
|
||||
if tHandle is None:
|
||||
logger.debug("No document selected, trying last viewed")
|
||||
tHandle = self.theProject.lastViewed
|
||||
if tHandle is None:
|
||||
logger.debug("No document selected, giving up")
|
||||
return False
|
||||
|
||||
if self.docViewer.loadText(tHandle) and not self.viewPane.isVisible():
|
||||
bPos = self.splitMain.sizes()
|
||||
self.viewPane.setVisible(True)
|
||||
vPos = [0,0]
|
||||
vPos[0] = int(bPos[1]/2)
|
||||
vPos[1] = bPos[1]-vPos[0]
|
||||
self.splitView.setSizes(vPos)
|
||||
self.docEditor.changeWidth()
|
||||
|
||||
return True
|
||||
|
||||
def importDocument(self):
|
||||
|
||||
lastPath = self.mainConf.lastPath
|
||||
|
||||
extFilter = [
|
||||
"Text files (*.txt)",
|
||||
"Markdown files (*.md)",
|
||||
"All files (*.*)",
|
||||
]
|
||||
dlgOpt = QFileDialog.Options()
|
||||
dlgOpt |= QFileDialog.DontUseNativeDialog
|
||||
inPath = QFileDialog.getOpenFileName(
|
||||
self,"Import File",lastPath,options=dlgOpt,filter=";;".join(extFilter)
|
||||
)
|
||||
if inPath:
|
||||
loadFile = inPath[0]
|
||||
self.mainConf.setLastPath(loadFile)
|
||||
else:
|
||||
return False
|
||||
|
||||
theText = None
|
||||
try:
|
||||
with open(loadFile,mode="rt",encoding="utf8") as inFile:
|
||||
theText = inFile.read()
|
||||
except Exception as e:
|
||||
self.makeAlert(
|
||||
["Could not read file. The file cannot be a binary file.",str(e)],
|
||||
nwAlert.ERROR
|
||||
)
|
||||
return False
|
||||
|
||||
if self.docEditor.theHandle is None:
|
||||
self.makeAlert(
|
||||
["Please open a document to import the text file into."],
|
||||
nwAlert.ERROR
|
||||
)
|
||||
return False
|
||||
|
||||
if not self.docEditor.isEmpty():
|
||||
if self.mainConf.showGUI:
|
||||
msgBox = QMessageBox()
|
||||
msgRes = msgBox.question(self, "Import Document",(
|
||||
"Importing the file will overwrite the current content of the document. "
|
||||
"Do you want to proceed?"
|
||||
))
|
||||
if msgRes != QMessageBox.Yes:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
self.docEditor.setText(theText)
|
||||
|
||||
return True
|
||||
|
||||
##
|
||||
# Tree Item Actions
|
||||
##
|
||||
|
||||
def openSelectedItem(self):
|
||||
tHandle = self.treeView.getSelectedHandle()
|
||||
if tHandle is None:
|
||||
logger.warning("No item selected")
|
||||
return False
|
||||
|
||||
logger.verbose("Opening item %s" % tHandle)
|
||||
nwItem = self.theProject.getItem(tHandle)
|
||||
if nwItem.itemType == nwItemType.FILE:
|
||||
logger.verbose("Requested item %s is a file" % tHandle)
|
||||
self.openDocument(tHandle)
|
||||
else:
|
||||
logger.verbose("Requested item %s is not a file" % tHandle)
|
||||
|
||||
return True
|
||||
|
||||
def editItem(self):
|
||||
tHandle = self.treeView.getSelectedHandle()
|
||||
if tHandle is None:
|
||||
logger.warning("No item selected")
|
||||
return
|
||||
|
||||
logger.verbose("Requesting change to item %s" % tHandle)
|
||||
if self.mainConf.showGUI:
|
||||
dlgProj = GuiItemEditor(self, self.theProject, tHandle)
|
||||
if dlgProj.exec_():
|
||||
self.treeView.setTreeItemValues(tHandle)
|
||||
|
||||
return
|
||||
|
||||
def rebuildTree(self):
|
||||
self._makeStatusIcons()
|
||||
self._makeImportIcons()
|
||||
self.treeView.clearTree()
|
||||
self.treeView.buildTree()
|
||||
return
|
||||
|
||||
def rebuildIndex(self):
|
||||
|
||||
if not self.hasProject:
|
||||
return False
|
||||
|
||||
logger.debug("Rebuilding indices ...")
|
||||
|
||||
self.treeView.saveTreeOrder()
|
||||
self.theIndex.clearIndex()
|
||||
nItems = len(self.theProject.treeOrder)
|
||||
|
||||
dlgProg = QProgressDialog("Scanning files ...", "Cancel", 0, nItems, self)
|
||||
dlgProg.setWindowModality(Qt.WindowModal)
|
||||
dlgProg.setMinimumDuration(0)
|
||||
dlgProg.setFixedWidth(480)
|
||||
dlgProg.setLabelText("Starting file scan ...")
|
||||
dlgProg.setValue(0)
|
||||
dlgProg.show()
|
||||
time.sleep(0.5)
|
||||
|
||||
nDone = 0
|
||||
for tHandle in self.theProject.treeOrder:
|
||||
|
||||
tItem = self.theProject.getItem(tHandle)
|
||||
|
||||
dlgProg.setValue(nDone)
|
||||
dlgProg.setLabelText("Scanning: %s" % tItem.itemName)
|
||||
logger.verbose("Scanning: %s" % tItem.itemName)
|
||||
|
||||
if tItem is not None and tItem.itemType == nwItemType.FILE:
|
||||
theDoc = NWDoc(self.theProject, self)
|
||||
theText = theDoc.openDocument(tHandle, False)
|
||||
|
||||
# Run Word Count
|
||||
cC, wC, pC = countWords(theText)
|
||||
tItem.setCharCount(cC)
|
||||
tItem.setWordCount(wC)
|
||||
tItem.setParaCount(pC)
|
||||
self.treeView.propagateCount(tHandle, wC)
|
||||
self.treeView.projectWordCount()
|
||||
|
||||
# Build tag index
|
||||
self.theIndex.scanText(tHandle, theText)
|
||||
|
||||
nDone += 1
|
||||
if dlgProg.wasCanceled():
|
||||
break
|
||||
|
||||
dlgProg.setValue(nItems)
|
||||
|
||||
return True
|
||||
|
||||
##
|
||||
# Main Dialogs
|
||||
##
|
||||
|
||||
def openProjectDialog(self):
|
||||
dlgOpt = QFileDialog.Options()
|
||||
dlgOpt |= QFileDialog.DontUseNativeDialog
|
||||
projFile, _ = QFileDialog.getOpenFileName(
|
||||
self, "Open novelWriter Project", "",
|
||||
"novelWriter Project File (%s);;All Files (*)" % nwFiles.PROJ_FILE,
|
||||
options=dlgOpt
|
||||
)
|
||||
if projFile:
|
||||
return projFile
|
||||
return None
|
||||
|
||||
def saveProjectDialog(self):
|
||||
dlgOpt = QFileDialog.Options()
|
||||
dlgOpt |= QFileDialog.ShowDirsOnly
|
||||
dlgOpt |= QFileDialog.DontUseNativeDialog
|
||||
projPath = QFileDialog.getExistingDirectory(
|
||||
self, "Save novelWriter Project", "", options=dlgOpt
|
||||
)
|
||||
if projPath:
|
||||
return projPath
|
||||
return None
|
||||
|
||||
def newProjectDialog(self):
|
||||
dlgOpt = QFileDialog.Options()
|
||||
dlgOpt |= QFileDialog.ShowDirsOnly
|
||||
dlgOpt |= QFileDialog.DontUseNativeDialog
|
||||
projPath = QFileDialog.getExistingDirectory(
|
||||
self, "Select Location for New novelWriter Project", "", options=dlgOpt
|
||||
)
|
||||
if projPath:
|
||||
return projPath
|
||||
return None
|
||||
|
||||
def editConfigDialog(self):
|
||||
dlgConf = GuiConfigEditor(self, self.theProject)
|
||||
if dlgConf.exec_() == QDialog.Accepted:
|
||||
logger.debug("Applying new preferences")
|
||||
self.initMain()
|
||||
self.theTheme.updateTheme()
|
||||
self.saveDocument()
|
||||
self.docEditor.initEditor()
|
||||
self.docViewer.initViewer()
|
||||
return True
|
||||
|
||||
def editProjectDialog(self):
|
||||
if self.hasProject:
|
||||
dlgProj = GuiProjectEditor(self, self.theProject)
|
||||
dlgProj.exec_()
|
||||
self._setWindowTitle(self.theProject.projName)
|
||||
return True
|
||||
|
||||
def exportProjectDialog(self):
|
||||
if self.hasProject:
|
||||
dlgExport = GuiExport(self, self.theProject)
|
||||
dlgExport.exec_()
|
||||
return True
|
||||
|
||||
def showTimeLineDialog(self):
|
||||
if self.hasProject:
|
||||
dlgTLine = GuiTimeLineView(self, self.theProject, self.theIndex)
|
||||
dlgTLine.exec_()
|
||||
return True
|
||||
|
||||
def showSessionLogDialog(self):
|
||||
if self.hasProject:
|
||||
dlgTLine = GuiSessionLogView(self, self.theProject)
|
||||
dlgTLine.exec_()
|
||||
return True
|
||||
|
||||
def makeAlert(self, theMessage, theLevel=nwAlert.INFO):
|
||||
"""Alert both the user and the logger at the same time. Message
|
||||
can be either a string or an array of strings. Severity level is
|
||||
0 = info, 1 = warning, and 2 = error.
|
||||
"""
|
||||
|
||||
if isinstance(theMessage, list):
|
||||
popMsg = " ".join(theMessage)
|
||||
logMsg = theMessage
|
||||
else:
|
||||
popMsg = theMessage
|
||||
logMsg = [theMessage]
|
||||
|
||||
msgBox = QMessageBox()
|
||||
if theLevel == nwAlert.INFO:
|
||||
for msgLine in logMsg:
|
||||
logger.info(msgLine)
|
||||
msgBox.information(self, "Information", popMsg)
|
||||
elif theLevel == nwAlert.WARN:
|
||||
for msgLine in logMsg:
|
||||
logger.warning(msgLine)
|
||||
msgBox.warning(self, "Warning", popMsg)
|
||||
elif theLevel == nwAlert.ERROR:
|
||||
for msgLine in logMsg:
|
||||
logger.error(msgLine)
|
||||
msgBox.critical(self, "Error", popMsg)
|
||||
elif theLevel == nwAlert.BUG:
|
||||
for msgLine in logMsg:
|
||||
logger.error(msgLine)
|
||||
popMsg += "<br>This is a bug!"
|
||||
msgBox.critical(self, "Internal Error", popMsg)
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Main Window Actions
|
||||
##
|
||||
|
||||
def closeMain(self):
|
||||
|
||||
if self.mainConf.showGUI and self.hasProject:
|
||||
msgBox = QMessageBox()
|
||||
msgRes = msgBox.question(
|
||||
self, "Exit", "Do you want to save changes and exit?"
|
||||
)
|
||||
if msgRes != QMessageBox.Yes:
|
||||
return False
|
||||
|
||||
logger.info("Exiting %s" % nw.__package__)
|
||||
self.closeProject(True)
|
||||
|
||||
self.mainConf.setWinSize(self.width(), self.height())
|
||||
self.mainConf.setTreeColWidths(self.treeView.getColumnSizes())
|
||||
self.mainConf.setMainPanePos(self.splitMain.sizes())
|
||||
self.mainConf.setDocPanePos(self.splitView.sizes())
|
||||
self.mainConf.saveConfig()
|
||||
|
||||
qApp.quit()
|
||||
|
||||
return True
|
||||
|
||||
def setFocus(self, paneNo):
|
||||
if paneNo == 1:
|
||||
self.treeView.setFocus()
|
||||
elif paneNo == 2:
|
||||
self.docEditor.setFocus()
|
||||
elif paneNo == 3:
|
||||
self.docViewer.setFocus()
|
||||
return
|
||||
|
||||
def closeDocEditor(self):
|
||||
self.closeDocument()
|
||||
self.theProject.setLastEdited(None)
|
||||
return
|
||||
|
||||
def closeDocViewer(self):
|
||||
self.docViewer.clearViewer()
|
||||
self.theProject.setLastViewed(None)
|
||||
bPos = self.splitMain.sizes()
|
||||
self.viewPane.setVisible(False)
|
||||
vPos = [bPos[1],0]
|
||||
self.splitView.setSizes(vPos)
|
||||
self.docEditor.changeWidth()
|
||||
return not self.viewPane.isVisible()
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _setWindowTitle(self, projName=None):
|
||||
winTitle = "%s" % nw.__package__
|
||||
if projName is not None:
|
||||
winTitle += " - %s" % projName
|
||||
self.setWindowTitle(winTitle)
|
||||
return True
|
||||
|
||||
def _autoSaveProject(self):
|
||||
if (self.hasProject and self.theProject.projChanged and
|
||||
self.theProject.projPath is not None):
|
||||
logger.debug("Autosaving project")
|
||||
self.saveProject(isAuto=True)
|
||||
return
|
||||
|
||||
def _autoSaveDocument(self):
|
||||
if self.hasProject and self.docEditor.docChanged:
|
||||
logger.debug("Autosaving document")
|
||||
self.saveDocument()
|
||||
return
|
||||
|
||||
def _makeStatusIcons(self):
|
||||
self.statusIcons = {}
|
||||
for sLabel, sCol, _ in self.theProject.statusItems:
|
||||
theIcon = QPixmap(32,32)
|
||||
theIcon.fill(QColor(*sCol))
|
||||
self.statusIcons[sLabel] = QIcon(theIcon)
|
||||
return
|
||||
|
||||
def _makeImportIcons(self):
|
||||
self.importIcons = {}
|
||||
for sLabel, sCol, _ in self.theProject.importItems:
|
||||
theIcon = QPixmap(32,32)
|
||||
theIcon.fill(QColor(*sCol))
|
||||
self.importIcons[sLabel] = QIcon(theIcon)
|
||||
return
|
||||
|
||||
##
|
||||
# Events
|
||||
##
|
||||
|
||||
def resizeEvent(self, theEvent):
|
||||
"""Extend QMainWindow.resizeEvent to signal dependent GUI
|
||||
elements that its pane may have changed size.
|
||||
"""
|
||||
QMainWindow.resizeEvent(self,theEvent)
|
||||
self.docEditor.changeWidth()
|
||||
return
|
||||
|
||||
def closeEvent(self, theEvent):
|
||||
if self.closeMain():
|
||||
theEvent.accept()
|
||||
else:
|
||||
theEvent.ignore()
|
||||
return
|
||||
|
||||
##
|
||||
# Signal Handlers
|
||||
##
|
||||
|
||||
def _treeSingleClick(self):
|
||||
sHandle = self.treeView.getSelectedHandle()
|
||||
if sHandle is not None:
|
||||
self.treeMeta.buildViewBox(sHandle)
|
||||
return
|
||||
|
||||
def _treeDoubleClick(self, tItem, colNo):
|
||||
tHandle = tItem.text(3)
|
||||
logger.verbose("User double clicked tree item with handle %s" % tHandle)
|
||||
nwItem = self.theProject.getItem(tHandle)
|
||||
if nwItem.itemType == nwItemType.FILE:
|
||||
logger.verbose("Requested item %s is a file" % tHandle)
|
||||
self.openDocument(tHandle)
|
||||
else:
|
||||
logger.verbose("Requested item %s is a folder" % tHandle)
|
||||
return
|
||||
|
||||
def _treeKeyPressReturn(self):
|
||||
tHandle = self.treeView.getSelectedHandle()
|
||||
logger.verbose("User pressed return on tree item with handle %s" % tHandle)
|
||||
nwItem = self.theProject.getItem(tHandle)
|
||||
if nwItem.itemType == nwItemType.FILE:
|
||||
logger.verbose("Requested item %s is a file" % tHandle)
|
||||
self.openDocument(tHandle)
|
||||
else:
|
||||
logger.verbose("Requested item %s is a folder" % tHandle)
|
||||
return
|
||||
|
||||
def _keyPressEscape(self):
|
||||
"""When the escape key is pressed somewhere in the main window,
|
||||
do the following, in order.
|
||||
"""
|
||||
if self.searchBar.isVisible():
|
||||
self.searchBar.setVisible(False)
|
||||
return
|
||||
return
|
||||
|
||||
def _splitMainMove(self, pWidth, pHeight):
|
||||
"""Alert dependent GUI elements that the main pane splitter has
|
||||
been moved.
|
||||
"""
|
||||
self.docEditor.changeWidth()
|
||||
return
|
||||
|
||||
def _splitViewMove(self, pWidth, pHeight):
|
||||
"""Alert dependent GUI elements that the main pane splitter has
|
||||
been moved.
|
||||
"""
|
||||
self.docEditor.changeWidth()
|
||||
return
|
||||
|
||||
# END Class GuiMain
|
||||
Reference in New Issue
Block a user