LOOPE - Lingo Object Oriented Programming Environment by Irv Kalb

Previous Chapter

Table of Contents

Next Chapter

 

Section 4 - Miscellaneous Objects

Chapter 1 - Cross Platform Path Manager

 

Cross Platform applications

On of the great benefits of using Director is that, for the most part, when writing code in Lingo, we don't have to worry much about the details of making programs work on a PC and a Macintosh. Most of the code we write in Lingo is automatically"cross platform". The engineers at Macromedia that developed Lingo did a great job of hiding many cross platform details.

However, there is one area that we as programmers must deal with in order to make programs truly cross platform compatible: the file system. The file system of PC's and Macintoshes are very similar but different, and therefore we must build file specs differently on the different platforms. This chapter will give us a way to encapsulate the code needed to deal with the difference in file systems into a single object.

 

Externally Linked files

As you start to build larger and larger Director projects, we will often find that it makes sense to have assets outside of the Director program and utilize linked cast members. That is, a member in the cast which is basically a pointer to a file on the hard disk. And with many types of projects, the external media we may want to use can change over time. For example, we may want to utilize different external files as the program runs. With many media types (Quicktime, Flash, etc.), we could change the "filename" property of a castmember to point to different files at different times:

member("MyLinkedQuickTimeFile").filename = <someFullFileSpec>

Using this approach, we can have a program use many different external files of the same type, using only a single cast member as a placeholder.

 

Other external files

There could be many other types of external files that a program may need to open (to read and/or write). The program may have configuration files, status files, or other text files that remember the state from the last time the user used the program. The program could also have database files that supply information. We need a cross platform way of generating these file specs.

 

Project Structure

When a project involves external files (either linked or not linked) to the project, such files typically live in the same folder as the projector. In larger projects, it is often a good idea to group similar types of media into subfolders that live in the same folder as the projector. For example, we might have a subfolder for Sound, one for QuickTime, one for Flash, perhaps one for Students, etc. The file tree of a such a project might look like this:

 

We need the ability to generate a full file spec to any file like these within the project hierarchy.

 

Folder separator

The Windows and Mac operating systems use a different character as the folder separator. Windows uses the slash character, "\", while the Mac uses the colon ":". A typical path on windows might look like:

C:\SomeFolder\SubFolder1\SubFolder2\SubFolder3\Filename.ext

whereas a similar path on a Mac might look like this:

MyHardDisk:SomeFolder:SubFolder1:SubFolder2:SubFolder3Filename.ext

To determine the proper folder separator character, here is a little trick. There is a Lingo system variable called "the moviePath". It returns the full path to the current movie. The last character of that path will be the appropriate folder separator for the operating system that the program is currently running on. Therefore, we can use the following Lingo statement to get the folder separator character:

  sDelim = the last char of the moviePath

Building the File Spec

In order to build file specs for each of the types of files mentioned above, we have to be concerned about building the full file spec using the appropriate folder separator (also known as a "delimiter" character). Without object oriented programming, the typical way to build file specs would be to declare a global variable and initialize it at the beginning of the program:

global gsDelim

on prepareMovie
  gsDelim = the
last char of the moviePath
  -- anything else you want to do here
end

Then later, when we want to build a file spec, we make use of this separator character to move down the folder hierarchy to build a file spec. To get to a file called "abc.mov" in a project subfolder called QuickTime, we might use the following code:

sFileSpec = the moviePath & gsDelim & "QuickTime" & gsDelim & "abc.mov"

This does work and will generate the correct filespec for this file. However, this approach is often prone to error because you must remember to add gsDelim in between all subfolders.

 

The OOP approach using a Path Manager

Using an object oriented approach, we would want to have a way to have an object build such filespecs for us. The knowledge of the folder separator would be kept within the object so we wouldn't have to carry around that piece of information throughout the program. Ideally, we would build a method in the object that would build these paths for us. That's exactly what we will do.

Here is a simple Path Manager that will do what we want:

 

-- Simple PathMgr

property psDelim
property psApplicationPath

on new me
  psApplicationPath = the
moviePath
  psDelim = the
last char of psApplicationPath -- get the delimiter
  return
me
end

on mBuildPath me, lsFolders, sFileName
  sPathToReturn = psApplicationPath -- starting point

  -- Add in subfolders
  repeat
with sThisFolder in lsFolders
    put (sThisFolder & psDelim) after sPathToReturn
  end
repeat

  -- If a file name was passed in, add that to the end
  if not(voidp(sFileName)) then
    put sFileName after sPathToReturn
  end
if
  return sPathToReturn
end

on mCleanUp me
  nothing
end

 

