Adding FolderItems to NSPasteboardMBS

I’d like to use the pasteboard to copy files between apps and to Finder.

What I have so far is:

For Each file As FolderItem In files
  
  Dim pbItem As New NSPasteboardItemMBS
  Dim url As String = file.URLPath
  Dim name As String = file.Name
  
  pbItem.stringForType(NSPasteboardMBS.NSURLPboardType) = url.ConvertEncoding(Encodings.UTF8)
  pbItem.stringForType(NSPasteboardMBS.NSPasteboardTypeString) = name.ConvertEncoding(Encodings.UTF8)
  
  pbItems.Append pbItem
  
Next file

Dim b As Boolean = pb.SetPasteboardItems(pbItems)
If b = False Then Beep

But this is giving resulting in b = false on the penultimate line.

What am I missing?

It can be done with the native Xojo clipboard class.

Public Sub folderitem(extends c as clipboard, assigns value as folderItem)
  #if targetMacOS then
    c.RawData( "public.file-url" )        = value.URLPath
    c.rawData( "public.utf8-plain-text" ) = value.displayName
  #endif
End Sub

But reading a folderitem from the clipboard requires a library because of a bug in Xojo. <https://xojo.com/issue/63416>

Public Function folderitem(extends c as clipboard) as folderItem
  #if TargetMacOS then
    if c.RawDataAvailable( "public.file-url" ) then
      Dim fURL as string  = c.rawdata( "public.file-url" )
      Dim nurl as integer = NSURLWithString( NSURLClass, fURL )
      return new folderItem( NSURLPath( nurl ), folderItem.pathModes.native, false )
    end if
  #endif
End Function
1 Like

Thanks again Sam. Yes this seems to work for individual files.

I was hoping to get something that would allow copying multiple files to the pasteboard/clipboard.

You may need to add multiple items.
So one item per NSPasteboardMBS.NSURLPboardType type.

That’s what I was trying to achieve with my code at the top of this thread. But it fails on
Dim b As Boolean = pb.SetPasteboardItems(pbItems).

Is there another way to do it?

I see, to my surprise Xojo doesn’t have a function for multiple items… Wow…

Edit: Just checked, when you copy files in the Finder, it adds multiple clipboard items, each one contains a single file. Which means none of my code handles this :frowning:

This code put a bunch of files from my desktop on to the pasteboard, which I could then paste into another folder in the finder. Use the MBS equivalents.

dim paths() as string = array( "/Volumes/rowlands/Desktop/OWDocumentSender.xojo_binary_project", _
"/Volumes/rowlands/Desktop/Screen Shot 2021-08-10 at 10.55.57 AM.png", _
"/Volumes/rowlands/Desktop/Screen Shot 2021-08-07 at 8.52.40 AM.png", _
"/Volumes/rowlands/Desktop/OWFileWell.xojo_binary_project" )

// --- NSURL conforms to the NSPasteboardWriting protocol, so we only need an array of NSURLS.
Dim nurlArray as integer = NSMutableArrayWithCapacity( NSMutableArrayClass, ubound( paths ) )
For each currentPath as string in paths
  NSMutableArrayAddObject( nurlArray, NSURLWithFilePath( NSURLClass, currentPath ) )
Next

// --- Once we have our NSArray of NSURLs, we then ask the NSPasteboard to write it.
Dim pb as integer = NSPasteboard_generalPasteboard( NSClassFromString( "NSPasteboard" ) )
call NSPasteboard_clearContents( pb )
if NSPasteboard_writeObjects( pb, nurlArray ) = false then
  break
end if
1 Like

So, I’ve first added a new variant of SetPasteboardItems taking NSURLMBS objects:

Dim paths() As String

paths.Append "/Users/cs/Pictures/test1.jpg"
paths.Append "/Users/cs/Pictures/test2.jpg"
paths.Append "/Users/cs/Pictures/test3.jpg"

Dim n As NSPasteboardMBS = NSPasteboardMBS.generalPasteboard

Dim URLs() As NSURLMBS
For Each path As String In paths
  
  Dim URL As NSURLMBS = NSURLMBS.fileURLWithPath(path)
  URLs.Append URL
  
Next

