diff --git a/distrib/msw/tmake/filelist.txt b/distrib/msw/tmake/filelist.txt index 054fa153c4..d4013fd5de 100644 --- a/distrib/msw/tmake/filelist.txt +++ b/distrib/msw/tmake/filelist.txt @@ -119,6 +119,7 @@ datstrm.cpp C B db.cpp C 32,B dbtable.cpp C 32,B dcbase.cpp C +dircmn.cpp C B dlgcmn.cpp C dobjcmn.cpp C dndcmn.cpp C diff --git a/docs/changes.txt b/docs/changes.txt index 7a975621d6..15f9740580 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -8,14 +8,15 @@ wxBase: - fixed problem with wxURL when using static version of the library - wxZipFSHandler::FindFirst() and FindNext() now correctly list directories -- wxMimeTypesManager now can create file associations too +- wxMimeTypesManager now can create file associations too (Chris Elliott) - wxCopyFile() respects the file permissions (Roland Scholz) - wxFTP::GetFileSize() added (Søren Erland Vestø) - wxDateTime::IsSameDate() bug fixed - wxTimeSpan::Format() now behaves more as expected, see docs -- wxLocale now provides much more convenient API for setting language and detecting - current system language. New API is more abstracted and truly cross-platform, - independent of underlying C runtime library. +- wxLocale now provides much more convenient API for setting language and + detecting current system language. New API is more abstracted and truly + cross-platform, independent of underlying C runtime library. +- wxDir::Traverse() added All (GUI): @@ -25,7 +26,7 @@ All (GUI): - FindOrCreateBrush/Pen() bug fix for invalid colour values - new wxXPMHandler for reading and writing XPM images - added new (now recommended) API for conversion between wxImage and wxBitmap - (wxBitmap::ConvertToImage() and wxBitmap::wxBitmap(wxImage&) instead of + (wxBitmap::ConvertToImage() and wxBitmap::wxBitmap(wxImage&) instead of wxImage methods and ctor) wxMSW: @@ -34,7 +35,7 @@ wxMSW: - fixed bug in MDI children flags (mis)handling - it is possible to compile wxCHMHelpController with other compilers than VisualC++ now and hhctrl.ocx is loaded at runtime. - + wxGTK: - added support for wchar_t (wxUSE_WCHAR_T) under Unix @@ -238,7 +239,7 @@ wxHTML: - almost complete rewrite of wxHtmlHelpController, including faster search, bookmarks, printing, setup dialog - and cross-platform binary compatible .cached files for faster + and cross-platform binary compatible .cached files for faster loading of large helpbooks, case insensitive search splitted into 3 parts: wxHtmlHelpData, Frame and Controller - added support for charsets and tag diff --git a/docs/latex/wx/dir.tex b/docs/latex/wx/dir.tex index 3b1e5b7e25..4c06f3bce3 100644 --- a/docs/latex/wx/dir.tex +++ b/docs/latex/wx/dir.tex @@ -1,8 +1,13 @@ -% -% automatically generated by HelpGen from -% include\wx\dir.h at 11/Dec/99 00:55:30 -% - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Name: dir.tex +%% Purpose: wxDir documentation +%% Author: Vadim Zeitlin +%% Modified by: +%% Created: 04.04.00 +%% RCS-ID: $Id$ +%% Copyright: (c) Vadim Zeitlin +%% License: wxWindows license +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{\class{wxDir}}\label{wxdir} @@ -10,6 +15,10 @@ wxDir is a portable equivalent of Unix {open/read/close}dir functions which allow enumerating of the files in a directory. wxDir allows enumerate files as well as directories. +wxDir also provides a flexible way to enumerate files recursively using +\helpref{Traverse}{wxdirtraverse} or a simpler +\helpref{GetAllFiles}{wxdirgetallfiles} function. + Example of use: \begin{verbatim} @@ -117,3 +126,117 @@ empty) and flags, return TRUE on success. Continue enumerating files satisfying the criteria specified by the last call to \helpref{GetFirst}{wxdirgetfirst}. +\membersection{wxDir::Traverse}{wxdirtraverse} + +\func{size\_t}{Traverse}{\param{wxDirTraverser& }{sink}, \param{const wxString& }{filespec = wxEmptyString}, \param{int }{flags = wxDIR\_DEFAULT}} + +Enumerate all files and directories under the given directory recursively +calling the element of the provided \helpref{wxDirTraverser}{wxdirtraverser} +object for each of them. + +More precisely, the function will really recurse into subdirectories if +{\it flags} contains {\tt wxDIR\_DIRS} flag. It will ignore the files (but +still possibly recurse into subdirectories) if {\tt wxDIR\_FILES} flag is +given. + +For each found directory, \helpref{sink.OnDir()}{wxdirtraverserondir} is called +and \helpref{sink.OnFile()}{wxdirtraverseronfile} is called for every file. +Depending on the return value, the enumeration may continue or stop. + +The function returns the total number of files found or {\tt (size\_t)-1} on +error. + +See also: \helpref{GetAllFiles}{wxdirgetallfiles} + +\membersection{wxDirTraverser::GetAllFiles}{wxdirtraversergetallfiles} + +\func{static size\_t}{GetAllFiles}{\param{const wxString& }{dirname}, \param{wxArrayString *}{files}, \param{const wxString& }{filespec = wxEmptyString}, \param{int }{flags = wxDIR\_DEFAULT}} + +The function appends the names of all the files under directory {\it dirname} +to the array {\it files} (note that its old contents is preserved). Only files +matching the {\it filespec} are taken, with empty spec matching all the files. + +The {\it flags} parameter should always include {\tt wxDIR\_FILES} or the array +would be unchanged and should include {\tt wxDIR\_DIRS} flag to recurse into +subdirectories (both flags are included in the value by default). + +See also: \helpref{Traverse}{wxdirtraverse} + +\section{\class{wxDirTraverser}}\label{wxdirtraverser} + +wxDirTraverser is an abstract interface which must be implemented by objects +passed to \helpref{Traverse}{wxdirtraverse} function. + +Example of use (this works almost like \helpref{GetAllFiles}{wxdirgetallfiles}): + +\begin{verbatim} + class wxDirTraverserSimple : public wxDirTraverser + { + public: + wxDirTraverserSimple(wxArrayString& files) : m_files(files) { } + + virtual wxDirTraverseResult OnFile(const wxString& filename) + { + m_files.Add(filename); + return wxDIR_CONTINUE; + } + + virtual wxDirTraverseResult OnDir(const wxString& WXUNUSED(dirname)) + { + return wxDIR_CONTINUE; + } + + private: + wxArrayString& m_files; + }; + + // get the names of all files in the array + wxArrayString files; + wxDirTraverserSimple traverser(files); + + wxDir dir(dirname); + dir.Traverse(traverser); +\end{verbatim} + +\wxheading{Derived from} + +No base class + +\wxheading{Constants} + +The elements of {\tt wxDirTraverseResult} are the possible return values of the +callback functions: + +{\small +\begin{verbatim} +enum wxDirTraverseResult +{ + wxDIR_IGNORE = -1, // ignore this directory but continue with others + wxDIR_STOP, // stop traversing + wxDIR_CONTINUE // continue into this directory +}; +\end{verbatim} + +\wxheading{Include files} + + + +\latexignore{\rtfignore{\wxheading{Members}}} + +\membersection{wxDirTraverser::OnFile}{wxdirtraverseronfile} + +\func{virtual wxDirTraverseResult}{OnFile}{\param{const wxString& }{filename}} + +This function is called for each file. It may return {\tt wxDIR\_STOP} to abort +traversing (for example, if the file being searched is found) or +{\tt wxDIR\_CONTINUE} to proceed. + +\membersection{wxDirTraverser::OnDir}{wxdirtraverserondir} + +\func{virtual wxDirTraverseResult}{OnDir}{\param{const wxString& }{dirname}} + +This function is called for each directory. It may return {\tt wxSIR\_STOP} +to abort traversing completely, {\tt wxDIR\_IGNORE} to skip this directory but +continue with others or {\tt wxDIR\_CONTINUE} to enumerate all files and +subdirectories in this directory. + diff --git a/include/wx/dir.h b/include/wx/dir.h index f8b0b67f3c..de2563540b 100644 --- a/include/wx/dir.h +++ b/include/wx/dir.h @@ -37,6 +37,32 @@ enum wxDIR_DEFAULT = wxDIR_FILES | wxDIR_DIRS | wxDIR_HIDDEN }; +// these constants are possible return value of wxDirTraverser::OnDir() +enum wxDirTraverseResult +{ + wxDIR_IGNORE = -1, // ignore this directory but continue with others + wxDIR_STOP, // stop traversing + wxDIR_CONTINUE // continue into this directory +}; + +// ---------------------------------------------------------------------------- +// wxDirTraverser: helper class for wxDir::Traverse() +// ---------------------------------------------------------------------------- + +class WXDLLEXPORT wxDirTraverser +{ +public: + // called for each file found by wxDir::Traverse() + // + // return wxDIR_STOP or wxDIR_CONTINUE from here + virtual wxDirTraverseResult OnFile(const wxString& filename) = 0; + + // called for each directory found by wxDir::Traverse() + // + // return one of the enum elements defined above + virtual wxDirTraverseResult OnDir(const wxString& dirname) = 0; +}; + // ---------------------------------------------------------------------------- // wxDir: portable equivalent of {open/read/close}dir functions // ---------------------------------------------------------------------------- @@ -65,6 +91,9 @@ public: // returns TRUE if the directory was successfully opened bool IsOpened() const; + // get the full name of the directory (without '/' at the end) + wxString GetName() const; + // file enumeration routines // ------------------------- @@ -78,27 +107,19 @@ public: // GetFirstNormal() bool GetNext(wxString *filename) const; - // TODO using scandir() when available later, emulating it otherwise -#if 0 - // get all files in the directory into an array, return TRUE on success + // enumerate all files in this directory and its subdirectories // - // this function uses Select() function to select the files - // unless the filespec is explicitly given and Compare() function to sort - // them - bool Read(wxArrayString& filenames, - const wxString& filespec = wxEmptyString) const; + // return the number of files found + size_t Traverse(wxDirTraverser& sink, + const wxString& filespec = wxEmptyString, + int flags = wxDIR_DEFAULT) const; -protected: - // this function is called by Read() if filespec is not specified in - // Read(): it should return TRUE if the file matches our selection - // criteria and FALSE otherwise - virtual bool Select(const wxChar* filename); - - // This function is called by Read() to sort the array: it should return - // -1, 0 or +1 if the first file is less than, equal to or greater than - // the second. The base class version does - virtual int Compare(const wxChar *filename1, const wxChar *filename2); -#endif // 0 + // simplest version of Traverse(): get the names of all files under this + // directory into filenames array, return the number of files + static size_t GetAllFiles(const wxString& dirname, + wxArrayString *files, + const wxString& filespec = wxEmptyString, + int flags = wxDIR_DEFAULT); private: friend class WXDLLEXPORT wxDirData; diff --git a/samples/console/console.cpp b/samples/console/console.cpp index 12d8911e08..c89655a181 100644 --- a/samples/console/console.cpp +++ b/samples/console/console.cpp @@ -36,10 +36,10 @@ // what to test (in alphabetic order)? //#define TEST_ARRAYS -#define TEST_CHARSET +//#define TEST_CHARSET //#define TEST_CMDLINE //#define TEST_DATETIME -//#define TEST_DIR +#define TEST_DIR //#define TEST_DLLLOADER //#define TEST_ENVIRON //#define TEST_EXECUTE @@ -206,6 +206,16 @@ static void ShowCmdLine(const wxCmdLineParser& parser) #include +#ifdef __UNIX__ + static const wxChar *ROOTDIR = _T("/"); + static const wxChar *TESTDIR = _T("/usr"); +#elif defined(__WXMSW__) + static const wxChar *ROOTDIR = _T("c:\\"); + static const wxChar *TESTDIR = _T("d:\\"); +#else + #error "don't know where the root directory is" +#endif + static void TestDirEnumHelper(wxDir& dir, int flags = wxDIR_DEFAULT, const wxString& filespec = wxEmptyString) @@ -228,6 +238,8 @@ static void TestDirEnumHelper(wxDir& dir, static void TestDirEnum() { + puts("*** Testing wxDir::GetFirst/GetNext ***"); + wxDir dir(wxGetCwd()); puts("Enumerating everything in current directory:"); @@ -248,13 +260,7 @@ static void TestDirEnum() puts("Enumerating files including hidden in current directory:"); TestDirEnumHelper(dir, wxDIR_FILES | wxDIR_HIDDEN); -#ifdef __UNIX__ - dir.Open("/"); -#elif defined(__WXMSW__) - dir.Open("c:\\"); -#else - #error "don't know where the root directory is" -#endif + dir.Open(ROOTDIR); puts("Enumerating everything in root directory:"); TestDirEnumHelper(dir, wxDIR_DEFAULT); @@ -273,6 +279,55 @@ static void TestDirEnum() TestDirEnumHelper(dirNo); } +class DirPrintTraverser : public wxDirTraverser +{ +public: + virtual wxDirTraverseResult OnFile(const wxString& filename) + { + return wxDIR_CONTINUE; + } + + virtual wxDirTraverseResult OnDir(const wxString& dirname) + { + wxString path, name, ext; + wxSplitPath(dirname, &path, &name, &ext); + + if ( !ext.empty() ) + name << _T('.') << ext; + + wxString indent; + for ( const wxChar *p = path.c_str(); *p; p++ ) + { + if ( wxIsPathSeparator(*p) ) + indent += _T(" "); + } + + printf("%s%s\n", indent.c_str(), name.c_str()); + + return wxDIR_CONTINUE; + } +}; + +static void TestDirTraverse() +{ + puts("*** Testing wxDir::Traverse() ***"); + + // enum all files + wxArrayString files; + size_t n = wxDir::GetAllFiles(TESTDIR, &files); + printf("There are %u files under '%s'\n", n, TESTDIR); + if ( n > 1 ) + { + printf("First one is '%s'\n", files[0u]); + printf(" last one is '%s'\n", files[n - 1]); + } + + // enum again with custom traverser + wxDir dir(TESTDIR); + DirPrintTraverser traverser; + dir.Traverse(traverser, _T(""), wxDIR_DIRS | wxDIR_HIDDEN); +} + #endif // TEST_DIR // ---------------------------------------------------------------------------- @@ -4601,7 +4656,9 @@ int main(int argc, char **argv) #endif // TEST_ARRAYS #ifdef TEST_DIR - TestDirEnum(); + if ( 0 ) + TestDirEnum(); + TestDirTraverse(); #endif // TEST_DIR #ifdef TEST_DLLLOADER diff --git a/src/common/dircmn.cpp b/src/common/dircmn.cpp new file mode 100644 index 0000000000..c39290a58c --- /dev/null +++ b/src/common/dircmn.cpp @@ -0,0 +1,162 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: src/common/dircmn.cpp +// Purpose: wxDir methods common to all implementations +// Author: Vadim Zeitlin +// Modified by: +// Created: 19.05.01 +// RCS-ID: $Id$ +// Copyright: (c) 2001 Vadim Zeitlin +// License: wxWindows license +/////////////////////////////////////////////////////////////////////////////// + +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +/* this is done in platform-specific files +#ifdef __GNUG__ + #pragma implementation "dir.h" +#endif +*/ + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/string.h" + #include "wx/log.h" + #include "wx/intl.h" +#endif //WX_PRECOMP + +#include "wx/dir.h" + +// ============================================================================ +// implementation +// ============================================================================ + +// ---------------------------------------------------------------------------- +// wxDir::Traverse() +// ---------------------------------------------------------------------------- + +size_t wxDir::Traverse(wxDirTraverser& sink, + const wxString& filespec, + int flags) const +{ + wxCHECK_MSG( IsOpened(), (size_t)-1, + _T("dir must be opened before traversing it") ); + + // the total number of files found + size_t nFiles = 0; + + // the name of this dir with path delimiter at the end + wxString prefix = GetName(); + prefix += wxFILE_SEP_PATH; + + // first, recurse into subdirs + if ( flags & wxDIR_DIRS ) + { + wxString dirname; + bool cont = GetFirst(&dirname, _T(""), wxDIR_DIRS | wxDIR_HIDDEN); + while ( cont ) + { + wxDirTraverseResult res = sink.OnDir(prefix + dirname); + + if ( res == wxDIR_STOP ) + break; + + if ( res == wxDIR_CONTINUE ) + { + wxDir subdir(prefix + dirname); + if ( subdir.IsOpened() ) + { + nFiles += subdir.Traverse(sink, filespec, flags); + } + } + else + { + wxASSERT_MSG( res == wxDIR_IGNORE, + _T("unexpected OnDir() return value") ); + } + + cont = GetNext(&dirname); + } + } + + // now enum our own files + if ( flags & wxDIR_FILES ) + { + flags &= ~wxDIR_DIRS; + + wxString filename; + bool cont = GetFirst(&filename, filespec, flags); + while ( cont ) + { + wxDirTraverseResult res = sink.OnFile(prefix + filename); + if ( res == wxDIR_STOP ) + break; + + wxASSERT_MSG( res == wxDIR_CONTINUE, + _T("unexpected OnFile() return value") ); + + nFiles++; + + cont = GetNext(&filename); + } + } + + return nFiles; +} + +// ---------------------------------------------------------------------------- +// wxDir::GetAllFiles() +// ---------------------------------------------------------------------------- + +class wxDirTraverserSimple : public wxDirTraverser +{ +public: + wxDirTraverserSimple(wxArrayString& files) : m_files(files) { } + + virtual wxDirTraverseResult OnFile(const wxString& filename) + { + m_files.Add(filename); + return wxDIR_CONTINUE; + } + + virtual wxDirTraverseResult OnDir(const wxString& WXUNUSED(dirname)) + { + return wxDIR_CONTINUE; + } + +private: + wxArrayString& m_files; +}; + +/* static */ +size_t wxDir::GetAllFiles(const wxString& dirname, + wxArrayString *files, + const wxString& filespec, + int flags) +{ + wxCHECK_MSG( files, (size_t)-1, _T("NULL pointer in wxDir::GetAllFiles") ); + + size_t nFiles = 0; + + wxDir dir(dirname); + if ( dir.IsOpened() ) + { + wxDirTraverserSimple traverser(*files); + + nFiles += dir.Traverse(traverser, filespec, flags); + } + + return nFiles; +} + diff --git a/src/msw/dir.cpp b/src/msw/dir.cpp index adfa2dd7e3..7d505e56bc 100644 --- a/src/msw/dir.cpp +++ b/src/msw/dir.cpp @@ -215,6 +215,8 @@ public: void Rewind(); bool Read(wxString *filename); + const wxString& GetName() const { return m_dirname; } + private: FIND_DATA m_finddata; @@ -403,6 +405,28 @@ bool wxDir::IsOpened() const return m_data != NULL; } +wxString wxDir::GetName() const +{ + wxString name; + if ( m_data ) + { + name = M_DIR->GetName(); + if ( !name.empty() ) + { + // bring to canonical Windows form + name.Replace(_T("/"), _T("\\")); + + if ( name.Last() == _T('\\') ) + { + // chop off the last (back)slash + name.Truncate(name.length() - 1); + } + } + } + + return name; +} + wxDir::~wxDir() { delete M_DIR; diff --git a/src/unix/dir.cpp b/src/unix/dir.cpp index 121ccda391..13b8d2a68a 100644 --- a/src/unix/dir.cpp +++ b/src/unix/dir.cpp @@ -65,6 +65,8 @@ public: void Rewind() { rewinddir(m_dir); } bool Read(wxString *filename); + const wxString& GetName() const { return m_dirname; } + private: DIR *m_dir; @@ -228,6 +230,22 @@ bool wxDir::IsOpened() const return m_data != NULL; } +wxString wxDir::GetName() const +{ + wxString name; + if ( m_data ) + { + name = M_DIR->GetName(); + if ( !name.empty() && (name.Last() == _T('/')) ) + { + // chop off the last (back)slash + name.Truncate(name.length() - 1); + } + } + + return name; +} + wxDir::~wxDir() { delete M_DIR;