mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-14 10:05:43 +01:00
c8969630f3
Introduced a real Property List Editor. Fixed Theme manager engine. Other minor fixes
604 lines
25 KiB
Swift
604 lines
25 KiB
Swift
/*
|
|
* vector sigma (https://github.com/vectorsigma72)
|
|
* Copyright 2020 vector sigma All Rights Reserved.
|
|
*
|
|
* The source code contained or described herein and all documents related
|
|
* to the source code ("Material") are owned by vector sigma.
|
|
* Title to the Material remains with vector sigma or its suppliers and licensors.
|
|
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
|
|
* No part of the Material may be used, copied, reproduced, modified, published,
|
|
* uploaded, posted, transmitted, distributed, or disclosed in any way without
|
|
* vector sigma's prior express written permission.
|
|
*
|
|
* No license under any patent, copyright, trade secret or other intellectual
|
|
* property right is granted to or conferred upon you by disclosure or delivery
|
|
* of the Materials, either expressly, by implication, inducement, estoppel or
|
|
* otherwise. Any license under such intellectual property rights must be
|
|
* express and approved by vector sigma in writing.
|
|
*
|
|
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
|
|
* this notice or any other notice embedded in Materials by vector sigma in any way.
|
|
*
|
|
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
|
|
* and all the users as long as the Material is used only within the
|
|
* source code and for the exclusive use of CloverBootloader, which must
|
|
* be free from any type of payment or commercial service for the license to be valid.
|
|
*/
|
|
|
|
import Cocoa
|
|
|
|
// MARK: Undo and redo support
|
|
@available(OSX 10.11, *)
|
|
extension PEOutlineView {
|
|
// MARK: Undo Manager object
|
|
|
|
/// Prepares PEOutlineView for the undo operation
|
|
func prepareUndoObj() -> AnyObject {
|
|
// nil is not expecte here. Be happy if throw an exception
|
|
return self.undoManager?.prepare(withInvocationTarget: self) as AnyObject
|
|
}
|
|
|
|
// MARK: Undo drag Item
|
|
/// Undo operation when an Item is dragged here an there in PEOutlineView
|
|
@objc func undo_DragAndDrop(item: PENode,
|
|
fromParent: PENode,
|
|
fromIndex: Int,
|
|
toParent: PENode,
|
|
toIndex: Int) {
|
|
self.prepareUndoObj().undo_DragAndDrop(item: item,
|
|
fromParent: toParent,
|
|
fromIndex: toIndex,
|
|
toParent: fromParent,
|
|
toIndex: fromIndex)
|
|
|
|
self.undoManager?.setActionName(localizedUndoRedoMoveItem)
|
|
let toParentCount = toParent.mutableChildren.count
|
|
let new = item.copy() as! PENode
|
|
if fromParent == toParent {
|
|
var mutatingToIndex = toIndex
|
|
if fromIndex < toIndex {
|
|
mutatingToIndex -= 1
|
|
}
|
|
fromParent.mutableChildren.removeObject(at: fromIndex)
|
|
fromParent.mutableChildren.insert(new, at: mutatingToIndex)
|
|
|
|
DispatchQueue.main.async {
|
|
self.reloadItem(fromParent, reloadChildren: true)
|
|
}
|
|
} else {
|
|
self.editorVC?.asyncExpand(item: toParent, expandParentOnly: false)
|
|
// drag and past from to another parent
|
|
if toParentCount == 0 {
|
|
toParent.mutableChildren.add(new)
|
|
DispatchQueue.main.async {
|
|
self.animator().moveItem(at: fromIndex,
|
|
inParent: fromParent,
|
|
to: toIndex,
|
|
inParent: toParent)
|
|
}
|
|
} else {
|
|
DispatchQueue.main.async {
|
|
gDeduplicateKeyInParent(parent:toParent, newNode: new)
|
|
|
|
toParent.mutableChildren.insert(new, at: toIndex)
|
|
fromParent.mutableChildren.removeObject(at: fromIndex)
|
|
|
|
|
|
self.animator().moveItem(at: fromIndex,
|
|
inParent: fromParent,
|
|
to: toIndex,
|
|
inParent: toParent)
|
|
self.reloadItem(toParent, reloadChildren: true)
|
|
let row = self.row(forItem: new)
|
|
if row >= 0 {
|
|
self.scrollRowToVisible(row)
|
|
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// select the new dragged item
|
|
DispatchQueue.main.async {
|
|
let toBeSelected = self.row(forItem: new)
|
|
if toBeSelected >= 0 {
|
|
self.scrollRowToVisible(toBeSelected)
|
|
self.selectRowIndexes(IndexSet(integer: toBeSelected), byExtendingSelection: false)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: Undo replace
|
|
/// Undo operation for Search & Replace in PEOutlineView
|
|
@objc func undo_FindAndReplace(nodes: NSArray, oldTreeData: NSArray, newTreeData: NSArray) {
|
|
self.prepareUndoObj().undo_FindAndReplace(nodes: nodes,
|
|
oldTreeData: newTreeData,
|
|
newTreeData: oldTreeData)
|
|
// select and expand, one by one, rows in oldNodes
|
|
self.undoManager?.setActionName(localizedReplace.lowercased())
|
|
|
|
var index : Int = 0
|
|
for n in nodes {
|
|
let node = n as! PENode
|
|
self.editorVC?.asyncExpand(item: node, expandParentOnly: true)
|
|
|
|
let newData = newTreeData.object(at: index) as? TagData
|
|
node.tagdata?.key = (newData?.key)!
|
|
node.tagdata?.value = newData?.value
|
|
DispatchQueue.main.async {
|
|
let row = self.row(forItem: node)
|
|
if row >= 0 {
|
|
self.scrollRowToVisible(row)
|
|
self.reloadData(forRowIndexes: IndexSet(integer: row), columnIndexes: IndexSet(integer: 0))
|
|
self.reloadData(forRowIndexes: IndexSet(integer: row), columnIndexes: IndexSet(integer: 2))
|
|
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
|
|
}
|
|
}
|
|
index+=1
|
|
}
|
|
|
|
self.editorVC?.performDelayedSearch()
|
|
}
|
|
|
|
// MARK: Undo set key string
|
|
/// Undo operation for a key editing (Key column) in PEOutlineView
|
|
@objc func undo_SetKey(node: PENode, newKey: String, oldKey: String) {
|
|
// we are setting the key for a specific row and ther's no need to reload the entire group
|
|
self.prepareUndoObj().undo_SetKey(node: node,
|
|
newKey: oldKey,
|
|
oldKey: newKey)
|
|
|
|
self.undoManager?.setActionName(localizedUndoRedoTyping)
|
|
node.tagdata?.key = newKey
|
|
|
|
self.editorVC?.asyncExpand(item: node, expandParentOnly: true)
|
|
//selecting the involved row, but we need to reload its view? isn't that already happened?
|
|
DispatchQueue.main.async {
|
|
self.reloadItem(node.parent, reloadChildren: false)
|
|
let row = self.row(forItem: node)
|
|
if row >= 0 {
|
|
self.scrollRowToVisible(row)
|
|
self.reloadData(forRowIndexes: IndexSet(integer: row), columnIndexes: IndexSet(integer: 0))
|
|
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: Undo Replace Existing key
|
|
/// Undo operation for replacing a duplicate key-value pairs in PEOutlineView
|
|
@objc func undo_ReplaceExisting(item: PENode,
|
|
inParent: PENode,
|
|
indexChild: Int,
|
|
editedNode: PENode,
|
|
oldKey: String,
|
|
newKey: String) {
|
|
|
|
self.prepareUndoObj().redo_ReplaceExisting(item: item,
|
|
inParent: inParent,
|
|
indexChild: indexChild,
|
|
editedNode: editedNode,
|
|
oldKey: newKey,
|
|
newKey: oldKey)
|
|
|
|
self.undoManager?.setActionName(localizedUndoRedoReplaceDuplicateKey)
|
|
// expand the node if isn't. (an item can be added to a not expanded group??)
|
|
self.editorVC?.asyncExpand(item: item, expandParentOnly: true)
|
|
inParent.mutableChildren.removeObject(at: indexChild)
|
|
DispatchQueue.main.async {
|
|
self.removeItems(at: IndexSet(integer: indexChild),
|
|
inParent: inParent,
|
|
withAnimation: [])
|
|
|
|
editedNode.tagdata?.key = newKey
|
|
|
|
// keep trak of the item because must be selected after the parent gets reloaded
|
|
let row = self.row(forItem: editedNode)
|
|
|
|
//reload the parent. In this case the parent it's the same
|
|
//self.outline.reloadItem(inParent, reloadChildren: true)
|
|
|
|
if row >= 0 {
|
|
self.scrollRowToVisible(row)
|
|
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: Redo Replace Existing key
|
|
/// Reversed undo operation (Redo) for replacing a duplicate key-value pairs in PEOutlineView
|
|
@objc func redo_ReplaceExisting(item: PENode,
|
|
inParent: PENode,
|
|
indexChild: Int,
|
|
editedNode: PENode,
|
|
oldKey: String,
|
|
newKey: String) {
|
|
|
|
self.prepareUndoObj().undo_ReplaceExisting(item: item,
|
|
inParent: inParent,
|
|
indexChild: indexChild,
|
|
editedNode: editedNode,
|
|
oldKey: newKey,
|
|
newKey: oldKey)
|
|
|
|
self.undoManager?.setActionName(localizedUndoRedoReplaceDuplicateKey)
|
|
// expand the node if isn't. (an item can be added to a not expanded group??)
|
|
self.editorVC?.asyncExpand(item: item, expandParentOnly: true)
|
|
|
|
inParent.mutableChildren.insert(item, at: indexChild)
|
|
editedNode.tagdata?.key = newKey
|
|
DispatchQueue.main.async {
|
|
self.insertItems(at: IndexSet(integer: indexChild),
|
|
inParent: inParent,
|
|
withAnimation: [])
|
|
|
|
|
|
// keep trak of the item because must be selected after the parent gets reloaded
|
|
let row = self.row(forItem: editedNode)
|
|
|
|
//reload the parent. In this case the parent it's the same
|
|
self.reloadItem(inParent, reloadChildren: true)
|
|
if row >= 0 {
|
|
self.scrollRowToVisible(row)
|
|
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: Undo set new value
|
|
/// Undo operation (Redo) editing a value (Value Column) in PEOutlineView.
|
|
/// See also undoableSetBoolValue(node: , sender:, originalValue:, actualValue:)
|
|
@objc func undo_SetValue(node: PENode, newValue: Any, oldValue: Any) {
|
|
let row = self.row(forItem: node)
|
|
self.prepareUndoObj().undo_SetValue(node: node,
|
|
newValue: oldValue,
|
|
oldValue: newValue)
|
|
|
|
self.editorVC?.asyncExpand(item: node, expandParentOnly: true)
|
|
|
|
self.undoManager?.setActionName(localizedUndoRedoTyping)
|
|
node.tagdata?.value = newValue
|
|
DispatchQueue.main.async {
|
|
self.reloadItem(node.parent, reloadChildren: false)
|
|
|
|
if row >= 0 {
|
|
self.scrollRowToVisible(row)
|
|
self.reloadData(forRowIndexes: IndexSet(integer: row), columnIndexes: IndexSet(integer: 2))
|
|
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: Undo change to Bool tag
|
|
/// Undo operation (Redo) editing a Bool value (Value Column) in PEOutlineView
|
|
@objc func undo_SetBoolValue(node: PENode,
|
|
sender: PEPopUpButton,
|
|
originalValue: Bool,
|
|
actualValue: Bool) {
|
|
self.prepareUndoObj().undo_SetBoolValue(node: node,
|
|
sender: sender,
|
|
originalValue: actualValue,
|
|
actualValue: originalValue)
|
|
|
|
self.undoManager?.setActionName(localizedUndoRedoChangeBoolValue)
|
|
node.tagdata?.value = actualValue
|
|
|
|
// root node cannot have parent and is always visible
|
|
if (node.parent != nil) {
|
|
self.editorVC?.asyncExpand(item: node, expandParentOnly: true)
|
|
}
|
|
|
|
DispatchQueue.main.async {
|
|
let row = self.row(forItem: node)
|
|
if row >= 0 {
|
|
self.scrollRowToVisible(row)
|
|
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
|
|
}
|
|
|
|
sender.animator().selectItem(withTitle:
|
|
actualValue ? localizedYes : localizedNo)
|
|
}
|
|
}
|
|
|
|
// MARK: Undo change tag
|
|
/// Undo operation changing a tag (Tag Column) in PEOutlineView.
|
|
@objc func undo_ChangeType(node: PENode,
|
|
newData: TagData,
|
|
oldData: TagData,
|
|
newChilds: [Any]?,
|
|
oldChilds: [Any]?,
|
|
sender: PEPopUpButton) {
|
|
|
|
self.prepareUndoObj().undo_ChangeType(node: node,
|
|
newData: oldData,
|
|
oldData: newData,
|
|
newChilds: oldChilds,
|
|
oldChilds: newChilds,
|
|
sender: sender)
|
|
self.undoManager?.setActionName(localizedUndoRedoChangeType)
|
|
|
|
node.representedObject = newData
|
|
node.mutableChildren.removeAllObjects()
|
|
if let childrens = newChilds {
|
|
node.mutableChildren.addObjects(from: childrens)
|
|
}
|
|
|
|
// root node cannot have parent and is always visible
|
|
if (node.parent != nil) {
|
|
self.editorVC?.asyncExpand(item: node, expandParentOnly: true)
|
|
}
|
|
|
|
DispatchQueue.main.async {
|
|
self.reloadItem(node, reloadChildren: true)
|
|
|
|
let row = self.row(forItem: node)
|
|
if row >= 0 {
|
|
self.scrollRowToVisible(row)
|
|
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: Undo change contenitor (Dictionary to Array and viceversa)
|
|
/// Undo operation changing a Dictionary to Array and viceversa (Tag Column) in PEOutlineView.
|
|
@objc func undo_ChangeContenitor(node: PENode,
|
|
sender: PEPopUpButton,
|
|
originalType: String,
|
|
actualType: String,
|
|
oldKeys: PEArray,
|
|
newKeys: PEArray) {
|
|
/*
|
|
We cannot leave old keys, but instead we should:
|
|
- Dictionary, must have localized "New Item -x" for each children
|
|
- Array, must be localized "Item x" for each children
|
|
*/
|
|
|
|
self.prepareUndoObj().undo_ChangeContenitor(node: node,
|
|
sender: sender,
|
|
originalType: actualType,
|
|
actualType: originalType,
|
|
oldKeys: newKeys,
|
|
newKeys: oldKeys)
|
|
|
|
self.undoManager?.setActionName(localizedUndoRedoChangeType)
|
|
|
|
// root node cannot have parent and is always visible
|
|
if (node.parent != nil) {
|
|
self.editorVC?.asyncExpand(item: node, expandParentOnly: false)
|
|
}
|
|
|
|
let actualTag = gPlistTag(from: actualType)
|
|
|
|
if actualTag == .Dictionary {
|
|
node.tagdata?.value = nil
|
|
node.tagdata?.type = .Dictionary
|
|
} else if actualTag == .Array {
|
|
node.tagdata?.value = nil
|
|
node.tagdata?.type = .Array
|
|
}
|
|
|
|
var index : Int = 0
|
|
for n in node.mutableChildren {
|
|
let modded : PENode = n as! PENode
|
|
modded.tagdata?.key = newKeys[index] as! String
|
|
index+=1
|
|
}
|
|
|
|
DispatchQueue.main.async {
|
|
sender.animator().selectItem(withTitle: gPlistTagStr(tag: actualTag).locale)
|
|
}
|
|
|
|
self.editorVC?.asyncExpand(item: node, expandParentOnly: true)
|
|
|
|
DispatchQueue.main.async {
|
|
let row = self.row(forItem: node)
|
|
if self.isItemExpanded(node) {
|
|
self.reloadItem(node, reloadChildren: true)
|
|
} else {
|
|
self.reloadItem(node, reloadChildren: false)
|
|
}
|
|
|
|
if row >= 0 {
|
|
self.scrollRowToVisible(row)
|
|
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: Undo Paste
|
|
/// Undo operation for "Paste" in PEOutlineView.
|
|
/// See redo_PasteItem(item: , inParent: , indexChild: ) for the reversed operation.
|
|
@objc func undo_Paste(item: PENode, inParent: PENode, indexChild: Int) {
|
|
self.prepareUndoObj().redo_Paste(item: item,
|
|
inParent: inParent,
|
|
indexChild: indexChild)
|
|
|
|
self.editorVC?.asyncExpand(item: item, expandParentOnly: true)
|
|
self.undoManager?.setActionName(localizedUndoRedoPasteItem)
|
|
inParent.mutableChildren.insert(item, at: indexChild)
|
|
|
|
DispatchQueue.main.async {
|
|
self.insertItems(at: IndexSet(integer: indexChild), inParent: inParent, withAnimation: [])
|
|
|
|
// basically we are adding a new Item. Reload the parent and the cildrens..and select new item
|
|
self.reloadItem(inParent, reloadChildren: true)
|
|
// find new item that should be there now..
|
|
let row = self.row(forItem: item)
|
|
if row >= 0 {
|
|
self.scrollRowToVisible(row)
|
|
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: Undo Paste (reverse)
|
|
/// Redo operation for "Paste" in PEOutlineView.
|
|
/// See undo_PasteItem(item: , inParent: , indexChild: ) for the reversed operation.
|
|
@objc func redo_Paste(item: PENode, inParent: PENode, indexChild: Int) {
|
|
self.prepareUndoObj().undo_Paste(item: item,
|
|
inParent: inParent,
|
|
indexChild: indexChild)
|
|
|
|
self.undoManager?.setActionName(localizedUndoRedoPasteItem)
|
|
// expand the node if isn't
|
|
self.editorVC?.asyncExpand(item: item, expandParentOnly: true)
|
|
// find the row. After deleting it, it will be the row to select again (if exist, othewise select last row in the outline)
|
|
DispatchQueue.main.async {
|
|
let row = self.row(forItem: item)
|
|
|
|
inParent.mutableChildren.removeObject(at: indexChild)
|
|
self.removeItems(at: IndexSet(integer: indexChild), inParent: inParent, withAnimation: [])
|
|
|
|
// basically we are removing a new Item just added. Reload the parent and the cildrens..
|
|
self.reloadItem(inParent, reloadChildren: true)
|
|
|
|
//..and select the next row
|
|
if row >= 0 {
|
|
if (self.item(atRow: row) != nil) {
|
|
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
|
|
} else {
|
|
// row it's out of bounds.. select last in the outline
|
|
let lastRow = self.numberOfRows - 1
|
|
if lastRow >= 0 {
|
|
self.scrollRowToVisible(lastRow)
|
|
self.selectRowIndexes(IndexSet(integer: lastRow), byExtendingSelection: false)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: Undo Cut
|
|
/// Undo operation for "Cut" in PEOutlineView.
|
|
/// See redo_Cut(item: , inParent: , indexChild: ) for the reversed operation.
|
|
@objc func undo_Cut(item: PENode, inParent: PENode, indexChild: Int) {
|
|
// find the row. After deleting it, it will be the row to select again (if exist, othewise select last row in the outline)
|
|
self.prepareUndoObj().redo_Cut(item: item,
|
|
inParent: inParent,
|
|
indexChild: indexChild)
|
|
self.editorVC?.asyncExpand(item: item, expandParentOnly: true)
|
|
self.undoManager?.setActionName(localizedUndoRedoCutItem)
|
|
inParent.mutableChildren.removeObject(at: indexChild)
|
|
self.removeItems(at: IndexSet(integer: indexChild), inParent: inParent, withAnimation: [])
|
|
// basically we are removing a new Item just added. Reload the parent and the cildrens..
|
|
|
|
|
|
DispatchQueue.main.async {
|
|
self.reloadItem(inParent, reloadChildren: true)
|
|
let row = self.row(forItem: item)
|
|
//..and select the next row
|
|
if row >= 0 {
|
|
if (self.item(atRow: row) != nil) {
|
|
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
|
|
} else {
|
|
// row it's out of bounds.. select last in the outline
|
|
let lastRow = self.numberOfRows - 1
|
|
if lastRow >= 0 {
|
|
self.scrollRowToVisible(lastRow)
|
|
self.selectRowIndexes(IndexSet(integer: lastRow), byExtendingSelection: false)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// MARK: Undo Cut (reverse)
|
|
/// Redo operation for "Cut" in PEOutlineView.
|
|
/// See undo_Cut(item: , inParent: , indexChild: ) for the reversed operation.
|
|
@objc func redo_Cut(item: PENode, inParent: PENode, indexChild: Int) {
|
|
self.prepareUndoObj().undo_Cut(item: item,
|
|
inParent: inParent,
|
|
indexChild: indexChild)
|
|
|
|
self.undoManager?.setActionName(localizedUndoRedoCutItem)
|
|
// expand the node if isn't
|
|
self.editorVC?.asyncExpand(item: item, expandParentOnly: true)
|
|
|
|
// we are adding an item..
|
|
inParent.mutableChildren.insert(item, at: indexChild)
|
|
DispatchQueue.main.async {
|
|
self.insertItems(at: IndexSet(integer: indexChild), inParent: inParent, withAnimation: [])
|
|
|
|
// select the new one after reloading the group
|
|
self.reloadItem(inParent, reloadChildren: true)
|
|
let row = self.row(forItem: item)
|
|
if row >= 0 {
|
|
self.scrollRowToVisible(row)
|
|
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: Undo add item
|
|
/// Undo operation adding New items in PEOutlineView.
|
|
/// See undo_Remove(item: , inParent: , indexChild: ) for the reverse operation.
|
|
@objc func undo_Add(item: PENode,
|
|
inParent: PENode,
|
|
indexChild: Int,
|
|
target outlineView: PEOutlineView) {
|
|
outlineView.prepareUndoObj().undo_Remove(item: item,
|
|
inParent: inParent,
|
|
indexChild: indexChild,
|
|
target: outlineView)
|
|
|
|
outlineView.undoManager?.setActionName(localizedUndoRedoAddNewItem)
|
|
|
|
outlineView.editorVC?.asyncExpand(item: item, expandParentOnly: true)
|
|
|
|
inParent.mutableChildren.insert(item, at: indexChild)
|
|
|
|
DispatchQueue.main.async {
|
|
outlineView.insertItems(at: IndexSet(integer: indexChild), inParent: inParent, withAnimation: [])
|
|
// select the new one after reloading the group
|
|
outlineView.reloadItem(inParent, reloadChildren: true)
|
|
let row = outlineView.row(forItem: item)
|
|
if row >= 0 {
|
|
outlineView.scrollRowToVisible(row)
|
|
outlineView.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: Redo remove Item
|
|
/// Redo operation adding New items in PEOutlineView.
|
|
/// See undo_Add(item: , inParent: , indexChild: ) for the reverse operation.
|
|
@objc func undo_Remove(item: PENode,
|
|
inParent: PENode,
|
|
indexChild: Int,
|
|
target outlineView: PEOutlineView) {
|
|
// find the row. After deleting it, it will be the row to select again (if exist, othewise select last row in the outline)
|
|
let row = outlineView.row(forItem: item)
|
|
|
|
outlineView.prepareUndoObj().undo_Add(item: item,
|
|
inParent: inParent,
|
|
indexChild: indexChild,
|
|
target: outlineView)
|
|
|
|
outlineView.undoManager?.setActionName(localizedUndoRedoRemoveItem)
|
|
// expand the node if isn't
|
|
outlineView.editorVC?.asyncExpand(item: item, expandParentOnly: true)
|
|
inParent.mutableChildren.removeObject(at: indexChild)
|
|
DispatchQueue.main.async {
|
|
outlineView.removeItems(at: IndexSet(integer: indexChild), inParent: inParent, withAnimation: [])
|
|
|
|
// Reload the parent and the cildrens..
|
|
outlineView.reloadItem(inParent, reloadChildren: true)
|
|
|
|
//..and select the next row
|
|
if row >= 0 {
|
|
outlineView.scrollRowToVisible(row)
|
|
if (outlineView.item(atRow: row) != nil) {
|
|
outlineView.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
|
|
} else {
|
|
// row it's out of bounds.. select last in the outline
|
|
let lastRow = outlineView.numberOfRows - 1
|
|
if lastRow >= 0 {
|
|
outlineView.selectRowIndexes(IndexSet(integer: lastRow), byExtendingSelection: false)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|