Merge pull request #632 from vkbo/actions_refactoring
Menu Actions Refactoring and Extension
This commit is contained in:
@@ -23,6 +23,7 @@ __pycache__
|
||||
/sample/meta
|
||||
*.bak
|
||||
*.lock
|
||||
ToC.txt
|
||||
|
||||
# PyTest
|
||||
/prof/
|
||||
|
||||
@@ -378,6 +378,8 @@ Most features are available as keyboard shortcuts. These are as follows:
|
||||
":kbd:`Ctrl`:kbd:`F7`", "Toggle spell checking."
|
||||
":kbd:`Ctrl`:kbd:`F10`", "Toggle automatic updating of project outline."
|
||||
":kbd:`Ctrl`:kbd:`Del`", "If in the project tree, move a document to trash, or delete a folder."
|
||||
":kbd:`Ctrl`:kbd:`Up`", "Move item one step up in the project tree."
|
||||
":kbd:`Ctrl`:kbd:`Down`", "Move item one step down in the project tree."
|
||||
":kbd:`Ctrl`:kbd:`'`", "Wrap selected text, or word under cursor, in single quotes."
|
||||
":kbd:`Ctrl`:kbd:`""`", "Wrap selected text, or word under cursor, in double quotes."
|
||||
":kbd:`Ctrl`:kbd:`Enter`", "Open the tag or reference under the cursor in the Viewer."
|
||||
@@ -392,9 +394,7 @@ Most features are available as keyboard shortcuts. These are as follows:
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`R`", "Close the document viewer."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`S`", "Save the current project."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`W`", "Close the current project."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`Z`", "Alternative sequence for redo last undo."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`Up`", "Move item one step up in the project tree."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`Down`", "Move item one step down in the project tree."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`Z`", "Undo move of project tree item."
|
||||
":kbd:`F1`", "Open the documentation. This will either open the Qt Assistant, if available, or send you to the documentation website."
|
||||
":kbd:`F2`", "If in the project tree, edit a document or folder settings. (Same as :kbd:`Ctrl`:kbd:`E`)"
|
||||
":kbd:`F3`", "Find next occurrence of search word in current document. (Same as :kbd:`Ctrl`:kbd:`G`)"
|
||||
|
||||
@@ -49,13 +49,15 @@ class nwConst():
|
||||
class nwLists():
|
||||
"""Lists used for grouping various other constants.
|
||||
"""
|
||||
|
||||
# Regular user-accessible item types
|
||||
REG_TYPES = {nwItemType.ROOT, nwItemType.FOLDER, nwItemType.FILE}
|
||||
|
||||
# Item classes where the full list of novel layouts are allowed
|
||||
CLS_NOVEL = {nwItemClass.NOVEL, nwItemClass.ARCHIVE}
|
||||
|
||||
# Item classes which do not require items to have same class
|
||||
FREE_CLASS = {nwItemClass.ARCHIVE, nwItemClass.TRASH}
|
||||
|
||||
# END Class nwLists
|
||||
|
||||
class nwRegEx():
|
||||
|
||||
@@ -646,6 +646,10 @@ class GuiDocEditor(QTextEdit):
|
||||
this class when calling these actions from other classes.
|
||||
"""
|
||||
logger.verbose("Requesting action: %s" % theAction.name)
|
||||
if not self.hasFocus():
|
||||
logger.verbose("Editor does not have focus")
|
||||
return False
|
||||
|
||||
if self.theHandle is None:
|
||||
logger.error("No document open")
|
||||
return False
|
||||
|
||||
+35
-34
@@ -115,22 +115,25 @@ class GuiMainMenu(QMenuBar):
|
||||
##
|
||||
|
||||
def setSpellCheck(self, theMode):
|
||||
"""Set the spell check check box to theMode. This is controlled
|
||||
by the document editor class, which holds the master spell check
|
||||
flag.
|
||||
"""Forward spell check check state to its action.
|
||||
"""
|
||||
self.aSpellCheck.setChecked(theMode)
|
||||
return
|
||||
|
||||
def setAutoOutline(self, theMode):
|
||||
"""Set the auto outline check box to theMode. Used during
|
||||
initialisation.
|
||||
"""Forward auto outline check state to its action.
|
||||
"""
|
||||
self.aAutoOutline.setChecked(theMode)
|
||||
return
|
||||
|
||||
def setFocusMode(self, theMode):
|
||||
"""Forward focus mode check state to its action.
|
||||
"""
|
||||
self.aFocusMode.setChecked(theMode)
|
||||
return
|
||||
|
||||
##
|
||||
# Menu Action
|
||||
# Slots
|
||||
##
|
||||
|
||||
def _toggleSpellCheck(self, isChecked=False):
|
||||
@@ -164,17 +167,11 @@ class GuiMainMenu(QMenuBar):
|
||||
return True
|
||||
|
||||
def _openWebsite(self, theUrl):
|
||||
"""Open an URL in the system's default browser.
|
||||
"""Open a URL in the system's default browser.
|
||||
"""
|
||||
QDesktopServices.openUrl(QUrl(theUrl))
|
||||
return True
|
||||
|
||||
def _openIssue(self):
|
||||
"""Open the issue tracker URL in the system's default browser.
|
||||
"""
|
||||
QDesktopServices.openUrl(QUrl(nw.__issuesurl__))
|
||||
return True
|
||||
|
||||
##
|
||||
# Menu Builders
|
||||
##
|
||||
@@ -263,19 +260,40 @@ class GuiMainMenu(QMenuBar):
|
||||
self.projMenu.addSeparator()
|
||||
|
||||
# Project > Edit
|
||||
self.aEditItem = QAction("Edit Project Item", self)
|
||||
self.aEditItem.setStatusTip("Change item settings")
|
||||
self.aEditItem = QAction("Edit Item", self)
|
||||
self.aEditItem.setStatusTip("Change project item settings")
|
||||
self.aEditItem.setShortcuts(["Ctrl+E", "F2"])
|
||||
self.aEditItem.triggered.connect(lambda: self.theParent.editItem(None))
|
||||
self.projMenu.addAction(self.aEditItem)
|
||||
|
||||
# Project > Delete
|
||||
self.aDeleteItem = QAction("Delete Project Item", self)
|
||||
self.aDeleteItem.setStatusTip("Delete selected item")
|
||||
self.aDeleteItem = QAction("Delete Item", self)
|
||||
self.aDeleteItem.setStatusTip("Delete selected project item")
|
||||
self.aDeleteItem.setShortcut("Ctrl+Del")
|
||||
self.aDeleteItem.triggered.connect(lambda: self.theParent.treeView.deleteItem(None))
|
||||
self.projMenu.addAction(self.aDeleteItem)
|
||||
|
||||
# Project > Move Up
|
||||
self.aMoveUp = QAction("Move Item Up", self)
|
||||
self.aMoveUp.setStatusTip("Move project item up")
|
||||
self.aMoveUp.setShortcut("Ctrl+Up")
|
||||
self.aMoveUp.triggered.connect(lambda: self._moveTreeItem(-1))
|
||||
self.projMenu.addAction(self.aMoveUp)
|
||||
|
||||
# Project > Move Down
|
||||
self.aMoveDown = QAction("Move Item Down", self)
|
||||
self.aMoveDown.setStatusTip("Move project item down")
|
||||
self.aMoveDown.setShortcut("Ctrl+Down")
|
||||
self.aMoveDown.triggered.connect(lambda: self._moveTreeItem(1))
|
||||
self.projMenu.addAction(self.aMoveDown)
|
||||
|
||||
# Project > Undo Last Action
|
||||
self.aMoveUndo = QAction("Undo Last Move", self)
|
||||
self.aMoveUndo.setStatusTip("Undo last item move")
|
||||
self.aMoveUndo.setShortcut("Ctrl+Shift+Z")
|
||||
self.aMoveUndo.triggered.connect(lambda: self.theParent.treeView.undoLastMove())
|
||||
self.projMenu.addAction(self.aMoveUndo)
|
||||
|
||||
# Project > Empty Trash
|
||||
self.aEmptyTrash = QAction("Empty Trash", self)
|
||||
self.aEmptyTrash.setStatusTip("Permanently delete all files in the Trash folder")
|
||||
@@ -891,23 +909,6 @@ class GuiMainMenu(QMenuBar):
|
||||
# Tools
|
||||
self.toolsMenu = self.addMenu("&Tools")
|
||||
|
||||
# Tools > Move Up
|
||||
self.aMoveUp = QAction("Move Tree Item Up", self)
|
||||
self.aMoveUp.setStatusTip("Move item up")
|
||||
self.aMoveUp.setShortcut("Ctrl+Shift+Up")
|
||||
self.aMoveUp.triggered.connect(lambda: self._moveTreeItem(-1))
|
||||
self.toolsMenu.addAction(self.aMoveUp)
|
||||
|
||||
# Tools > Move Down
|
||||
self.aMoveDown = QAction("Move Tree Item Down", self)
|
||||
self.aMoveDown.setStatusTip("Move item down")
|
||||
self.aMoveDown.setShortcut("Ctrl+Shift+Down")
|
||||
self.aMoveDown.triggered.connect(lambda: self._moveTreeItem(1))
|
||||
self.toolsMenu.addAction(self.aMoveDown)
|
||||
|
||||
# Tools > Separator
|
||||
self.toolsMenu.addSeparator()
|
||||
|
||||
# Tools > Toggle Spell Check
|
||||
self.aSpellCheck = QAction("Check Spelling", self)
|
||||
self.aSpellCheck.setStatusTip("Toggle check spelling")
|
||||
|
||||
+138
-49
@@ -33,12 +33,12 @@ from time import time
|
||||
from PyQt5.QtCore import Qt, QSize, pyqtSignal
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import (
|
||||
qApp, QTreeWidget, QTreeWidgetItem, QAbstractItemView, QMenu, QAction
|
||||
QTreeWidget, QTreeWidgetItem, QAbstractItemView, QMenu, QAction
|
||||
)
|
||||
|
||||
from nw.core import NWDoc
|
||||
from nw.constants import (
|
||||
nwLabels, nwItemType, nwItemClass, nwItemLayout, nwAlert, nwConst
|
||||
nwLabels, nwItemType, nwItemClass, nwItemLayout, nwAlert, nwConst, nwLists
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -68,6 +68,7 @@ class GuiProjectTree(QTreeWidget):
|
||||
self._treeMap = {}
|
||||
self._treeChanged = False
|
||||
self._timeChanged = 0
|
||||
self._lastMove = {}
|
||||
|
||||
##
|
||||
# Build GUI
|
||||
@@ -297,10 +298,10 @@ class GuiProjectTree(QTreeWidget):
|
||||
|
||||
# Save the text and index it
|
||||
newDoc.saveDocument(newText)
|
||||
self.theParent.theIndex.scanText(tHandle, newText)
|
||||
self.theIndex.scanText(tHandle, newText)
|
||||
|
||||
# Get Word Counts
|
||||
cC, wC, pC = self.theParent.theIndex.getCounts(tHandle)
|
||||
cC, wC, pC = self.theIndex.getCounts(tHandle)
|
||||
nwItem.setCharCount(cC)
|
||||
nwItem.setWordCount(wC)
|
||||
nwItem.setParaCount(pC)
|
||||
@@ -335,7 +336,7 @@ class GuiProjectTree(QTreeWidget):
|
||||
logger.error("No project open")
|
||||
return False
|
||||
|
||||
if qApp.focusWidget() != self:
|
||||
if not self.hasFocus():
|
||||
return False
|
||||
|
||||
tHandle = self.getSelectedHandle()
|
||||
@@ -361,6 +362,7 @@ class GuiProjectTree(QTreeWidget):
|
||||
return False
|
||||
cItem = pItem.takeChild(tIndex)
|
||||
pItem.insertChild(nIndex, cItem)
|
||||
self._recordLastMove(cItem, pItem, tIndex)
|
||||
|
||||
self.clearSelection()
|
||||
cItem.setSelected(True)
|
||||
@@ -517,8 +519,9 @@ class GuiProjectTree(QTreeWidget):
|
||||
|
||||
theDoc = NWDoc(self.theProject, self.theParent)
|
||||
theDoc.deleteDocument(tHandle)
|
||||
del self.theProject.projTree[tHandle]
|
||||
self.theIndex.deleteHandle(tHandle)
|
||||
self._deleteTreeItem(tHandle)
|
||||
self._setTreeChanged(True)
|
||||
|
||||
else:
|
||||
# The file is not already in the trash folder, so we
|
||||
@@ -541,11 +544,12 @@ class GuiProjectTree(QTreeWidget):
|
||||
tIndex = trItemP.indexOfChild(trItemS)
|
||||
trItemC = trItemP.takeChild(tIndex)
|
||||
trItemT.addChild(trItemC)
|
||||
nwItemS.setParent(self.theProject.projTree.trashRoot())
|
||||
self._updateItemParent(tHandle)
|
||||
self.propagateCount(tHandle, wCount)
|
||||
|
||||
self._setTreeChanged(True)
|
||||
self.theIndex.deleteHandle(tHandle)
|
||||
self._recordLastMove(trItemS, trItemP, tIndex)
|
||||
self._setTreeChanged(True)
|
||||
|
||||
elif nwItemS.itemType == nwItemType.FOLDER:
|
||||
logger.debug("User requested folder %s deleted" % tHandle)
|
||||
@@ -556,7 +560,8 @@ class GuiProjectTree(QTreeWidget):
|
||||
tIndex = trItemP.indexOfChild(trItemS)
|
||||
if trItemS.childCount() == 0:
|
||||
trItemP.takeChild(tIndex)
|
||||
del self.theProject.projTree[tHandle]
|
||||
self._deleteTreeItem(tHandle)
|
||||
self._setTreeChanged(True)
|
||||
else:
|
||||
self.makeAlert((
|
||||
"Cannot delete folder. It is not empty. "
|
||||
@@ -570,7 +575,7 @@ class GuiProjectTree(QTreeWidget):
|
||||
tIndex = self.indexOfTopLevelItem(trItemS)
|
||||
if trItemS.childCount() == 0:
|
||||
self.takeTopLevelItem(tIndex)
|
||||
del self.theProject.projTree[tHandle]
|
||||
self._deleteTreeItem(tHandle)
|
||||
self.theParent.mainMenu.setAvailableRoot()
|
||||
self._setTreeChanged(True)
|
||||
else:
|
||||
@@ -679,6 +684,52 @@ class GuiProjectTree(QTreeWidget):
|
||||
logger.debug("%d items added to the project tree" % iCount)
|
||||
return True
|
||||
|
||||
def undoLastMove(self):
|
||||
"""Attempt to undo the last action.
|
||||
"""
|
||||
srcItem = self._lastMove.get("item", None)
|
||||
dstItem = self._lastMove.get("parent", None)
|
||||
dstIndex = self._lastMove.get("index", None)
|
||||
|
||||
if not self.hasFocus():
|
||||
return False
|
||||
|
||||
if srcItem is None or dstItem is None or dstIndex is None:
|
||||
logger.verbose("No tree move to undo")
|
||||
return False
|
||||
|
||||
if srcItem not in self._treeMap.values():
|
||||
logger.warning("Source item no longer exists")
|
||||
return False
|
||||
|
||||
if dstItem not in self._treeMap.values():
|
||||
logger.warning("Previous parent item no longer exists")
|
||||
return False
|
||||
|
||||
dstIndex = min(max(0, dstIndex), dstItem.childCount())
|
||||
wCount = int(srcItem.data(self.C_COUNT, Qt.UserRole))
|
||||
sHandle = srcItem.data(self.C_NAME, Qt.UserRole)
|
||||
dHandle = dstItem.data(self.C_NAME, Qt.UserRole)
|
||||
logger.debug("Moving item %s back to %s, index %d" % (
|
||||
sHandle, dHandle, dstIndex
|
||||
))
|
||||
|
||||
self.propagateCount(sHandle, 0)
|
||||
parItem = srcItem.parent()
|
||||
srcIndex = parItem.indexOfChild(srcItem)
|
||||
movItem = parItem.takeChild(srcIndex)
|
||||
dstItem.insertChild(dstIndex, movItem)
|
||||
|
||||
snItem = self.theProject.projTree[sHandle]
|
||||
dnItem = self.theProject.projTree[dHandle]
|
||||
self._postItemMove(sHandle, snItem, dnItem, wCount)
|
||||
|
||||
self.clearSelection()
|
||||
movItem.setSelected(True)
|
||||
self._lastMove = {}
|
||||
|
||||
return True
|
||||
|
||||
def getSelectedHandle(self):
|
||||
"""Get the currently selected handle. If multiple items are
|
||||
selected, return the first.
|
||||
@@ -787,46 +838,30 @@ class GuiProjectTree(QTreeWidget):
|
||||
self.makeAlert("The item cannot be moved to that location.", nwAlert.ERROR)
|
||||
return
|
||||
|
||||
pItem = sItem.parent()
|
||||
pIndex = 0
|
||||
if pItem is not None:
|
||||
pIndex = pItem.indexOfChild(sItem)
|
||||
|
||||
wCount = int(sItem.data(self.C_COUNT, Qt.UserRole))
|
||||
isFile = snItem.itemType == nwItemType.FILE
|
||||
isRoot = snItem.itemType == nwItemType.ROOT
|
||||
onFile = dnItem.itemType == nwItemType.FILE
|
||||
|
||||
isSame = snItem.itemClass == dnItem.itemClass
|
||||
isNone = snItem.itemClass == nwItemClass.NO_CLASS
|
||||
isNote = snItem.itemLayout == nwItemLayout.NOTE
|
||||
onFile = dnItem.itemType == nwItemType.FILE
|
||||
isRoot = snItem.itemType == nwItemType.ROOT
|
||||
onFree = dnItem.itemClass == nwItemClass.ARCHIVE
|
||||
onFree |= dnItem.itemClass == nwItemClass.TRASH
|
||||
onFree &= snItem.itemType == nwItemType.FILE
|
||||
isOnTop = self.dropIndicatorPosition() == QAbstractItemView.OnItem
|
||||
if (isSame or isNone or isNote or onFree) and not (onFile and isOnTop) and not isRoot:
|
||||
onFree = dnItem.itemClass in nwLists.FREE_CLASS and isFile
|
||||
|
||||
allowDrop = isSame or isNone or isNote or onFree
|
||||
allowDrop &= not (self.dropIndicatorPosition() == QAbstractItemView.OnItem and onFile)
|
||||
|
||||
if allowDrop and not isRoot:
|
||||
logger.debug("Drag'n'drop of item %s accepted" % sHandle)
|
||||
self.propagateCount(sHandle, 0)
|
||||
QTreeWidget.dropEvent(self, theEvent)
|
||||
self._updateItemParent(sHandle)
|
||||
|
||||
# If the item does not have the same class as the target,
|
||||
# and the target is not a free root folder, update its class
|
||||
if not (isSame or onFree):
|
||||
logger.debug("Item %s class has been changed from %s to %s" % (
|
||||
sHandle,
|
||||
snItem.itemClass.name,
|
||||
dnItem.itemClass.name
|
||||
))
|
||||
snItem.setClass(dnItem.itemClass)
|
||||
self.setTreeItemValues(sHandle)
|
||||
|
||||
self.propagateCount(sHandle, wCount)
|
||||
|
||||
# The items dropped into archive or trash should be removed
|
||||
# from the project index, for all other items, we rescan the
|
||||
# file to ensure the index is up to date.
|
||||
if onFree:
|
||||
self.theIndex.deleteHandle(sHandle)
|
||||
else:
|
||||
self.theIndex.reIndexHandle(sHandle)
|
||||
|
||||
# Trigger dependent updates
|
||||
self._setTreeChanged(True)
|
||||
self._emitItemChange(sHandle)
|
||||
self._postItemMove(sHandle, snItem, dnItem, wCount)
|
||||
self._recordLastMove(sItem, pItem, pIndex)
|
||||
|
||||
else:
|
||||
theEvent.ignore()
|
||||
@@ -839,22 +874,63 @@ class GuiProjectTree(QTreeWidget):
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _postItemMove(self, sHandle, snItem, dnItem, wCount):
|
||||
"""Run various maintenance tasks for a moved item.
|
||||
"""
|
||||
isFile = snItem.itemType == nwItemType.FILE
|
||||
isSame = snItem.itemClass == dnItem.itemClass
|
||||
onFree = dnItem.itemClass in nwLists.FREE_CLASS and isFile
|
||||
|
||||
self._updateItemParent(sHandle)
|
||||
|
||||
# If the item does not have the same class as the target,
|
||||
# and the target is not a free root folder, update its class
|
||||
if not (isSame or onFree):
|
||||
logger.debug("Item %s class has been changed from %s to %s" % (
|
||||
sHandle, snItem.itemClass.name, dnItem.itemClass.name
|
||||
))
|
||||
snItem.setClass(dnItem.itemClass)
|
||||
self.setTreeItemValues(sHandle)
|
||||
|
||||
self.propagateCount(sHandle, wCount)
|
||||
|
||||
# The items dropped into archive or trash should be removed
|
||||
# from the project index, for all other items, we rescan the
|
||||
# file to ensure the index is up to date.
|
||||
if onFree:
|
||||
self.theIndex.deleteHandle(sHandle)
|
||||
else:
|
||||
self.theIndex.reIndexHandle(sHandle)
|
||||
|
||||
# Trigger dependent updates
|
||||
self._setTreeChanged(True)
|
||||
self._emitItemChange(sHandle)
|
||||
|
||||
return
|
||||
|
||||
def _getTreeItem(self, tHandle):
|
||||
"""Returns the QTreeWidgetItem of a given item handle.
|
||||
"""
|
||||
return self._treeMap.get(tHandle, None)
|
||||
|
||||
def _scanChildren(self, theList, theItem, theIndex):
|
||||
def _deleteTreeItem(self, tHandle):
|
||||
"""Delete a tree item from the project and the map.
|
||||
"""
|
||||
del self.theProject.projTree[tHandle]
|
||||
self._treeMap.pop(tHandle, None)
|
||||
return
|
||||
|
||||
def _scanChildren(self, theList, tItem, tIndex):
|
||||
"""This is a recursive function returning all items in a tree
|
||||
starting at a given QTreeWidgetItem.
|
||||
"""
|
||||
tHandle = theItem.data(self.C_NAME, Qt.UserRole)
|
||||
tHandle = tItem.data(self.C_NAME, Qt.UserRole)
|
||||
nwItem = self.theProject.projTree[tHandle]
|
||||
nwItem.setExpanded(theItem.isExpanded())
|
||||
nwItem.setOrder(theIndex)
|
||||
nwItem.setExpanded(tItem.isExpanded())
|
||||
nwItem.setOrder(tIndex)
|
||||
theList.append(tHandle)
|
||||
for i in range(theItem.childCount()):
|
||||
self._scanChildren(theList, theItem.child(i), i)
|
||||
for i in range(tItem.childCount()):
|
||||
self._scanChildren(theList, tItem.child(i), i)
|
||||
return theList
|
||||
|
||||
def _addTreeItem(self, nwItem, nHandle=None):
|
||||
@@ -984,6 +1060,19 @@ class GuiProjectTree(QTreeWidget):
|
||||
|
||||
return
|
||||
|
||||
def _recordLastMove(self, srcItem, parItem, parIndex):
|
||||
"""Record the last action so that it can be undone.
|
||||
"""
|
||||
prevItem = self._lastMove.get("item", None)
|
||||
if prevItem is None or srcItem != prevItem:
|
||||
self._lastMove = {
|
||||
"item": srcItem,
|
||||
"parent": parItem,
|
||||
"index": parIndex,
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
# END Class GuiProjectTree
|
||||
|
||||
class GuiProjectTreeMenu(QMenu):
|
||||
|
||||
+2
-2
@@ -1167,11 +1167,11 @@ class GuiMain(QMainWindow):
|
||||
"""
|
||||
if self.docEditor.theHandle is None:
|
||||
logger.error("No document open, so not activating Focus Mode")
|
||||
self.mainMenu.aFocusMode.setChecked(self.isFocusMode)
|
||||
self.mainMenu.setFocusMode(self.isFocusMode)
|
||||
return False
|
||||
|
||||
self.isFocusMode = not self.isFocusMode
|
||||
self.mainMenu.aFocusMode.setChecked(self.isFocusMode)
|
||||
self.mainMenu.setFocusMode(self.isFocusMode)
|
||||
if self.isFocusMode:
|
||||
logger.debug("Activating Focus Mode")
|
||||
self.mainTabs.setCurrentWidget(self.splitDocs)
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
|
||||
Table of Contents
|
||||
=================
|
||||
|
||||
File Name Class Layout Document Label
|
||||
---------------------------------------------------------------------
|
||||
content/53b69b83cdafc.nwd NOVEL TITLE Title Page
|
||||
content/974e400180a99.nwd NOVEL PAGE Page
|
||||
content/edca4be2fcaf8.nwd NOVEL PARTITION Part One
|
||||
content/6a2d6d5f4f401.nwd NOVEL CHAPTER Chapter One
|
||||
content/636b6aa9b697b.nwd NOVEL SCENE Making a Scene
|
||||
content/bc0cbd2a407f3.nwd NOVEL SCENE Another Scene
|
||||
content/ba8a28a246524.nwd NOVEL UNNUMBERED Interlude
|
||||
content/96b68994dfa3d.nwd NOVEL NOTE A Note on Structure
|
||||
content/88706ddc78b1b.nwd NOVEL CHAPTER Chapter Two
|
||||
content/ae7339df26ded.nwd NOVEL SCENE We Found John!
|
||||
content/14298de4d9524.nwd CHARACTER NOTE John Smith
|
||||
content/bb2c23b3c42cc.nwd CHARACTER NOTE Jane Smith
|
||||
content/b3e74dbc1f584.nwd WORLD NOTE Earth
|
||||
content/f1471bef9f2ae.nwd WORLD NOTE Space
|
||||
content/5eaea4e8cdee8.nwd WORLD NOTE Mars
|
||||
content/8a5deb88c0e97.nwd NOVEL SCENE Old File
|
||||
content/b8136a5a774a0.nwd NOVEL SCENE Delete Me!
|
||||
@@ -1,13 +1,13 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="1.1rc1" hexVersion="0x010100c1" fileVersion="1.2" timeStamp="2021-01-31 18:12:05">
|
||||
<novelWriterXML appVersion="1.2a0" hexVersion="0x010200a0" fileVersion="1.2" timeStamp="2021-01-31 22:28:54">
|
||||
<project>
|
||||
<name>Sample Project</name>
|
||||
<title>Sample Project</title>
|
||||
<author>Jane Smith</author>
|
||||
<author>Jay Doh</author>
|
||||
<saveCount>874</saveCount>
|
||||
<saveCount>881</saveCount>
|
||||
<autoCount>157</autoCount>
|
||||
<editTime>42533</editTime>
|
||||
<editTime>42668</editTime>
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>False</doBackup>
|
||||
@@ -117,7 +117,7 @@
|
||||
<status>1st Draft</status>
|
||||
<exported>True</exported>
|
||||
<layout>SCENE</layout>
|
||||
<charCount>1811</charCount>
|
||||
<charCount>1810</charCount>
|
||||
<wordCount>318</wordCount>
|
||||
<paraCount>8</paraCount>
|
||||
<cursorPos>1880</cursorPos>
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
|
||||
Table of Contents
|
||||
===================
|
||||
|
||||
File Name Class Document Label
|
||||
--------------------------------------------------------------------------------
|
||||
content/04468803b92e1.nwd WORLD Ancient Europe
|
||||
content/2426c6f0ca922.nwd PLOT Main
|
||||
content/441420a886d82.nwd NOVEL Chapter Two
|
||||
content/47666c91c7ccf.nwd NOVEL Scene Five
|
||||
content/4c4f28287af27.nwd CHARACTER Mr. Nobody
|
||||
content/7a992350f3eb6.nwd NOVEL Lorem Ipsum
|
||||
content/846352075de7d.nwd NOVEL Interlude
|
||||
content/88243afbe5ed8.nwd NOVEL Scene One
|
||||
content/88d59a277361b.nwd NOVEL Prologue
|
||||
content/8c58a65414c23.nwd NOVEL Front Matter
|
||||
content/db7e733775d4d.nwd NOVEL Act One
|
||||
content/eb103bc70c90c.nwd NOVEL Scene Three
|
||||
content/f8c0562e50f1b.nwd NOVEL Scene Four
|
||||
content/f96ec11c6a3da.nwd NOVEL Scene Two
|
||||
content/fb609cd8319dc.nwd NOVEL Chapter One
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
|
||||
Table of Contents
|
||||
===================
|
||||
|
||||
File Name Class Document Label
|
||||
--------------------------------------------------------------------------------
|
||||
content/8c659a11cd429.nwd NOVEL New Scene
|
||||
content/a35baf2e93843.nwd NOVEL Title Page
|
||||
content/f5ab3e30151e1.nwd NOVEL New Chapter
|
||||
|
||||
@@ -28,8 +28,10 @@ from tools import cmpFiles
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QTextCursor
|
||||
from PyQt5.QtWidgets import QAction, QMessageBox
|
||||
from PyQt5.QtWidgets import QAction, QMessageBox, QDialog
|
||||
|
||||
from nw.gui.itemeditor import GuiItemEditor
|
||||
from nw.gui.doceditor import GuiDocEditor
|
||||
from nw.gui.projtree import GuiProjectTree
|
||||
from nw.constants import nwItemType, nwDocAction
|
||||
|
||||
@@ -43,7 +45,11 @@ def testGuiEditor_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, refDir, outDi
|
||||
"""
|
||||
# Block message box
|
||||
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes)
|
||||
monkeypatch.setattr(QMessageBox, "information", lambda *args: QMessageBox.Yes)
|
||||
monkeypatch.setattr(GuiItemEditor, "exec_", lambda *args: None)
|
||||
monkeypatch.setattr(GuiItemEditor, "result", lambda *args: QDialog.Accepted)
|
||||
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *args: True)
|
||||
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *args: True)
|
||||
|
||||
# Create new, save, close project
|
||||
nwGUI.theProject.projTree.setSeed(42)
|
||||
@@ -353,6 +359,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
"""
|
||||
# Block message box
|
||||
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes)
|
||||
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *args: True)
|
||||
|
||||
nwGUI.theProject.projTree.setSeed(42)
|
||||
assert nwGUI.openProject(nwLipsum)
|
||||
|
||||
@@ -38,6 +38,7 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
"""
|
||||
# Block message box
|
||||
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes)
|
||||
monkeypatch.setattr(QMessageBox, "information", lambda *args: QMessageBox.Yes)
|
||||
|
||||
# Open project
|
||||
nwGUI.theProject.projTree.setSeed(42)
|
||||
|
||||
@@ -27,6 +27,7 @@ from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QTextCursor, QTextBlock
|
||||
from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox
|
||||
|
||||
from nw.gui.doceditor import GuiDocEditor
|
||||
from nw.constants import nwUnicode, nwDocAction, nwDocInsert, nwKeyWords
|
||||
|
||||
keyDelay = 2
|
||||
@@ -39,6 +40,7 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
"""
|
||||
# Block message box
|
||||
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes)
|
||||
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *args: True)
|
||||
|
||||
# Test Document Action with No Project
|
||||
assert not nwGUI.docEditor.docAction(nwDocAction.COPY)
|
||||
|
||||
@@ -37,6 +37,7 @@ def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
"""
|
||||
# Block message box
|
||||
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes)
|
||||
monkeypatch.setattr(QMessageBox, "information", lambda *args: QMessageBox.Yes)
|
||||
|
||||
assert nwGUI.openProject(nwLipsum)
|
||||
nwGUI.mainConf.lastPath = nwLipsum
|
||||
|
||||
@@ -48,6 +48,7 @@ def testGuiProjSettings_Dialog(qtbot, monkeypatch, nwGUI, fncDir, fncProj, outDi
|
||||
|
||||
# Block message box
|
||||
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes)
|
||||
monkeypatch.setattr(QMessageBox, "critical", lambda *args: QMessageBox.Yes)
|
||||
|
||||
# Check that we cannot open when there is no project
|
||||
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
|
||||
|
||||
@@ -38,6 +38,9 @@ def testGuiProjTree_TreeItems(qtbot, caplog, monkeypatch, nwGUI, nwMinimal):
|
||||
"""
|
||||
# Block message box
|
||||
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes)
|
||||
monkeypatch.setattr(QMessageBox, "critical", lambda *args: QMessageBox.Yes)
|
||||
monkeypatch.setattr(QMessageBox, "warning", lambda *args: QMessageBox.Yes)
|
||||
monkeypatch.setattr(QMessageBox, "information", lambda *args: QMessageBox.Yes)
|
||||
monkeypatch.setattr(GuiMain, "editItem", lambda *args: None)
|
||||
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *args: True)
|
||||
|
||||
@@ -96,12 +99,12 @@ def testGuiProjTree_TreeItems(qtbot, caplog, monkeypatch, nwGUI, nwMinimal):
|
||||
nwTree.setSelectedHandle("8c659a11cd429")
|
||||
|
||||
# Shift focus and try to move item
|
||||
monkeypatch.setattr("PyQt5.QtWidgets.qApp.focusWidget", lambda: None)
|
||||
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *args: False)
|
||||
assert not nwTree.moveTreeItem(1)
|
||||
assert nwTree.getTreeFromHandle("a6d311a93600a") == [
|
||||
"a6d311a93600a", "f5ab3e30151e1", "8c659a11cd429", "44cb730c42048", "71ee45a3c0db9"
|
||||
]
|
||||
monkeypatch.setattr("PyQt5.QtWidgets.qApp.focusWidget", lambda: nwTree)
|
||||
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *args: True)
|
||||
|
||||
# Move second item up twice (should give same result)
|
||||
nwGUI.mainMenu.aMoveUp.activate(QAction.Trigger)
|
||||
@@ -131,6 +134,15 @@ def testGuiProjTree_TreeItems(qtbot, caplog, monkeypatch, nwGUI, nwMinimal):
|
||||
"a6d311a93600a", "f5ab3e30151e1", "44cb730c42048", "71ee45a3c0db9", "8c659a11cd429"
|
||||
]
|
||||
|
||||
# Move up twice, and undo
|
||||
nwTree._lastMove = {}
|
||||
nwGUI.mainMenu.aMoveUp.activate(QAction.Trigger)
|
||||
nwGUI.mainMenu.aMoveUp.activate(QAction.Trigger)
|
||||
nwGUI.mainMenu.aMoveUndo.activate(QAction.Trigger)
|
||||
assert nwTree.getTreeFromHandle("a6d311a93600a") == [
|
||||
"a6d311a93600a", "f5ab3e30151e1", "44cb730c42048", "71ee45a3c0db9", "8c659a11cd429"
|
||||
]
|
||||
|
||||
# Move a root item (top level items are different) twice
|
||||
nwTree.flushTreeOrder()
|
||||
assert nwGUI.theProject.projTree._treeOrder.index("9d5247ab588e0") == 10
|
||||
|
||||
@@ -46,6 +46,7 @@ def testGuiProjectWizard_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
|
||||
"""
|
||||
# Block message box
|
||||
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes)
|
||||
monkeypatch.setattr(QMessageBox, "critical", lambda *args: QMessageBox.Yes)
|
||||
|
||||
if sys.platform.startswith("darwin"):
|
||||
# Disable for macOS because the test segfaults on QWizard.show()
|
||||
|
||||
Reference in New Issue
Block a user