wxWidgets / wxWidgets

Cross-Platform C++ GUI Library
https://www.wxwidgets.org/
5.78k stars 1.7k forks source link

Create a wxFilePath object #9174

Closed wxtrac closed 2 years ago

wxtrac commented 22 years ago

Issue migrated from trac ticket # 9174

priority: normal

2001-12-05 03:29:35: wordman created the issue


I would very much like to see an object-oriented system for managing file paths introduced to wxWindows.

RATIONALE

The reasons for wanting an object-oriented file path system (as opposed to just using wxString to represent a full path) are these:

1) There are a number of manipulations that are commonly done to file paths. Currently, wxWindows has a number of utility routines to handle these, but it is more proper object-oriented behavior to bundle the behavior and the data into an object. For example, how often do you have code like this in your applications?

if ( m_FilePath.Last() != wxFILE_SEP_PATH )
    m_FilePath += wxFILE_SEP_PATH;

An object-oriented path system would take care of this kind of stuff, such as pushing and popping leaf names, extracting the filename from the end of path, etc.

2) C++ is a fairly strongly typed language that allows creation of new types. One huge advantage this provides programmers is that, using typed data, methods can be set up to generate compile errors if you try to pass them bad data. If, for example, you try to pass a wxPen object to a routine expecting a wxDC object, the compiler will (rightly) yell at you, preventing it from becoming a nasty run-time problem. The current method for file paths used by wxWindows completely avoids this, allowing any arbitrary string to get passed into a method that really will only work when passed a valid file path.

Using object-oriented path objects would solve this issue, making file paths a defined type.

3) Not all operating systems wxWindows support use string-based full path names to define a file location, or use different path schemes. How many ifdefs have you written to prepend a drive letter to a path on the Win32 platform?

The Mac OS, to give a more extreme example, discourages full paths, though it can handle them. The reason for this is that full file paths are not guaranteed to represent unique files under Mac OS, because multiple volumes can be mounted that have the same name (under the Mac OS, the name of the volume is the first node of the path). For example, suppose you have a hard-drive named "Scratch", then insert a floppy disk named "Scratch". If both of these have a file named "Test.txt" on the root level, what file does "Scratch:Test.txt" represent?

Instead, all of the Mac file APIs use something called an FSSpec to represent a file uniquely. The FSSpec consists of a volume reference number (assigned when the drive is mounted), a directory ID (unique to the volume) and a filename (unique to the directory).

If you are used to using full paths for everything, this can be a little freaky at first, but it has some cool side effects. Say you have an application that saves a download directory (or a config directory or something) as a preference. In-between application sessions, you move this directory. If your application saved the directory the correct way (i.e. not using a full path), it will still find the directory even after it moved. (Imagine, for example, just randomly moving your /etc directory and everything just working fine. That is, in effect, possible under the Mac OS.) This is how, for example, Mac users can arbitrarily re- name their system folders.

