1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Tweaks to streams and gzip compressor.

This commit is contained in:
Julian Storer 2011-09-14 15:34:05 +01:00
parent 9332efc10d
commit 9a9f570781
27 changed files with 460 additions and 463 deletions

View file

@ -688,27 +688,21 @@ FileInputStream* File::createInputStream() const
FileOutputStream* File::createOutputStream (const int bufferSize) const FileOutputStream* File::createOutputStream (const int bufferSize) const
{ {
ScopedPointer <FileOutputStream> out (new FileOutputStream (*this, bufferSize)); ScopedPointer<FileOutputStream> out (new FileOutputStream (*this, bufferSize));
if (out->failedToOpen()) return out->failedToOpen() ? nullptr
return nullptr; : out.release();
return out.release();
} }
//============================================================================== //==============================================================================
bool File::appendData (const void* const dataToAppend, bool File::appendData (const void* const dataToAppend,
const int numberOfBytes) const const int numberOfBytes) const
{ {
if (numberOfBytes > 0) if (numberOfBytes <= 0)
{ return true;
FileOutputStream out (*this, 8192);
return (! out.failedToOpen()) FileOutputStream out (*this, 8192);
&& out.write (dataToAppend, numberOfBytes); return out.openedOk() && out.write (dataToAppend, numberOfBytes);
}
return true;
} }
bool File::replaceWithData (const void* const dataToWrite, bool File::replaceWithData (const void* const dataToWrite,
@ -728,15 +722,13 @@ bool File::appendText (const String& text,
const bool asUnicode, const bool asUnicode,
const bool writeUnicodeHeaderBytes) const const bool writeUnicodeHeaderBytes) const
{ {
const ScopedPointer <FileOutputStream> out (createOutputStream()); FileOutputStream out (*this);
if (out != nullptr) if (out.failedToOpen())
{ return false;
out->writeText (text, asUnicode, writeUnicodeHeaderBytes);
return true;
}
return false; out.writeText (text, asUnicode, writeUnicodeHeaderBytes);
return true;
} }
bool File::replaceWithText (const String& textToWrite, bool File::replaceWithText (const String& textToWrite,

View file

@ -53,6 +53,8 @@ int64 FileInputStream::getTotalLength()
int FileInputStream::read (void* buffer, int bytesToRead) int FileInputStream::read (void* buffer, int bytesToRead)
{ {
jassert (buffer != nullptr && bytesToRead >= 0);
if (needToSeek) if (needToSeek)
{ {
if (juce_fileSetPosition (fileHandle, currentPosition) < 0) if (juce_fileSetPosition (fileHandle, currentPosition) < 0)

View file

@ -58,7 +58,18 @@ public:
The result will be ok if the file opened successfully. If an error occurs while 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. 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(); int64 getTotalLength();
@ -67,7 +78,6 @@ public:
int64 getPosition(); int64 getPosition();
bool setPosition (int64 pos); bool setPosition (int64 pos);
private: private:
//============================================================================== //==============================================================================
File file; File file;

View file

@ -84,6 +84,8 @@ void FileOutputStream::flush()
bool FileOutputStream::write (const void* const src, const int numBytes) bool FileOutputStream::write (const void* const src, const int numBytes)
{ {
jassert (src != nullptr && numBytes >= 0);
if (bytesInBuffer + numBytes < bufferSize) if (bytesInBuffer + numBytes < bufferSize)
{ {
memcpy (buffer + bytesInBuffer, src, (size_t) numBytes); memcpy (buffer + bytesInBuffer, src, (size_t) numBytes);

View file

@ -51,9 +51,6 @@ public:
use File::deleteFile() before opening the stream, or use setPosition(0) use File::deleteFile() before opening the stream, or use setPosition(0)
after it's opened (although this won't truncate the file). 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 @see TemporaryFile
*/ */
FileOutputStream (const File& fileToWriteTo, FileOutputStream (const File& fileToWriteTo,
@ -71,12 +68,17 @@ public:
The result will be ok if the file opened successfully. If an error occurs while 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. 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. /** Returns true if the stream couldn't be opened for some reason.
@see getResult() @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(); void flush();

View file

@ -136,6 +136,8 @@ public:
int read (void* buffer, int bytesToRead) int read (void* buffer, int bytesToRead)
{ {
jassert (buffer != nullptr && bytesToRead >= 0);
if (stream == nullptr) if (stream == nullptr)
return 0; return 0;

View file

@ -361,6 +361,8 @@ public:
int read (void* buffer, int bytesToRead) int read (void* buffer, int bytesToRead)
{ {
jassert (buffer != nullptr && bytesToRead >= 0);
if (finished || isError()) if (finished || isError())
{ {
return 0; return 0;

View file

@ -988,6 +988,8 @@ public:
int read (void* const dest, const int numBytes) int read (void* const dest, const int numBytes)
{ {
jassert (dest != nullptr);
if (readHandle == 0 && childPID != 0) if (readHandle == 0 && childPID != 0)
readHandle = fdopen (pipeHandle, "r"); readHandle = fdopen (pipeHandle, "r");

View file

@ -145,6 +145,7 @@ public:
int read (void* buffer, int bytesToRead) int read (void* buffer, int bytesToRead)
{ {
jassert (buffer != nullptr && bytesToRead >= 0);
DWORD bytesRead = 0; DWORD bytesRead = 0;
if (! (finished || isError())) if (! (finished || isError()))

View file

@ -131,6 +131,8 @@ void BufferedInputStream::ensureBuffered()
int BufferedInputStream::read (void* destBuffer, int maxBytesToRead) int BufferedInputStream::read (void* destBuffer, int maxBytesToRead)
{ {
jassert (destBuffer != nullptr && maxBytesToRead >= 0);
if (position >= bufferStart if (position >= bufferStart
&& position + maxBytesToRead <= lastReadPos) && position + maxBytesToRead <= lastReadPos)
{ {

View file

@ -208,7 +208,7 @@ int InputStream::readIntoMemoryBlock (MemoryBlock& block, int numBytes)
String InputStream::readEntireStreamAsString() String InputStream::readEntireStreamAsString()
{ {
MemoryOutputStream mo; MemoryOutputStream mo;
mo.writeFromInputStream (*this, -1); mo << *this;
return mo.toString(); return mo.toString();
} }

View file

@ -59,16 +59,16 @@ public:
virtual bool isExhausted() = 0; 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 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 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). 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 @param maxBytesToRead the maximum number of bytes to read - make sure the
memory block passed in is big enough to contain this 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 @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 maxBytesToRead if the stream is exhausted before it gets that far

View file

@ -65,7 +65,8 @@ int64 MemoryInputStream::getTotalLength()
int MemoryInputStream::read (void* const buffer, const int howMany) 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)); const int num = jmin (howMany, (int) (dataSize - position));
if (num <= 0) if (num <= 0)
return 0; return 0;

View file

@ -81,6 +81,8 @@ void MemoryOutputStream::prepareToWrite (int numBytes)
bool MemoryOutputStream::write (const void* const buffer, int howMany) bool MemoryOutputStream::write (const void* const buffer, int howMany)
{ {
jassert (buffer != nullptr && howMany >= 0);
if (howMany > 0) if (howMany > 0)
{ {
prepareToWrite (howMany); prepareToWrite (howMany);

View file

@ -298,11 +298,13 @@ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const MemoryBlock&
OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const File& fileToRead) OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const File& fileToRead)
{ {
const ScopedPointer<FileInputStream> in (fileToRead.createInputStream()); FileInputStream in (fileToRead);
return stream << in;
if (in != nullptr) }
stream.writeFromInputStream (*in, -1);
OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, InputStream& streamToRead)
{
stream.writeFromInputStream (streamToRead, -1);
return stream; return stream;
} }

View file

@ -83,10 +83,12 @@ public:
that needs to be overloaded - the base class has methods for writing other that needs to be overloaded - the base class has methods for writing other
types of data which use this to do the work. 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 @returns false if the write operation fails for some reason
*/ */
virtual bool write (const void* dataToWrite, virtual bool write (const void* dataToWrite,
int howManyBytes) = 0; int numberOfBytes) = 0;
//============================================================================== //==============================================================================
/** Writes a single byte to the stream. /** 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. */ /** Writes the contents of a file to a stream. */
OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const File& fileToRead); 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. /** Writes a new-line to a stream.
You can use the predefined symbol 'newLine' to invoke this, e.g. You can use the predefined symbol 'newLine' to invoke this, e.g.
@code @code

View file

@ -61,6 +61,8 @@ bool SubregionStream::setPosition (int64 newPosition)
int SubregionStream::read (void* destBuffer, int maxBytesToRead) int SubregionStream::read (void* destBuffer, int maxBytesToRead)
{ {
jassert (destBuffer != nullptr && maxBytesToRead >= 0);
if (lengthOfSourceStream < 0) if (lengthOfSourceStream < 0)
{ {
return source->read (destBuffer, maxBytesToRead); return source->read (destBuffer, maxBytesToRead);

View file

@ -42,14 +42,12 @@ class GZIPCompressorOutputStream::GZIPCompressorHelper
{ {
public: public:
GZIPCompressorHelper (const int compressionLevel, const int windowBits) GZIPCompressorHelper (const int compressionLevel, const int windowBits)
: data (nullptr), : buffer ((size_t) gzipCompBufferSize),
dataSize (0),
compLevel (compressionLevel), compLevel (compressionLevel),
strategy (0), strategy (0),
setParams (true), isFirstDeflate (true),
streamIsValid (false), streamIsValid (false),
finished (false), finished (false)
shouldFinish (false)
{ {
using namespace zlibNamespace; using namespace zlibNamespace;
zerostruct (stream); zerostruct (stream);
@ -61,67 +59,74 @@ public:
~GZIPCompressorHelper() ~GZIPCompressorHelper()
{ {
using namespace zlibNamespace;
if (streamIsValid) 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; const uint8* data = nullptr;
dataSize = size; 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 <zlibNamespace::Bytef> 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; using namespace zlibNamespace;
if (streamIsValid) if (streamIsValid)
{ {
stream.next_in = const_cast <uint8*> (data); stream.next_in = const_cast <uint8*> (data);
stream.next_out = dest; stream.next_out = buffer;
stream.avail_in = (z_uInt) dataSize; stream.avail_in = (z_uInt) dataSize;
stream.avail_out = (z_uInt) destSize; stream.avail_out = (z_uInt) gzipCompBufferSize;
const int result = setParams ? deflateParams (&stream, compLevel, strategy) const int result = isFirstDeflate ? deflateParams (&stream, compLevel, strategy)
: deflate (&stream, shouldFinish ? Z_FINISH : Z_NO_FLUSH); : deflate (&stream, flushMode);
isFirstDeflate = false;
setParams = false;
switch (result) switch (result)
{ {
case Z_STREAM_END: case Z_STREAM_END:
finished = true; finished = true;
// Deliberate fall-through.. // Deliberate fall-through..
case Z_OK: case Z_OK:
data += dataSize - stream.avail_in; {
dataSize = stream.avail_in; 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, int compressionLevel,
const bool deleteDestStream, const bool deleteDestStream,
const int windowBits) const int windowBits)
: destStream (destStream_, deleteDestStream), : destStream (destStream_, deleteDestStream)
buffer ((size_t) GZIPCompressorHelper::gzipCompBufferSize)
{ {
jassert (destStream_ != nullptr);
if (compressionLevel < 1 || compressionLevel > 9) if (compressionLevel < 1 || compressionLevel > 9)
compressionLevel = -1; compressionLevel = -1;
@ -140,48 +146,20 @@ GZIPCompressorOutputStream::GZIPCompressorOutputStream (OutputStream* const dest
GZIPCompressorOutputStream::~GZIPCompressorOutputStream() GZIPCompressorOutputStream::~GZIPCompressorOutputStream()
{ {
flushInternal(); flush();
}
//==============================================================================
void GZIPCompressorOutputStream::flushInternal()
{
if (! helper->finished)
{
helper->shouldFinish = true;
while (! helper->finished)
doNextBlock();
}
destStream->flush();
} }
void GZIPCompressorOutputStream::flush() void GZIPCompressorOutputStream::flush()
{ {
flushInternal(); helper->finish (*destStream);
destStream->flush();
} }
bool GZIPCompressorOutputStream::write (const void* destBuffer, int howMany) bool GZIPCompressorOutputStream::write (const void* destBuffer, int howMany)
{ {
if (! helper->finished) jassert (destBuffer != nullptr && howMany >= 0);
{
helper->setInput (static_cast <const uint8*> (destBuffer), (size_t) howMany);
while (! helper->needsInput()) return helper->write (static_cast <const uint8*> (destBuffer), howMany, *destStream);
{
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);
} }
int64 GZIPCompressorOutputStream::getPosition() int64 GZIPCompressorOutputStream::getPosition()
@ -231,7 +209,7 @@ public:
MemoryInputStream compressedInput (compressed.getData(), compressed.getDataSize(), false); MemoryInputStream compressedInput (compressed.getData(), compressed.getDataSize(), false);
GZIPDecompressorInputStream unzipper (compressedInput); GZIPDecompressorInputStream unzipper (compressedInput);
uncompressed.writeFromInputStream (unzipper, -1); uncompressed << unzipper;
} }
expectEquals ((int) uncompressed.getDataSize(), expectEquals ((int) uncompressed.getDataSize(),

View file

@ -35,6 +35,10 @@
/** /**
A stream which uses zlib to compress the data written into it. 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 @see GZIPDecompressorInputStream
*/ */
class JUCE_API GZIPCompressorOutputStream : public OutputStream class JUCE_API GZIPCompressorOutputStream : public OutputStream
@ -64,7 +68,13 @@ public:
~GZIPCompressorOutputStream(); ~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(); void flush();
int64 getPosition(); int64 getPosition();
bool setPosition (int64 newPosition); bool setPosition (int64 newPosition);
bool write (const void* destBuffer, int howMany); bool write (const void* destBuffer, int howMany);
@ -81,12 +91,10 @@ public:
private: private:
//============================================================================== //==============================================================================
OptionalScopedPointer<OutputStream> destStream; OptionalScopedPointer<OutputStream> destStream;
HeapBlock <uint8> buffer;
class GZIPCompressorHelper; class GZIPCompressorHelper;
friend class ScopedPointer <GZIPCompressorHelper>; friend class ScopedPointer <GZIPCompressorHelper>;
ScopedPointer <GZIPCompressorHelper> helper; ScopedPointer <GZIPCompressorHelper> helper;
bool doNextBlock();
void flushInternal();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GZIPCompressorOutputStream); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GZIPCompressorOutputStream);
}; };

View file

@ -192,52 +192,49 @@ int64 GZIPDecompressorInputStream::getTotalLength()
int GZIPDecompressorInputStream::read (void* destBuffer, int howMany) 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 <uint8*> (destBuffer);
if (destBuffer != nullptr) while (! helper->error)
{ {
int numRead = 0; const int n = helper->doNextBlock (d, howMany);
uint8* d = static_cast <uint8*> (destBuffer); currentPos += n;
while (! helper->error) if (n == 0)
{ {
const int n = helper->doNextBlock (d, howMany); if (helper->finished || helper->needsDictionary)
currentPos += n;
if (n == 0)
{ {
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; isEof = true;
return numRead; 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 }
{ else
numRead += n; {
howMany -= n; numRead += n;
d += n; howMany -= n;
d += n;
if (howMany <= 0) if (howMany <= 0)
return numRead; return numRead;
}
} }
} }
} }

View file

@ -354,60 +354,65 @@ int ZipFile::findEndOfZipEntryTable (InputStream& input, int& numEntries)
return 0; return 0;
} }
bool ZipFile::uncompressTo (const File& targetDirectory, Result ZipFile::uncompressTo (const File& targetDirectory,
const bool shouldOverwriteFiles) const bool shouldOverwriteFiles)
{ {
for (int i = 0; i < entries.size(); ++i) 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)); Result result (uncompressEntry (i, targetDirectory, shouldOverwriteFiles));
if (result.failed())
if (zei->entry.filename.endsWithChar ('/')) return result;
{
return targetFile.createDirectory(); // (entry is a directory, not a file)
}
else
{
ScopedPointer<InputStream> in (createStreamForEntry (index));
if (in != nullptr)
{
if (shouldOverwriteFiles && ! targetFile.deleteFile())
return false;
if ((! targetFile.exists()) && targetFile.getParentDirectory().createDirectory())
{
ScopedPointer<FileOutputStream> 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;
}
}
}
}
} }
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<InputStream> 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) bool writeSource (OutputStream& target)
{ {
checksum = 0; checksum = 0;
ScopedPointer<FileInputStream> input (file.createInputStream()); FileInputStream input (file);
if (input == nullptr) if (input.failedToOpen())
return false; return false;
const int bufferSize = 2048; const int bufferSize = 2048;
HeapBlock<unsigned char> buffer (bufferSize); HeapBlock<unsigned char> 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) if (bytesRead < 0)
return false; return false;

View file

@ -145,24 +145,25 @@ public:
@param targetDirectory the root folder to uncompress to @param targetDirectory the root folder to uncompress to
@param shouldOverwriteFiles whether to overwrite existing files with similarly-named ones @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, Result uncompressTo (const File& targetDirectory,
bool shouldOverwriteFiles = true); bool shouldOverwriteFiles = true);
/** Uncompresses one of the entries from the zip file. /** 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 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. 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 targetDirectory the root folder to uncompress into
@param shouldOverwriteFiles whether to overwrite existing files with similarly-named ones @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, Result uncompressEntry (int index,
const File& targetDirectory, const File& targetDirectory,
bool shouldOverwriteFiles = true); bool shouldOverwriteFiles = true);
//============================================================================== //==============================================================================

View file

@ -241,7 +241,7 @@ Image JPEGImageFormat::decodeImage (InputStream& in)
using namespace JPEGHelpers; using namespace JPEGHelpers;
MemoryOutputStream mb; MemoryOutputStream mb;
mb.writeFromInputStream (in, -1); mb << in;
Image image; Image image;

View file

@ -145,16 +145,16 @@ Drawable* Drawable::createFromImageData (const void* data, const size_t numBytes
Drawable* Drawable::createFromImageDataStream (InputStream& dataSource) Drawable* Drawable::createFromImageDataStream (InputStream& dataSource)
{ {
MemoryOutputStream mo; MemoryOutputStream mo;
mo.writeFromInputStream (dataSource, -1); mo << dataSource;
return createFromImageData (mo.getData(), mo.getDataSize()); return createFromImageData (mo.getData(), mo.getDataSize());
} }
Drawable* Drawable::createFromImageFile (const File& file) Drawable* Drawable::createFromImageFile (const File& file)
{ {
const ScopedPointer <FileInputStream> fin (file.createInputStream()); FileInputStream fin (file);
return fin != nullptr ? createFromImageDataStream (*fin) : nullptr; return fin.openedOk() ? createFromImageDataStream (fin) : nullptr;
} }
//============================================================================== //==============================================================================

View file

@ -387,12 +387,12 @@ void ScrollBar::mouseWheelMove (const MouseEvent&,
float wheelIncrementX, float wheelIncrementX,
float wheelIncrementY) float wheelIncrementY)
{ {
float increment = vertical ? wheelIncrementY : wheelIncrementX; float increment = 10.0f * (vertical ? wheelIncrementY : wheelIncrementX);
if (increment < 0) if (increment < 0)
increment = jmin (increment * 10.0f, -1.0f); increment = jmin (increment, -1.0f);
else if (increment > 0) else if (increment > 0)
increment = jmax (increment * 10.0f, 1.0f); increment = jmax (increment, 1.0f);
setCurrentRange (visibleRange - singleStepSize * increment); setCurrentRange (visibleRange - singleStepSize * increment);
} }
@ -419,20 +419,15 @@ bool ScrollBar::keyPressed (const KeyPress& key)
if (! isVisible()) if (! isVisible())
return false; return false;
if (key.isKeyCode (KeyPress::upKey) || key.isKeyCode (KeyPress::leftKey)) if (key.isKeyCode (KeyPress::upKey)
moveScrollbarInSteps (-1); || key.isKeyCode (KeyPress::leftKey)) moveScrollbarInSteps (-1);
else if (key.isKeyCode (KeyPress::downKey) || key.isKeyCode (KeyPress::rightKey)) else if (key.isKeyCode (KeyPress::downKey)
moveScrollbarInSteps (1); || key.isKeyCode (KeyPress::rightKey)) moveScrollbarInSteps (1);
else if (key.isKeyCode (KeyPress::pageUpKey)) else if (key.isKeyCode (KeyPress::pageUpKey)) moveScrollbarInPages (-1);
moveScrollbarInPages (-1); else if (key.isKeyCode (KeyPress::pageDownKey)) moveScrollbarInPages (1);
else if (key.isKeyCode (KeyPress::pageDownKey)) else if (key.isKeyCode (KeyPress::homeKey)) scrollToTop();
moveScrollbarInPages (1); else if (key.isKeyCode (KeyPress::endKey)) scrollToBottom();
else if (key.isKeyCode (KeyPress::homeKey)) else return false;
scrollToTop();
else if (key.isKeyCode (KeyPress::endKey))
scrollToBottom();
else
return false;
return true; return true;
} }

View file

@ -30,7 +30,6 @@ class Slider::PopupDisplayComponent : public BubbleComponent,
public Timer public Timer
{ {
public: public:
//==============================================================================
PopupDisplayComponent (Slider& owner_) PopupDisplayComponent (Slider& owner_)
: owner (owner_), : owner (owner_),
font (15.0f, Font::bold) font (15.0f, Font::bold)
@ -53,12 +52,7 @@ public:
void updatePosition (const String& newText) void updatePosition (const String& newText)
{ {
if (text != newText) text = newText;
{
text = newText;
repaint();
}
BubbleComponent::setPosition (&owner); BubbleComponent::setPosition (&owner);
repaint(); repaint();
} }
@ -962,36 +956,18 @@ void Slider::resized()
if (incDecButtonsSideBySide) if (incDecButtonsSideBySide)
{ {
decButton->setBounds (buttonRect.getX(), decButton->setBounds (buttonRect.removeFromLeft (buttonRect.getWidth() / 2));
buttonRect.getY(),
buttonRect.getWidth() / 2,
buttonRect.getHeight());
decButton->setConnectedEdges (Button::ConnectedOnRight); decButton->setConnectedEdges (Button::ConnectedOnRight);
incButton->setBounds (buttonRect.getCentreX(),
buttonRect.getY(),
buttonRect.getWidth() / 2,
buttonRect.getHeight());
incButton->setConnectedEdges (Button::ConnectedOnLeft); incButton->setConnectedEdges (Button::ConnectedOnLeft);
} }
else else
{ {
incButton->setBounds (buttonRect.getX(), decButton->setBounds (buttonRect.removeFromBottom (buttonRect.getHeight() / 2));
buttonRect.getY(),
buttonRect.getWidth(),
buttonRect.getHeight() / 2);
incButton->setConnectedEdges (Button::ConnectedOnBottom);
decButton->setBounds (buttonRect.getX(),
buttonRect.getCentreY(),
buttonRect.getWidth(),
buttonRect.getHeight() / 2);
decButton->setConnectedEdges (Button::ConnectedOnTop); decButton->setConnectedEdges (Button::ConnectedOnTop);
incButton->setConnectedEdges (Button::ConnectedOnBottom);
} }
incButton->setBounds (buttonRect);
} }
} }
@ -1000,52 +976,90 @@ void Slider::focusOfChildComponentChanged (FocusChangeType)
repaint(); 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; switch (result)
case 2: slider->setSliderStyle (Slider::Rotary); break; {
case 3: slider->setSliderStyle (Slider::RotaryHorizontalDrag); break; case 1: slider->setVelocityBasedMode (! slider->getVelocityBasedMode()); break;
case 4: slider->setSliderStyle (Slider::RotaryVerticalDrag); break; case 2: slider->setSliderStyle (Slider::Rotary); break;
default: 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) void Slider::mouseDown (const MouseEvent& e)
{ {
mouseWasHidden = false; mouseWasHidden = false;
incDecDragged = false; incDecDragged = false;
mousePosWhenLastDragged = e.getPosition(); mouseDragStartPos = mousePosWhenLastDragged = e.getPosition();
mouseDragStartX = e.getMouseDownX();
mouseDragStartY = e.getMouseDownY();
if (isEnabled()) if (isEnabled())
{ {
if (e.mods.isPopupMenu() && menuEnabled) if (e.mods.isPopupMenu() && menuEnabled)
{ {
menuShown = true; showPopupMenu();
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));
} }
else if (maximum > minimum) else if (maximum > minimum)
{ {
@ -1054,44 +1068,16 @@ void Slider::mouseDown (const MouseEvent& e)
if (valueBox != nullptr) if (valueBox != nullptr)
valueBox->hideEditor (true); valueBox->hideEditor (true);
sliderBeingDragged = 0; sliderBeingDragged = getThumbIndexAt (e);
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;
}
}
minMaxDiff = (double) valueMax.getValue() - (double) valueMin.getValue(); minMaxDiff = (double) valueMax.getValue() - (double) valueMin.getValue();
lastAngle = rotaryStart + (rotaryEnd - rotaryStart) lastAngle = rotaryStart + (rotaryEnd - rotaryStart)
* valueToProportionOfLength (currentValue.getValue()); * valueToProportionOfLength (currentValue.getValue());
valueWhenLastDragged = ((sliderBeingDragged == 2) ? valueMax valueWhenLastDragged = (sliderBeingDragged == 2 ? valueMax
: ((sliderBeingDragged == 1) ? valueMin : (sliderBeingDragged == 1 ? valueMin
: currentValue)).getValue(); : currentValue)).getValue();
valueOnMouseDown = valueWhenLastDragged; valueOnMouseDown = valueWhenLastDragged;
if (popupDisplayEnabled) if (popupDisplayEnabled)
@ -1108,7 +1094,6 @@ void Slider::mouseDown (const MouseEvent& e)
} }
sendDragStart(); sendDragStart();
mouseDrag (e); mouseDrag (e);
} }
} }
@ -1150,10 +1135,9 @@ void Slider::restoreMouseIfHidden()
for (int i = Desktop::getInstance().getNumMouseSources(); --i >= 0;) for (int i = Desktop::getInstance().getNumMouseSources(); --i >= 0;)
Desktop::getInstance().getMouseSource(i)->enableUnboundedMouseMovement (false); Desktop::getInstance().getMouseSource(i)->enableUnboundedMouseMovement (false);
const double pos = (sliderBeingDragged == 2) ? getMaxValue() const double pos = sliderBeingDragged == 2 ? getMaxValue()
: ((sliderBeingDragged == 1) ? getMinValue() : (sliderBeingDragged == 1 ? getMinValue()
: (double) currentValue.getValue()); : (double) currentValue.getValue());
Point<int> mousePos; Point<int> mousePos;
if (style == RotaryHorizontalDrag || style == RotaryVerticalDrag) 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), double angle = std::atan2 ((double) dx, (double) -dy);
std::abs (a1 + double_Pi * 2.0 - a2), while (angle < 0.0)
std::abs (a2 + double_Pi * 2.0 - a1)); 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() if (isEnabled()
&& (! menuShown) && (! menuShown)
&& (maximum > minimum)) && (maximum > minimum)
&& ! (style == LinearBar && e.mouseWasClicked() && valueBox != nullptr && valueBox->isEditable()))
{ {
if (style == Rotary) if (style == Rotary)
{ {
const int dx = e.x - sliderRect.getCentreX(); handleRotaryDrag (e);
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;
}
} }
else else
{ {
if (style == LinearBar && e.mouseWasClicked()
&& valueBox != nullptr && valueBox->isEditable())
return;
if (style == IncDecButtons && ! incDecDragged) if (style == IncDecButtons && ! incDecDragged)
{ {
if (e.getDistanceFromDragStart() < 10 || e.mouseWasClicked()) if (e.getDistanceFromDragStart() < 10 || e.mouseWasClicked())
return; return;
incDecDragged = true; incDecDragged = true;
mouseDragStartX = e.x; mouseDragStartPos = e.getPosition();
mouseDragStartY = e.y;
} }
if ((isVelocityBased == (userKeyOverridesVelocity ? e.mods.testFlags (ModifierKeys::ctrlModifier | ModifierKeys::commandModifier | ModifierKeys::altModifier) if (isVelocityBased == (userKeyOverridesVelocity && e.mods.testFlags (ModifierKeys::ctrlModifier
: false)) | ModifierKeys::commandModifier
|| ((maximum - minimum) / sliderRegionSize < interval)) | ModifierKeys::altModifier))
{ || (maximum - minimum) / sliderRegionSize < interval)
const int mousePos = (isHorizontal() || style == RotaryHorizontalDrag) ? e.x : e.y; handleAbsoluteDrag (e);
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));
}
}
else else
{ handleVelocityDrag (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;
}
}
} }
valueWhenLastDragged = jlimit (minimum, maximum, valueWhenLastDragged); valueWhenLastDragged = jlimit (minimum, maximum, valueWhenLastDragged);

View file

@ -827,8 +827,7 @@ private:
int velocityModeThreshold; int velocityModeThreshold;
float rotaryStart, rotaryEnd; float rotaryStart, rotaryEnd;
int numDecimalPlaces; int numDecimalPlaces;
Point<int> mousePosWhenLastDragged; Point<int> mouseDragStartPos, mousePosWhenLastDragged;
int mouseDragStartX, mouseDragStartY;
int sliderRegionStart, sliderRegionSize; int sliderRegionStart, sliderRegionSize;
int sliderBeingDragged; int sliderBeingDragged;
int pixelsForFullDragExtent; int pixelsForFullDragExtent;
@ -854,6 +853,11 @@ private:
ScopedPointer <PopupDisplayComponent> popupDisplay; ScopedPointer <PopupDisplayComponent> popupDisplay;
Component* parentForPopupDisplay; Component* parentForPopupDisplay;
void showPopupMenu();
int getThumbIndexAt (const MouseEvent&);
void handleRotaryDrag (const MouseEvent&);
void handleAbsoluteDrag (const MouseEvent&);
void handleVelocityDrag (const MouseEvent&);
float getLinearSliderPos (double value); float getLinearSliderPos (double value);
void restoreMouseIfHidden(); void restoreMouseIfHidden();
void sendDragStart(); void sendDragStart();