To start with, it only has a new method, an mBuildPath method, and our standard mCleanUp method (which doesn't do anything in this object).

In the new method, we get the current application path and store it away in a property variable. Then, we get the folder separator character from the end of the application path, and store it away in a property variable. These two property variables will be used later to build paths.

The key to this object is the mBuildPath method. As inputs, it takes a list of folders and an optional file spec. The idea is really quite simple. The method builds up a path string by starting with the previously saved application path, then appending each given subfolder followed by the previously saved separator character. If there was a filename passed in, it appends that filename to the end of the string before returning the result. The file name is optional because there are cases where we might only need the folder path, without referring to a specific file.

 

Usage:

At the start of the program, we instantiate a Path Manager object and store the reference to it in a global variable called goPathMgr, like this:

 

global goPathMgr

on prepareMovie
  goPathMgr = new(script
"PathMgr")
  -- anything else you want to do here
end

Then when we want to build a path, we call the mBuildPath method of the object. To use the same example as we did earlier in this chapter, let's say we want to build a filespec to the file "abc.mov" in the QuickTime subfolder. We would do this by making the following call:

sFileSpec = goPathMgr.mBuildFileSpec(["QuickTime"], "abc.mov")

The power of this approach becomes more evident when in larger projects which involve subfolders within subfolders within subfolders of external files. For example, for better organization, we may want to have a "media" folder which contains a subfolder for each of your different media types. In this case, to get to a specific QuickTime file, we must go to the media folder and then into the QuickTime subfolder. To build such a path, we build up the hierarchy in the list of subfolders passed in to the mBuildFileSpec method like this:

sFileSpec = goPathMgr.mBuildFileSpec(["media", "QuickTime"], "abc.mov")

 

Alternative approach:

If, however, you think of filespecs with colons or slashes as separator characters to start with, then you could use that style as a different way (or an additional approach) to managing paths. You could specify the path layed out in either Windows or Mac OS format, and modify the path based to match the platform on which the code is running. For example, if you "think in Windows", then you could call a method like this:

sFileSpec = goPathMgr.mBuildFileSpecFromPath("media/QuickTime/abc.mov")

Or if you "think in Mac OS", then you could call a routine like this:

sFileSpec = goPathMgr.mBuildFileSpecFromPath("media:QuickTime:abc.mov")

Then you could build a new method in the Path Manager called mBuildFileSpecFromPath that would accept this type of path as a parameter. It would go through the input string and change all occurrances or colons or slashes to the value of the property variable psDelim, then return the appropriate full path.

 

Additional Features:

The Path Manager can be extended to allow for more functionality. For example, on a large project, we might make reference to certain subfolders very often, for example, we might often need to build file specs in the Sounds or QuickTime or Flash subfolders. We might like to compute such paths once, and save them away. Rather than saving such paths in global variables, e.g., gSoundsPath, gQuickTimesPath, etc. it would be a nice addition to the Path Manager to do this for us. This final version of the Path Manager has this ability:

 

-- PathMgr Full Script

property psDelim
property psApplicationPath
property plsSavedPaths

on new me
  psApplicationPath = the
moviePath
  psDelim = the
last char of psApplicationPath -- get the delimiter

  plsSavedPaths = [:] -- start as an empty prop list

  return me
end

on mBuildPath me, lsFolders, sFileName
  sPathToReturn = psApplicationPath -- starting point

  -- Add in subfolders
  repeat
with sThisFolder in lsFolders
    put (sThisFolder & psDelim) after sPathToReturn
  end
repeat

  -- If a file name was passed in, add that to the end
  if not(voidp(sFileName)) then
    put sFileName after sPathToReturn
  end
if
  return sPathToReturn
end

on mSetSavedPath me, symPath, sPath
  plsSavedPaths[symPath] = sPath
end

on mGetSavedPath me, symPath
  sPath = plsSavedPaths[symPath]
  if
voidp(sPath) then
    alert("No path has been saved for folder:"
&& symPath)
    return
""
  end
if
  return sPath
end

on mAddFoldersToPath me, sPath, lsFolders
  -- add in subfolders
  repeat
with sThisFolder in lsFolders
    put (sThisFolder & psDelim) after sPath
  end
repeat
  return sPath
end

on mGetFolderDelimiter me
  return psDelim
end


on mCleanUp me
  nothing
end

The mSetSavedPath allows to us save away a path inside the Path Manager and associate it with a symbol. For example, we might have the following code at the startup of our program:

 

global goPathMgr

on prepareMovie
  goPathMgr = new(script
"PathMgr")
  sSoundPath = goPathMgr.mBuildPath(["media", "Sounds"])
  goPathMgr.mSetSavedPath(#sounds, sSoundPath)
  sFlashPath = goPathMgr.mBuildPath(["media", "Flash"])
  goPathMgr.mSetSavedPath(#flash, sFlashPath)
  -- anything else you want to do here
end

Then to build the a filespec for a sound called "Hello.aif", all we need is this:

sFileSpec = goPathMgr.mGetSavedPath(#sounds) & "Hello.aif"

The Path Manager above also has a method for getting the folder separator character should we need it. And it has another method that will add subfolders to any given path should we need to dig down deeper into a folder hierarchy

 

Conclusion

Using a Path Manager object allows us to isolate the knowledge of how path specs differ on different platforms, and put this knowldege into a single script. By encapsulating this into the Path Manager, the rest of the program can use paths without ever having to deal with the operating specific folder separator character. The Path Manager can also be used to store commonly used paths for use later in the program. The Path Manager can be extended by adding any methods we may need, to deal with paths in a cross platform way.

Previous Chapter

Table of Contents

Next Chapter