Having an object-oriented path system would allow an OS like the Mac OS implement its freaky scheme under the covers, with the application code being none the wiser. This is pretty much the whole point of a cross- platform framework. (And, frankly, I was a little shocked it wasn't already in wxWindows.)

REQUIREMENTS

There may be other ways to accomplish the same goals, but this seems to me to be the way to give wxWindows a object-oriented path system (the would be compatible with the Mac's system):

A) Define wxDirectory (or maybe wxDirectoryPath). This object represents a specific directory on a machine (or one to be created). This object would know how to push and pop leaves in the path, and would have information about directories that the OS considers "special" (e.g. Win32 system directory, temp directory, etc.) This object, by itself, cannot represent a file, only a directory.

The interface should look, minimally, like this:

class WXDLLEXPORT wxDirectory { public: wxDirectory(); wxDirectory( const wxDirectory &a_rDir ); wxDirectory( const wxString &a_rDir ); virtual ~wxDirectory();

wxDirectory& operator=(const wxString& a_rDir);
wxDirectory& operator=(const wxDirectory& 

a_rDir);

// Overide the += operation to push a leaf.
void operator+=(const wxString& a_rLeaf) { 

PushLeaf(a_rLeaf); }

// A wxDirectory can be used like a constant 

string object. // This also allows the use of the wxString comparison operations. operator wxString() const;

// Take the last leaf off of directory path. 

For example, // if the wxDirectory represented "var/logs/foo", calling // PopLeaf would change the wxDirectory to represent // "var/logs" and set a_rLeaf to "foo". bool PopLeaf( wxString& a_rLeaf );

// Push appends a leaf onto the path. This 

should be used // only to push leaves that are directories and not // be used to push on a file name. Use wxFilePath for that // purpose. Note, this method will properly figure out // the path separator. bool PushLeaf( const wxString& a_rLeaf );

// Sets the path to be a root volume, clearing 

the rest of the path. // This takes a parameter indicating how the path is set. For example, // the parameter can indicate to set the path to the root of the boot // device, or by volume name instead. Some of the settings of this // parameter are useful (or even allowed) only to certain platforms. // For *nix, this parameter is ignored, and path will always be set to "/". // For Windows, a_rName, if used, should be a drive letter (e.g. "D"). // For the Mac, a_rName, if used, should be a drive name (e.g. "Macintosh HD"). // Using a positive number n on Mac or Win32 will set the path to the // nth mounted drive. enum { kVol_ByName = -2, // Set volume by name given in a_rName kVol_System = -1, // Set volume to boot volume (e.g. "C:\") kVol_First = 0, // If a positive number passed, use that kVol_Second = 1 // ordinal volume. // .. etc } VolumeType; void SetVolume( short a_nVolume,const wxString &a_rName = "" );

// Set directory path to a special directory 

on the system. Some // operating systems have more special directories than others. // (The Mac has about 20.) typedef enum { kDir_System, // System directory (e.g. "C:\winnt\" or the Mac SystemFolder) kDir_Temp, // Temp directory (e.g. "C:\temp", "/tmp" or Mac temp dir) kDir_Extensions, // The Mac extensions folder, or the sys32 directory kDir_Preferences // The Mac prefs folder. // .. etc } DirectoryType; void SetToSpecial( DirectoryType a_nType ); }

Note that internal data of this object will be a wxString on most platforms, but it doesn't have to be. For example, you could represent it as a tree structure or something else. The point it, it doesn't really matter, as long as the interface is implemented.

B) Define wxFilePath. This object represents the unique location of a file that exists or might exist on a machine (or, potentially, network). The wxFilePath contains a wxDirectory object, and is usually constructed with one. The interface for this object could be:

class WXDLLEXPORT wxFilePath { public: wxFilePath(); wxFilePath( const wxDirectory &a_rDir,const wxString &a_rFilename ); wxFilePath( const wxString &a_rFullPath ); wxFilePath( const wxFilePath &a_rPath ); virtual ~wxFilePath();

wxFilePath& operator=(const wxFilePath& 

a_rPath); wxFilePath& operator=(const wxString& a_rFullPath);

// A wxFilePathcan be used like a constant 

string object. // This also allows the use of the wxString comparison operations. operator wxString() const;

// Get a reference to the file's internal 

directory object. const wxDirectory &GetDirectory() const;

// Get the leaf name of the file.
const wxString  &GetFileName() const;

// Get the full path of the file.
const wxString  &GetFullPath() const;

// Get the type of the file. For Win32 

systems, this would be the file // extension. For the Mac, this would be the internal file type. const wxString &GetFileType() const;

// ... etc.

}

C) All APIs that accept or return file or directory paths need to be altered to take wxDirectory or wxFilePath references instead of wxString objects. This is a big task, but not as bad as it sounds. There really aren't that many routines that do this, maybe 60 or so.

Note: to prevent a bunch of code from breaking, both wxFilePath and wxDirectory need to have wxString operators. This will allow existing code to compile and work without having to explicitly move to using wxFilePath and wxDirectory objects.

D) Any methods that take a single wxString where the string can be either a file or a directory (I don't think there are any) would need to be split into two versions, one taking a wxFilePath, the other taking a wxDirectory.

E) Just to be really safe, the definition of wxFilePath and wxDirectory might be ifdef'd with a compile constant. The else clause of the ifdef would be:

#define wxFilePath wxString
#define wxDirectory wxString

This might prove impractical, though.

F) Implementation of the classes would need to be platform specific, naturally. Since most of the file code already is anyway, this should not be much of a problem.

G) The existing file path utility functions should be deprecated, with the functionality incorporated into wxFilePath or wxDirectory.

wxtrac commented 22 years ago

2002-02-06 15:03:25: @vadz commented


Please have a look at wxFileName class in 2.3 (a.k.a. main branch) - it is not perfect yet but already works well enough to be used. Let us know about any comments/ideas about how to improve wxFileName on the wxWin mailing lists!

Thanks!