diff --git a/docs/changes.txt b/docs/changes.txt index 8ad5b40a3b..ac8bcb5aed 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -438,6 +438,7 @@ All: - Correct bugs when using wxTextInputStream with wxConvAuto (Leon Buikstra). - Don't crash when input is empty in wxFileConfig ctor (Lukasz Michalski). - Correct wxSocket::Peek() to not block (Anders Larsen). +- Added IEC and SI units support to GetHumanReadableSize() (Julien Weinzorn). Unix: diff --git a/include/wx/filename.h b/include/wx/filename.h index 1a6abd73eb..cc8806b275 100644 --- a/include/wx/filename.h +++ b/include/wx/filename.h @@ -65,6 +65,15 @@ enum wxPathFormat wxPATH_MAX // Not a valid value for specifying path format }; +// different convention that may be used with GetHumanReadableSize() +enum wxSizeConvention +{ + wxSIZE_CONV_TRADIONAL, // 1024 bytes = 1 KB + wxSIZE_CONV_IEC, // 1024 bytes = 1 KiB + wxSIZE_CONV_SI // 1000 bytes = 1 KB +}; + + // the kind of normalization to do with the file name: these values can be // or'd together to perform several operations at once enum wxPathNormalize @@ -540,11 +549,15 @@ public: static wxULongLong GetSize(const wxString &file); // returns the size in a human readable form - wxString GetHumanReadableSize(const wxString &nullsize = wxGetTranslation(wxT("Not available")), - int precision = 1) const; - static wxString GetHumanReadableSize(const wxULongLong &sz, - const wxString &nullsize = wxGetTranslation(wxT("Not available")), - int precision = 1); + wxString + GetHumanReadableSize(const wxString& nullsize = _("Not available"), + int precision = 1, + wxSizeConvention conv = wxSIZE_CONV_TRADIONAL) const; + static wxString + GetHumanReadableSize(const wxULongLong& sz, + const wxString& nullsize = _("Not available"), + int precision = 1, + wxSizeConvention conv = wxSIZE_CONV_TRADIONAL); #endif // wxUSE_LONGLONG diff --git a/interface/wx/filename.h b/interface/wx/filename.h index 3f40781ab7..17c783f03c 100644 --- a/interface/wx/filename.h +++ b/interface/wx/filename.h @@ -28,6 +28,25 @@ enum wxPathFormat wxPATH_MAX //!< Not a valid value for specifying path format }; +/** + Different conventions for human readable sizes. + + @see wxFileName::GetHumanReadableSize(). + + @since 2.9.1 +*/ +enum wxSizeConvention +{ + /// 1000 bytes = 1KiB. + wxSIZE_CONV_REAL_SI, + + /// 1000 bytes = 1KB. + wxSIZE_CONV_TRAD_1000, + + /// 1024 bytes = 1KB. + wxSIZE_CONV_TRAD_1024 +}; + /** The kind of normalization to do with the file name: these values can be @@ -522,30 +541,41 @@ public: */ static wxString GetHomeDir(); + //@{ /** - Returns the size of the file in a human-readable form. + Returns the representation of the file size in a human-readable form. - If the size could not be retrieved the @c failmsg string - is returned. In case of success, the returned string is - a floating-point number with @c precision decimal digits - followed by the size unit (B, kB, MB, GB, TB: respectively - bytes, kilobytes, megabytes, gigabytes, terabytes). + In the first version, the size of this file is used. In the second one, + the specified size @a bytes is used. + + If the file size could not be retrieved or @a bytes is ::wxInvalidSize + or zero, the @c failmsg string is returned. + + Otherwise the returned string is a floating-point number with @c + precision decimal digits followed by the abbreviation of the unit used. + By default the traditional, although incorrect, convention of using SI + units for multiples of 1024 is used, i.e. returned string will use + suffixes of B, KB, MB, GB, TB for bytes, kilobytes, megabytes, + gigabytes and terabytes respectively. With the IEC convention the names + of the units are changed to B, KiB, MiB, GiB and TiB fofr bytes, + kibibytes, mebibyes, gibibytes and tebibytes. Finally, with SI + convention the same B, KB, MB, GB and TB suffixes are used but in their + correct SI meaning, i.e. as multiples of 1000 and not 1024. + + Support for the different size conventions is new in wxWidgets 2.9.1, + in previous versions only the traditional convention was implemented. */ - wxString GetHumanReadableSize(const wxString& failmsg = "Not available", - int precision = 1) const; + wxString + GetHumanReadableSize(const wxString& failmsg = _("Not available"), + int precision = 1, + wxSizeConvention conv = wxSIZE_CONV_TRADIONAL) const; - /** - Returns the size of the given number of bytes in a human-readable form. - - If @a bytes is ::wxInvalidSize or zero, then @a nullsize is returned. - - In case of success, the returned string is a floating-point number with - @a precision decimal digits followed by the size unit (B, kB, MB, GB, - TB: respectively bytes, kilobytes, megabytes, gigabytes, terabytes). - */ - static wxString GetHumanReadableSize(const wxULongLong& bytes, - const wxString& nullsize = "Not available", - int precision = 1); + static wxString + GetHumanReadableSize(const wxULongLong& bytes, + const wxString& nullsize = _("Not available"), + int precision = 1, + wxSizeConvention conv = wxSIZE_CONV_REAL_SI); + //@} /** Return the long form of the path (returns identity on non-Windows platforms). diff --git a/src/common/filename.cpp b/src/common/filename.cpp index 917c89de0f..8fa76ef7bf 100644 --- a/src/common/filename.cpp +++ b/src/common/filename.cpp @@ -2647,27 +2647,53 @@ wxULongLong wxFileName::GetSize(const wxString &filename) /* static */ wxString wxFileName::GetHumanReadableSize(const wxULongLong &bs, const wxString &nullsize, - int precision) + int precision, + wxSizeConvention conv) { - 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) + // deal with trivial case first + 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); + // depending on the convention used the multiplier may be either 1000 or + // 1024 and the binary infix may be empty (for "KB") or "i" (for "KiB") + double multiplier; + wxString biInfix; - return wxString::Format(_("%.*f TB"), precision, bytesize/TERABYTESIZE); + switch ( conv ) + { + case wxSIZE_CONV_IEC: + biInfix = "i"; + // fall through + + case wxSIZE_CONV_TRADIONAL: + multiplier = 1024.; + break; + + case wxSIZE_CONV_SI: + multiplier = 1000; + break; + } + + const double kiloByteSize = multiplier; + const double megaByteSize = multiplier * kiloByteSize; + const double gigaByteSize = multiplier * megaByteSize; + const double teraByteSize = multiplier * gigaByteSize; + + const double bytesize = bs.ToDouble(); + + wxString result; + if ( bytesize < kiloByteSize ) + result.Printf("%s B", bs.ToString()); + else if ( bytesize < megaByteSize ) + result.Printf("%.*f K%sB", precision, bytesize/kiloByteSize, biInfix); + else if (bytesize < gigaByteSize) + result.Printf("%.*f M%sB", precision, bytesize/megaByteSize, biInfix); + else if (bytesize < teraByteSize) + result.Printf("%.*f G%sB", precision, bytesize/gigaByteSize, biInfix); + else + result.Printf("%.*f T%sB", precision, bytesize/teraByteSize, biInfix); + + return result; } wxULongLong wxFileName::GetSize() const @@ -2675,9 +2701,11 @@ wxULongLong wxFileName::GetSize() const return GetSize(GetFullPath()); } -wxString wxFileName::GetHumanReadableSize(const wxString &failmsg, int precision) const +wxString wxFileName::GetHumanReadableSize(const wxString& failmsg, + int precision, + wxSizeConvention conv) const { - return GetHumanReadableSize(GetSize(), failmsg, precision); + return GetHumanReadableSize(GetSize(), failmsg, precision, conv); } #endif // wxUSE_LONGLONG diff --git a/tests/filename/filenametest.cpp b/tests/filename/filenametest.cpp index c1a9c5e6d4..cdeaef074a 100644 --- a/tests/filename/filenametest.cpp +++ b/tests/filename/filenametest.cpp @@ -125,6 +125,7 @@ private: CPPUNIT_TEST( TestStrip ); CPPUNIT_TEST( TestNormalize ); CPPUNIT_TEST( TestReplace ); + CPPUNIT_TEST( TestGetHumanReadable ); #ifdef __WINDOWS__ CPPUNIT_TEST( TestShortLongPath ); #endif // __WINDOWS__ @@ -139,6 +140,7 @@ private: void TestStrip(); void TestNormalize(); void TestReplace(); + void TestGetHumanReadable(); #ifdef __WINDOWS__ void TestShortLongPath(); #endif // __WINDOWS__ @@ -478,6 +480,39 @@ void FileNameTestCase::TestReplace() fn.GetFullPath(wxPATH_UNIX) ); } +void FileNameTestCase::TestGetHumanReadable() +{ + static const struct TestData + { + const char *result; + wxULongLong size; + int prec; + wxSizeConvention conv; + } testData[] = + { + { "NA", 0, 1, wxSIZE_CONV_TRADIONAL }, + { "2.0 KB", 2000, 1, wxSIZE_CONV_TRADIONAL }, + { "1.953 KiB", 2000, 3, wxSIZE_CONV_IEC }, + { "2.000 KB", 2000, 3, wxSIZE_CONV_SI }, + { "297 KB", 304351, 0, wxSIZE_CONV_TRADIONAL }, + { "304 KB", 304351, 0, wxSIZE_CONV_SI }, + }; + + for ( unsigned n = 0; n < WXSIZEOF(testData); n++ ) + { + const TestData& td = testData[n]; + + CPPUNIT_ASSERT_EQUAL + ( + td.result, + wxFileName::GetHumanReadableSize(td.size, "NA", td.prec, td.conv) + ); + } + + // also test the default convention value + CPPUNIT_ASSERT_EQUAL( "1.4 MB", wxFileName::GetHumanReadableSize(1512993, "") ); +} + void FileNameTestCase::TestStrip() { CPPUNIT_ASSERT_EQUAL( "", wxFileName::StripExtension("") );