Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add file template feature #1688

Merged
merged 10 commits into from
Feb 8, 2024
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
Loading