From 23b8a2629903027852d7506162b7b0f44bd9f912 Mon Sep 17 00:00:00 2001
From: Julian Smart <julian@anthemion.co.uk>
Date: Fri, 18 Aug 2006 15:30:28 +0000
Subject: [PATCH] Applied patch [ 1531615 ] size support for wxFileName

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@40659 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
---
 docs/latex/wx/dir.tex      | 21 +++++++++-
 docs/latex/wx/filename.tex | 27 +++++++++++++
 include/wx/dir.h           |  3 ++
 src/common/dircmn.cpp      | 71 +++++++++++++++++++++++++++++++++
 src/common/filename.cpp    | 82 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 203 insertions(+), 1 deletion(-)

diff --git a/docs/latex/wx/dir.tex b/docs/latex/wx/dir.tex
index efc0792a5b..c9b6a29d53 100644
--- a/docs/latex/wx/dir.tex
+++ b/docs/latex/wx/dir.tex
@@ -137,7 +137,7 @@ See also: \helpref{Traverse}{wxdirtraverse}
 \constfunc{bool}{GetFirst}{\param{wxString* }{filename}, \param{const wxString\& }{filespec = wxEmptyString}, \param{int }{flags = wxDIR\_DEFAULT}}
 
 Start enumerating all files matching {\it filespec} (or all files if it is
-empty) and flags, return true on success.
+empty) and {\it flags}, return \true on success.
 
 
 \membersection{wxDir::GetName}\label{wxdirgetname}
@@ -156,6 +156,25 @@ Continue enumerating files which satisfy the criteria specified by the last
 call to \helpref{GetFirst}{wxdirgetfirst}.
 
 
+\membersection{wxDir::GetTotalSize}\label{wxdirgettotalsize}
+
+\func{static wxULongLong}{GetTotalSize}{\param{const wxString\& }{dir}, \param{wxArrayString* }{filesSkipped = NULL}}
+
+Returns the size (in bytes) of all files recursively found in {\tt dir} or
+{\tt wxInvalidSize} in case of error.
+
+In case it happens that while traversing folders a file's size can not be read,
+that file is added to the {\tt filesSkipped} array, if not \NULL, and then
+skipped.
+This usually happens with some special folders which are locked by the operating system
+or by another process. Remember that when {\tt filesSkipped->GetCount()} is not zero,
+then the returned value is not 100\% accurate and, if the skipped files were big, it could be
+far from real size of the directory.
+
+See also: \helpref{wxFileName::GetHumanReadableSize}{wxfilenamegethumanreadablesize},
+\helpref{wxGetDiskSpace}{wxgetdiskspace}
+
+
 \membersection{wxDir::HasFiles}\label{wxdirhasfiles}
 
 \func{bool}{HasFiles}{\param{const wxString\& }{filespec = wxEmptyString}}
diff --git a/docs/latex/wx/filename.tex b/docs/latex/wx/filename.tex
index 16dc6935b2..311783e728 100644
--- a/docs/latex/wx/filename.tex
+++ b/docs/latex/wx/filename.tex
@@ -513,6 +513,33 @@ This is the same as calling \helpref{GetPath}{wxfilenamegetpath}
 Return the short form of the path (returns identity on non-Windows platforms).
 
 
