A bit of shuffling in the menu, and connected a lot more menu items to actions.

This commit is contained in:
Veronica K. B. Olsen
2019-04-27 22:05:01 +02:00
parent bd24b58926
commit 65d4fcc968
5 changed files with 217 additions and 156 deletions
+61 -23
View File
@@ -16,7 +16,7 @@ import nw
from os import path
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtGui import QIcon, QFont, QColor
from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QAbstractItemView, QInputDialog, QLineEdit
from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QAbstractItemView, QInputDialog, QLineEdit, QApplication
from nw.enum import nwItemType, nwItemClass
from nw.project.item import NWItem
@@ -30,12 +30,13 @@ class GuiDocTree(QTreeWidget):
C_FLAGS = 2
C_HANDLE = 3
def __init__(self, theProject):
QTreeWidget.__init__(self)
def __init__(self, theParent, theProject):
QTreeWidget.__init__(self, theParent)
logger.debug("Initialising DocTree ...")
self.mainConf = nw.CONFIG
self.debugGUI = self.mainConf.debugGUI
self.theParent = theParent
self.theProject = theProject
self.theMap = {}
@@ -64,38 +65,75 @@ class GuiDocTree(QTreeWidget):
return
def newTreeItem(self, pHandle, itemType, itemClass):
def newTreeItem(self, itemType, itemClass):
pHandle = self.getSelectedHandle()
if itemClass is None and pHandle is not None:
itemClass = self.theProject.getItem(pHandle).itemClass
logger.verbose("Adding new item of type %s and class %s to handle %s" % (
itemType.name,itemClass.name,str(pHandle))
)
# Create the new item
if itemType == nwItemType.FILE:
tHandle = self.theProject.newFile("New File", itemClass, pHandle)
elif itemType == nwItemType.FOLDER:
tHandle = self.theProject.newFolder("New Folder", itemClass, pHandle)
elif itemType == nwItemType.ROOT:
if itemType == nwItemType.ROOT:
tHandle = self.theProject.newRoot(NWItem.CLASS_NAME[itemClass], itemClass)
else:
logger.error("Failed to add new item")
return
# If no parent has been selected, make the new file under the root NOVEL item.
if pHandle is None:
pHandle = self.theProject.findRootItem(nwItemClass.NOVEL)
# If still nothing, give up
if pHandle is None:
logger.error("Did not find anywhere to add the item!")
return False
# Now check if the selected item is a file, in which case the new file will be a sibling
pItem = self.theProject.getItem(pHandle)
if pItem.itemType == nwItemType.FILE:
pHandle = pItem.parHandle
# If we again has no home, give up
if pHandle is None:
logger.error("Did not find anywhere to add the item!")
return False
# If we're still here, add the file
if itemType == nwItemType.FILE:
tHandle = self.theProject.newFile("New File", itemClass, pHandle)
elif itemType == nwItemType.FOLDER:
tHandle = self.theProject.newFolder("New Folder", itemClass, pHandle)
else:
logger.error("Failed to add new item")
return False
# Add the new item to the tree
nwItem = self.theProject.getItem(tHandle)
self._addTreeItem(nwItem)
trItem = self._addTreeItem(nwItem)
if pHandle is not None:
self.theMap[pHandle].setExpanded(True)
self.clearSelection()
trItem.setSelected(True)
self.theParent.editItem()
return
def moveTreeItem(self, tHandle, nStep):
tItem = self.theMap[tHandle]
pItem = tItem.parent()
tIndex = pItem.indexOfChild(tItem)
nChild = pItem.childCount()
nIndex = tIndex + nStep
if nIndex < 0 or nIndex >= nChild: return
cItem = pItem.takeChild(tIndex)
pItem.insertChild(nIndex, cItem)
def moveTreeItem(self, nStep):
"""Move an item up or down in the tree, but only if the treeView has focus. This also
applies when the menu is used.
"""
if QApplication.focusWidget() == self:
tHandle = self.getSelectedHandle()
tItem = self.theMap[tHandle]
pItem = tItem.parent()
tIndex = pItem.indexOfChild(tItem)
nChild = pItem.childCount()
nIndex = tIndex + nStep
if nIndex < 0 or nIndex >= nChild: return
cItem = pItem.takeChild(tIndex)
pItem.insertChild(nIndex, cItem)
self.clearSelection()
cItem.setSelected(True)
return
def renameTreeItem(self, tHandle):
@@ -187,7 +225,7 @@ class GuiDocTree(QTreeWidget):
elif nwItem.itemType == nwItemType.FILE:
newItem.setIcon(self.C_NAME, QIcon.fromTheme("x-office-document"))
return True
return newItem
def propagateCount(self, tHandle, theCount, nDepth=0):
tItem = self.theMap[tHandle]
+86 -58
View File
@@ -16,7 +16,7 @@ import nw
from PyQt5.QtWidgets import qApp, QMenuBar, QAction
from PyQt5.QtGui import QIcon
from nw.enum import nwItemClass, nwDocAction
from nw.enum import nwItemType, nwItemClass, nwDocAction
logger = logging.getLogger(__name__)
@@ -31,15 +31,17 @@ class GuiMainMenu(QMenuBar):
self.theProject = theProject
self._buildProjectMenu()
self._buildStructureMenu()
self._buildDocumentMenu()
self._buildEditMenu()
self._buildViewMenu()
self._buildFormatMenu()
self._buildToolsMenu()
self._buildHelpMenu()
# Function Pointers
self._docAction = self.theParent.docEditor.docAction
self._docAction = self.theParent.docEditor.docAction
self._moveTreeItem = self.theParent.treeView.moveTreeItem
self._newTreeItem = self.theParent.treeView.newTreeItem
logger.debug("Main Menu initialisation complete")
@@ -101,9 +103,6 @@ class GuiMainMenu(QMenuBar):
menuItem.triggered.connect(lambda menuItem, n=n : self.openRecentProject(menuItem, n))
recentMenu.addAction(menuItem)
# Project > Separator
self.projMenu.addSeparator()
# Project > Project Settings
menuItem = QAction(QIcon.fromTheme("document-properties"), "Project Settings", self)
menuItem.setStatusTip("Project Settings")
@@ -113,6 +112,40 @@ class GuiMainMenu(QMenuBar):
# Project > Separator
self.projMenu.addSeparator()
# Project > New Root
rootMenu = self.projMenu.addMenu(QIcon.fromTheme("folder-new"), "Create Root Folder")
self.rootItems = {}
self.rootItems[nwItemClass.NOVEL] = QAction(QIcon.fromTheme("folder-new"), "Novel Root", rootMenu)
self.rootItems[nwItemClass.PLOT] = QAction(QIcon.fromTheme("folder-new"), "Plot Root", rootMenu)
self.rootItems[nwItemClass.CHARACTER] = QAction(QIcon.fromTheme("folder-new"), "Character Root", rootMenu)
self.rootItems[nwItemClass.WORLD] = QAction(QIcon.fromTheme("folder-new"), "Location Root", rootMenu)
self.rootItems[nwItemClass.TIMELINE] = QAction(QIcon.fromTheme("folder-new"), "Timeline Root", rootMenu)
self.rootItems[nwItemClass.OBJECT] = QAction(QIcon.fromTheme("folder-new"), "Object Root", rootMenu)
self.rootItems[nwItemClass.CUSTOM] = QAction(QIcon.fromTheme("folder-new"), "Custom Root", rootMenu)
rootMenu.addActions(self.rootItems.values())
# Project > New Folder
menuItem = QAction(QIcon.fromTheme("folder-new"), "Create Folder", self)
menuItem.setStatusTip("Create Folder")
menuItem.setShortcut("Ctrl+Shift+N")
menuItem.triggered.connect(lambda : self._newTreeItem(nwItemType.FOLDER, None))
self.projMenu.addAction(menuItem)
# Project > Rename Folder
menuItem = QAction(QIcon.fromTheme("folder-new"), "Rename Folder", self)
menuItem.setStatusTip("Rename Selected Folder")
menuItem.setShortcut("Ctrl+Shift+E")
self.projMenu.addAction(menuItem)
# Project > Delete Folder
menuItem = QAction(QIcon.fromTheme("edit-delete"), "Delete Folder", self)
menuItem.setStatusTip("Delete Selected Folder")
menuItem.setShortcut("Ctrl+Shift+Del")
self.projMenu.addAction(menuItem)
# Project > Separator
self.projMenu.addSeparator()
# Project > Exit
menuItem = QAction(QIcon.fromTheme("application-exit"), "Exit", self)
menuItem.setStatusTip("Exit %s" % nw.__package__)
@@ -122,58 +155,6 @@ class GuiMainMenu(QMenuBar):
return
def _buildStructureMenu(self):
# Structure
self.structMenu = self.addMenu("&Structure")
# Structure > New Folder
menuItem = QAction(QIcon.fromTheme("folder-new"), "Create Folder", self)
menuItem.setStatusTip("Create Folder")
menuItem.setShortcut("Ctrl+Shift+N")
self.structMenu.addAction(menuItem)
# Structure > New Root
rootMenu = self.structMenu.addMenu(QIcon.fromTheme("folder-new"), "Create Root Group")
self.rootItems = {}
self.rootItems[nwItemClass.NOVEL] = QAction(QIcon.fromTheme("folder-new"), "Novel Root", rootMenu)
self.rootItems[nwItemClass.PLOT] = QAction(QIcon.fromTheme("folder-new"), "Plot Root", rootMenu)
self.rootItems[nwItemClass.CHARACTER] = QAction(QIcon.fromTheme("folder-new"), "Character Root", rootMenu)
self.rootItems[nwItemClass.WORLD] = QAction(QIcon.fromTheme("folder-new"), "Location Root", rootMenu)
self.rootItems[nwItemClass.TIMELINE] = QAction(QIcon.fromTheme("folder-new"), "Timeline Root", rootMenu)
self.rootItems[nwItemClass.OBJECT] = QAction(QIcon.fromTheme("folder-new"), "Object Root", rootMenu)
self.rootItems[nwItemClass.CUSTOM] = QAction(QIcon.fromTheme("folder-new"), "Custom Root", rootMenu)
rootMenu.addActions(self.rootItems.values())
# Structure > Rename Folder
menuItem = QAction(QIcon.fromTheme("folder-new"), "Rename Folder", self)
menuItem.setStatusTip("Rename Selected Folder")
menuItem.setShortcut("Ctrl+Shift+E")
self.structMenu.addAction(menuItem)
# Structure > Delete Folder
menuItem = QAction(QIcon.fromTheme("edit-delete"), "Delete Folder", self)
menuItem.setStatusTip("Delete Selected Folder")
menuItem.setShortcut("Ctrl+Shift+Del")
self.structMenu.addAction(menuItem)
# Structure > Separator
self.structMenu.addSeparator()
# Structure > Move Up
menuItem = QAction(QIcon.fromTheme("go-up"), "Move Item Up", self)
menuItem.setStatusTip("Move Item Up")
menuItem.setShortcut("Ctrl+Up")
self.structMenu.addAction(menuItem)
# Structure > Move Down
menuItem = QAction(QIcon.fromTheme("go-down"), "Move Item Down", self)
menuItem.setStatusTip("Move Item Down")
menuItem.setShortcut("Ctrl+Down")
self.structMenu.addAction(menuItem)
return
def _buildDocumentMenu(self):
# Document
@@ -183,12 +164,14 @@ class GuiMainMenu(QMenuBar):
menuItem = QAction(QIcon.fromTheme("document-new"), "&New Document", self)
menuItem.setStatusTip("Create New Document")
menuItem.setShortcut("Ctrl+N")
menuItem.triggered.connect(lambda : self._newTreeItem(nwItemType.FILE, None))
self.docuMenu.addAction(menuItem)
# Document > Open
menuItem = QAction(QIcon.fromTheme("document-open"), "&Open Document", self)
menuItem.setStatusTip("Open Selected Document")
menuItem.setShortcut("Ctrl+O")
menuItem.triggered.connect(self.theParent.openSelectedItem)
self.docuMenu.addAction(menuItem)
# Document > Save
@@ -229,6 +212,34 @@ class GuiMainMenu(QMenuBar):
return
def _buildViewMenu(self):
# View
self.viewMenu = self.addMenu("&View")
# View > TreeView
menuItem = QAction(QIcon.fromTheme("go-home"), "TreeView", self)
menuItem.setStatusTip("Move to TreeView Panel")
menuItem.setShortcut("Ctrl+1")
menuItem.triggered.connect(lambda : self.theParent.setFocus(1))
self.viewMenu.addAction(menuItem)
# View > Document Pane 1
menuItem = QAction(QIcon.fromTheme("go-first"), "Left Document Pane", self)
menuItem.setStatusTip("Move to Left Document Pane")
menuItem.setShortcut("Ctrl+2")
menuItem.triggered.connect(lambda : self.theParent.setFocus(2))
self.viewMenu.addAction(menuItem)
# View > Document Pane 2
menuItem = QAction(QIcon.fromTheme("go-last"), "Right Document Pane", self)
menuItem.setStatusTip("Move to Right Document Pane")
menuItem.setShortcut("Ctrl+3")
menuItem.triggered.connect(lambda : self.theParent.setFocus(3))
self.viewMenu.addAction(menuItem)
return
def _buildEditMenu(self):
# Edit
@@ -341,6 +352,23 @@ class GuiMainMenu(QMenuBar):
# Tools
self.toolsMenu = self.addMenu("&Tools")
# Tools > Move Up
self.toolsMoveUp = QAction(QIcon.fromTheme("go-up"), "Move Tree Item Up", self)
self.toolsMoveUp.setStatusTip("Move Item Up")
self.toolsMoveUp.setShortcut("Ctrl+Shift+Up")
self.toolsMoveUp.triggered.connect(lambda : self._moveTreeItem(-1))
self.toolsMenu.addAction(self.toolsMoveUp)
# Tools > Move Down
self.toolsMoveDown = QAction(QIcon.fromTheme("go-down"), "Move Tree Item Down", self)
self.toolsMoveDown.setStatusTip("Move Item Down")
self.toolsMoveDown.setShortcut("Ctrl+Shift+Down")
self.toolsMoveDown.triggered.connect(lambda : self._moveTreeItem(1))
self.toolsMenu.addAction(self.toolsMoveDown)
# Tools > Separator
self.toolsMenu.addSeparator()
# Tools > Settings
menuItem = QAction(QIcon.fromTheme("preferences-system"), "Preferences", self)
menuItem.setStatusTip("Preferences")
+48 -59
View File
@@ -14,12 +14,11 @@ import logging
import nw
from os import path
from PyQt5.QtWidgets import qApp, QWidget, QMainWindow, QVBoxLayout, QFrame, QSplitter, QAction, QToolBar, QFileDialog, QStackedWidget
from PyQt5.QtCore import Qt, QSize, pyqtSlot
from PyQt5.QtWidgets import QWidget, QMainWindow, QVBoxLayout, QFrame, QSplitter, QFileDialog, QStackedWidget, QShortcut
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt
from nw.gui.doctree import GuiDocTree
from nw.gui.doctreectx import GuiDocTreeCtx
from nw.gui.doceditor import GuiDocEditor
from nw.gui.docdetails import GuiDocDetails
from nw.gui.mainmenu import GuiMainMenu
@@ -29,7 +28,7 @@ from nw.gui.statusbar import GuiMainStatus
from nw.project.project import NWProject
from nw.project.document import NWDoc
from nw.project.item import NWItem
from nw.enum import nwItemType, nwItemClass, nwDocAction, nwItemAction
from nw.enum import nwItemType, nwItemAction
logger = logging.getLogger(__name__)
@@ -50,7 +49,7 @@ class GuiMain(QMainWindow):
# Main GUI Elements
self.docEditor = GuiDocEditor(self)
self.docDetails = GuiDocDetails(self.theProject)
self.treeView = GuiDocTree(self.theProject)
self.treeView = GuiDocTree(self, self.theProject)
self.mainMenu = GuiMainMenu(self, self.theProject)
self.statusBar = GuiMainStatus()
@@ -75,11 +74,10 @@ class GuiMain(QMainWindow):
self.setCentralWidget(self.splitMain)
# Build GUI Elements
self.treeView.setContextMenuPolicy(Qt.CustomContextMenu)
self.treeView.customContextMenuRequested.connect(self._openDocTreeContextMenu)
self.treeView.itemSelectionChanged.connect(self._treeSingleClick)
self.treeView.itemDoubleClicked.connect(self._treeDoubleClick)
self.treeView.buildTree()
QShortcut(Qt.Key_Return, self.treeView, context=Qt.WidgetShortcut, activated=self._treeKeyPressReturn)
# Set Main Window Elements
self.setMenuBar(self.mainMenu)
@@ -138,24 +136,40 @@ class GuiMain(QMainWindow):
return
def saveDocument(self):
docHtml = self.docEditor.getText()
self.theDocument.theItem.setCharCount(self.docEditor.charCount)
self.theDocument.theItem.setWordCount(self.docEditor.wordCount)
self.theDocument.theItem.setParaCount(self.docEditor.paraCount)
self.theDocument.saveDocument(docHtml)
if self.theDocument.theItem is not None:
docHtml = self.docEditor.getText()
self.theDocument.theItem.setCharCount(self.docEditor.charCount)
self.theDocument.theItem.setWordCount(self.docEditor.wordCount)
self.theDocument.theItem.setParaCount(self.docEditor.paraCount)
self.theDocument.saveDocument(docHtml)
return
##
# Tree Item Actions
##
def editItem(self):
def openSelectedItem(self):
tHandle = self.treeView.getSelectedHandle()
logger.verbose("Requesting change to item %s" % tHandle)
if tHandle is None:
logger.warning("No item selected")
return
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
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)
dlgProj = GuiItemEditor(self, self.theProject, tHandle)
dlgProj.exec_()
@@ -202,6 +216,14 @@ class GuiMain(QMainWindow):
self.mainConf.saveConfig()
return
def setFocus(self, paneNo):
if paneNo == 1:
self.treeView.setFocus()
elif paneNo == 2:
if self.stackPane.currentIndex() == self.stackDoc:
self.docEditor.setFocus()
return
##
# Internal Functions
##
@@ -213,50 +235,6 @@ class GuiMain(QMainWindow):
self.setWindowTitle(winTitle)
return True
##
# DocTree Context Menu
##
def _openDocTreeContextMenu(self, thePosition):
ctxMenu = GuiDocTreeCtx(self.treeView, self.theProject, thePosition)
selHandle = ctxMenu.selHandle
selAction = ctxMenu.selAction
selClass = ctxMenu.selClass
selType = ctxMenu.selType
selTarget = ctxMenu.selTarget
# print(selHandle, selAction, selClass, selType, selTarget)
if selAction == nwItemAction.ADD_ROOT:
self.treeView.newTreeItem(selHandle, selType, selClass)
elif selAction == nwItemAction.ADD_FOLDER:
self.treeView.newTreeItem(selHandle, selType, selClass)
elif selAction == nwItemAction.ADD_FILE:
self.treeView.newTreeItem(selHandle, selType, selClass)
elif selAction == nwItemAction.MOVE_UP:
self.treeView.moveTreeItem(selHandle, -1)
elif selAction == nwItemAction.MOVE_DOWN:
self.treeView.moveTreeItem(selHandle, 1)
elif selAction == nwItemAction.MOVE_TO:
pass
elif selAction == nwItemAction.MOVE_TRASH:
pass
elif selAction == nwItemAction.SPLIT:
pass
elif selAction == nwItemAction.MERGE:
pass
elif selAction == nwItemAction.DELETE:
pass
elif selAction == nwItemAction.DELETE_ROOT:
pass
elif selAction == nwItemAction.EMPTY_TRASH:
pass
elif selAction == nwItemAction.RENAME:
self.treeView.renameTreeItem(selHandle)
return
##
# Events
##
@@ -295,6 +273,17 @@ class GuiMain(QMainWindow):
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 _splitMainMove(self, pWidth, pHeight):
"""Alert dependent GUI elements that the main pane splitter has been moved.
"""
+7 -1
View File
@@ -266,6 +266,12 @@ class NWProject():
logger.error("No tree item with handle %s" % str(tHandle))
return None
def findRootItem(self, theClass):
for aRoot in self.treeRoots:
if theClass == self.projTree[aRoot].itemClass:
return self.projTree[aRoot].itemHandle
return None
def getActionList(self, tHandle):
"""Returns a dictionary of possible actions to perform on a give handle.
"""
@@ -345,7 +351,7 @@ class NWProject():
"""Checks if there already is a root entry of class 'theClass' in the
root of the project tree.
"""
if theClass == nwItemClass.NO_CLASS:
if theClass == nwItemClass.CUSTOM:
return True
for aRoot in self.treeRoots:
if theClass == self.projTree[aRoot].itemClass:
+15 -15
View File
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="0.0.1" fileVersion="1.0" timeStamp="2019-04-24 23:22:52">
<novelWriterXML appVersion="0.0.1" fileVersion="1.0" timeStamp="2019-04-27 16:47:59">
<project>
<name>Sample Project</name>
<title>Sample Project</title>
@@ -60,20 +60,7 @@
<children>True</children>
<expanded>True</expanded>
</item>
<item handle="14298de4d9524" order="0" parent="f6622b4617424">
<name>John Smith</name>
<type>FILE</type>
<class>CHARACTER</class>
<status>0</status>
<depth>1</depth>
<children>False</children>
<expanded>False</expanded>
<layout>NOTE</layout>
<charCount>42</charCount>
<wordCount>8</wordCount>
<paraCount>1</paraCount>
</item>
<item handle="bb2c23b3c42cc" order="1" parent="f6622b4617424">
<item handle="bb2c23b3c42cc" order="0" parent="f6622b4617424">
<name>Jane Smith</name>
<type>FILE</type>
<class>CHARACTER</class>
@@ -86,6 +73,19 @@
<wordCount>9</wordCount>
<paraCount>1</paraCount>
</item>
<item handle="14298de4d9524" order="1" parent="f6622b4617424">
<name>John Smith</name>
<type>FILE</type>
<class>CHARACTER</class>
<status>0</status>
<depth>1</depth>
<children>False</children>
<expanded>False</expanded>
<layout>NOTE</layout>
<charCount>42</charCount>
<wordCount>8</wordCount>
<paraCount>1</paraCount>
</item>
<item handle="73eee34351f85" order="2" parent="None">
<name>Locations</name>
<type>ROOT</type>