diff --git a/manuskript/mainWindow.py b/manuskript/mainWindow.py index 19670e2c..5618f25b 100644 --- a/manuskript/mainWindow.py +++ b/manuskript/mainWindow.py @@ -439,23 +439,42 @@ class MainWindow(QMainWindow, Ui_MainWindow): # mainEditor). So we just pass along the signal. def documentsCopy(self): + "Copy selected item(s)." if self._lastFocus: self._lastFocus.copy() def documentsCut(self): + "Cut selected item(s)." if self._lastFocus: self._lastFocus.cut() def documentsPaste(self): + "Paste clipboard item(s) into selected item." if self._lastFocus: self._lastFocus.paste() def documentsDuplicate(self): + "Duplicate selected item(s)." if self._lastFocus: self._lastFocus.duplicate() def documentsDelete(self): + "Delete selected item(s)." if self._lastFocus: self._lastFocus.delete() def documentsMoveUp(self): + "Move up selected item(s)." if self._lastFocus: self._lastFocus.moveUp() def documentsMoveDown(self): + "Move Down selected item(s)." if self._lastFocus: self._lastFocus.moveDown() + def documentsSplitDialog(self): - print("documentsSplitDialog::FIXME") + "Opens a dialog to split selected items." + if self._lastFocus: self._lastFocus.splitDialog() + # current items or selected items? + pass + # use outlineBasics, to do that on all selected items. + # use editorWidget to do that on selected text. + def documentsSplitCursor(self): - print("documentsSplitCursor::FIXME") + """ + Split current item (open in text editor) at cursor position. If there is + a text selection, that selection becomes the title of the new scene. + """ + if self._lastFocus and self._lastFocus == self.mainEditor: + self.mainEditor.splitCursor() def documentsMerge(self): print("documentsMerge::FIXME") diff --git a/manuskript/models/outlineModel.py b/manuskript/models/outlineModel.py index 8831c550..c38260a0 100644 --- a/manuskript/models/outlineModel.py +++ b/manuskript/models/outlineModel.py @@ -652,6 +652,7 @@ class outlineItem(): if column == Outline.text.value: wc = wordCount(data) self.setData(Outline.wordCount.value, wc) + self.emitDataChanged(cols=[Outline.text.value]) # new in 0.5.0 if column == Outline.compile.value: self.emitDataChanged(cols=[Outline.title.value, Outline.compile.value], recursive=True) @@ -894,9 +895,39 @@ class outlineItem(): item.setData(Outline.text.value, subTxt) # Inserting item - self.parent().insertChild(self.row()+k, item) + #self.parent().insertChild(self.row()+k, item) + self._model.insertItem(item, self.row()+k, self.parent().index()) k += 1 + def splitAt(self, position, length=0): + """ + Splits note at position p. + + If length is bigger than 0, it describes the length of the title, made + from the character following position. + """ + + txt = self.text() + + # Stores the new text + self.setData(Outline.text.value, txt[:position]) + + # Create a copy + item = self.copy() + + # Update title + if length > 0: + title = txt[position:position+length].replace("\n", "") + else: + title = "{}_{}".format(item.title(), 2) + item.setData(Outline.title.value, title) + + # Set text + item.setData(Outline.text.value, txt[position+length:]) + + # Inserting item using the model to signal views + self._model.insertItem(item, self.row()+1, self.parent().index()) + ############################################################################### # XML ############################################################################### diff --git a/manuskript/settings.py b/manuskript/settings.py index 4beb4b5b..5cb1bb43 100644 --- a/manuskript/settings.py +++ b/manuskript/settings.py @@ -96,13 +96,14 @@ frequencyAnalyzer = { viewMode = "fiction" # simple, fiction saveToZip = True +dontShowDeleteWarning = False def save(filename=None, protocol=None): global spellcheck, dict, corkSliderFactor, viewSettings, corkSizeFactor, folderView, lastTab, openIndexes, \ autoSave, autoSaveDelay, saveOnQuit, autoSaveNoChanges, autoSaveNoChangesDelay, outlineViewColumns, \ corkBackground, corkStyle, fullScreenTheme, defaultTextType, textEditor, revisions, frequencyAnalyzer, viewMode, \ - saveToZip + saveToZip, dontShowDeleteWarning allSettings = { "viewSettings": viewSettings, @@ -127,6 +128,7 @@ def save(filename=None, protocol=None): "frequencyAnalyzer": frequencyAnalyzer, "viewMode": viewMode, "saveToZip": saveToZip, + "dontShowDeleteWarning": dontShowDeleteWarning, } #pp=pprint.PrettyPrinter(indent=4, compact=False) @@ -294,3 +296,7 @@ def load(string, fromString=False, protocol=None): if "saveToZip" in allSettings: global saveToZip saveToZip = allSettings["saveToZip"] + + if "dontShowDeleteWarning" in allSettings: + global dontShowDeleteWarning + dontShowDeleteWarning = allSettings["dontShowDeleteWarning"] diff --git a/manuskript/ui/editors/editorWidget.py b/manuskript/ui/editors/editorWidget.py index 309d8eee..10903072 100644 --- a/manuskript/ui/editors/editorWidget.py +++ b/manuskript/ui/editors/editorWidget.py @@ -8,6 +8,7 @@ from manuskript import settings from manuskript.functions import AUC, mainWindow from manuskript.ui.editors.editorWidget_ui import Ui_editorWidget_ui from manuskript.ui.views.textEditView import textEditView +from manuskript.ui.tools.splitDialog import splitDialog class editorWidget(QWidget, Ui_editorWidget_ui): @@ -330,7 +331,9 @@ class editorWidget(QWidget, Ui_editorWidget_ui): ############################################################################### def getCurrentItemView(self): - if self.folderView == "outline": + if self.stack.currentIndex() == 0: + return self.txtRedacText + elif self.folderView == "outline": return self.outlineView elif self.folderView == "cork": return self.corkView @@ -351,9 +354,46 @@ class editorWidget(QWidget, Ui_editorWidget_ui): if self.getCurrentItemView(): self.getCurrentItemView().moveUp() def moveDown(self): if self.getCurrentItemView(): self.getCurrentItemView().moveDown() - def documentsSplitDialog(self): - print("documentsSplitDialog::FIXME") - def documentsSplitCursor(self): - print("documentsSplitCursor::FIXME") + + def splitDialog(self): + """ + Opens a dialog to split selected items. + """ + if self.getCurrentItemView() == self.txtRedacText: + # Text editor + if not self.currentIndex.isValid(): + return + + sel = self.txtRedacText.textCursor().selectedText() + # selectedText uses \u2029 instead of \n, no idea why. + sel = sel.replace("\u2029", "\n") + splitDialog(self, [self.currentIndex], mark=sel) + + elif self.getCurrentItemView(): + # One of the view + self.getCurrentItemView().splitDialog() + + def splitCursor(self): + """ + Splits items at cursor position. If there is a selection, that selection + becomes the new item's title. + + Call context: Only works when editing a file. + """ + + if not self.currentIndex.isValid(): + return + + if self.getCurrentItemView() == self.txtRedacText: + c = self.txtRedacText.textCursor() + + title = c.selectedText() + # selection can be backward + pos = min(c.selectionStart(), c.selectionEnd()) + + item = self.currentIndex.internalPointer() + + item.splitAt(pos, len(title)) + def documentsMerge(self): print("documentsMerge::FIXME") diff --git a/manuskript/ui/editors/mainEditor.py b/manuskript/ui/editors/mainEditor.py index b4f920f7..3c51f1dc 100644 --- a/manuskript/ui/editors/mainEditor.py +++ b/manuskript/ui/editors/mainEditor.py @@ -255,10 +255,8 @@ class mainEditor(QWidget, Ui_mainEditor): def delete(self): self.currentEditor().delete() def moveUp(self): self.currentEditor().moveUp() def moveDown(self): self.currentEditor().moveDown() - def documentsSplitDialog(self): - print("documentsSplitDialog::FIXME") - def documentsSplitCursor(self): - print("documentsSplitCursor::FIXME") + def splitDialog(self): self.currentEditor().splitDialog() + def splitCursor(self): self.currentEditor().splitCursor() def documentsMerge(self): print("documentsMerge::FIXME") diff --git a/manuskript/ui/tools/splitDialog.py b/manuskript/ui/tools/splitDialog.py new file mode 100644 index 00000000..d77456d8 --- /dev/null +++ b/manuskript/ui/tools/splitDialog.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# --!-- coding: utf8 --!-- +from PyQt5.QtWidgets import QInputDialog + + +class splitDialog(QInputDialog): + """ + Opens a dialog to split indexes. + """ + def __init__(self, parent, indexes, mark=None): + """ + @param parent: a QWidget, for the dialog. + @param indexes: a list of QModelIndex in the outlineModel + @param default: the default split mark + """ + QInputDialog.__init__(self, parent) + + description = self.tr(""" +