+\membersection{wxFileName::GetSize}\label{wxfilenamegetsize}
+
+\constfunc{wxULongLong}{GetSize}{\void}
+
+\func{static wxULongLong}{GetSize}{\param{const wxString\& }{filename}}
+
+Returns the size of this file (first form) or the size of the given file (second form).
+If the file does not exist or its size could not be read (because e.g. the file is locked
+by another process) the returned value is {\tt wxInvalidSize}.
+
+
+\membersection{wxFileName::GetHumanReadableSize}\label{wxfilenamegethumanreadablesize}
+
+\constfunc{wxString}{GetHumanReadableSize}{\param{const wxString\& }{failmsg = "Not available"}, \param{int }{precision = 1}}
+
+\func{static wxString}{GetHumanReadableSize}{\param{const wxULongLong\& }{bytes}, \param{const wxString\& }{nullsize = "Not available"}, \param{int }{precision = 1}}
+
+Returns the size of this file (first form) or the given number of bytes (second form)
+in a human-readable form.
+
+If the size could not be retrieved the {\tt failmsg} string is returned (first form).
+If {\tt bytes} is {\tt wxInvalidSize} or zero, then {\tt nullsize} is returned (second form).
+
+In case of success, the returned string is a floating-point number with {\tt precision} decimal digits
+followed by the size unit (B, kB, MB, GB, TB: respectively bytes, kilobytes, megabytes, gigabytes, terabytes).
+
+
 \membersection{wxFileName::GetTimes}\label{wxfilenamegettimes}
 
 \constfunc{bool}{GetTimes}{\param{wxDateTime* }{dtAccess}, \param{wxDateTime* }{dtMod}, \param{wxDateTime* }{dtCreate}}
diff --git a/include/wx/dir.h b/include/wx/dir.h
index 212d0586a2..647a49c2f5 100644
--- a/include/wx/dir.h
+++ b/include/wx/dir.h
@@ -145,6 +145,9 @@ public:
                               const wxString& filespec,
                               int flags = wxDIR_DEFAULT);
 
+    // returns the size of all directories recursively found in given path
+    static wxULongLong GetTotalSize(const wxString &dir, wxArrayString *filesSkipped = NULL);
+
 private:
     friend class wxDirData;
 
diff --git a/src/common/dircmn.cpp b/src/common/dircmn.cpp
index bed42a56a9..1674029b6d 100644
--- a/src/common/dircmn.cpp
+++ b/src/common/dircmn.cpp
@@ -33,6 +33,7 @@
 #endif //WX_PRECOMP
 
 #include "wx/dir.h"
+#include "wx/filename.h"
 
 // ============================================================================
 // implementation
@@ -284,3 +285,73 @@ wxString wxDir::FindFirst(const wxString& dirname,
 
     return wxEmptyString;
 }
+
+
+// ----------------------------------------------------------------------------
+// wxDir::GetTotalSize()
+// ----------------------------------------------------------------------------
+
+class wxDirTraverserSumSize : public wxDirTraverser
+{
+public:
+    wxDirTraverserSumSize() { m_skippedFiles=false; }
+
+    virtual wxDirTraverseResult OnFile(const wxString& filename)
+    {
+        wxULongLong sz = wxFileName::GetSize(filename);
+
+        // wxFileName::GetSize won't use this class again as
+        // we're passing it a file and not a directory;
+        // thus we are sure to avoid an endless loop
+        if (sz == wxInvalidSize)
+        {
+            // if the GetSize() failed (this can happen because e.g. a
+            // file is locked by another process), we can proceed but
+            // we need to at least warn the user that the resulting
+            // final size could be not reliable (if e.g. the locked
+            // file is very big).
+            m_skippedFiles.Add(filename);
+            return wxDIR_CONTINUE;
+        }
+
+        m_sz += sz;
+        return wxDIR_CONTINUE;
+    }
+
+    virtual wxDirTraverseResult OnDir(const wxString& WXUNUSED(dirname))
+    {
+        return wxDIR_CONTINUE;
+    }
+
+    wxULongLong GetTotalSize() const
+        { return m_sz; }
+    wxArrayString &FilesSkipped()
+        { return m_skippedFiles; }
+
+protected:
+    wxULongLong m_sz;
+    wxArrayString m_skippedFiles;
+};
+
+wxULongLong wxDir::GetTotalSize(const wxString &dirname, wxArrayString *filesSkipped)
+{
+    if (!wxDirExists(dirname))
+        return wxInvalidSize;
+
+    // to get the size of this directory and its contents we need
+    // to recursively walk it...
+    wxDir dir(dirname);
+    if ( !dir.IsOpened() )
+        return wxInvalidSize;
+
+    wxDirTraverserSumSize traverser;
+    if (dir.Traverse(traverser) == (size_t)-1 ||
+        traverser.GetTotalSize() == 0)
+        return wxInvalidSize;
+
+    if (filesSkipped)
+        *filesSkipped = traverser.FilesSkipped();
+
+    return traverser.GetTotalSize();
+}
+
diff --git a/src/common/filename.cpp b/src/common/filename.cpp
index 12bc12daa7..1be91db75f 100644
--- a/src/common/filename.cpp
+++ b/src/common/filename.cpp
@@ -139,6 +139,10 @@
 #define MAX_PATH _MAX_PATH
 #endif
 
