Skip to content

Commit

Permalink
Add file template feature (#1688)
Browse files Browse the repository at this point in the history
  • Loading branch information
vkbo committed Feb 8, 2024
2 parents 321e57a + 8ff292f commit 467772c
Show file tree
Hide file tree
Showing 27 changed files with 505 additions and 115 deletions.
1 change: 1 addition & 0 deletions novelwriter/assets/icons/typicons_dark/icons.conf
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ cls_none = typ_cancel.svg
cls_novel = typ_book.svg
cls_object = typ_key.svg
cls_plot = typ_puzzle.svg
cls_template = typ_document-add-col.svg
cls_timeline = typ_calendar.svg
cls_trash = typ_trash.svg
cls_world = typ_location.svg
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions novelwriter/assets/icons/typicons_light/icons.conf
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ cls_none = typ_cancel.svg
cls_novel = typ_book.svg
cls_object = typ_key.svg
cls_plot = typ_puzzle.svg
cls_template = typ_document-add-col.svg
cls_timeline = typ_calendar.svg
cls_trash = typ_trash.svg
cls_world = typ_location.svg
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions novelwriter/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ class nwLabels:
nwItemClass.ENTITY: QT_TRANSLATE_NOOP("Constant", "Entities"),
nwItemClass.CUSTOM: QT_TRANSLATE_NOOP("Constant", "Custom"),
nwItemClass.ARCHIVE: QT_TRANSLATE_NOOP("Constant", "Archive"),
nwItemClass.TEMPLATE: QT_TRANSLATE_NOOP("Constant", "Templates"),
nwItemClass.TRASH: QT_TRANSLATE_NOOP("Constant", "Trash"),
}
CLASS_ICON = {
Expand All @@ -199,6 +200,7 @@ class nwLabels:
nwItemClass.ENTITY: "cls_entity",
nwItemClass.CUSTOM: "cls_custom",
nwItemClass.ARCHIVE: "cls_archive",
nwItemClass.TEMPLATE: "cls_template",
nwItemClass.TRASH: "cls_trash",
}
LAYOUT_NAME = {
Expand Down
5 changes: 3 additions & 2 deletions novelwriter/core/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,8 +485,9 @@ def checkThese(self, tBits: list[str], tHandle: str) -> list[bool]:

# For a tag, only the first value is accepted, the rest are ignored
if tBits[0] == nwKeyWords.TAG_KEY and nBits > 1:
if tBits[1] in self._tagsIndex:
isGood[1] = self._tagsIndex.tagHandle(tBits[1]) == tHandle
check, _ = self.parseValue(tBits[1])
if check in self._tagsIndex:
isGood[1] = self._tagsIndex.tagHandle(check) == tHandle
else:
isGood[1] = True
return isGood
Expand Down
24 changes: 21 additions & 3 deletions novelwriter/core/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,15 +334,33 @@ def getImportStatus(self, incIcon=True):

def isNovelLike(self) -> bool:
"""Check if the item is of a novel-like class."""
return self._class in (nwItemClass.NOVEL, nwItemClass.ARCHIVE)
return self._class in (
nwItemClass.NOVEL,
nwItemClass.ARCHIVE,
nwItemClass.TEMPLATE,
)

def isTemplateFile(self) -> bool:
"""Check if the item is a template file."""
return self._type == nwItemType.FILE and self._class == nwItemClass.TEMPLATE

def documentAllowed(self) -> bool:
"""Check if the item is allowed to be of document layout."""
return self._class in (nwItemClass.NOVEL, nwItemClass.ARCHIVE, nwItemClass.TRASH)
return self._class in (
nwItemClass.NOVEL,
nwItemClass.ARCHIVE,
nwItemClass.TEMPLATE,
nwItemClass.TRASH,
)

def isInactiveClass(self) -> bool:
"""Check if the item is in an inactive class."""
return self._class in (nwItemClass.NO_CLASS, nwItemClass.ARCHIVE, nwItemClass.TRASH)
return self._class in (
nwItemClass.NO_CLASS,
nwItemClass.ARCHIVE,
nwItemClass.TEMPLATE,
nwItemClass.TRASH,
)

def isRootType(self) -> bool:
"""Check if item is a root item."""
Expand Down
37 changes: 30 additions & 7 deletions novelwriter/core/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,24 +175,47 @@ def writeNewFile(self, tHandle: str, hLevel: int, isDocument: bool, text: str =
will not run if the file exists and is not empty.
"""
tItem = self._tree[tHandle]
if tItem is None:
return False
if not tItem.isFileType():
if not (tItem and tItem.isFileType()):
return False

newDoc = self._storage.getDocument(tHandle)
if (newDoc.readDocument() or "").strip():
return False

hshText = "#"*minmax(hLevel, 1, 4)
newText = f"{hshText} {tItem.itemName}\n\n{text}"
indent = "#"*minmax(hLevel, 1, 4)
text = f"{indent} {tItem.itemName}\n\n{text}"

if tItem.isNovelLike() and isDocument:
tItem.setLayout(nwItemLayout.DOCUMENT)
else:
tItem.setLayout(nwItemLayout.NOTE)

newDoc.writeDocument(newText)
self._index.scanText(tHandle, newText)
newDoc.writeDocument(text)
self._index.scanText(tHandle, text)

return True

def copyFileContent(self, tHandle: str, sHandle: str) -> bool:
"""Copy content to a new document after it is created. This
will not run if the file exists and is not empty.
"""
tItem = self._tree[tHandle]
if not (tItem and tItem.isFileType()):
return False

sItem = self._tree[sHandle]
if not (sItem and sItem.isFileType()):
return False

newDoc = self._storage.getDocument(tHandle)
if (newDoc.readDocument() or "").strip():
return False

logger.debug("Populating '%s' with text from '%s'", tHandle, sHandle)
text = self._storage.getDocument(sHandle).readDocument() or ""
newDoc.writeDocument(text)
sItem.setLayout(tItem.itemLayout)
self._index.scanText(tHandle, text)

return True

Expand Down
3 changes: 2 additions & 1 deletion novelwriter/core/projectxml.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ class ProjectXMLReader:
as attributes. The id attribute was also added to the project.
Rev 1: Drops the titleFormat node from settings. 2.1 Beta 1.
Rev 2: Drops the title node from project. 2.3 Beta 1.
Rev 2: Drops the title node from project and adds the TEMPLATE
class for items. 2.3 Beta 1.
"""

def __init__(self, path: str | Path) -> None:
Expand Down
2 changes: 1 addition & 1 deletion novelwriter/dialogs/editlabel.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def __init__(self, parent: QWidget, text: str = "") -> None:
mSp = CONFIG.pxInt(12)

# Item Label
self.labelValue = QLineEdit()
self.labelValue = QLineEdit(self)
self.labelValue.setMinimumWidth(mVd)
self.labelValue.setMaxLength(200)
self.labelValue.setText(text)
Expand Down
6 changes: 3 additions & 3 deletions novelwriter/dialogs/projectsettings.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ def __init__(self, parent: QWidget, isStatus: bool) -> None:
self.dnButton.clicked.connect(lambda: self._moveItem(1))

# Edit Form
self.editName = QLineEdit()
self.editName = QLineEdit(self)
self.editName.setMaxLength(40)
self.editName.setPlaceholderText(self.tr("Select item to edit"))
self.editName.setEnabled(False)
Expand Down Expand Up @@ -621,12 +621,12 @@ def __init__(self, parent: QWidget) -> None:
self.delButton.clicked.connect(self._delEntry)

# Edit Form
self.editKey = QLineEdit()
self.editKey = QLineEdit(self)
self.editKey.setPlaceholderText(self.tr("Select item to edit"))
self.editKey.setEnabled(False)
self.editKey.setMaxLength(40)

self.editValue = QLineEdit()
self.editValue = QLineEdit(self)
self.editValue.setEnabled(False)
self.editValue.setMaxLength(80)

Expand Down
2 changes: 1 addition & 1 deletion novelwriter/dialogs/wordlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def __init__(self, mainGui: GuiMain) -> None:
self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
self.listBox.setSortingEnabled(True)

self.newEntry = QLineEdit()
self.newEntry = QLineEdit(self)

self.addButton = QPushButton(SHARED.theme.getIcon("add"), "")
self.addButton.clicked.connect(self._doAdd)
Expand Down
3 changes: 2 additions & 1 deletion novelwriter/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ class nwItemClass(Enum):
ENTITY = 7
CUSTOM = 8
ARCHIVE = 9
TRASH = 10
TEMPLATE = 10
TRASH = 11

# END Enum nwItemClass

Expand Down
17 changes: 8 additions & 9 deletions novelwriter/gui/dochighlight.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ def __init__(self, document: QTextDocument) -> None:

logger.debug("Create: GuiDocHighlighter")

self._tItem = None
self._tHandle = None
self._isInactive = False
self._spellCheck = False
self._spellErr = QTextCharFormat()

Expand Down Expand Up @@ -238,11 +238,10 @@ def setSpellCheck(self, state: bool) -> None:
def setHandle(self, tHandle: str) -> None:
"""Set the handle of the currently highlighted document."""
self._tHandle = tHandle
self._tItem = SHARED.project.tree[tHandle]
logger.debug(
"Syntax highlighter %s for item '%s'",
"enabled" if self._tItem else "disabled", tHandle
self._isInactive = (
item.isInactiveClass() if (item := SHARED.project.tree[tHandle]) else False
)
logger.debug("Syntax highlighter enabled for item '%s'", tHandle)
return

##
Expand Down Expand Up @@ -286,16 +285,16 @@ def highlightBlock(self, text: str) -> None:
for n, bit in enumerate(bits):
xPos = pos[n]
xLen = len(bit)
if not isGood[n]:
self.setFormat(xPos, xLen, self._hStyles["codeinval"])
elif n == 0:
if n == 0 and isGood[n]:
self.setFormat(xPos, xLen, self._hStyles["keyword"])
else:
elif isGood[n] and not self._isInactive:
one, two = index.parseValue(bit)
self.setFormat(xPos, len(one), self._hStyles["value"])
if two:
yPos = xPos + len(bit) - len(two)
self.setFormat(yPos, len(two), self._hStyles["optional"])
elif not self._isInactive:
self.setFormat(xPos, xLen, self._hStyles["codeinval"])

# We never want to run the spell checker on keyword/values,
# so we force a return here
Expand Down
Loading

0 comments on commit 467772c

Please sign in to comment.