It is currently Sat Aug 15, 2020 9:13 am

All times are UTC + 1 hour




Post new topic Reply to topic  [ 15 posts ] 
Author Message
 Post subject: [Cocoa] Extra file operations
PostPosted: Thu Jul 16, 2015 5:21 pm 
Offline
Addict
Addict
User avatar

Joined: Tue Dec 23, 2003 3:54 am
Posts: 1839
As long as you Cocoa masters keep giving these great answers, I will keep asking Cocoa questions :D

Today I am wondering how to
(a) move a file to the Trash (instead of a pure delete)
(b) display the "Get Info" window about a file

I have figured out both of these using Windows API (Recycle Bin and Properties) but I am still learning how to work with Cocoa...

Thanks for any tips!

_________________
On GitHub: PB Includes - IDE Tools - Color Themes - IDE Branches - TabBarGadget Mods


Top
 Profile  
Reply with quote  
 Post subject: Re: [Cocoa] Extra file operations
PostPosted: Thu Jul 16, 2015 7:37 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3647
Location: Netherlands
Moving to trash can be done with the NSWorkspace methods recycleURLs:completionHandler: or performFileOperation:source:destination:files:tag:

_________________
macOS 10.15 Catalina, Windows 10


Top
 Profile  
Reply with quote  
 Post subject: Re: [Cocoa] Extra file operations
PostPosted: Thu Jul 16, 2015 8:16 pm 
Offline
Addict
Addict
User avatar

Joined: Thu Apr 21, 2005 2:38 pm
Posts: 1753
Location: Germany
The following code example needs at least Mountain Lion to work. It creates a file and moves it to trash. If a file with the same filename already exists in trash, the file will be renamed to a unique filename before moving it to trash. You can simply prove it by executing this program a second time - the program moved to trash will obtain a modified filename...

I have tested the code example successfully on Mountain Lion and Yoesmite.

The NSWorkspace method recycleURLs:completionHandler: proposed by Wilbert won't work in PureBasic because it utilizes a completion handler block. This is a technique which I still haven't been able to implement in PureBasic. It would be very interesting if Wilbert knows a way... :twisted:

The NSWorkspace method performFileOperation:source:destination:files:tag: would indeed be an alternative that even should work with MacOS versions older than Mountain Lion!
Code:
EnableExplicit

Define Error.I = CocoaMessage(0, 0, "NSError alloc")
Define Result.I
Define TestFile.S = GetTemporaryDirectory() + "Test.Txt"
Define TestFileURL.I
Define TrashFileURL.I

If OSVersion() < #PB_OS_MacOSX_10_8
  MessageRequester("Error",
    "Sorry, but this program needs at least Mountain Lion!")
  End
EndIf