+
+wxULongLong wxInvalidSize = (unsigned)-1;
+
+
 // ----------------------------------------------------------------------------
 // private classes
 // ----------------------------------------------------------------------------
@@ -2019,6 +2023,84 @@ bool wxFileName::GetTimes(wxDateTime *dtAccess,
 
 #endif // wxUSE_DATETIME
 
+
+// ----------------------------------------------------------------------------
+// file size functions
+// ----------------------------------------------------------------------------
+
+/* static */
+wxULongLong wxFileName::GetSize(const wxString &filename)
+{
+    if (!wxFileExists(filename))
+        return wxInvalidSize;
+
+#ifdef __WIN32__
+    wxFileHandle f(filename, wxFileHandle::Read);
+    if (!f.IsOk())
+        return wxInvalidSize;
+
+    DWORD lpFileSizeHigh;
+    DWORD ret = GetFileSize(f, &lpFileSizeHigh);
+    if (ret == INVALID_FILE_SIZE)
+        return wxInvalidSize;
+
+    // compose the low-order and high-order byte sizes
+    return wxULongLong(ret | (lpFileSizeHigh << sizeof(WORD)*2));
+
+#else           // ! __WIN32__
+
+    wxStructStat st;
+#ifndef wxNEED_WX_UNISTD_H
+    if (wxStat( filename.fn_str() , &st) != 0)
+#else
+    if (wxStat( filename, &st) != 0)
+#endif
+        return wxInvalidSize;
+    return wxULongLong(st.st_size);
+#endif
+}
+
+/* static */
+wxString wxFileName::GetHumanReadableSize(const wxULongLong &bs,
+                                          const wxString &nullsize,
+                                          int precision)
+{
+    static const double KILOBYTESIZE = 1024.0;
+    static const double MEGABYTESIZE = 1024.0*KILOBYTESIZE;
+    static const double GIGABYTESIZE = 1024.0*MEGABYTESIZE;
+    static const double TERABYTESIZE = 1024.0*GIGABYTESIZE;
+
+    if (bs == 0 || bs == wxInvalidSize)
+        return nullsize;
+
+    double bytesize = bs.ToDouble();
+    if (bytesize < KILOBYTESIZE)
+        return wxString::Format(_("%s B"), bs.ToString().c_str());
+    if (bytesize < MEGABYTESIZE)
+        return wxString::Format(_("%.*f kB"), precision, bytesize/KILOBYTESIZE);
+    if (bytesize < GIGABYTESIZE)
+        return wxString::Format(_("%.*f MB"), precision, bytesize/MEGABYTESIZE);
+    if (bytesize < TERABYTESIZE)
+        return wxString::Format(_("%.*f GB"), precision, bytesize/GIGABYTESIZE);
+
+    return wxString::Format(_("%.*f TB"), precision, bytesize/TERABYTESIZE);
+}
+
+wxULongLong wxFileName::GetSize() const
+{
+    return GetSize(GetFullPath());
+}
+
+wxString wxFileName::GetHumanReadableSize(const wxString &failmsg, int precision) const
+{
+    return GetHumanReadableSize(GetSize(), failmsg, precision);
+}
+
+
+// ----------------------------------------------------------------------------
+// Mac-specific functions
+// ----------------------------------------------------------------------------
+
 #ifdef __WXMAC__
 
 const short kMacExtensionMaxLength = 16 ;
-- 
2.47.2

