diff --git a/Info.plist b/Info.plist index 03e6795..d262d4e 100644 --- a/Info.plist +++ b/Info.plist @@ -4,10 +4,8 @@ CFBundleDocumentTypes - CFBundleIconFiles - - dead_bush.icns - + CFBundleIconFile + nbte.icns CFBundleIdentifier jaquadro.NBTExplorer CFBundleName diff --git a/Mac/AppDelegate.cs b/Mac/AppDelegate.cs index ab26231..08a210d 100644 --- a/Mac/AppDelegate.cs +++ b/Mac/AppDelegate.cs @@ -156,6 +156,21 @@ namespace NBTExplorer get { return _menuInsertCompound; } } + partial void ActionCut (MonoMac.Foundation.NSObject sender) + { + mainWindowController.Window.ActionCut(); + } + + partial void ActionCopy (MonoMac.Foundation.NSObject sender) + { + mainWindowController.Window.ActionCopy(); + } + + partial void ActionPaste (MonoMac.Foundation.NSObject sender) + { + mainWindowController.Window.ActionPaste(); + } + partial void ActionRename (NSObject sender) { mainWindowController.Window.ActionRenameValue(); diff --git a/Mac/MainWindow.cs b/Mac/MainWindow.cs index d01db7e..6f638ba 100644 --- a/Mac/MainWindow.cs +++ b/Mac/MainWindow.cs @@ -33,6 +33,7 @@ namespace NBTExplorer { InitializeIconRegistry(); FormHandlers.Register(); + NbtClipboardController.Initialize(new NbtClipboardControllerMac()); } private AppDelegate _appDelegate; @@ -360,6 +361,21 @@ namespace NBTExplorer get { return _mainOutlineView.ItemAtRow (_mainOutlineView.SelectedRow) as TreeDataNode; } } + public void ActionCopy () + { + CopyNode (SelectedNode); + } + + public void ActionCut () + { + CutNode (SelectedNode); + } + + public void ActionPaste () + { + PasteNode (SelectedNode); + } + public void ActionEditValue () { EditNode(SelectedNode); @@ -440,6 +456,48 @@ namespace NBTExplorer CreateNode (SelectedNode, TagType.TAG_COMPOUND); } + private void CopyNode (TreeDataNode node) + { + if (node == null) + return; + + if (!node.Data.CanCopyNode) + return; + + node.Data.CopyNode(); + } + + private void CutNode (TreeDataNode node) + { + if (node == null) + return; + + if (!node.Data.CanCutNode) + return; + + if (node.Data.CutNode()) { + TreeDataNode parent = node.Parent; + UpdateUI(parent.Data); + node.Remove(); + _mainOutlineView.ReloadItem(parent, true); + } + } + + private void PasteNode (TreeDataNode node) + { + if (node == null) + return; + + if (!node.Data.CanPasteIntoNode) + return; + + if (node.Data.PasteNode()) { + //node.Text = dataNode.NodeDisplay; + RefreshChildNodes(node, node.Data); + UpdateUI(node.Data); + } + } + private void EditNode (TreeDataNode node) { if (node == null) @@ -622,9 +680,9 @@ namespace NBTExplorer _appDelegate.MenuInsertCompound.Enabled = node.CanCreateTag(TagType.TAG_COMPOUND); _appDelegate.MenuSave.Enabled = CheckModifications(); - _appDelegate.MenuCopy.Enabled = node.CanCopyNode; - _appDelegate.MenuCut.Enabled = node.CanCutNode; - _appDelegate.MenuPaste.Enabled = node.CanPasteIntoNode; + _appDelegate.MenuCopy.Enabled = node.CanCopyNode && NbtClipboardController.IsInitialized; + _appDelegate.MenuCut.Enabled = node.CanCutNode && NbtClipboardController.IsInitialized; + _appDelegate.MenuPaste.Enabled = node.CanPasteIntoNode && NbtClipboardController.IsInitialized; _appDelegate.MenuDelete.Enabled = node.CanDeleteNode; _appDelegate.MenuEditValue.Enabled = node.CanEditNode; _appDelegate.MenuRename.Enabled = node.CanRenameNode; diff --git a/Mac/NbtClipboardControllerMac.cs b/Mac/NbtClipboardControllerMac.cs new file mode 100644 index 0000000..40ec93c --- /dev/null +++ b/Mac/NbtClipboardControllerMac.cs @@ -0,0 +1,175 @@ +using System; +using MonoMac.AppKit; +using MonoMac.Foundation; +using MonoMac.ObjCRuntime; +using Substrate.Nbt; +using System.Runtime.InteropServices; + +namespace NBTExplorer.Mac +{ + public class NbtClipboardControllerMac : INbtClipboardController + { + public void CopyToClipboard (NbtClipboardData data) + { + NbtClipboardDataMac dataItem = new NbtClipboardDataMac(data); + + NSPasteboard pasteboard = NSPasteboard.GeneralPasteboard; + pasteboard.ClearContents(); + pasteboard.WriteObjects(new NSPasteboardReading[] { dataItem }); + } + + public NbtClipboardData CopyFromClipboard () + { + NSPasteboard pasteboard = NSPasteboard.GeneralPasteboard; + NSObject[] items = pasteboard.ReadObjectsForClasses (new NSPasteboardReading[] { NbtClipboardDataMac.Type }, new NSDictionary()); + + foreach (NbtClipboardDataMac item in items) { + if (item == null) + continue; + + TagNode node = item.Node; + if (node == null) + continue; + + return new NbtClipboardData(item.Name, node); + } + + return null; + } + + public bool ContainsData + { + get { + NSPasteboard pasteboard = NSPasteboard.GeneralPasteboard; + NSObject[] items = pasteboard.ReadObjectsForClasses (new NSPasteboardReading[] { NbtClipboardDataMac.Type }, new NSDictionary()); + return items != null && items.Length > 0; + } + } + } + + [Adopts("NSCoding")] + [Adopts("NSPasteboardReading")] + [Adopts("NSPasteboardWriting")] + [Register("NbtClipboardDataMac")] + public class NbtClipboardDataMac : NSPasteboardReading + { + static AdoptsAttribute _writingProtocol = new AdoptsAttribute ("NSPasteboardWriting"); + static AdoptsAttribute _readingProtocol = new AdoptsAttribute ("NSPasteboardReading"); + static AdoptsAttribute _codingProtocol = new AdoptsAttribute("NSCoding"); + + private static string _pasteboardItemName = "jaquadro.nbtexplorer.nbtClipboardDataMac"; + + public static NbtClipboardDataMac Type + { + get { + NbtClipboardDataMac inst = new NbtClipboardDataMac (); + return inst; + } + } + + private string _name; + private byte[] _data; + + private bool _bypassProtocolCheck = true; + + private NbtClipboardDataMac () + { + _name = ""; + _data = new byte[0]; + } + + public NbtClipboardDataMac (NbtClipboardData data) + { + Name = data.Name; + Node = data.Node; + } + + public override bool ConformsToProtocol (IntPtr protocol) + { + // XXX: This is a hack! There seems to be a bug in MonoMac resulting in different handle addresses + // for two protocols of (seemingly) the same name, so we have no way to make objc accept that we + // implement a given protocol. objc runtime method protocol_getName should be able to help us, but it + // crashes the runtime. + if (_bypassProtocolCheck) + return true; + + if (protocol == _readingProtocol.ProtocolHandle) + return true; + if (protocol == _writingProtocol.ProtocolHandle) + return true; + if (protocol == _codingProtocol.ProtocolHandle) + return true; + return base.ConformsToProtocol (protocol); + } + + public string Name + { + get { return _name; } + set { _name = value; } + } + + public TagNode Node + { + get { return NbtClipboardData.DeserializeNode(_data); } + set { _data = NbtClipboardData.SerializeNode(value); } + } + + [Export ("initWithCoder:")] + public NbtClipboardDataMac (NSCoder coder) + : base(NSObjectFlag.Empty) + { + _name = (NSString)coder.DecodeObject("name"); + _data = coder.DecodeBytes("data"); + } + + [Export ("encodeWithCoder:")] + public void Encode (NSCoder coder) + { + coder.Encode ((NSString)_name, "name"); + coder.Encode (_data, "data"); + } + + [Export("writableTypesForPasteboard:")] + public string[] GetWritableTypesForPasteboard (NSPasteboard pasteboard) + { + return new string[] { _pasteboardItemName }; + } + + [Export("pasteboardPropertyListForType:")] + public NSObject GetPasteboardPropertyListForType (string type) + { + if (type == _pasteboardItemName) + return NSKeyedArchiver.ArchivedDataWithRootObject(this); + else + return null; + } + + [Export ("writingOptionsForType:pasteboard:")] + public NSPasteboardWritingOptions GetWritingOptionsForType (string type, NSPasteboard pasteboard) + { + return 0; + } + + public override string[] GetReadableTypesForPasteboard (NSPasteboard pasteboard) + { + return new string[] { _pasteboardItemName }; + } + + public override NSPasteboardReadingOptions GetReadingOptionsForType (string type, NSPasteboard pasteboard) + { + if (type == _pasteboardItemName) + return NSPasteboardReadingOptions.AsKeyedArchive; + else + return 0; + } + + // XXX: This is a hack. Not sure how to properly implement, but it's required either by pasteboard reading, + // or is a side-effect of our protocol conformance hack. + [Export("isSubclassOfClass:")] + public bool IsSubclassOf (NSObject cl) + { + return false; + } + } +} + diff --git a/NBTExplorerMac.csproj b/NBTExplorerMac.csproj index 48955bc..347a7bc 100644 --- a/NBTExplorerMac.csproj +++ b/NBTExplorerMac.csproj @@ -69,8 +69,8 @@ - - False + + ..\monomac\src\MonoMac.dll @@ -183,6 +183,8 @@ EditValueWindow.cs + + @@ -244,4 +246,8 @@ + + + + \ No newline at end of file diff --git a/Resources/Dead_Bush_256.icns b/Resources/Dead_Bush_256.icns new file mode 100644 index 0000000..6f81493 Binary files /dev/null and b/Resources/Dead_Bush_256.icns differ diff --git a/nbte.icns b/nbte.icns new file mode 100644 index 0000000..cce3f62 Binary files /dev/null and b/nbte.icns differ