If CreateFile(0, TestFile)
  WriteString(0, "This file should be moved to the trash can!")
  CloseFile(0)

  TestFileURL = CocoaMessage(0, 0,
    "NSURL fileURLWithPath:$", @TestFile,
    "isDirectory:", #NO)
  TrashFileURL = CocoaMessage(0, 0, "NSURL alloc")

  If TestFileURL
    If TrashFileURL
      Result = CocoaMessage(0, CocoaMessage(0, 0, "NSFileManager defaultManager"),
        "trashItemAtURL:", TestFileURL,
        "resultingItemURL:", @TrashFileURL,
        "error:", @Error)

      If Result = #YES
        MessageRequester("Success", "The file '" + GetFilePart(TestFile) +
          "'" + #CR$ + "was successfully moved to trash as file" + #CR$ + "'" +
          PeekS(CocoaMessage(0, CocoaMessage(0, TrashFileURL,
          "lastPathComponent"), "UTF8String"), -1, #PB_UTF8) + "'")
      Else
        MessageRequester("Error", "Moving file " + GetFilePart(TestFile) +
          " to trash has failed!" + #CR$ + #CR$ + PeekS(CocoaMessage(0,
          CocoaMessage(0, Error, "localizedDescription"), "UTF8String"),
          -1, #PB_UTF8))
      EndIf

      CocoaMessage(0, TrashFileURL, "release")
    EndIf

    CocoaMessage(0, TestFileURL, "release")
  EndIf
EndIf


Last edited by Shardik on Thu Jul 16, 2015 9:37 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: [Cocoa] Extra file operations
PostPosted: Thu Jul 16, 2015 8:56 pm 
Offline
Addict
Addict

Joined: Mon Aug 04, 2008 10:56 pm
Posts: 1124
Location: Seattle, USA
I like it!

So instead of saving my VIF (very important file) over the old version I can move the old file into the trash and save the new version. In a way it is like a backup and with the auto renaming I can actually have a sequence of old versions.

Is there an extra If/then loop in there?
Code:
  If TestFileURL
    If TrashFileURL

_________________
MacBook Pro-r, OSX 10.14.6 , PB 5.70LTS


Top
 Profile  
Reply with quote  
 Post subject: Re: [Cocoa] Extra file operations
PostPosted: Thu Jul 16, 2015 8:58 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3647
Location: Netherlands
Shardik wrote:
The NSWorkspace method recycleURLs:completionHandler: proposed by Wilbert won't work in PureBasic because it utilizes a completion handler block. This is a technique which I still haven't been able to implement in PureBasic. It would be very interesting if Wilbert knows a way... :twisted:

I posted a little info on blocks in this thread viewtopic.php?f=19&t=57219
In this case however, you are allowed to pass 'nil' for the completionHandler which is much more convenient.

_________________
macOS 10.15 Catalina, Windows 10


Top
 Profile  
Reply with quote  
 Post subject: Re: [Cocoa] Extra file operations
PostPosted: Thu Jul 16, 2015 9:34 pm 
Offline
Addict
Addict
User avatar

Joined: Thu Apr 21, 2005 2:38 pm
Posts: 1753
Location: Germany
WilliamL wrote:
Is there an extra If/then loop in there?
Code:
  If TestFileURL
    If TrashFileURL
During testing I separated these If statements because each URL needs a corresponding "release" just before the corresponding EndIf... :wink:

Inititially I coded
Code:
If TestFileURL And TrashFileURL ...


wilbert wrote:
I posted a little info on blocks in this thread viewtopic.php?f=19&t=57219
How could I have overlooked this... :oops:

About a year ago I had to stop my programming trials in PureBasic with the GameKit framework in implementing GameCenter queries because with each new OS X release the improved GameKit API methods increased their usage of completion handler blocks. Now I know what experiments I will try the coming weekend... :wink:

Nevertheless as an alternative I have recently made huge progress in controlling the Mac's Game Center app by utilizing the accessibilty API in PureBasic and automatically evaluating all the pinball table highscores where I am on global rank 1 (9 tables until now) and where I have been beaten recently... :lol:


Top
 Profile  
Reply with quote  
 Post subject: Re: [Cocoa] Extra file operations
PostPosted: Thu Jul 16, 2015 9:59 pm 
Offline
Addict
Addict

Joined: Mon Aug 04, 2008 10:56 pm
Posts: 1124
Location: Seattle, USA
@Shardik, no, you were right. I didn't see the difference between TestFileURL and TrashFileURL. My mistake.

Ok, it works perfectly except after moving the file to the trash, and sucessfully saving the new file, I get back to the 'WaitWindowEvent()' and I get an 'Invalid Memory Access' error. [later] ok, if I take out 'CocoaMessage(0, TrashFileURL, "release")' I don't get the error message. Is this action necessary?

see corrected code "here"

_________________
MacBook Pro-r, OSX 10.14.6 , PB 5.70LTS


Last edited by WilliamL on Fri Jul 17, 2015 9:30 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: [Cocoa] Extra file operations
PostPosted: Fri Jul 17, 2015 5:31 pm 
Offline
Addict
Addict
User avatar

Joined: Thu Apr 21, 2005 2:38 pm
Posts: 1753
Location: Germany
kenmo wrote:
(b) display the "Get Info" window about a file

This code example displays the "Get Info" window for a specified file (successfully tested with Snow Leopard and Yosemite):
Code:
EnableExplicit

ImportC ""
  NSPerformService(*ItemName, *NSPasteboard)
EndImport

Define Pasteboard.I
Define TargetFile.S = #PB_Compiler_Home + "logo.png"
Define TypeArray.I

; ----- Create Pasteboard
Pasteboard = CocoaMessage(0, 0, "NSPasteboard pasteboardWithUniqueName")

If Pasteboard
  TypeArray = CocoaMessage(0, 0,
    "NSArray arrayWithObject:$", @"NSStringPboardType")

  If TypeArray
    CocoaMessage(0, Pasteboard,
      "declareTypes:", TypeArray,
      "owner:", 0)

    If CocoaMessage(0, Pasteboard,
      "setString:$", @TargetFile,
      "forType:$", @"NSStringPboardType") = #YES

      ; ----- Show info for selected file
      NSPerformService(CocoaMessage(0, 0,
        "NSString stringWithString:$", @"Finder/Show Info"), Pasteboard)
    EndIf
  EndIf
EndIf

If you also need to open the Finder with the specified file selected you may try the following extended code:
Code:
EnableExplicit

ImportC ""
  NSPerformService(*ItemName, *NSPasteboard)
EndImport

Define Pasteboard.I
Define TargetFile.S = #PB_Compiler_Home + "logo.png"
Define TypeArray.I
Define Workspace.I = CocoaMessage(0, 0, "NSWorkspace sharedWorkspace")

If Workspace
  ; ----- Open Finder and select target file
  CocoaMessage(0, Workspace,
    "selectFile:$", @TargetFile,
    "inFileViewerRootedAtPath:$", @"")

  ; ----- Create Pasteboard
  Pasteboard = CocoaMessage(0, 0, "NSPasteboard pasteboardWithUniqueName")

  If Pasteboard
    TypeArray = CocoaMessage(0, 0,
      "NSArray arrayWithObject:$", @"NSStringPboardType")

    If TypeArray
      CocoaMessage(0, Pasteboard,
        "declareTypes:", TypeArray,
        "owner:", 0)

      If CocoaMessage(0, Pasteboard,
        "setString:$", @TargetFile,
        "forType:$", @"NSStringPboardType") = #YES

        ; ----- Show info for selected file
        NSPerformService(CocoaMessage(0, 0,
          "NSString stringWithString:$", @"Finder/Show Info"), Pasteboard)
      EndIf
    EndIf
  EndIf
EndIf


Top
 Profile  
Reply with quote  
 Post subject: Re: [Cocoa] Extra file operations
PostPosted: Fri Jul 17, 2015 5:57 pm 
Offline
Addict
Addict
User avatar

Joined: Thu Apr 21, 2005 2:38 pm
Posts: 1753
Location: Germany
WilliamL wrote:
Ok, it works perfectly except after moving the file to the trash, and sucessfully saving the new file, I get back to the 'WaitWindowEvent()' and I get an 'Invalid Memory Access' error. [later] ok, if I take out 'CocoaMessage(0, TrashFileURL, "release")' I don't get the error message. Is this action necessary?

William, you are right. Wilbert has sent me a PM with following explanations for mistakes in my code from above (Wilbert, again thank you very much, you are simply the best mentor I can imagine!):
wilbert wrote:
A few remarks about your code example ...
Return objects like resultingItemURL and error should not be allocated nor released.
The method itself allocates them and they are autoreleased.
For objects you create yourself, in general those created with alloc or new need to be released manually and other ones are autoreleased.
The object created by NSURL fileURLWithPath therefore is an autorelease object so you should not release it manually otherwise it might result in a crash.

For other readers of this thread I therefore decided against editing my above code example but to post here the modified version with the changes proposed by Wilbert so that others may also learn from my mistakes:
Code:
EnableExplicit

Define Error.I
Define Result.I
Define TestFile.S = GetTemporaryDirectory() + "Test.Txt"
Define TestFileURL.I
Define TrashFileURL.I

If OSVersion() < #PB_OS_MacOSX_10_8
  MessageRequester("Error",
    "Sorry, but this program needs at least Mountain Lion!")
  End
EndIf

If CreateFile(0, TestFile)
  WriteString(0, "This file should be moved to the trash can!")
  CloseFile(0)

  TestFileURL = CocoaMessage(0, 0,
    "NSURL fileURLWithPath:$", @TestFile,
    "isDirectory:", #NO)

  If TestFileURL
    Result = CocoaMessage(0, CocoaMessage(0, 0, "NSFileManager defaultManager"),
      "trashItemAtURL:", TestFileURL,
      "resultingItemURL:", @TrashFileURL,
      "error:", @Error)

    If Result = #YES
      MessageRequester("Success", "The file '" + GetFilePart(TestFile) +
        "'" + #CR$ + "was successfully moved to trash as file" + #CR$ + "'" +
        PeekS(CocoaMessage(0, CocoaMessage(0, TrashFileURL,
        "lastPathComponent"), "UTF8String"), -1, #PB_UTF8) + "'")
    Else
      MessageRequester("Error", "Moving file " + GetFilePart(TestFile) +
        " to trash has failed!" + #CR$ + #CR$ + PeekS(CocoaMessage(0,
        CocoaMessage(0, Error, "localizedDescription"), "UTF8String"),
        -1, #PB_UTF8))
    EndIf
  EndIf
EndIf


Top
 Profile  
Reply with quote  
 Post subject: Re: [Cocoa] Extra file operations
PostPosted: Fri Jul 17, 2015 6:14 pm 
Offline
Addict
Addict

Joined: Mon Aug 04, 2008 10:56 pm
Posts: 1124
Location: Seattle, USA
Thanks Shardik for the new code it works perfectly.

I appreciate Wilbert's and your efforts to understand Cocoa and it is a great addition to the PB forum. I can't thank you both enough and I'm sure there are many others that feel the same way. :D

_________________
MacBook Pro-r, OSX 10.14.6 , PB 5.70LTS


Last edited by WilliamL on Fri Jul 17, 2015 9:31 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: [Cocoa] Extra file operations
PostPosted: Fri Jul 17, 2015 9:20 pm 
Offline
Addict
Addict
User avatar

Joined: Tue Dec 23, 2003 3:54 am
Posts: 1839
Lots of great information here, thank you! I will try it out as soon as possible.

_________________
On GitHub: PB Includes - IDE Tools - Color Themes - IDE Branches - TabBarGadget Mods


Top
 Profile  
Reply with quote  
 Post subject: Re: [Cocoa] Extra file operations
PostPosted: Sat Jul 18, 2015 10:58 am 
Offline
Addict
Addict
User avatar

Joined: Thu Apr 21, 2005 2:38 pm
Posts: 1753
Location: Germany
Here is another code example to move a file to trash that utilizes the NSWorkspace method recycleURLs:completionHandler: proposed by Wilbert. It works with MacOS versions beginning with Snow Leopard and I have tested it successfully on Snow Leopard and Yosemite:
Code:
EnableExplicit

Define FileArray.I
Define TestFile.S = GetTemporaryDirectory() + "Test.Txt"
Define TestFileURL.I
Define Workspace.I = CocoaMessage(0, 0, "NSWorkspace sharedWorkspace")

If OSVersion() < #PB_OS_MacOSX_10_6
  MessageRequester("Error",
    "Sorry, but this program needs at least Snow Leopard!")
  End
EndIf

If Workspace
  If CreateFile(0, TestFile)
    WriteString(0, "This file should be moved to the trash can!")
    CloseFile(0)

    ; ----- Create URL from TestFile string
    TestFileURL = CocoaMessage(0, 0,
      "NSURL fileURLWithPath:$", @TestFile,
      "isDirectory:", #NO)

    If TestFileURL
      ; ----- Create Array of URLs using TestFileURL (only 1 element)
      FileArray = CocoaMessage(0, 0, "NSMutableArray arrayWithCapacity:", 1)
      CocoaMessage(0, FileArray, "addObject:", TestFileURL)

      If FileArray
        ; ----- Move the URLs to the trash in the same manner as in the Finder
        CocoaMessage(0, Workspace,
          "recycleURLs:", FileArray,
          "completionHandler:", 0)
        MessageRequester("Info", "The file '" + TestFile + "'" + #CR$ +
          "was successfully moved to trash!")
      EndIf
    EndIf
  EndIf
EndIf


Top
 Profile  
Reply with quote  
 Post subject: Re: [Cocoa] Extra file operations
PostPosted: Wed Jul 22, 2015 2:46 am 
Offline
Addict
Addict
User avatar

Joined: Tue Dec 23, 2003 3:54 am
Posts: 1839
Shardik, is it "safe" to package your Get Info code in a procedure like this?

Do any of these objects need to be freed? Is it bad to call these repeatedly (I'm not sure what a "pasteboard" is yet)...

Code:
Procedure GetInfo(File.s)
  Protected Pasteboard.i = CocoaMessage(0, 0, "NSPasteboard pasteboardWithUniqueName")
  If (Pasteboard)
    Protected TypeArray.i = CocoaMessage(0, 0, "NSArray arrayWithObject:$", @"NSStringPboardType")
    If (TypeArray)
      CocoaMessage(0, Pasteboard, "declareTypes:", TypeArray, "owner:", 0)
      If (CocoaMessage(0, Pasteboard, "setString:$", @File, "forType:$", @"NSStringPboardType") = #YES)
        NSPerformService(CocoaMessage(0, 0, "NSString stringWithString:$", @"Finder/Show Info"), Pasteboard)
      EndIf
    EndIf
  EndIf
EndProcedure

_________________
On GitHub: PB Includes - IDE Tools - Color Themes - IDE Branches - TabBarGadget Mods


Top
 Profile  
Reply with quote  
 Post subject: Re: [Cocoa] Extra file operations
PostPosted: Thu Jul 23, 2015 8:22 pm 
Offline
Addict
Addict
User avatar

Joined: Thu Apr 21, 2005 2:38 pm
Posts: 1753
Location: Germany
kenmo wrote:
Shardik, is it "safe" to package your Get Info code in a procedure like this?

Do any of these objects need to be freed? Is it bad to call these repeatedly (I'm not sure what a "pasteboard" is yet)...

At first you still have to import NSPerformService() for your procedure GetInfo(File.s) to work:
Code:
ImportC ""
  NSPerformService(*ItemName, *NSPasteboard)
EndImport

An NSPasteboard is a clipboard. I assume that PureBasic on MacOS X is wrapping NSPasteboard methods for its native clipboard commands. But PureBasic's clipboard is a public clipboard whereas the Cocoa method pasteboardWithUniqueName will create a unique clipboard which won't interfere with the contents of other clipboards. This unique clipboard will be closed automatically when your program ends, so in order to not create a new unique clipboard with each call of your procedure I would use Static to create this clipboard together with the TypeArray only once in your procedure:
Code:
ImportC ""
  NSPerformService(*ItemName, *NSPasteboard)
EndImport

Procedure GetInfo(File.s)
  Static Pasteboard.i
  Protected TypeArray.i

  If Pasteboard = 0
    Pasteboard = CocoaMessage(0, 0, "NSPasteboard pasteboardWithUniqueName")

    If Pasteboard
      TypeArray.i = CocoaMessage(0, 0,
        "NSArray arrayWithObject:$", @"NSStringPboardType")

      If TypeArray
        CocoaMessage(0, Pasteboard, "declareTypes:", TypeArray, "owner:", 0)
      EndIf
    EndIf
  EndIf

  If CocoaMessage(0, Pasteboard,
    "setString:$", @File,
    "forType:$", @"NSStringPboardType") = #YES
    NSPerformService(CocoaMessage(0, 0,
      "NSString stringWithString:$", @"Finder/Show Info"), Pasteboard)
  EndIf
EndProcedure


Top
 Profile  
Reply with quote  
 Post subject: Re: [Cocoa] Extra file operations
PostPosted: Thu Jul 23, 2015 11:38 pm 
Offline
Addict
Addict
User avatar

Joined: Tue Dec 23, 2003 3:54 am
Posts: 1839
Thanks for the additional information! I will use one static Pasteboard pointer, and I can leave TypeArray as a Protected.

Shardik wrote:
At first you still have to import NSPerformService() for your procedure GetInfo(File.s) to work:
Of course. I also like to wrap them in compiler safeguards, so that multiple snippets in the same project can import the same API function without PB compilation errors.

Code:
ImportC ""
  CompilerIf (Not Defined(NSPerformService, #PB_Procedure))
    NSPerformService(*ItemName, *NSPasteboard)
  CompilerEndif
EndImport

_________________
On GitHub: PB Includes - IDE Tools - Color Themes - IDE Branches - TabBarGadget Mods


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 15 posts ] 

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye