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:
parent
9332efc10d
commit
9a9f570781
27 changed files with 460 additions and 463 deletions
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()))
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue