diff --git a/modules/juce_core/files/juce_File.cpp b/modules/juce_core/files/juce_File.cpp index ce0d092338..416223ed58 100644 --- a/modules/juce_core/files/juce_File.cpp +++ b/modules/juce_core/files/juce_File.cpp @@ -688,27 +688,21 @@ FileInputStream* File::createInputStream() const FileOutputStream* File::createOutputStream (const int bufferSize) const { - ScopedPointer out (new FileOutputStream (*this, bufferSize)); + ScopedPointer out (new FileOutputStream (*this, bufferSize)); - if (out->failedToOpen()) - return nullptr; - - return out.release(); + return out->failedToOpen() ? nullptr + : out.release(); } //============================================================================== bool File::appendData (const void* const dataToAppend, const int numberOfBytes) const { - if (numberOfBytes > 0) - { - FileOutputStream out (*this, 8192); + if (numberOfBytes <= 0) + return true; - return (! out.failedToOpen()) - && out.write (dataToAppend, numberOfBytes); - } - - return true; + FileOutputStream out (*this, 8192); + return out.openedOk() && out.write (dataToAppend, numberOfBytes); } bool File::replaceWithData (const void* const dataToWrite, @@ -728,15 +722,13 @@ bool File::appendText (const String& text, const bool asUnicode, const bool writeUnicodeHeaderBytes) const { - const ScopedPointer out (createOutputStream()); + FileOutputStream out (*this); - if (out != nullptr) - { - out->writeText (text, asUnicode, writeUnicodeHeaderBytes); - return true; - } + if (out.failedToOpen()) + return false; - return false; + out.writeText (text, asUnicode, writeUnicodeHeaderBytes); + return true; } bool File::replaceWithText (const String& textToWrite, diff --git a/modules/juce_core/files/juce_FileInputStream.cpp b/modules/juce_core/files/juce_FileInputStream.cpp index 1c69e532e5..714573f060 100644 --- a/modules/juce_core/files/juce_FileInputStream.cpp +++ b/modules/juce_core/files/juce_FileInputStream.cpp @@ -53,6 +53,8 @@ int64 FileInputStream::getTotalLength() int FileInputStream::read (void* buffer, int bytesToRead) { + jassert (buffer != nullptr && bytesToRead >= 0); + if (needToSeek) { if (juce_fileSetPosition (fileHandle, currentPosition) < 0) diff --git a/modules/juce_core/files/juce_FileInputStream.h b/modules/juce_core/files/juce_FileInputStream.h index 82741365a7..1e45ff14c6 100644 --- a/modules/juce_core/files/juce_FileInputStream.h +++ b/modules/juce_core/files/juce_FileInputStream.h @@ -58,7 +58,18 @@ public: The result will be ok if the file opened successfully. If an error occurs while opening or reading from the file, this will contain an error message. */ - Result getStatus() const { return status; } + const Result& getStatus() const noexcept { return status; } + + /** Returns true if the stream couldn't be opened for some reason. + @see getResult() + */ + bool failedToOpen() const noexcept { return status.failed(); } + + /** Returns true if the stream opened without problems. + @see getResult() + */ + bool openedOk() const noexcept { return status.wasOk(); } + //============================================================================== int64 getTotalLength(); @@ -67,7 +78,6 @@ public: int64 getPosition(); bool setPosition (int64 pos); - private: //============================================================================== File file; diff --git a/modules/juce_core/files/juce_FileOutputStream.cpp b/modules/juce_core/files/juce_FileOutputStream.cpp index 3d220bac84..fcfb0d3e4d 100644 --- a/modules/juce_core/files/juce_FileOutputStream.cpp +++ b/modules/juce_core/files/juce_FileOutputStream.cpp @@ -84,6 +84,8 @@ void FileOutputStream::flush() bool FileOutputStream::write (const void* const src, const int numBytes) { + jassert (src != nullptr && numBytes >= 0); + if (bytesInBuffer + numBytes < bufferSize) { memcpy (buffer + bytesInBuffer, src, (size_t) numBytes); diff --git a/modules/juce_core/files/juce_FileOutputStream.h b/modules/juce_core/files/juce_FileOutputStream.h index 0d873d6878..870c3b14b4 100644 --- a/modules/juce_core/files/juce_FileOutputStream.h +++ b/modules/juce_core/files/juce_FileOutputStream.h @@ -51,9 +51,6 @@ public: use File::deleteFile() before opening the stream, or use setPosition(0) after it's opened (although this won't truncate the file). - It's better to use File::createOutputStream() to create one of these, rather - than using the class directly. - @see TemporaryFile */ FileOutputStream (const File& fileToWriteTo, @@ -71,12 +68,17 @@ public: The result will be ok if the file opened successfully. If an error occurs while opening or writing to the file, this will contain an error message. */ - Result getStatus() const { return status; } + const Result& getStatus() const noexcept { return status; } /** Returns true if the stream couldn't be opened for some reason. @see getResult() */ - bool failedToOpen() const { return status.failed(); } + bool failedToOpen() const noexcept { return status.failed(); } + + /** Returns true if the stream opened without problems. + @see getResult() + */ + bool openedOk() const noexcept { return status.wasOk(); } //============================================================================== void flush(); diff --git a/modules/juce_core/native/juce_android_Network.cpp b/modules/juce_core/native/juce_android_Network.cpp index f2ba34348b..db05fcd422 100644 --- a/modules/juce_core/native/juce_android_Network.cpp +++ b/modules/juce_core/native/juce_android_Network.cpp @@ -136,6 +136,8 @@ public: int read (void* buffer, int bytesToRead) { + jassert (buffer != nullptr && bytesToRead >= 0); + if (stream == nullptr) return 0; diff --git a/modules/juce_core/native/juce_mac_Network.mm b/modules/juce_core/native/juce_mac_Network.mm index e576b6bbdb..ff12a41835 100644 --- a/modules/juce_core/native/juce_mac_Network.mm +++ b/modules/juce_core/native/juce_mac_Network.mm @@ -361,6 +361,8 @@ public: int read (void* buffer, int bytesToRead) { + jassert (buffer != nullptr && bytesToRead >= 0); + if (finished || isError()) { return 0; diff --git a/modules/juce_core/native/juce_posix_SharedCode.h b/modules/juce_core/native/juce_posix_SharedCode.h index dd986e9ea0..4a9395ed0e 100644 --- a/modules/juce_core/native/juce_posix_SharedCode.h +++ b/modules/juce_core/native/juce_posix_SharedCode.h @@ -988,6 +988,8 @@ public: int read (void* const dest, const int numBytes) { + jassert (dest != nullptr); + if (readHandle == 0 && childPID != 0) readHandle = fdopen (pipeHandle, "r"); diff --git a/modules/juce_core/native/juce_win32_Network.cpp b/modules/juce_core/native/juce_win32_Network.cpp index d04364562c..2b923f065b 100644 --- a/modules/juce_core/native/juce_win32_Network.cpp +++ b/modules/juce_core/native/juce_win32_Network.cpp @@ -145,6 +145,7 @@ public: int read (void* buffer, int bytesToRead) { + jassert (buffer != nullptr && bytesToRead >= 0); DWORD bytesRead = 0; if (! (finished || isError())) diff --git a/modules/juce_core/streams/juce_BufferedInputStream.cpp b/modules/juce_core/streams/juce_BufferedInputStream.cpp index 7e2571fa63..420fb8c661 100644 --- a/modules/juce_core/streams/juce_BufferedInputStream.cpp +++ b/modules/juce_core/streams/juce_BufferedInputStream.cpp @@ -131,6 +131,8 @@ void BufferedInputStream::ensureBuffered() int BufferedInputStream::read (void* destBuffer, int maxBytesToRead) { + jassert (destBuffer != nullptr && maxBytesToRead >= 0); + if (position >= bufferStart && position + maxBytesToRead <= lastReadPos) { diff --git a/modules/juce_core/streams/juce_InputStream.cpp b/modules/juce_core/streams/juce_InputStream.cpp index 5879d6eb15..1fd2994d11 100644 --- a/modules/juce_core/streams/juce_InputStream.cpp +++ b/modules/juce_core/streams/juce_InputStream.cpp @@ -208,7 +208,7 @@ int InputStream::readIntoMemoryBlock (MemoryBlock& block, int numBytes) String InputStream::readEntireStreamAsString() { MemoryOutputStream mo; - mo.writeFromInputStream (*this, -1); + mo << *this; return mo.toString(); } diff --git a/modules/juce_core/streams/juce_InputStream.h b/modules/juce_core/streams/juce_InputStream.h index 3305578a60..a9bd62ec76 100644 --- a/modules/juce_core/streams/juce_InputStream.h +++ b/modules/juce_core/streams/juce_InputStream.h @@ -59,16 +59,16 @@ public: virtual bool isExhausted() = 0; //============================================================================== - /** Reads a set of bytes from the stream into a memory buffer. + /** Reads some data from the stream into a memory buffer. This is the only read method that subclasses actually need to implement, as the InputStream base class implements the other read methods in terms of this one (although it's often more efficient for subclasses to implement them directly). - @param destBuffer the destination buffer for the data + @param destBuffer the destination buffer for the data. This must not be null. @param maxBytesToRead the maximum number of bytes to read - make sure the memory block passed in is big enough to contain this - many bytes. + many bytes. This value must not be negative. @returns the actual number of bytes that were read, which may be less than maxBytesToRead if the stream is exhausted before it gets that far diff --git a/modules/juce_core/streams/juce_MemoryInputStream.cpp b/modules/juce_core/streams/juce_MemoryInputStream.cpp index 1f621e4510..6382afbe10 100644 --- a/modules/juce_core/streams/juce_MemoryInputStream.cpp +++ b/modules/juce_core/streams/juce_MemoryInputStream.cpp @@ -65,7 +65,8 @@ int64 MemoryInputStream::getTotalLength() int MemoryInputStream::read (void* const buffer, const int howMany) { - jassert (howMany >= 0); + jassert (buffer != nullptr && howMany >= 0); + const int num = jmin (howMany, (int) (dataSize - position)); if (num <= 0) return 0; diff --git a/modules/juce_core/streams/juce_MemoryOutputStream.cpp b/modules/juce_core/streams/juce_MemoryOutputStream.cpp index ae6e3ba273..4ff05f9c2f 100644 --- a/modules/juce_core/streams/juce_MemoryOutputStream.cpp +++ b/modules/juce_core/streams/juce_MemoryOutputStream.cpp @@ -81,6 +81,8 @@ void MemoryOutputStream::prepareToWrite (int numBytes) bool MemoryOutputStream::write (const void* const buffer, int howMany) { + jassert (buffer != nullptr && howMany >= 0); + if (howMany > 0) { prepareToWrite (howMany); diff --git a/modules/juce_core/streams/juce_OutputStream.cpp b/modules/juce_core/streams/juce_OutputStream.cpp index c014213a5f..b532e8be16 100644 --- a/modules/juce_core/streams/juce_OutputStream.cpp +++ b/modules/juce_core/streams/juce_OutputStream.cpp @@ -298,11 +298,13 @@ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const MemoryBlock& OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const File& fileToRead) { - const ScopedPointer in (fileToRead.createInputStream()); - - if (in != nullptr) - stream.writeFromInputStream (*in, -1); + FileInputStream in (fileToRead); + return stream << in; +} +OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, InputStream& streamToRead) +{ + stream.writeFromInputStream (streamToRead, -1); return stream; } diff --git a/modules/juce_core/streams/juce_OutputStream.h b/modules/juce_core/streams/juce_OutputStream.h index 6531120887..36f2dcbe0f 100644 --- a/modules/juce_core/streams/juce_OutputStream.h +++ b/modules/juce_core/streams/juce_OutputStream.h @@ -83,10 +83,12 @@ public: that needs to be overloaded - the base class has methods for writing other types of data which use this to do the work. + @param dataToWrite the target buffer to receive the data. This must not be null. + @param numberOfBytes the number of bytes to write. This must not be negative. @returns false if the write operation fails for some reason */ virtual bool write (const void* dataToWrite, - int howManyBytes) = 0; + int numberOfBytes) = 0; //============================================================================== /** Writes a single byte to the stream. @@ -243,6 +245,9 @@ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const MemoryBlock& /** Writes the contents of a file to a stream. */ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const File& fileToRead); +/** Writes the complete contents of an input stream to an output stream. */ +OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, InputStream& streamToRead); + /** Writes a new-line to a stream. You can use the predefined symbol 'newLine' to invoke this, e.g. @code diff --git a/modules/juce_core/streams/juce_SubregionStream.cpp b/modules/juce_core/streams/juce_SubregionStream.cpp index 0288a4e499..9cef91cc9e 100644 --- a/modules/juce_core/streams/juce_SubregionStream.cpp +++ b/modules/juce_core/streams/juce_SubregionStream.cpp @@ -61,6 +61,8 @@ bool SubregionStream::setPosition (int64 newPosition) int SubregionStream::read (void* destBuffer, int maxBytesToRead) { + jassert (destBuffer != nullptr && maxBytesToRead >= 0); + if (lengthOfSourceStream < 0) { return source->read (destBuffer, maxBytesToRead); diff --git a/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp b/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp index 57293bbce2..7691fa2af9 100644 --- a/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp +++ b/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp @@ -42,14 +42,12 @@ class GZIPCompressorOutputStream::GZIPCompressorHelper { public: GZIPCompressorHelper (const int compressionLevel, const int windowBits) - : data (nullptr), - dataSize (0), + : buffer ((size_t) gzipCompBufferSize), compLevel (compressionLevel), strategy (0), - setParams (true), + isFirstDeflate (true), streamIsValid (false), - finished (false), - shouldFinish (false) + finished (false) { using namespace zlibNamespace; zerostruct (stream); @@ -61,67 +59,74 @@ public: ~GZIPCompressorHelper() { - using namespace zlibNamespace; if (streamIsValid) - deflateEnd (&stream); + zlibNamespace::deflateEnd (&stream); } - bool needsInput() const noexcept + bool write (const uint8* data, int dataSize, OutputStream& destStream) { - return dataSize <= 0; + // When you call flush() on a gzip stream, the stream is closed, and you can + // no longer continue to write data to it! + jassert (! finished); + + while (dataSize > 0) + if (! doNextBlock (data, dataSize, destStream, Z_NO_FLUSH)) + return false; + + return true; } - void setInput (const uint8* const newData, const size_t size) noexcept + void finish (OutputStream& destStream) { - data = newData; - dataSize = size; + const uint8* data = nullptr; + int dataSize = 0; + + while (! finished) + doNextBlock (data, dataSize, destStream, Z_FINISH); } - int doNextBlock (uint8* const dest, const int destSize) noexcept +private: + enum { gzipCompBufferSize = 32768 }; + + HeapBlock buffer; + zlibNamespace::z_stream stream; + int compLevel, strategy; + bool isFirstDeflate, streamIsValid, finished; + + bool doNextBlock (const uint8*& data, int& dataSize, OutputStream& destStream, const int flushMode) { using namespace zlibNamespace; if (streamIsValid) { - stream.next_in = const_cast (data); - stream.next_out = dest; - stream.avail_in = (z_uInt) dataSize; - stream.avail_out = (z_uInt) destSize; + stream.next_in = const_cast (data); + stream.next_out = buffer; + stream.avail_in = (z_uInt) dataSize; + stream.avail_out = (z_uInt) gzipCompBufferSize; - const int result = setParams ? deflateParams (&stream, compLevel, strategy) - : deflate (&stream, shouldFinish ? Z_FINISH : Z_NO_FLUSH); - - setParams = false; + const int result = isFirstDeflate ? deflateParams (&stream, compLevel, strategy) + : deflate (&stream, flushMode); + isFirstDeflate = false; switch (result) { - case Z_STREAM_END: - finished = true; - // Deliberate fall-through.. - case Z_OK: - data += dataSize - stream.avail_in; - dataSize = stream.avail_in; + case Z_STREAM_END: + finished = true; + // Deliberate fall-through.. + case Z_OK: + { + data += dataSize - stream.avail_in; + dataSize = (int) stream.avail_in; + const int bytesDone = (int) (gzipCompBufferSize - stream.avail_out); + return bytesDone <= 0 || destStream.write (buffer, bytesDone); + } - return (int) (destSize - stream.avail_out); - - default: - break; + default: + break; } } - return 0; + return false; } - - enum { gzipCompBufferSize = 32768 }; - -private: - zlibNamespace::z_stream stream; - const uint8* data; - size_t dataSize; - int compLevel, strategy; - bool setParams, streamIsValid; - -public: - bool finished, shouldFinish; }; //============================================================================== @@ -129,9 +134,10 @@ GZIPCompressorOutputStream::GZIPCompressorOutputStream (OutputStream* const dest int compressionLevel, const bool deleteDestStream, const int windowBits) - : destStream (destStream_, deleteDestStream), - buffer ((size_t) GZIPCompressorHelper::gzipCompBufferSize) + : destStream (destStream_, deleteDestStream) { + jassert (destStream_ != nullptr); + if (compressionLevel < 1 || compressionLevel > 9) compressionLevel = -1; @@ -140,48 +146,20 @@ GZIPCompressorOutputStream::GZIPCompressorOutputStream (OutputStream* const dest GZIPCompressorOutputStream::~GZIPCompressorOutputStream() { - flushInternal(); -} - -//============================================================================== -void GZIPCompressorOutputStream::flushInternal() -{ - if (! helper->finished) - { - helper->shouldFinish = true; - - while (! helper->finished) - doNextBlock(); - } - - destStream->flush(); + flush(); } void GZIPCompressorOutputStream::flush() { - flushInternal(); + helper->finish (*destStream); + destStream->flush(); } bool GZIPCompressorOutputStream::write (const void* destBuffer, int howMany) { - if (! helper->finished) - { - helper->setInput (static_cast (destBuffer), (size_t) howMany); + jassert (destBuffer != nullptr && howMany >= 0); - while (! helper->needsInput()) - { - if (! doNextBlock()) - return false; - } - } - - return true; -} - -bool GZIPCompressorOutputStream::doNextBlock() -{ - const int len = helper->doNextBlock (buffer, (int) GZIPCompressorHelper::gzipCompBufferSize); - return len <= 0 || destStream->write (buffer, len); + return helper->write (static_cast (destBuffer), howMany, *destStream); } int64 GZIPCompressorOutputStream::getPosition() @@ -231,7 +209,7 @@ public: MemoryInputStream compressedInput (compressed.getData(), compressed.getDataSize(), false); GZIPDecompressorInputStream unzipper (compressedInput); - uncompressed.writeFromInputStream (unzipper, -1); + uncompressed << unzipper; } expectEquals ((int) uncompressed.getDataSize(), diff --git a/modules/juce_core/zip/juce_GZIPCompressorOutputStream.h b/modules/juce_core/zip/juce_GZIPCompressorOutputStream.h index ba95ebb85e..9ac10b06ca 100644 --- a/modules/juce_core/zip/juce_GZIPCompressorOutputStream.h +++ b/modules/juce_core/zip/juce_GZIPCompressorOutputStream.h @@ -35,6 +35,10 @@ /** A stream which uses zlib to compress the data written into it. + Important note: When you call flush() on a GZIPCompressorOutputStream, + the gzip data is closed - this means that no more data can be written to + it, and any subsequent attempts to call write() will cause an assertion. + @see GZIPDecompressorInputStream */ class JUCE_API GZIPCompressorOutputStream : public OutputStream @@ -64,7 +68,13 @@ public: ~GZIPCompressorOutputStream(); //============================================================================== + /** Flushes and closes the stream. + Note that unlike most streams, when you call flush() on a GZIPCompressorOutputStream, + the stream is closed - this means that no more data can be written to it, and any + subsequent attempts to call write() will cause an assertion. + */ void flush(); + int64 getPosition(); bool setPosition (int64 newPosition); bool write (const void* destBuffer, int howMany); @@ -81,12 +91,10 @@ public: private: //============================================================================== OptionalScopedPointer destStream; - HeapBlock buffer; + class GZIPCompressorHelper; friend class ScopedPointer ; ScopedPointer helper; - bool doNextBlock(); - void flushInternal(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GZIPCompressorOutputStream); }; diff --git a/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp b/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp index 3cc212418c..635036f35f 100644 --- a/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp +++ b/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp @@ -192,52 +192,49 @@ int64 GZIPDecompressorInputStream::getTotalLength() int GZIPDecompressorInputStream::read (void* destBuffer, int howMany) { - if ((howMany > 0) && ! isEof) + jassert (destBuffer != nullptr && howMany >= 0); + + if (howMany > 0 && ! isEof) { - jassert (destBuffer != nullptr); + int numRead = 0; + uint8* d = static_cast (destBuffer); - if (destBuffer != nullptr) + while (! helper->error) { - int numRead = 0; - uint8* d = static_cast (destBuffer); + const int n = helper->doNextBlock (d, howMany); + currentPos += n; - while (! helper->error) + if (n == 0) { - const int n = helper->doNextBlock (d, howMany); - currentPos += n; - - if (n == 0) + if (helper->finished || helper->needsDictionary) { - if (helper->finished || helper->needsDictionary) + isEof = true; + return numRead; + } + + if (helper->needsInput()) + { + activeBufferSize = sourceStream->read (buffer, (int) GZIPDecompressHelper::gzipDecompBufferSize); + + if (activeBufferSize > 0) + { + helper->setInput (buffer, (size_t) activeBufferSize); + } + else { isEof = true; return numRead; } - - if (helper->needsInput()) - { - activeBufferSize = sourceStream->read (buffer, (int) GZIPDecompressHelper::gzipDecompBufferSize); - - if (activeBufferSize > 0) - { - helper->setInput (buffer, (size_t) activeBufferSize); - } - else - { - isEof = true; - return numRead; - } - } } - else - { - numRead += n; - howMany -= n; - d += n; + } + else + { + numRead += n; + howMany -= n; + d += n; - if (howMany <= 0) - return numRead; - } + if (howMany <= 0) + return numRead; } } } diff --git a/modules/juce_core/zip/juce_ZipFile.cpp b/modules/juce_core/zip/juce_ZipFile.cpp index 8d7f37929c..46b59e0730 100644 --- a/modules/juce_core/zip/juce_ZipFile.cpp +++ b/modules/juce_core/zip/juce_ZipFile.cpp @@ -354,60 +354,65 @@ int ZipFile::findEndOfZipEntryTable (InputStream& input, int& numEntries) return 0; } -bool ZipFile::uncompressTo (const File& targetDirectory, - const bool shouldOverwriteFiles) +Result ZipFile::uncompressTo (const File& targetDirectory, + const bool shouldOverwriteFiles) { for (int i = 0; i < entries.size(); ++i) - if (! uncompressEntry (i, targetDirectory, shouldOverwriteFiles)) - return false; - - return true; -} - -bool ZipFile::uncompressEntry (const int index, - const File& targetDirectory, - bool shouldOverwriteFiles) -{ - const ZipEntryInfo* zei = entries [index]; - - if (zei != nullptr) { - const File targetFile (targetDirectory.getChildFile (zei->entry.filename)); - - if (zei->entry.filename.endsWithChar ('/')) - { - return targetFile.createDirectory(); // (entry is a directory, not a file) - } - else - { - ScopedPointer in (createStreamForEntry (index)); - - if (in != nullptr) - { - if (shouldOverwriteFiles && ! targetFile.deleteFile()) - return false; - - if ((! targetFile.exists()) && targetFile.getParentDirectory().createDirectory()) - { - ScopedPointer out (targetFile.createOutputStream()); - - if (out != nullptr) - { - out->writeFromInputStream (*in, -1); - out = nullptr; - - targetFile.setCreationTime (zei->entry.fileTime); - targetFile.setLastModificationTime (zei->entry.fileTime); - targetFile.setLastAccessTime (zei->entry.fileTime); - - return true; - } - } - } - } + Result result (uncompressEntry (i, targetDirectory, shouldOverwriteFiles)); + if (result.failed()) + return result; } - return false; + return Result::ok(); +} + +Result ZipFile::uncompressEntry (const int index, + const File& targetDirectory, + bool shouldOverwriteFiles) +{ + const ZipEntryInfo* zei = entries.getUnchecked (index); + + const File targetFile (targetDirectory.getChildFile (zei->entry.filename)); + + if (zei->entry.filename.endsWithChar ('/')) + { + return targetFile.createDirectory(); // (entry is a directory, not a file) + } + else + { + ScopedPointer in (createStreamForEntry (index)); + + if (in == nullptr) + return Result::fail ("Failed to open the zip file for reading"); + + if (targetFile.exists()) + { + if (! shouldOverwriteFiles) + return Result::ok(); + + if (! targetFile.deleteFile()) + return Result::fail ("Failed to write to target file: " + targetFile.getFullPathName()); + } + + if (! targetFile.getParentDirectory().createDirectory()) + return Result::fail ("Failed to create target folder: " + targetFile.getParentDirectory().getFullPathName()); + + { + FileOutputStream out (targetFile); + + if (out.failedToOpen()) + return Result::fail ("Failed to write to target file: " + targetFile.getFullPathName()); + + out << *in; + } + + targetFile.setCreationTime (zei->entry.fileTime); + targetFile.setLastModificationTime (zei->entry.fileTime); + targetFile.setLastAccessTime (zei->entry.fileTime); + + return Result::ok(); + } } @@ -485,17 +490,17 @@ private: bool writeSource (OutputStream& target) { checksum = 0; - ScopedPointer input (file.createInputStream()); + FileInputStream input (file); - if (input == nullptr) + if (input.failedToOpen()) return false; const int bufferSize = 2048; HeapBlock buffer (bufferSize); - while (! input->isExhausted()) + while (! input.isExhausted()) { - const int bytesRead = input->read (buffer, bufferSize); + const int bytesRead = input.read (buffer, bufferSize); if (bytesRead < 0) return false; diff --git a/modules/juce_core/zip/juce_ZipFile.h b/modules/juce_core/zip/juce_ZipFile.h index bd0073f4ac..e3131cf764 100644 --- a/modules/juce_core/zip/juce_ZipFile.h +++ b/modules/juce_core/zip/juce_ZipFile.h @@ -145,24 +145,25 @@ public: @param targetDirectory the root folder to uncompress to @param shouldOverwriteFiles whether to overwrite existing files with similarly-named ones - @returns true if all the files are successfully unzipped + @returns success if the file is successfully unzipped */ - bool uncompressTo (const File& targetDirectory, - bool shouldOverwriteFiles = true); + Result uncompressTo (const File& targetDirectory, + bool shouldOverwriteFiles = true); /** Uncompresses one of the entries from the zip file. This will expand the entry and write it in a target directory. The entry's path is used to determine which subfolder of the target should contain the new file. - @param index the index of the entry to uncompress + @param index the index of the entry to uncompress - this must be a valid index + between 0 and (getNumEntries() - 1). @param targetDirectory the root folder to uncompress into @param shouldOverwriteFiles whether to overwrite existing files with similarly-named ones - @returns true if the files is successfully unzipped + @returns success if all the files are successfully unzipped */ - bool uncompressEntry (int index, - const File& targetDirectory, - bool shouldOverwriteFiles = true); + Result uncompressEntry (int index, + const File& targetDirectory, + bool shouldOverwriteFiles = true); //============================================================================== diff --git a/modules/juce_graphics/image_formats/juce_JPEGLoader.cpp b/modules/juce_graphics/image_formats/juce_JPEGLoader.cpp index b0b51be303..0733ddb34b 100644 --- a/modules/juce_graphics/image_formats/juce_JPEGLoader.cpp +++ b/modules/juce_graphics/image_formats/juce_JPEGLoader.cpp @@ -241,7 +241,7 @@ Image JPEGImageFormat::decodeImage (InputStream& in) using namespace JPEGHelpers; MemoryOutputStream mb; - mb.writeFromInputStream (in, -1); + mb << in; Image image; diff --git a/modules/juce_gui_basics/drawables/juce_Drawable.cpp b/modules/juce_gui_basics/drawables/juce_Drawable.cpp index 4e1ceda0f6..8d2aaeaa3a 100644 --- a/modules/juce_gui_basics/drawables/juce_Drawable.cpp +++ b/modules/juce_gui_basics/drawables/juce_Drawable.cpp @@ -145,16 +145,16 @@ Drawable* Drawable::createFromImageData (const void* data, const size_t numBytes Drawable* Drawable::createFromImageDataStream (InputStream& dataSource) { MemoryOutputStream mo; - mo.writeFromInputStream (dataSource, -1); + mo << dataSource; return createFromImageData (mo.getData(), mo.getDataSize()); } Drawable* Drawable::createFromImageFile (const File& file) { - const ScopedPointer fin (file.createInputStream()); + FileInputStream fin (file); - return fin != nullptr ? createFromImageDataStream (*fin) : nullptr; + return fin.openedOk() ? createFromImageDataStream (fin) : nullptr; } //============================================================================== diff --git a/modules/juce_gui_basics/layout/juce_ScrollBar.cpp b/modules/juce_gui_basics/layout/juce_ScrollBar.cpp index 9fed1d3593..3cd6384b6d 100644 --- a/modules/juce_gui_basics/layout/juce_ScrollBar.cpp +++ b/modules/juce_gui_basics/layout/juce_ScrollBar.cpp @@ -387,12 +387,12 @@ void ScrollBar::mouseWheelMove (const MouseEvent&, float wheelIncrementX, float wheelIncrementY) { - float increment = vertical ? wheelIncrementY : wheelIncrementX; + float increment = 10.0f * (vertical ? wheelIncrementY : wheelIncrementX); if (increment < 0) - increment = jmin (increment * 10.0f, -1.0f); + increment = jmin (increment, -1.0f); else if (increment > 0) - increment = jmax (increment * 10.0f, 1.0f); + increment = jmax (increment, 1.0f); setCurrentRange (visibleRange - singleStepSize * increment); } @@ -419,20 +419,15 @@ bool ScrollBar::keyPressed (const KeyPress& key) if (! isVisible()) return false; - if (key.isKeyCode (KeyPress::upKey) || key.isKeyCode (KeyPress::leftKey)) - moveScrollbarInSteps (-1); - else if (key.isKeyCode (KeyPress::downKey) || key.isKeyCode (KeyPress::rightKey)) - moveScrollbarInSteps (1); - else if (key.isKeyCode (KeyPress::pageUpKey)) - moveScrollbarInPages (-1); - else if (key.isKeyCode (KeyPress::pageDownKey)) - moveScrollbarInPages (1); - else if (key.isKeyCode (KeyPress::homeKey)) - scrollToTop(); - else if (key.isKeyCode (KeyPress::endKey)) - scrollToBottom(); - else - return false; + if (key.isKeyCode (KeyPress::upKey) + || key.isKeyCode (KeyPress::leftKey)) moveScrollbarInSteps (-1); + else if (key.isKeyCode (KeyPress::downKey) + || key.isKeyCode (KeyPress::rightKey)) moveScrollbarInSteps (1); + else if (key.isKeyCode (KeyPress::pageUpKey)) moveScrollbarInPages (-1); + else if (key.isKeyCode (KeyPress::pageDownKey)) moveScrollbarInPages (1); + else if (key.isKeyCode (KeyPress::homeKey)) scrollToTop(); + else if (key.isKeyCode (KeyPress::endKey)) scrollToBottom(); + else return false; return true; } diff --git a/modules/juce_gui_basics/widgets/juce_Slider.cpp b/modules/juce_gui_basics/widgets/juce_Slider.cpp index d66b4a96fe..68f11b5ead 100644 --- a/modules/juce_gui_basics/widgets/juce_Slider.cpp +++ b/modules/juce_gui_basics/widgets/juce_Slider.cpp @@ -30,7 +30,6 @@ class Slider::PopupDisplayComponent : public BubbleComponent, public Timer { public: - //============================================================================== PopupDisplayComponent (Slider& owner_) : owner (owner_), font (15.0f, Font::bold) @@ -53,12 +52,7 @@ public: void updatePosition (const String& newText) { - if (text != newText) - { - text = newText; - repaint(); - } - + text = newText; BubbleComponent::setPosition (&owner); repaint(); } @@ -962,36 +956,18 @@ void Slider::resized() if (incDecButtonsSideBySide) { - decButton->setBounds (buttonRect.getX(), - buttonRect.getY(), - buttonRect.getWidth() / 2, - buttonRect.getHeight()); - + decButton->setBounds (buttonRect.removeFromLeft (buttonRect.getWidth() / 2)); decButton->setConnectedEdges (Button::ConnectedOnRight); - - incButton->setBounds (buttonRect.getCentreX(), - buttonRect.getY(), - buttonRect.getWidth() / 2, - buttonRect.getHeight()); - incButton->setConnectedEdges (Button::ConnectedOnLeft); } else { - incButton->setBounds (buttonRect.getX(), - buttonRect.getY(), - buttonRect.getWidth(), - buttonRect.getHeight() / 2); - - incButton->setConnectedEdges (Button::ConnectedOnBottom); - - decButton->setBounds (buttonRect.getX(), - buttonRect.getCentreY(), - buttonRect.getWidth(), - buttonRect.getHeight() / 2); - + decButton->setBounds (buttonRect.removeFromBottom (buttonRect.getHeight() / 2)); decButton->setConnectedEdges (Button::ConnectedOnTop); + incButton->setConnectedEdges (Button::ConnectedOnBottom); } + + incButton->setBounds (buttonRect); } } @@ -1000,52 +976,90 @@ void Slider::focusOfChildComponentChanged (FocusChangeType) repaint(); } -static void sliderMenuCallback (int result, Slider* slider) +namespace SliderHelpers { - if (slider != nullptr) + double smallestAngleBetween (double a1, double a2) noexcept { - switch (result) + return jmin (std::abs (a1 - a2), + std::abs (a1 + double_Pi * 2.0 - a2), + std::abs (a2 + double_Pi * 2.0 - a1)); + } + + void sliderMenuCallback (int result, Slider* slider) + { + if (slider != nullptr) { - case 1: slider->setVelocityBasedMode (! slider->getVelocityBasedMode()); break; - case 2: slider->setSliderStyle (Slider::Rotary); break; - case 3: slider->setSliderStyle (Slider::RotaryHorizontalDrag); break; - case 4: slider->setSliderStyle (Slider::RotaryVerticalDrag); break; - default: break; + switch (result) + { + case 1: slider->setVelocityBasedMode (! slider->getVelocityBasedMode()); break; + case 2: slider->setSliderStyle (Slider::Rotary); break; + case 3: slider->setSliderStyle (Slider::RotaryHorizontalDrag); break; + case 4: slider->setSliderStyle (Slider::RotaryVerticalDrag); break; + default: break; + } } } } +void Slider::showPopupMenu() +{ + menuShown = true; + + PopupMenu m; + m.setLookAndFeel (&getLookAndFeel()); + m.addItem (1, TRANS ("velocity-sensitive mode"), true, isVelocityBased); + m.addSeparator(); + + if (style == Rotary || style == RotaryHorizontalDrag || style == RotaryVerticalDrag) + { + PopupMenu rotaryMenu; + rotaryMenu.addItem (2, TRANS ("use circular dragging"), true, style == Rotary); + rotaryMenu.addItem (3, TRANS ("use left-right dragging"), true, style == RotaryHorizontalDrag); + rotaryMenu.addItem (4, TRANS ("use up-down dragging"), true, style == RotaryVerticalDrag); + + m.addSubMenu (TRANS ("rotary mode"), rotaryMenu); + } + + m.showMenuAsync (PopupMenu::Options(), + ModalCallbackFunction::forComponent (SliderHelpers::sliderMenuCallback, this)); +} + +int Slider::getThumbIndexAt (const MouseEvent& e) +{ + const bool isTwoValue = (style == TwoValueHorizontal || style == TwoValueVertical); + const bool isThreeValue = (style == ThreeValueHorizontal || style == ThreeValueVertical); + + if (isTwoValue || isThreeValue) + { + const float mousePos = (float) (isVertical() ? e.y : e.x); + + const float normalPosDistance = std::abs (getLinearSliderPos (currentValue.getValue()) - mousePos); + const float minPosDistance = std::abs (getLinearSliderPos (valueMin.getValue()) - 0.1f - mousePos); + const float maxPosDistance = std::abs (getLinearSliderPos (valueMax.getValue()) + 0.1f - mousePos); + + if (isTwoValue) + return maxPosDistance <= minPosDistance ? 2 : 1; + + if (normalPosDistance >= minPosDistance && maxPosDistance >= minPosDistance) + return 1; + else if (normalPosDistance >= maxPosDistance) + return 2; + } + + return 0; +} + void Slider::mouseDown (const MouseEvent& e) { mouseWasHidden = false; incDecDragged = false; - mousePosWhenLastDragged = e.getPosition(); - mouseDragStartX = e.getMouseDownX(); - mouseDragStartY = e.getMouseDownY(); + mouseDragStartPos = mousePosWhenLastDragged = e.getPosition(); if (isEnabled()) { if (e.mods.isPopupMenu() && menuEnabled) { - menuShown = true; - - PopupMenu m; - m.setLookAndFeel (&getLookAndFeel()); - m.addItem (1, TRANS ("velocity-sensitive mode"), true, isVelocityBased); - m.addSeparator(); - - if (style == Rotary || style == RotaryHorizontalDrag || style == RotaryVerticalDrag) - { - PopupMenu rotaryMenu; - rotaryMenu.addItem (2, TRANS ("use circular dragging"), true, style == Rotary); - rotaryMenu.addItem (3, TRANS ("use left-right dragging"), true, style == RotaryHorizontalDrag); - rotaryMenu.addItem (4, TRANS ("use up-down dragging"), true, style == RotaryVerticalDrag); - - m.addSubMenu (TRANS ("rotary mode"), rotaryMenu); - } - - m.showMenuAsync (PopupMenu::Options(), - ModalCallbackFunction::forComponent (sliderMenuCallback, this)); + showPopupMenu(); } else if (maximum > minimum) { @@ -1054,44 +1068,16 @@ void Slider::mouseDown (const MouseEvent& e) if (valueBox != nullptr) valueBox->hideEditor (true); - sliderBeingDragged = 0; - - if (style == TwoValueHorizontal - || style == TwoValueVertical - || style == ThreeValueHorizontal - || style == ThreeValueVertical) - { - const float mousePos = (float) (isVertical() ? e.y : e.x); - - const float normalPosDistance = std::abs (getLinearSliderPos (currentValue.getValue()) - mousePos); - const float minPosDistance = std::abs (getLinearSliderPos (valueMin.getValue()) - 0.1f - mousePos); - const float maxPosDistance = std::abs (getLinearSliderPos (valueMax.getValue()) + 0.1f - mousePos); - - if (style == TwoValueHorizontal || style == TwoValueVertical) - { - if (maxPosDistance <= minPosDistance) - sliderBeingDragged = 2; - else - sliderBeingDragged = 1; - } - else if (style == ThreeValueHorizontal || style == ThreeValueVertical) - { - if (normalPosDistance >= minPosDistance && maxPosDistance >= minPosDistance) - sliderBeingDragged = 1; - else if (normalPosDistance >= maxPosDistance) - sliderBeingDragged = 2; - } - } + sliderBeingDragged = getThumbIndexAt (e); minMaxDiff = (double) valueMax.getValue() - (double) valueMin.getValue(); lastAngle = rotaryStart + (rotaryEnd - rotaryStart) * valueToProportionOfLength (currentValue.getValue()); - valueWhenLastDragged = ((sliderBeingDragged == 2) ? valueMax - : ((sliderBeingDragged == 1) ? valueMin - : currentValue)).getValue(); - + valueWhenLastDragged = (sliderBeingDragged == 2 ? valueMax + : (sliderBeingDragged == 1 ? valueMin + : currentValue)).getValue(); valueOnMouseDown = valueWhenLastDragged; if (popupDisplayEnabled) @@ -1108,7 +1094,6 @@ void Slider::mouseDown (const MouseEvent& e) } sendDragStart(); - mouseDrag (e); } } @@ -1150,10 +1135,9 @@ void Slider::restoreMouseIfHidden() for (int i = Desktop::getInstance().getNumMouseSources(); --i >= 0;) Desktop::getInstance().getMouseSource(i)->enableUnboundedMouseMovement (false); - const double pos = (sliderBeingDragged == 2) ? getMaxValue() - : ((sliderBeingDragged == 1) ? getMinValue() - : (double) currentValue.getValue()); - + const double pos = sliderBeingDragged == 2 ? getMaxValue() + : (sliderBeingDragged == 1 ? getMinValue() + : (double) currentValue.getValue()); Point mousePos; if (style == RotaryHorizontalDrag || style == RotaryVerticalDrag) @@ -1194,13 +1178,122 @@ void Slider::modifierKeysChanged (const ModifierKeys& modifiers) } } -namespace SliderHelpers +void Slider::handleRotaryDrag (const MouseEvent& e) { - double smallestAngleBetween (double a1, double a2) noexcept + const int dx = e.x - sliderRect.getCentreX(); + const int dy = e.y - sliderRect.getCentreY(); + + if (dx * dx + dy * dy > 25) { - return jmin (std::abs (a1 - a2), - std::abs (a1 + double_Pi * 2.0 - a2), - std::abs (a2 + double_Pi * 2.0 - a1)); + double angle = std::atan2 ((double) dx, (double) -dy); + while (angle < 0.0) + angle += double_Pi * 2.0; + + if (rotaryStop && ! e.mouseWasClicked()) + { + if (std::abs (angle - lastAngle) > double_Pi) + { + if (angle >= lastAngle) + angle -= double_Pi * 2.0; + else + angle += double_Pi * 2.0; + } + + if (angle >= lastAngle) + angle = jmin (angle, (double) jmax (rotaryStart, rotaryEnd)); + else + angle = jmax (angle, (double) jmin (rotaryStart, rotaryEnd)); + } + else + { + while (angle < rotaryStart) + angle += double_Pi * 2.0; + + if (angle > rotaryEnd) + { + if (SliderHelpers::smallestAngleBetween (angle, rotaryStart) + <= SliderHelpers::smallestAngleBetween (angle, rotaryEnd)) + angle = rotaryStart; + else + angle = rotaryEnd; + } + } + + const double proportion = (angle - rotaryStart) / (rotaryEnd - rotaryStart); + valueWhenLastDragged = proportionOfLengthToValue (jlimit (0.0, 1.0, proportion)); + lastAngle = angle; + } +} + +void Slider::handleAbsoluteDrag (const MouseEvent& e) +{ + const int mousePos = (isHorizontal() || style == RotaryHorizontalDrag) ? e.x : e.y; + + double scaledMousePos = (mousePos - sliderRegionStart) / (double) sliderRegionSize; + + if (style == RotaryHorizontalDrag + || style == RotaryVerticalDrag + || style == IncDecButtons + || ((style == LinearHorizontal || style == LinearVertical || style == LinearBar) + && ! snapsToMousePos)) + { + const int mouseDiff = (style == RotaryHorizontalDrag + || style == LinearHorizontal + || style == LinearBar + || (style == IncDecButtons && incDecDragDirectionIsHorizontal())) + ? e.x - mouseDragStartPos.getX() + : mouseDragStartPos.getY() - e.y; + + double newPos = valueToProportionOfLength (valueOnMouseDown) + + mouseDiff * (1.0 / pixelsForFullDragExtent); + + valueWhenLastDragged = proportionOfLengthToValue (jlimit (0.0, 1.0, newPos)); + + if (style == IncDecButtons) + { + incButton->setState (mouseDiff < 0 ? Button::buttonNormal : Button::buttonDown); + decButton->setState (mouseDiff > 0 ? Button::buttonNormal : Button::buttonDown); + } + } + else + { + if (isVertical()) + scaledMousePos = 1.0 - scaledMousePos; + + valueWhenLastDragged = proportionOfLengthToValue (jlimit (0.0, 1.0, scaledMousePos)); + } +} + +void Slider::handleVelocityDrag (const MouseEvent& e) +{ + const int mouseDiff = (isHorizontal() || style == RotaryHorizontalDrag + || (style == IncDecButtons && incDecDragDirectionIsHorizontal())) + ? e.x - mousePosWhenLastDragged.getX() + : e.y - mousePosWhenLastDragged.getY(); + + const double maxSpeed = jmax (200, sliderRegionSize); + double speed = jlimit (0.0, maxSpeed, (double) abs (mouseDiff)); + + if (speed != 0) + { + speed = 0.2 * velocityModeSensitivity + * (1.0 + std::sin (double_Pi * (1.5 + jmin (0.5, velocityModeOffset + + jmax (0.0, (double) (speed - velocityModeThreshold)) + / maxSpeed)))); + + if (mouseDiff < 0) + speed = -speed; + + if (isVertical() || style == RotaryVerticalDrag + || (style == IncDecButtons && ! incDecDragDirectionIsHorizontal())) + speed = -speed; + + const double currentPos = valueToProportionOfLength (valueWhenLastDragged); + + valueWhenLastDragged = proportionOfLengthToValue (jlimit (0.0, 1.0, currentPos + speed)); + + e.source.enableUnboundedMouseMovement (true, false); + mouseWasHidden = true; } } @@ -1208,144 +1301,31 @@ void Slider::mouseDrag (const MouseEvent& e) { if (isEnabled() && (! menuShown) - && (maximum > minimum)) + && (maximum > minimum) + && ! (style == LinearBar && e.mouseWasClicked() && valueBox != nullptr && valueBox->isEditable())) { if (style == Rotary) { - const int dx = e.x - sliderRect.getCentreX(); - const int dy = e.y - sliderRect.getCentreY(); - - if (dx * dx + dy * dy > 25) - { - double angle = std::atan2 ((double) dx, (double) -dy); - while (angle < 0.0) - angle += double_Pi * 2.0; - - if (rotaryStop && ! e.mouseWasClicked()) - { - if (std::abs (angle - lastAngle) > double_Pi) - { - if (angle >= lastAngle) - angle -= double_Pi * 2.0; - else - angle += double_Pi * 2.0; - } - - if (angle >= lastAngle) - angle = jmin (angle, (double) jmax (rotaryStart, rotaryEnd)); - else - angle = jmax (angle, (double) jmin (rotaryStart, rotaryEnd)); - } - else - { - while (angle < rotaryStart) - angle += double_Pi * 2.0; - - if (angle > rotaryEnd) - { - if (SliderHelpers::smallestAngleBetween (angle, rotaryStart) - <= SliderHelpers::smallestAngleBetween (angle, rotaryEnd)) - angle = rotaryStart; - else - angle = rotaryEnd; - } - } - - const double proportion = (angle - rotaryStart) / (rotaryEnd - rotaryStart); - - valueWhenLastDragged = proportionOfLengthToValue (jlimit (0.0, 1.0, proportion)); - - lastAngle = angle; - } + handleRotaryDrag (e); } else { - if (style == LinearBar && e.mouseWasClicked() - && valueBox != nullptr && valueBox->isEditable()) - return; - if (style == IncDecButtons && ! incDecDragged) { if (e.getDistanceFromDragStart() < 10 || e.mouseWasClicked()) return; incDecDragged = true; - mouseDragStartX = e.x; - mouseDragStartY = e.y; + mouseDragStartPos = e.getPosition(); } - if ((isVelocityBased == (userKeyOverridesVelocity ? e.mods.testFlags (ModifierKeys::ctrlModifier | ModifierKeys::commandModifier | ModifierKeys::altModifier) - : false)) - || ((maximum - minimum) / sliderRegionSize < interval)) - { - const int mousePos = (isHorizontal() || style == RotaryHorizontalDrag) ? e.x : e.y; - - double scaledMousePos = (mousePos - sliderRegionStart) / (double) sliderRegionSize; - - if (style == RotaryHorizontalDrag - || style == RotaryVerticalDrag - || style == IncDecButtons - || ((style == LinearHorizontal || style == LinearVertical || style == LinearBar) - && ! snapsToMousePos)) - { - const int mouseDiff = (style == RotaryHorizontalDrag - || style == LinearHorizontal - || style == LinearBar - || (style == IncDecButtons && incDecDragDirectionIsHorizontal())) - ? e.x - mouseDragStartX - : mouseDragStartY - e.y; - - double newPos = valueToProportionOfLength (valueOnMouseDown) - + mouseDiff * (1.0 / pixelsForFullDragExtent); - - valueWhenLastDragged = proportionOfLengthToValue (jlimit (0.0, 1.0, newPos)); - - if (style == IncDecButtons) - { - incButton->setState (mouseDiff < 0 ? Button::buttonNormal : Button::buttonDown); - decButton->setState (mouseDiff > 0 ? Button::buttonNormal : Button::buttonDown); - } - } - else - { - if (isVertical()) - scaledMousePos = 1.0 - scaledMousePos; - - valueWhenLastDragged = proportionOfLengthToValue (jlimit (0.0, 1.0, scaledMousePos)); - } - } + if (isVelocityBased == (userKeyOverridesVelocity && e.mods.testFlags (ModifierKeys::ctrlModifier + | ModifierKeys::commandModifier + | ModifierKeys::altModifier)) + || (maximum - minimum) / sliderRegionSize < interval) + handleAbsoluteDrag (e); else - { - const int mouseDiff = (isHorizontal() || style == RotaryHorizontalDrag - || (style == IncDecButtons && incDecDragDirectionIsHorizontal())) - ? e.x - mousePosWhenLastDragged.getX() - : e.y - mousePosWhenLastDragged.getY(); - - const double maxSpeed = jmax (200, sliderRegionSize); - double speed = jlimit (0.0, maxSpeed, (double) abs (mouseDiff)); - - if (speed != 0) - { - speed = 0.2 * velocityModeSensitivity - * (1.0 + std::sin (double_Pi * (1.5 + jmin (0.5, velocityModeOffset - + jmax (0.0, (double) (speed - velocityModeThreshold)) - / maxSpeed)))); - - if (mouseDiff < 0) - speed = -speed; - - if (isVertical() || style == RotaryVerticalDrag - || (style == IncDecButtons && ! incDecDragDirectionIsHorizontal())) - speed = -speed; - - const double currentPos = valueToProportionOfLength (valueWhenLastDragged); - - valueWhenLastDragged = proportionOfLengthToValue (jlimit (0.0, 1.0, currentPos + speed)); - - e.source.enableUnboundedMouseMovement (true, false); - mouseWasHidden = true; - } - } + handleVelocityDrag (e); } valueWhenLastDragged = jlimit (minimum, maximum, valueWhenLastDragged); diff --git a/modules/juce_gui_basics/widgets/juce_Slider.h b/modules/juce_gui_basics/widgets/juce_Slider.h index 56b3f152d3..881a233dd4 100644 --- a/modules/juce_gui_basics/widgets/juce_Slider.h +++ b/modules/juce_gui_basics/widgets/juce_Slider.h @@ -827,8 +827,7 @@ private: int velocityModeThreshold; float rotaryStart, rotaryEnd; int numDecimalPlaces; - Point mousePosWhenLastDragged; - int mouseDragStartX, mouseDragStartY; + Point mouseDragStartPos, mousePosWhenLastDragged; int sliderRegionStart, sliderRegionSize; int sliderBeingDragged; int pixelsForFullDragExtent; @@ -854,6 +853,11 @@ private: ScopedPointer popupDisplay; Component* parentForPopupDisplay; + void showPopupMenu(); + int getThumbIndexAt (const MouseEvent&); + void handleRotaryDrag (const MouseEvent&); + void handleAbsoluteDrag (const MouseEvent&); + void handleVelocityDrag (const MouseEvent&); float getLinearSliderPos (double value); void restoreMouseIfHidden(); void sendDragStart();