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