realbasic-nug
[Top] [All Lists]

Re: FolderItem.delete Oddities?

To: REALbasic NUG <realbasic-nug@lists.realsoftware.com>
Subject: Re: FolderItem.delete Oddities?
From: Lars Jensen <larsjensen@gmail.com>
Date: Tue, 31 Mar 2009 13:47:45 -0700
Authentication-results: mx.google.com; spf=neutral (google.com: 74.124.194.228 is neither permitted nor denied by best guess record for domain of realbasic-nug-bounces@lists.realsoftware.com) smtp.mail=realbasic-nug-bounces@lists.realsoftware.com; dkim=neutral (body hash did not verify) header.i=@gmail.com
Delivered-to: listarchive@realsoftware.com
Dkim-signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:received:in-reply-to:references :date:message-id:subject:from:to:content-type :content-transfer-encoding; bh=HTt+wysad9sZWwrDsrFXvJHNEtL/JzlCiYPLnI0nZAU=; b=LrC4lI/pXjHL+tNYqrL1ObC/0DBFkrc9e8EZSxc46FTXT1jlhA8pNXiqb0LPmFLDI7 krCTx7kMAcDW7m/oBT1ev8zlsE8fsJZwK2WJUi1n9j1nuWpiAvgBQLhWwmqdNxcC9OH+ t0ruRHrypqVCyvjHSrqjhZhX6vkA/qbSiGTA8=
Domainkey-signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :content-type:content-transfer-encoding; b=iqVAStD8zZmkgg+u1NalclsOUqN0+jvk56n9IABASnsgvLsizXNcWGZKSTcBoaHQZV 9hJx/7dlJ5FiV7ocDneKETnZt0IAqHwEMtkboAb5DqDHF+HmvwsVZPYcJ/WfM1U1FUFt fE0ebDYxzd4GsJFXqQMvuH9D5HGyBDk4diV0s=
In-reply-to: <49D27A86.3090701@inspiringapps.com>
References: <382799.72156.qm@web65614.mail.ac4.yahoo.com> <A0B9E68F-F447-4291-9727-E72D8648E1B4@online.de> <58031FB4-BB70-45EC-B698-FBB7998DC783@mac.com> <E7631244-FE97-4299-BC4A-79EDF81E8393@online.de> <89fe53800903311240y1999380dt2fbfe60ce44cc408@mail.gmail.com> <2DA6A6CD-2049-437C-B9DC-F95144365F35@mac.com> <89fe53800903311300h3ddcb293w3be9cb54acb7d865@mail.gmail.com> <49D27A86.3090701@inspiringapps.com>
Reply-to: REALbasic NUG <realbasic-nug@lists.realsoftware.com>
Sender: realbasic-nug-bounces@lists.realsoftware.com
> This is a good point, and it suggests that we should simply add an Items
> (and TrueItems) extension method to FolderItem and have done with it.
> This is trivial to do; no need to wait for RS to add it for us.

I think it's somewhere between trivial and satisfying.  :)   Using
built-in RB functions is easy but slow on Windows if there are
hundreds of children. Constructing FolderItems was the bottleneck in
my tests.

I adapted some of Aaron's code (along with advice from this list) to
greatly speed this up on Windows by returning only the child names,
which can be further filtered so as to create only the FolderItems
that you need. What follows is the resulting ItemNames function, with
a bonus IsHidden function that reflects what I have learned about
hidden files on Mac and Windows. Comments on both are welcome.

==============

Function ItemNames(extends f as folderItem, ReturnFileNames as boolean
= true, ReturnFolderNames as boolean = true) As String()
  // Returns an array containing the names of child items
  // within the given folder item. Returns files and/or
  // folders, as directed by the input booleans.
  //
  // Returns an empty array if f doesn't exist, or if f
  // isn't a directory, or if both input booleans are false.
  //
  // The iteration is not recursive. On Windows, the special
  // directories "." and ".." are ignored.

  dim result() as string

  if f.Directory then

    #if TargetWin32

      // On Windows, RB's f.Item(i) is slow for folders with large
      // child item counts, so we use Declares on Windows instead.
      // Adapted from Aaron Ballman - see http://tinyurl.com/5susum

      Soft Declare Function FindFirstFileA Lib "Kernel32" (path as
CString, data as Ptr) as Integer
      Soft Declare Function FindFirstFileW Lib "Kernel32" (path as
WString, data as Ptr) as Integer
      Soft Declare Function FindNextFileA Lib "Kernel32" (handle as
Integer, data as Ptr) as Boolean
      Soft Declare Function FindNextFileW Lib "Kernel32" (handle as
Integer, data as Ptr) as Boolean
      Declare Sub FindClose Lib "Kernel32" (handle as Integer)

      dim UnicodeIsAvailable as boolean =