Split selected item(s) at the given mark.

+ +

If one of the selected item is a folder, it will be applied + recursively to all of it's children items.

+ +

The split mark can contain folling escret ape sequences: +

+

+ +

Mark:

+ """) + + if not mark: + mark = "\\n---\\n" + mark = mark.replace("\n", "\\n") + mark = mark.replace("\t", "\\t") + + self.setLabelText(description) + self.setTextValue(mark) + self.setWindowTitle(self.tr("Split item(s)")) + + r = self.exec() + + mark = self.textValue() + + if r and mark: + + mark = mark.replace("\\n", "\n") + mark = mark.replace("\\t", "\t") + + for idx in indexes: + if idx.isValid(): + item = idx.internalPointer() + item.split(mark) diff --git a/manuskript/ui/views/outlineBasics.py b/manuskript/ui/views/outlineBasics.py index 36c6c4b3..f43f5b43 100644 --- a/manuskript/ui/views/outlineBasics.py +++ b/manuskript/ui/views/outlineBasics.py @@ -2,14 +2,16 @@ # --!-- coding: utf8 --!-- from PyQt5.QtCore import Qt, QSignalMapper, QSize from PyQt5.QtGui import QIcon, QCursor -from PyQt5.QtWidgets import QAbstractItemView, qApp, QMenu, QAction -from PyQt5.QtWidgets import QListWidget, QWidgetAction, QListWidgetItem, QLineEdit +from PyQt5.QtWidgets import QAbstractItemView, qApp, QMenu, QAction, \ + QListWidget, QWidgetAction, QListWidgetItem, \ + QLineEdit, QInputDialog, QMessageBox, QCheckBox from manuskript import settings from manuskript.enums import Outline from manuskript.functions import mainWindow from manuskript.functions import toInt, customIcons from manuskript.models.outlineModel import outlineItem +from manuskript.ui.tools.splitDialog import splitDialog class outlineBasics(QAbstractItemView): @@ -53,7 +55,7 @@ class outlineBasics(QAbstractItemView): title = mouseIndex.internalPointer().title() else: - title = self.tr("Root") + title = qApp.translate("outlineBasics", "Root") if len(title) > 25: title = title[:25] + "…" @@ -67,10 +69,10 @@ class outlineBasics(QAbstractItemView): # Open item(s) in new tab if mouseIndex in sel and len(sel) > 1: - actionTitle = self.tr("Open {} items in new tabs").format(len(sel)) + actionTitle = qApp.translate("outlineBasics", "Open {} items in new tabs").format(len(sel)) self._indexesToOpen = sel else: - actionTitle = self.tr("Open {} in a new tab").format(title) + actionTitle = qApp.translate("outlineBasics", "Open {} in a new tab").format(title) self._indexesToOpen = [mouseIndex] self.actNewTab = QAction(QIcon.fromTheme("go-right"), actionTitle, menu) @@ -284,6 +286,27 @@ class outlineBasics(QAbstractItemView): self.delete() def delete(self): + """ + Shows a warning, and then deletes currently selected indexes. + """ + if not settings.dontShowDeleteWarning: + msg = QMessageBox(QMessageBox.Warning, + qApp.translate("outlineBasics", "About to remove"), + qApp.translate("outlineBasics", + "

You're about to delete {} item(s).

Are you sure?

" + ).format(len(self.getSelection())), + QMessageBox.Yes | QMessageBox.Cancel) + + chk = QCheckBox("&Don't show this warning in the future.") + msg.setCheckBox(chk) + ret = msg.exec() + + if ret == QMessageBox.Cancel: + return + + if chk.isChecked(): + settings.dontShowDeleteWarning = True + self.model().removeIndexes(self.getSelection()) def duplicate(self): @@ -295,7 +318,7 @@ class outlineBasics(QAbstractItemView): Move selected items up or down. """ - # we store selected indexes + # we store selected indexesret currentID = self.model().ID(self.currentIndex()) selIDs = [self.model().ID(i) for i in self.selectedIndexes()] @@ -316,7 +339,7 @@ class outlineBasics(QAbstractItemView): sm.clear() [sm.select(idx, sm.Select) for idx in selIdx] sm.setCurrentIndex(self.model().getIndexByID(currentID), sm.Select) - #self.setSelectionModel(sm) + #self.setSmsgBoxelectionModel(sm) # Unblock signals self.blockSignals(False) @@ -342,6 +365,19 @@ class outlineBasics(QAbstractItemView): def moveUp(self): self.move(-1) def moveDown(self): self.move(+1) + def splitDialog(self): + """ + Opens a dialog to split selected items. + + Call context: if at least one index is selected. Folder or text. + """ + + indexes = self.getSelection() + if len(indexes) == 0: + return + + splitDialog(self, indexes) + def setPOV(self, POV): for i in self.getSelection(): self.model().setData(i.sibling(i.row(), Outline.POV.value), str(POV)) diff --git a/manuskript/ui/views/textEditView.py b/manuskript/ui/views/textEditView.py index bb98996a..1c2513f5 100644 --- a/manuskript/ui/views/textEditView.py +++ b/manuskript/ui/views/textEditView.py @@ -93,7 +93,7 @@ class textEditView(QTextEdit): default_locale = QLocale.system().name() if default_locale is None: default_locale = enchant.list_dicts()[0][0] - + return default_locale def setModel(self, model):