Call n.clearContents

If n.SetPasteboardItems(URLs) Then
  Break // okay
Else
  Break // failed
End If

I will change SetPasteboardItems to take variant array, so you can pass various objects including folder items:

SetPasteboardItems(items() as Variant) as Boolean

New sample code:

Dim paths() As String

paths.Append "/Users/cs/Pictures/test1.jpg"
paths.Append "/Users/cs/Pictures/test2.png"
paths.Append "/Users/cs/Pictures/test3.jpg"

Dim n As NSPasteboardMBS = NSPasteboardMBS.generalPasteboard

Dim files() As Variant
For Each path As String In paths
  
  files.Append new FolderItem(path, FolderItem.PathModes.Native)
  
Next

Call n.clearContents

If n.SetPasteboardItems(files) Then
  Break // okay
Else
  Break // failed
End If

Then it works fine now with newer plugins.

Thank you Sam and Christian. Its quick responses like this that make Xojo great.

2 Likes

I believe you could if you’d create a single item with name NSFilenamesPboardType (or dyn.ah62d4rv4gu8y6y4grf0gn5xbrzw1gydcr7u1e3cytf2gn, which is the equivalent UTI) as RawData, with the contents being a plist array with the paths, like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
	<string>/path/to/file1</string>
	<string>/path/to/file2</string>
</array>
</plist>

I’ve just tried this:

Dim s As String = k_plist_Header
For Each file As folderitem In files
  s = s + EndOfLine + "<String>" + file.NativePath + "</String>"
Next file
s = s + EndOfLine + k_plist_footer

Dim n As NSPasteboardMBS = NSPasteboardMBS.generalPasteboard
Dim i As Integer = n.declareType(NSPasteboardMBS.NSFilenamesPboardType)
n.dataForType(NSPasteboard

MBS.NSFilenamesPboardType) = s

with the result in the clipboard of:

<?xml version="1.0" Encoding="UTF-8"?>
<!DOCTYPE plist Public "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<Array>
<String>/Volumes/Extreme SSD 1TB/test - empty/2020/01/P1011755.JPG</String>
<String>/Volumes/Extreme SSD 1TB/test - empty/2020/01/01/P1011755.JPG</String>
<String>/Volumes/Extreme SSD 1TB/test - empty/2020/Q1/P1011755.JPG</String>
</Array>
</plist>

But this doesn’t seem to work when pasting back to finder or other apps. Also tried using URLPaths - but also not working.

As it is non-urgent I might just wait for Christian’s magic.

Could you wait for me to upload pr2 for the new function for you?

Certainly can.

James, use my ShowClipboards (made with Xojo :slight_smile: ) to see what your test program puts into the pasteboard and compare that with what the Finder does. Maybe that would help identify the problem.

Hold on - you changed the case of the tags in the XML. That can’t work (it’s never a good idea to “improve” an example before making sure that it’s working with the original).

Here is a working example:

dim s as String = "<?xml version=""1.0"" encoding=""UTF-8""?>"+EndOfLine.Unix+_
"<!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd"">"+EndOfLine.Unix+_
"<plist version=""1.0"">"+EndOfLine.Unix+_
"<array>"+EndOfLine.Unix+_
"<string>/Library/Preferences/com.apple.dock.plist</string>"+EndOfLine.Unix+_
"</array>"+EndOfLine.Unix+_
"</plist>"

// not working:
'dim c as new Clipboard
'c.AddRawData s, "dyn.ah62d4rv4gu8y6y4grf0gn5xbrzw1gydcr7u1e3cytf2gn"
'c.Close

// working, with MBS:
Dim n As NSPasteboardMBS = NSPasteboardMBS.generalPasteboard
call n.clearContents
Dim i As Integer = n.declareType(NSPasteboardMBS.NSFilenamesPboardType)
n.dataForType(NSPasteboardMBS.NSFilenamesPboardType) = s

thanks.

MBS Xojo Plugins, version 21.4pr2

I agree. In this instance it was caused by copying your xml into a method in the Xojo IDE, which then automatically capitalised the text prior to me copying into constant fields. Note to self. Care when copying XML into IDE.

But yes, your new version does work. Thank you.