System.IsFunctionAvailable("FindFirstFileW", "Kernel32")

      dim ChildData as MemoryBlock // WIN32_FIND_DATA struct
      dim ChildHandle as integer

      if UnicodeIsAvailable then

        ChildData = new MemoryBlock(592)
        ChildHandle = FindFirstFileW(f.AbsolutePath + "*.*", ChildData)

      else

        ChildData = new MemoryBlock(318)
        ChildHandle = FindFirstFileA(f.AbsolutePath + "*.*", ChildData)

      end if

      if ChildHandle <> -1 then

        dim ChildAttrs as UInt32 // first 4 bytes of WIN32_FIND_DATA
        dim ChildName as string

        // Loop through remaining items in the folder.

        dim FoundNextChild as Boolean

        do // loop through remaining children

          ChildAttrs = ChildData.UInt32Value(0)
          const NameOffset = 44

          if UnicodeIsAvailable then

            ChildName = ChildData.WString(NameOffset)
            FoundNextChild = FindNextFileW(ChildHandle, ChildData)

          else

            ChildName = ChildData.CString(NameOffset)
            FoundNextChild = FindNextFileA(ChildHandle, ChildData)

          end if

          // Now that we have its name and attributes, we can decide
          // whether this child should be added to our return array.

          dim ChildIsFolder as boolean = (ChildAttrs and UInt32(16)) <> 0

          if (ReturnFileNames and not ChildIsFolder) or _
            (ReturnFolderNames and ChildIsFolder) then

            if childName <> "." and childName <> ".." then
              result.Append(ChildName)
            end if

          end if

        loop until not FoundNextChild // should really test
GetLastError for ERROR_NO_MORE_FILES

        FindClose ChildHandle

      end if

    #else

      // On non-Windows systems, pure RB code seems pretty fast.

      dim child as FolderItem

      for i as integer = 1 to f.Count

        child = f.TrueItem(i)

        if (ReturnFileNames and not Child.Directory) or _
          (ReturnFolderNames and Child.Directory) then
          result.Append child.Name
        end if

      next i

    #endif

  end if

  return result

End Function

==============

Function IsHidden(extends f as FolderItem, PerformMacOSXCheck as
boolean = true) As Boolean

  // Returns true if the indicated item is hidden
  // in the Finder on Mac OS X, or in the Explorer
  // on Windows.
  //
  // On Windows or Mac Classic, you could test f.Visible,
  // but on Mac OS X, that test isn't sufficient. There
  // are two other ways to keep an item from appearing
  // in the OS X Finder:
  //
  // 1) Start the item's name with a '.' character.
  //    For example, Mac OS X maintains an invisible
  //    file called ".DS_Store" in each folder.
  //
  // 2) List the item in a file named ".hidden" (which
  //    will itself be invisible, according to #1), in
  //    the same directory as the item to be hidden.
  //
  // There is a ".hidden" file in the root folder of
  // every Mac OS X volume. Any other folder can have
  // one too, but they're rare outside the root.
  //
  // By default, we do the more extensive check, on the
  // theory that if you just want the Visible-level check,
  // you can just call that. Or you can pass in False
  // for PerformMacOSXCheck.

  dim Visible as boolean = f.Visible
  dim fHidden as folderItem
  dim stream as TextInputStream
  dim hiddenFiles as string
  dim sep as string

  // If it's invisible according to the .Visible flag,
  // that's good enough on any platform. But if we're
  // checking for OS X style visibility, we have to
  // dig a little deeper.

  if Visible then

    if PerformMacOSXCheck then

      if f.Name.Left(1) = "." then

        Visible = false // starts with "." -> invisible

      else

        if f.Parent <> nil then // the root is always visible

          fHidden = f.Parent.Child(".hidden")

          if fHidden <> nil and fHidden.Exists and _
            not fHidden.Directory then

            stream = fHidden.OpenAsTextFile
            if stream <> nil then

              sep = chr(10)

              hiddenFiles = sep + stream.ReadAll + sep
              if hiddenFiles.InStr(sep + f.Name + sep) > 0 then

                Visible = false // it's in our ".hidden" list

              end if

            end if

          end if

        end if

      end if

    end if

  end if

  return not Visible

End Function

==============

lj

_______________________________________________
Unsubscribe or switch delivery mode:
<http://www.realsoftware.com/support/listmanager/>

Search the archives:
<http://support.realsoftware.com/listarchives/lists.html>


<Prev in Thread] Current Thread [Next in Thread>