mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
First check-in of the "jucequake": a major re-organisation of the library to break it up into modules. For more details about this, see the website forum..
This commit is contained in:
parent
1a21c89755
commit
b70e0a28d2
1527 changed files with 90380 additions and 396643 deletions
|
|
@ -0,0 +1,613 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
CPlusPlusCodeTokeniser::CPlusPlusCodeTokeniser() {}
|
||||
CPlusPlusCodeTokeniser::~CPlusPlusCodeTokeniser() {}
|
||||
|
||||
//==============================================================================
|
||||
namespace CppTokeniser
|
||||
{
|
||||
bool isIdentifierStart (const juce_wchar c) noexcept
|
||||
{
|
||||
return CharacterFunctions::isLetter (c)
|
||||
|| c == '_' || c == '@';
|
||||
}
|
||||
|
||||
bool isIdentifierBody (const juce_wchar c) noexcept
|
||||
{
|
||||
return CharacterFunctions::isLetterOrDigit (c)
|
||||
|| c == '_' || c == '@';
|
||||
}
|
||||
|
||||
bool isReservedKeyword (String::CharPointerType token, const int tokenLength) noexcept
|
||||
{
|
||||
static const char* const keywords2Char[] =
|
||||
{ "if", "do", "or", "id", 0 };
|
||||
|
||||
static const char* const keywords3Char[] =
|
||||
{ "for", "int", "new", "try", "xor", "and", "asm", "not", 0 };
|
||||
|
||||
static const char* const keywords4Char[] =
|
||||
{ "bool", "void", "this", "true", "long", "else", "char",
|
||||
"enum", "case", "goto", "auto", 0 };
|
||||
|
||||
static const char* const keywords5Char[] =
|
||||
{ "while", "bitor", "break", "catch", "class", "compl", "const", "false",
|
||||
"float", "short", "throw", "union", "using", "or_eq", 0 };
|
||||
|
||||
static const char* const keywords6Char[] =
|
||||
{ "return", "struct", "and_eq", "bitand", "delete", "double", "extern",
|
||||
"friend", "inline", "not_eq", "public", "sizeof", "static", "signed",
|
||||
"switch", "typeid", "wchar_t", "xor_eq", 0};
|
||||
|
||||
static const char* const keywords7Char[] =
|
||||
{ "default", "mutable", "private", "typedef", "nullptr", "virtual", 0 };
|
||||
|
||||
static const char* const keywordsOther[] =
|
||||
{ "noexcept", "const_cast", "continue", "explicit", "namespace",
|
||||
"operator", "protected", "register", "reinterpret_cast", "static_cast",
|
||||
"template", "typename", "unsigned", "volatile", "constexpr",
|
||||
"@implementation", "@interface", "@end", "@synthesize", "@dynamic", "@public",
|
||||
"@private", "@property", "@protected", "@class", 0 };
|
||||
|
||||
const char* const* k;
|
||||
|
||||
switch (tokenLength)
|
||||
{
|
||||
case 2: k = keywords2Char; break;
|
||||
case 3: k = keywords3Char; break;
|
||||
case 4: k = keywords4Char; break;
|
||||
case 5: k = keywords5Char; break;
|
||||
case 6: k = keywords6Char; break;
|
||||
case 7: k = keywords7Char; break;
|
||||
|
||||
default:
|
||||
if (tokenLength < 2 || tokenLength > 16)
|
||||
return false;
|
||||
|
||||
k = keywordsOther;
|
||||
break;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
while (k[i] != 0)
|
||||
{
|
||||
if (token.compare (CharPointer_ASCII (k[i])) == 0)
|
||||
return true;
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int parseIdentifier (CodeDocument::Iterator& source) noexcept
|
||||
{
|
||||
int tokenLength = 0;
|
||||
String::CharPointerType::CharType possibleIdentifier [100];
|
||||
String::CharPointerType possible (possibleIdentifier);
|
||||
|
||||
while (isIdentifierBody (source.peekNextChar()))
|
||||
{
|
||||
const juce_wchar c = source.nextChar();
|
||||
|
||||
if (tokenLength < 20)
|
||||
possible.write (c);
|
||||
|
||||
++tokenLength;
|
||||
}
|
||||
|
||||
if (tokenLength > 1 && tokenLength <= 16)
|
||||
{
|
||||
possible.writeNull();
|
||||
|
||||
if (isReservedKeyword (String::CharPointerType (possibleIdentifier), tokenLength))
|
||||
return CPlusPlusCodeTokeniser::tokenType_builtInKeyword;
|
||||
}
|
||||
|
||||
return CPlusPlusCodeTokeniser::tokenType_identifier;
|
||||
}
|
||||
|
||||
bool skipNumberSuffix (CodeDocument::Iterator& source)
|
||||
{
|
||||
const juce_wchar c = source.peekNextChar();
|
||||
if (c == 'l' || c == 'L' || c == 'u' || c == 'U')
|
||||
source.skip();
|
||||
|
||||
if (CharacterFunctions::isLetterOrDigit (source.peekNextChar()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isHexDigit (const juce_wchar c) noexcept
|
||||
{
|
||||
return (c >= '0' && c <= '9')
|
||||
|| (c >= 'a' && c <= 'f')
|
||||
|| (c >= 'A' && c <= 'F');
|
||||
}
|
||||
|
||||
bool parseHexLiteral (CodeDocument::Iterator& source) noexcept
|
||||
{
|
||||
if (source.nextChar() != '0')
|
||||
return false;
|
||||
|
||||
juce_wchar c = source.nextChar();
|
||||
if (c != 'x' && c != 'X')
|
||||
return false;
|
||||
|
||||
int numDigits = 0;
|
||||
while (isHexDigit (source.peekNextChar()))
|
||||
{
|
||||
++numDigits;
|
||||
source.skip();
|
||||
}
|
||||
|
||||
if (numDigits == 0)
|
||||
return false;
|
||||
|
||||
return skipNumberSuffix (source);
|
||||
}
|
||||
|
||||
bool isOctalDigit (const juce_wchar c) noexcept
|
||||
{
|
||||
return c >= '0' && c <= '7';
|
||||
}
|
||||
|
||||
bool parseOctalLiteral (CodeDocument::Iterator& source) noexcept
|
||||
{
|
||||
if (source.nextChar() != '0')
|
||||
return false;
|
||||
|
||||
if (! isOctalDigit (source.nextChar()))
|
||||
return false;
|
||||
|
||||
while (isOctalDigit (source.peekNextChar()))
|
||||
source.skip();
|
||||
|
||||
return skipNumberSuffix (source);
|
||||
}
|
||||
|
||||
bool isDecimalDigit (const juce_wchar c) noexcept
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
bool parseDecimalLiteral (CodeDocument::Iterator& source) noexcept
|
||||
{
|
||||
int numChars = 0;
|
||||
while (isDecimalDigit (source.peekNextChar()))
|
||||
{
|
||||
++numChars;
|
||||
source.skip();
|
||||
}
|
||||
|
||||
if (numChars == 0)
|
||||
return false;
|
||||
|
||||
return skipNumberSuffix (source);
|
||||
}
|
||||
|
||||
bool parseFloatLiteral (CodeDocument::Iterator& source) noexcept
|
||||
{
|
||||
int numDigits = 0;
|
||||
|
||||
while (isDecimalDigit (source.peekNextChar()))
|
||||
{
|
||||
source.skip();
|
||||
++numDigits;
|
||||
}
|
||||
|
||||
const bool hasPoint = (source.peekNextChar() == '.');
|
||||
|
||||
if (hasPoint)
|
||||
{
|
||||
source.skip();
|
||||
|
||||
while (isDecimalDigit (source.peekNextChar()))
|
||||
{
|
||||
source.skip();
|
||||
++numDigits;
|
||||
}
|
||||
}
|
||||
|
||||
if (numDigits == 0)
|
||||
return false;
|
||||
|
||||
juce_wchar c = source.peekNextChar();
|
||||
const bool hasExponent = (c == 'e' || c == 'E');
|
||||
|
||||
if (hasExponent)
|
||||
{
|
||||
source.skip();
|
||||
|
||||
c = source.peekNextChar();
|
||||
if (c == '+' || c == '-')
|
||||
source.skip();
|
||||
|
||||
int numExpDigits = 0;
|
||||
while (isDecimalDigit (source.peekNextChar()))
|
||||
{
|
||||
source.skip();
|
||||
++numExpDigits;
|
||||
}
|
||||
|
||||
if (numExpDigits == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
c = source.peekNextChar();
|
||||
if (c == 'f' || c == 'F')
|
||||
source.skip();
|
||||
else if (! (hasExponent || hasPoint))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int parseNumber (CodeDocument::Iterator& source)
|
||||
{
|
||||
const CodeDocument::Iterator original (source);
|
||||
|
||||
if (parseFloatLiteral (source))
|
||||
return CPlusPlusCodeTokeniser::tokenType_floatLiteral;
|
||||
|
||||
source = original;
|
||||
|
||||
if (parseHexLiteral (source))
|
||||
return CPlusPlusCodeTokeniser::tokenType_integerLiteral;
|
||||
|
||||
source = original;
|
||||
|
||||
if (parseOctalLiteral (source))
|
||||
return CPlusPlusCodeTokeniser::tokenType_integerLiteral;
|
||||
|
||||
source = original;
|
||||
|
||||
if (parseDecimalLiteral (source))
|
||||
return CPlusPlusCodeTokeniser::tokenType_integerLiteral;
|
||||
|
||||
source = original;
|
||||
source.skip();
|
||||
|
||||
return CPlusPlusCodeTokeniser::tokenType_error;
|
||||
}
|
||||
|
||||
void skipQuotedString (CodeDocument::Iterator& source) noexcept
|
||||
{
|
||||
const juce_wchar quote = source.nextChar();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const juce_wchar c = source.nextChar();
|
||||
|
||||
if (c == quote || c == 0)
|
||||
break;
|
||||
|
||||
if (c == '\\')
|
||||
source.skip();
|
||||
}
|
||||
}
|
||||
|
||||
void skipComment (CodeDocument::Iterator& source) noexcept
|
||||
{
|
||||
bool lastWasStar = false;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const juce_wchar c = source.nextChar();
|
||||
|
||||
if (c == 0 || (c == '/' && lastWasStar))
|
||||
break;
|
||||
|
||||
lastWasStar = (c == '*');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int CPlusPlusCodeTokeniser::readNextToken (CodeDocument::Iterator& source)
|
||||
{
|
||||
int result = tokenType_error;
|
||||
source.skipWhitespace();
|
||||
|
||||
juce_wchar firstChar = source.peekNextChar();
|
||||
|
||||
switch (firstChar)
|
||||
{
|
||||
case 0:
|
||||
source.skip();
|
||||
break;
|
||||
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
result = CppTokeniser::parseNumber (source);
|
||||
break;
|
||||
|
||||
case '.':
|
||||
result = CppTokeniser::parseNumber (source);
|
||||
|
||||
if (result == tokenType_error)
|
||||
result = tokenType_punctuation;
|
||||
|
||||
break;
|
||||
|
||||
case ',':
|
||||
case ';':
|
||||
case ':':
|
||||
source.skip();
|
||||
result = tokenType_punctuation;
|
||||
break;
|
||||
|
||||
case '(':
|
||||
case ')':
|
||||
case '{':
|
||||
case '}':
|
||||
case '[':
|
||||
case ']':
|
||||
source.skip();
|
||||
result = tokenType_bracket;
|
||||
break;
|
||||
|
||||
case '"':
|
||||
case '\'':
|
||||
CppTokeniser::skipQuotedString (source);
|
||||
result = tokenType_stringLiteral;
|
||||
break;
|
||||
|
||||
case '+':
|
||||
result = tokenType_operator;
|
||||
source.skip();
|
||||
|
||||
if (source.peekNextChar() == '+')
|
||||
source.skip();
|
||||
else if (source.peekNextChar() == '=')
|
||||
source.skip();
|
||||
|
||||
break;
|
||||
|
||||
case '-':
|
||||
source.skip();
|
||||
result = CppTokeniser::parseNumber (source);
|
||||
|
||||
if (result == tokenType_error)
|
||||
{
|
||||
result = tokenType_operator;
|
||||
|
||||
if (source.peekNextChar() == '-')
|
||||
source.skip();
|
||||
else if (source.peekNextChar() == '=')
|
||||
source.skip();
|
||||
}
|
||||
break;
|
||||
|
||||
case '*':
|
||||
case '%':
|
||||
case '=':
|
||||
case '!':
|
||||
result = tokenType_operator;
|
||||
source.skip();
|
||||
|
||||
if (source.peekNextChar() == '=')
|
||||
source.skip();
|
||||
|
||||
break;
|
||||
|
||||
case '/':
|
||||
result = tokenType_operator;
|
||||
source.skip();
|
||||
|
||||
if (source.peekNextChar() == '=')
|
||||
{
|
||||
source.skip();
|
||||
}
|
||||
else if (source.peekNextChar() == '/')
|
||||
{
|
||||
result = tokenType_comment;
|
||||
source.skipToEndOfLine();
|
||||
}
|
||||
else if (source.peekNextChar() == '*')
|
||||
{
|
||||
source.skip();
|
||||
result = tokenType_comment;
|
||||
CppTokeniser::skipComment (source);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '?':
|
||||
case '~':
|
||||
source.skip();
|
||||
result = tokenType_operator;
|
||||
break;
|
||||
|
||||
case '<':
|
||||
source.skip();
|
||||
result = tokenType_operator;
|
||||
|
||||
if (source.peekNextChar() == '=')
|
||||
{
|
||||
source.skip();
|
||||
}
|
||||
else if (source.peekNextChar() == '<')
|
||||
{
|
||||
source.skip();
|
||||
|
||||
if (source.peekNextChar() == '=')
|
||||
source.skip();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '>':
|
||||
source.skip();
|
||||
result = tokenType_operator;
|
||||
|
||||
if (source.peekNextChar() == '=')
|
||||
{
|
||||
source.skip();
|
||||
}
|
||||
else if (source.peekNextChar() == '<')
|
||||
{
|
||||
source.skip();
|
||||
|
||||
if (source.peekNextChar() == '=')
|
||||
source.skip();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '|':
|
||||
source.skip();
|
||||
result = tokenType_operator;
|
||||
|
||||
if (source.peekNextChar() == '=')
|
||||
{
|
||||
source.skip();
|
||||
}
|
||||
else if (source.peekNextChar() == '|')
|
||||
{
|
||||
source.skip();
|
||||
|
||||
if (source.peekNextChar() == '=')
|
||||
source.skip();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '&':
|
||||
source.skip();
|
||||
result = tokenType_operator;
|
||||
|
||||
if (source.peekNextChar() == '=')
|
||||
{
|
||||
source.skip();
|
||||
}
|
||||
else if (source.peekNextChar() == '&')
|
||||
{
|
||||
source.skip();
|
||||
|
||||
if (source.peekNextChar() == '=')
|
||||
source.skip();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '^':
|
||||
source.skip();
|
||||
result = tokenType_operator;
|
||||
|
||||
if (source.peekNextChar() == '=')
|
||||
{
|
||||
source.skip();
|
||||
}
|
||||
else if (source.peekNextChar() == '^')
|
||||
{
|
||||
source.skip();
|
||||
|
||||
if (source.peekNextChar() == '=')
|
||||
source.skip();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '#':
|
||||
result = tokenType_preprocessor;
|
||||
source.skipToEndOfLine();
|
||||
break;
|
||||
|
||||
default:
|
||||
if (CppTokeniser::isIdentifierStart (firstChar))
|
||||
result = CppTokeniser::parseIdentifier (source);
|
||||
else
|
||||
source.skip();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
StringArray CPlusPlusCodeTokeniser::getTokenTypes()
|
||||
{
|
||||
const char* const types[] =
|
||||
{
|
||||
"Error",
|
||||
"Comment",
|
||||
"C++ keyword",
|
||||
"Identifier",
|
||||
"Integer literal",
|
||||
"Float literal",
|
||||
"String literal",
|
||||
"Operator",
|
||||
"Bracket",
|
||||
"Punctuation",
|
||||
"Preprocessor line",
|
||||
0
|
||||
};
|
||||
|
||||
return StringArray (types);
|
||||
}
|
||||
|
||||
const Colour CPlusPlusCodeTokeniser::getDefaultColour (const int tokenType)
|
||||
{
|
||||
const uint32 colours[] =
|
||||
{
|
||||
0xffcc0000, // error
|
||||
0xff00aa00, // comment
|
||||
0xff0000cc, // keyword
|
||||
0xff000000, // identifier
|
||||
0xff880000, // int literal
|
||||
0xff885500, // float literal
|
||||
0xff990099, // string literal
|
||||
0xff225500, // operator
|
||||
0xff000055, // bracket
|
||||
0xff004400, // punctuation
|
||||
0xff660000 // preprocessor
|
||||
};
|
||||
|
||||
if (tokenType >= 0 && tokenType < numElementsInArray (colours))
|
||||
return Colour (colours [tokenType]);
|
||||
|
||||
return Colours::black;
|
||||
}
|
||||
|
||||
bool CPlusPlusCodeTokeniser::isReservedKeyword (const String& token) noexcept
|
||||
{
|
||||
return CppTokeniser::isReservedKeyword (token.getCharPointer(), token.length());
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_CPLUSPLUSCODETOKENISER_JUCEHEADER__
|
||||
#define __JUCE_CPLUSPLUSCODETOKENISER_JUCEHEADER__
|
||||
|
||||
#include "juce_CodeTokeniser.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A simple lexical analyser for syntax colouring of C++ code.
|
||||
|
||||
@see SyntaxAnalyser, CodeEditorComponent, CodeDocument
|
||||
*/
|
||||
class JUCE_API CPlusPlusCodeTokeniser : public CodeTokeniser
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
CPlusPlusCodeTokeniser();
|
||||
~CPlusPlusCodeTokeniser();
|
||||
|
||||
//==============================================================================
|
||||
enum TokenType
|
||||
{
|
||||
tokenType_error = 0,
|
||||
tokenType_comment,
|
||||
tokenType_builtInKeyword,
|
||||
tokenType_identifier,
|
||||
tokenType_integerLiteral,
|
||||
tokenType_floatLiteral,
|
||||
tokenType_stringLiteral,
|
||||
tokenType_operator,
|
||||
tokenType_bracket,
|
||||
tokenType_punctuation,
|
||||
tokenType_preprocessor
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
int readNextToken (CodeDocument::Iterator& source);
|
||||
StringArray getTokenTypes();
|
||||
const Colour getDefaultColour (int tokenType);
|
||||
|
||||
/** This is a handy method for checking whether a string is a c++ reserved keyword. */
|
||||
static bool isReservedKeyword (const String& token) noexcept;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_LEAK_DETECTOR (CPlusPlusCodeTokeniser);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_CPLUSPLUSCODETOKENISER_JUCEHEADER__
|
||||
969
modules/juce_gui_extra/code_editor/juce_CodeDocument.cpp
Normal file
969
modules/juce_gui_extra/code_editor/juce_CodeDocument.cpp
Normal file
|
|
@ -0,0 +1,969 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
class CodeDocumentLine
|
||||
{
|
||||
public:
|
||||
CodeDocumentLine (const String::CharPointerType& line_,
|
||||
const int lineLength_,
|
||||
const int numNewLineChars,
|
||||
const int lineStartInFile_)
|
||||
: line (line_, (size_t) lineLength_),
|
||||
lineStartInFile (lineStartInFile_),
|
||||
lineLength (lineLength_),
|
||||
lineLengthWithoutNewLines (lineLength_ - numNewLineChars)
|
||||
{
|
||||
}
|
||||
|
||||
static void createLines (Array <CodeDocumentLine*>& newLines, const String& text)
|
||||
{
|
||||
String::CharPointerType t (text.getCharPointer());
|
||||
int charNumInFile = 0;
|
||||
bool finished = false;
|
||||
|
||||
while (! (finished || t.isEmpty()))
|
||||
{
|
||||
String::CharPointerType startOfLine (t);
|
||||
int startOfLineInFile = charNumInFile;
|
||||
int lineLength = 0;
|
||||
int numNewLineChars = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const juce_wchar c = t.getAndAdvance();
|
||||
|
||||
if (c == 0)
|
||||
{
|
||||
finished = true;
|
||||
break;
|
||||
}
|
||||
|
||||
++charNumInFile;
|
||||
++lineLength;
|
||||
|
||||
if (c == '\r')
|
||||
{
|
||||
++numNewLineChars;
|
||||
|
||||
if (*t == '\n')
|
||||
{
|
||||
++t;
|
||||
++charNumInFile;
|
||||
++lineLength;
|
||||
++numNewLineChars;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == '\n')
|
||||
{
|
||||
++numNewLineChars;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
newLines.add (new CodeDocumentLine (startOfLine, lineLength,
|
||||
numNewLineChars, startOfLineInFile));
|
||||
}
|
||||
|
||||
jassert (charNumInFile == text.length());
|
||||
}
|
||||
|
||||
bool endsWithLineBreak() const noexcept
|
||||
{
|
||||
return lineLengthWithoutNewLines != lineLength;
|
||||
}
|
||||
|
||||
void updateLength() noexcept
|
||||
{
|
||||
lineLength = 0;
|
||||
lineLengthWithoutNewLines = 0;
|
||||
|
||||
String::CharPointerType t (line.getCharPointer());
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const juce_wchar c = t.getAndAdvance();
|
||||
|
||||
if (c == 0)
|
||||
break;
|
||||
|
||||
++lineLength;
|
||||
|
||||
if (c != '\n' && c != '\r')
|
||||
lineLengthWithoutNewLines = lineLength;
|
||||
}
|
||||
}
|
||||
|
||||
String line;
|
||||
int lineStartInFile, lineLength, lineLengthWithoutNewLines;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
CodeDocument::Iterator::Iterator (CodeDocument* const document_)
|
||||
: document (document_),
|
||||
charPointer (nullptr),
|
||||
line (0),
|
||||
position (0)
|
||||
{
|
||||
}
|
||||
|
||||
CodeDocument::Iterator::Iterator (const CodeDocument::Iterator& other)
|
||||
: document (other.document),
|
||||
charPointer (other.charPointer),
|
||||
line (other.line),
|
||||
position (other.position)
|
||||
{
|
||||
}
|
||||
|
||||
CodeDocument::Iterator& CodeDocument::Iterator::operator= (const CodeDocument::Iterator& other) noexcept
|
||||
{
|
||||
document = other.document;
|
||||
charPointer = other.charPointer;
|
||||
line = other.line;
|
||||
position = other.position;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CodeDocument::Iterator::~Iterator() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
juce_wchar CodeDocument::Iterator::nextChar()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (charPointer.getAddress() == nullptr)
|
||||
{
|
||||
CodeDocumentLine* const l = document->lines[line];
|
||||
|
||||
if (l == nullptr)
|
||||
return 0;
|
||||
|
||||
charPointer = l->line.getCharPointer();
|
||||
}
|
||||
|
||||
const juce_wchar result = charPointer.getAndAdvance();
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
++line;
|
||||
charPointer = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
++position;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CodeDocument::Iterator::skip()
|
||||
{
|
||||
nextChar();
|
||||
}
|
||||
|
||||
void CodeDocument::Iterator::skipToEndOfLine()
|
||||
{
|
||||
if (charPointer.getAddress() == nullptr)
|
||||
{
|
||||
CodeDocumentLine* const l = document->lines[line];
|
||||
|
||||
if (l == nullptr)
|
||||
return;
|
||||
|
||||
charPointer = l->line.getCharPointer();
|
||||
}
|
||||
|
||||
position += (int) charPointer.length();
|
||||
++line;
|
||||
charPointer = nullptr;
|
||||
}
|
||||
|
||||
juce_wchar CodeDocument::Iterator::peekNextChar() const
|
||||
{
|
||||
if (charPointer.getAddress() == nullptr)
|
||||
{
|
||||
CodeDocumentLine* const l = document->lines[line];
|
||||
|
||||
if (l == nullptr)
|
||||
return 0;
|
||||
|
||||
charPointer = l->line.getCharPointer();
|
||||
}
|
||||
|
||||
const juce_wchar c = *charPointer;
|
||||
|
||||
if (c != 0)
|
||||
return c;
|
||||
|
||||
CodeDocumentLine* const l = document->lines [line + 1];
|
||||
return l == nullptr ? 0 : l->line[0];
|
||||
}
|
||||
|
||||
void CodeDocument::Iterator::skipWhitespace()
|
||||
{
|
||||
while (CharacterFunctions::isWhitespace (peekNextChar()))
|
||||
skip();
|
||||
}
|
||||
|
||||
bool CodeDocument::Iterator::isEOF() const noexcept
|
||||
{
|
||||
return charPointer.getAddress() == nullptr && line >= document->lines.size();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
CodeDocument::Position::Position() noexcept
|
||||
: owner (0), characterPos (0), line (0),
|
||||
indexInLine (0), positionMaintained (false)
|
||||
{
|
||||
}
|
||||
|
||||
CodeDocument::Position::Position (const CodeDocument* const ownerDocument,
|
||||
const int line_, const int indexInLine_) noexcept
|
||||
: owner (const_cast <CodeDocument*> (ownerDocument)),
|
||||
characterPos (0), line (line_),
|
||||
indexInLine (indexInLine_), positionMaintained (false)
|
||||
{
|
||||
setLineAndIndex (line_, indexInLine_);
|
||||
}
|
||||
|
||||
CodeDocument::Position::Position (const CodeDocument* const ownerDocument,
|
||||
const int characterPos_) noexcept
|
||||
: owner (const_cast <CodeDocument*> (ownerDocument)),
|
||||
positionMaintained (false)
|
||||
{
|
||||
setPosition (characterPos_);
|
||||
}
|
||||
|
||||
CodeDocument::Position::Position (const Position& other) noexcept
|
||||
: owner (other.owner), characterPos (other.characterPos), line (other.line),
|
||||
indexInLine (other.indexInLine), positionMaintained (false)
|
||||
{
|
||||
jassert (*this == other);
|
||||
}
|
||||
|
||||
CodeDocument::Position::~Position()
|
||||
{
|
||||
setPositionMaintained (false);
|
||||
}
|
||||
|
||||
CodeDocument::Position& CodeDocument::Position::operator= (const Position& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
const bool wasPositionMaintained = positionMaintained;
|
||||
if (owner != other.owner)
|
||||
setPositionMaintained (false);
|
||||
|
||||
owner = other.owner;
|
||||
line = other.line;
|
||||
indexInLine = other.indexInLine;
|
||||
characterPos = other.characterPos;
|
||||
setPositionMaintained (wasPositionMaintained);
|
||||
|
||||
jassert (*this == other);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool CodeDocument::Position::operator== (const Position& other) const noexcept
|
||||
{
|
||||
jassert ((characterPos == other.characterPos)
|
||||
== (line == other.line && indexInLine == other.indexInLine));
|
||||
|
||||
return characterPos == other.characterPos
|
||||
&& line == other.line
|
||||
&& indexInLine == other.indexInLine
|
||||
&& owner == other.owner;
|
||||
}
|
||||
|
||||
bool CodeDocument::Position::operator!= (const Position& other) const noexcept
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
void CodeDocument::Position::setLineAndIndex (const int newLineNum, const int newIndexInLine)
|
||||
{
|
||||
jassert (owner != nullptr);
|
||||
|
||||
if (owner->lines.size() == 0)
|
||||
{
|
||||
line = 0;
|
||||
indexInLine = 0;
|
||||
characterPos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (newLineNum >= owner->lines.size())
|
||||
{
|
||||
line = owner->lines.size() - 1;
|
||||
|
||||
CodeDocumentLine* const l = owner->lines.getUnchecked (line);
|
||||
jassert (l != nullptr);
|
||||
|
||||
indexInLine = l->lineLengthWithoutNewLines;
|
||||
characterPos = l->lineStartInFile + indexInLine;
|
||||
}
|
||||
else
|
||||
{
|
||||
line = jmax (0, newLineNum);
|
||||
|
||||
CodeDocumentLine* const l = owner->lines.getUnchecked (line);
|
||||
jassert (l != nullptr);
|
||||
|
||||
if (l->lineLengthWithoutNewLines > 0)
|
||||
indexInLine = jlimit (0, l->lineLengthWithoutNewLines, newIndexInLine);
|
||||
else
|
||||
indexInLine = 0;
|
||||
|
||||
characterPos = l->lineStartInFile + indexInLine;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CodeDocument::Position::setPosition (const int newPosition)
|
||||
{
|
||||
jassert (owner != nullptr);
|
||||
|
||||
line = 0;
|
||||
indexInLine = 0;
|
||||
characterPos = 0;
|
||||
|
||||
if (newPosition > 0)
|
||||
{
|
||||
int lineStart = 0;
|
||||
int lineEnd = owner->lines.size();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (lineEnd - lineStart < 4)
|
||||
{
|
||||
for (int i = lineStart; i < lineEnd; ++i)
|
||||
{
|
||||
CodeDocumentLine* const l = owner->lines.getUnchecked (i);
|
||||
|
||||
int index = newPosition - l->lineStartInFile;
|
||||
|
||||
if (index >= 0 && (index < l->lineLength || i == lineEnd - 1))
|
||||
{
|
||||
line = i;
|
||||
indexInLine = jmin (l->lineLengthWithoutNewLines, index);
|
||||
characterPos = l->lineStartInFile + indexInLine;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
const int midIndex = (lineStart + lineEnd + 1) / 2;
|
||||
CodeDocumentLine* const mid = owner->lines.getUnchecked (midIndex);
|
||||
|
||||
if (newPosition >= mid->lineStartInFile)
|
||||
lineStart = midIndex;
|
||||
else
|
||||
lineEnd = midIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CodeDocument::Position::moveBy (int characterDelta)
|
||||
{
|
||||
jassert (owner != nullptr);
|
||||
|
||||
if (characterDelta == 1)
|
||||
{
|
||||
setPosition (getPosition());
|
||||
|
||||
// If moving right, make sure we don't get stuck between the \r and \n characters..
|
||||
if (line < owner->lines.size())
|
||||
{
|
||||
CodeDocumentLine* const l = owner->lines.getUnchecked (line);
|
||||
if (indexInLine + characterDelta < l->lineLength
|
||||
&& indexInLine + characterDelta >= l->lineLengthWithoutNewLines + 1)
|
||||
++characterDelta;
|
||||
}
|
||||
}
|
||||
|
||||
setPosition (characterPos + characterDelta);
|
||||
}
|
||||
|
||||
const CodeDocument::Position CodeDocument::Position::movedBy (const int characterDelta) const
|
||||
{
|
||||
CodeDocument::Position p (*this);
|
||||
p.moveBy (characterDelta);
|
||||
return p;
|
||||
}
|
||||
|
||||
const CodeDocument::Position CodeDocument::Position::movedByLines (const int deltaLines) const
|
||||
{
|
||||
CodeDocument::Position p (*this);
|
||||
p.setLineAndIndex (getLineNumber() + deltaLines, getIndexInLine());
|
||||
return p;
|
||||
}
|
||||
|
||||
const juce_wchar CodeDocument::Position::getCharacter() const
|
||||
{
|
||||
const CodeDocumentLine* const l = owner->lines [line];
|
||||
return l == nullptr ? 0 : l->line [getIndexInLine()];
|
||||
}
|
||||
|
||||
String CodeDocument::Position::getLineText() const
|
||||
{
|
||||
const CodeDocumentLine* const l = owner->lines [line];
|
||||
return l == nullptr ? String::empty : l->line;
|
||||
}
|
||||
|
||||
void CodeDocument::Position::setPositionMaintained (const bool isMaintained)
|
||||
{
|
||||
if (isMaintained != positionMaintained)
|
||||
{
|
||||
positionMaintained = isMaintained;
|
||||
|
||||
if (owner != nullptr)
|
||||
{
|
||||
if (isMaintained)
|
||||
{
|
||||
jassert (! owner->positionsToMaintain.contains (this));
|
||||
owner->positionsToMaintain.add (this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If this happens, you may have deleted the document while there are Position objects that are still using it...
|
||||
jassert (owner->positionsToMaintain.contains (this));
|
||||
owner->positionsToMaintain.removeValue (this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
CodeDocument::CodeDocument()
|
||||
: undoManager (std::numeric_limits<int>::max(), 10000),
|
||||
currentActionIndex (0),
|
||||
indexOfSavedState (-1),
|
||||
maximumLineLength (-1),
|
||||
newLineChars ("\r\n")
|
||||
{
|
||||
}
|
||||
|
||||
CodeDocument::~CodeDocument()
|
||||
{
|
||||
}
|
||||
|
||||
String CodeDocument::getAllContent() const
|
||||
{
|
||||
return getTextBetween (Position (this, 0),
|
||||
Position (this, lines.size(), 0));
|
||||
}
|
||||
|
||||
String CodeDocument::getTextBetween (const Position& start, const Position& end) const
|
||||
{
|
||||
if (end.getPosition() <= start.getPosition())
|
||||
return String::empty;
|
||||
|
||||
const int startLine = start.getLineNumber();
|
||||
const int endLine = end.getLineNumber();
|
||||
|
||||
if (startLine == endLine)
|
||||
{
|
||||
CodeDocumentLine* const line = lines [startLine];
|
||||
return (line == nullptr) ? String::empty : line->line.substring (start.getIndexInLine(), end.getIndexInLine());
|
||||
}
|
||||
|
||||
MemoryOutputStream mo;
|
||||
mo.preallocate ((size_t) (end.getPosition() - start.getPosition() + 4));
|
||||
|
||||
const int maxLine = jmin (lines.size() - 1, endLine);
|
||||
|
||||
for (int i = jmax (0, startLine); i <= maxLine; ++i)
|
||||
{
|
||||
const CodeDocumentLine* line = lines.getUnchecked(i);
|
||||
int len = line->lineLength;
|
||||
|
||||
if (i == startLine)
|
||||
{
|
||||
const int index = start.getIndexInLine();
|
||||
mo << line->line.substring (index, len);
|
||||
}
|
||||
else if (i == endLine)
|
||||
{
|
||||
len = end.getIndexInLine();
|
||||
mo << line->line.substring (0, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
mo << line->line;
|
||||
}
|
||||
}
|
||||
|
||||
return mo.toString();
|
||||
}
|
||||
|
||||
int CodeDocument::getNumCharacters() const noexcept
|
||||
{
|
||||
const CodeDocumentLine* const lastLine = lines.getLast();
|
||||
return (lastLine == nullptr) ? 0 : lastLine->lineStartInFile + lastLine->lineLength;
|
||||
}
|
||||
|
||||
String CodeDocument::getLine (const int lineIndex) const noexcept
|
||||
{
|
||||
const CodeDocumentLine* const line = lines [lineIndex];
|
||||
return (line == nullptr) ? String::empty : line->line;
|
||||
}
|
||||
|
||||
int CodeDocument::getMaximumLineLength() noexcept
|
||||
{
|
||||
if (maximumLineLength < 0)
|
||||
{
|
||||
maximumLineLength = 0;
|
||||
|
||||
for (int i = lines.size(); --i >= 0;)
|
||||
maximumLineLength = jmax (maximumLineLength, lines.getUnchecked(i)->lineLength);
|
||||
}
|
||||
|
||||
return maximumLineLength;
|
||||
}
|
||||
|
||||
void CodeDocument::deleteSection (const Position& startPosition, const Position& endPosition)
|
||||
{
|
||||
remove (startPosition.getPosition(), endPosition.getPosition(), true);
|
||||
}
|
||||
|
||||
void CodeDocument::insertText (const Position& position, const String& text)
|
||||
{
|
||||
insert (text, position.getPosition(), true);
|
||||
}
|
||||
|
||||
void CodeDocument::replaceAllContent (const String& newContent)
|
||||
{
|
||||
remove (0, getNumCharacters(), true);
|
||||
insert (newContent, 0, true);
|
||||
}
|
||||
|
||||
bool CodeDocument::loadFromStream (InputStream& stream)
|
||||
{
|
||||
remove (0, getNumCharacters(), false);
|
||||
insert (stream.readEntireStreamAsString(), 0, false);
|
||||
setSavePoint();
|
||||
clearUndoHistory();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CodeDocument::writeToStream (OutputStream& stream)
|
||||
{
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
{
|
||||
String temp (lines.getUnchecked(i)->line); // use a copy to avoid bloating the memory footprint of the stored string.
|
||||
const char* utf8 = temp.toUTF8();
|
||||
|
||||
if (! stream.write (utf8, (int) strlen (utf8)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CodeDocument::setNewLineCharacters (const String& newLineChars_) noexcept
|
||||
{
|
||||
jassert (newLineChars_ == "\r\n" || newLineChars_ == "\n" || newLineChars_ == "\r");
|
||||
newLineChars = newLineChars_;
|
||||
}
|
||||
|
||||
void CodeDocument::newTransaction()
|
||||
{
|
||||
undoManager.beginNewTransaction (String::empty);
|
||||
}
|
||||
|
||||
void CodeDocument::undo()
|
||||
{
|
||||
newTransaction();
|
||||
undoManager.undo();
|
||||
}
|
||||
|
||||
void CodeDocument::redo()
|
||||
{
|
||||
undoManager.redo();
|
||||
}
|
||||
|
||||
void CodeDocument::clearUndoHistory()
|
||||
{
|
||||
undoManager.clearUndoHistory();
|
||||
}
|
||||
|
||||
void CodeDocument::setSavePoint() noexcept
|
||||
{
|
||||
indexOfSavedState = currentActionIndex;
|
||||
}
|
||||
|
||||
bool CodeDocument::hasChangedSinceSavePoint() const noexcept
|
||||
{
|
||||
return currentActionIndex != indexOfSavedState;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
namespace CodeDocumentHelpers
|
||||
{
|
||||
int getCharacterType (const juce_wchar character) noexcept
|
||||
{
|
||||
return (CharacterFunctions::isLetterOrDigit (character) || character == '_')
|
||||
? 2 : (CharacterFunctions::isWhitespace (character) ? 0 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
const CodeDocument::Position CodeDocument::findWordBreakAfter (const Position& position) const noexcept
|
||||
{
|
||||
Position p (position);
|
||||
const int maxDistance = 256;
|
||||
int i = 0;
|
||||
|
||||
while (i < maxDistance
|
||||
&& CharacterFunctions::isWhitespace (p.getCharacter())
|
||||
&& (i == 0 || (p.getCharacter() != '\n'
|
||||
&& p.getCharacter() != '\r')))
|
||||
{
|
||||
++i;
|
||||
p.moveBy (1);
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
const int type = CodeDocumentHelpers::getCharacterType (p.getCharacter());
|
||||
|
||||
while (i < maxDistance && type == CodeDocumentHelpers::getCharacterType (p.getCharacter()))
|
||||
{
|
||||
++i;
|
||||
p.moveBy (1);
|
||||
}
|
||||
|
||||
while (i < maxDistance
|
||||
&& CharacterFunctions::isWhitespace (p.getCharacter())
|
||||
&& (i == 0 || (p.getCharacter() != '\n'
|
||||
&& p.getCharacter() != '\r')))
|
||||
{
|
||||
++i;
|
||||
p.moveBy (1);
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
const CodeDocument::Position CodeDocument::findWordBreakBefore (const Position& position) const noexcept
|
||||
{
|
||||
Position p (position);
|
||||
const int maxDistance = 256;
|
||||
int i = 0;
|
||||
bool stoppedAtLineStart = false;
|
||||
|
||||
while (i < maxDistance)
|
||||
{
|
||||
const juce_wchar c = p.movedBy (-1).getCharacter();
|
||||
|
||||
if (c == '\r' || c == '\n')
|
||||
{
|
||||
stoppedAtLineStart = true;
|
||||
|
||||
if (i > 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (! CharacterFunctions::isWhitespace (c))
|
||||
break;
|
||||
|
||||
p.moveBy (-1);
|
||||
++i;
|
||||
}
|
||||
|
||||
if (i < maxDistance && ! stoppedAtLineStart)
|
||||
{
|
||||
const int type = CodeDocumentHelpers::getCharacterType (p.movedBy (-1).getCharacter());
|
||||
|
||||
while (i < maxDistance && type == CodeDocumentHelpers::getCharacterType (p.movedBy (-1).getCharacter()))
|
||||
{
|
||||
p.moveBy (-1);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void CodeDocument::checkLastLineStatus()
|
||||
{
|
||||
while (lines.size() > 0
|
||||
&& lines.getLast()->lineLength == 0
|
||||
&& (lines.size() == 1 || ! lines.getUnchecked (lines.size() - 2)->endsWithLineBreak()))
|
||||
{
|
||||
// remove any empty lines at the end if the preceding line doesn't end in a newline.
|
||||
lines.removeLast();
|
||||
}
|
||||
|
||||
const CodeDocumentLine* const lastLine = lines.getLast();
|
||||
|
||||
if (lastLine != nullptr && lastLine->endsWithLineBreak())
|
||||
{
|
||||
// check that there's an empty line at the end if the preceding one ends in a newline..
|
||||
lines.add (new CodeDocumentLine (String::empty.getCharPointer(), 0, 0, lastLine->lineStartInFile + lastLine->lineLength));
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void CodeDocument::addListener (CodeDocument::Listener* const listener) noexcept
|
||||
{
|
||||
listeners.add (listener);
|
||||
}
|
||||
|
||||
void CodeDocument::removeListener (CodeDocument::Listener* const listener) noexcept
|
||||
{
|
||||
listeners.remove (listener);
|
||||
}
|
||||
|
||||
void CodeDocument::sendListenerChangeMessage (const int startLine, const int endLine)
|
||||
{
|
||||
Position startPos (this, startLine, 0);
|
||||
Position endPos (this, endLine, 0);
|
||||
|
||||
listeners.call (&CodeDocument::Listener::codeDocumentChanged, startPos, endPos);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class CodeDocumentInsertAction : public UndoableAction
|
||||
{
|
||||
public:
|
||||
CodeDocumentInsertAction (CodeDocument& owner_, const String& text_, const int insertPos_) noexcept
|
||||
: owner (owner_),
|
||||
text (text_),
|
||||
insertPos (insertPos_)
|
||||
{
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
owner.currentActionIndex++;
|
||||
owner.insert (text, insertPos, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
owner.currentActionIndex--;
|
||||
owner.remove (insertPos, insertPos + text.length(), false);
|
||||
return true;
|
||||
}
|
||||
|
||||
int getSizeInUnits() { return text.length() + 32; }
|
||||
|
||||
private:
|
||||
CodeDocument& owner;
|
||||
const String text;
|
||||
int insertPos;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (CodeDocumentInsertAction);
|
||||
};
|
||||
|
||||
void CodeDocument::insert (const String& text, const int insertPos, const bool undoable)
|
||||
{
|
||||
if (text.isEmpty())
|
||||
return;
|
||||
|
||||
if (undoable)
|
||||
{
|
||||
undoManager.perform (new CodeDocumentInsertAction (*this, text, insertPos));
|
||||
}
|
||||
else
|
||||
{
|
||||
Position pos (this, insertPos);
|
||||
const int firstAffectedLine = pos.getLineNumber();
|
||||
int lastAffectedLine = firstAffectedLine + 1;
|
||||
|
||||
CodeDocumentLine* const firstLine = lines [firstAffectedLine];
|
||||
String textInsideOriginalLine (text);
|
||||
|
||||
if (firstLine != nullptr)
|
||||
{
|
||||
const int index = pos.getIndexInLine();
|
||||
textInsideOriginalLine = firstLine->line.substring (0, index)
|
||||
+ textInsideOriginalLine
|
||||
+ firstLine->line.substring (index);
|
||||
}
|
||||
|
||||
maximumLineLength = -1;
|
||||
Array <CodeDocumentLine*> newLines;
|
||||
CodeDocumentLine::createLines (newLines, textInsideOriginalLine);
|
||||
jassert (newLines.size() > 0);
|
||||
|
||||
CodeDocumentLine* const newFirstLine = newLines.getUnchecked (0);
|
||||
newFirstLine->lineStartInFile = firstLine != nullptr ? firstLine->lineStartInFile : 0;
|
||||
lines.set (firstAffectedLine, newFirstLine);
|
||||
|
||||
if (newLines.size() > 1)
|
||||
{
|
||||
for (int i = 1; i < newLines.size(); ++i)
|
||||
{
|
||||
CodeDocumentLine* const l = newLines.getUnchecked (i);
|
||||
lines.insert (firstAffectedLine + i, l);
|
||||
}
|
||||
|
||||
lastAffectedLine = lines.size();
|
||||
}
|
||||
|
||||
int i, lineStart = newFirstLine->lineStartInFile;
|
||||
for (i = firstAffectedLine; i < lines.size(); ++i)
|
||||
{
|
||||
CodeDocumentLine* const l = lines.getUnchecked (i);
|
||||
l->lineStartInFile = lineStart;
|
||||
lineStart += l->lineLength;
|
||||
}
|
||||
|
||||
checkLastLineStatus();
|
||||
|
||||
const int newTextLength = text.length();
|
||||
for (i = 0; i < positionsToMaintain.size(); ++i)
|
||||
{
|
||||
CodeDocument::Position* const p = positionsToMaintain.getUnchecked(i);
|
||||
|
||||
if (p->getPosition() >= insertPos)
|
||||
p->setPosition (p->getPosition() + newTextLength);
|
||||
}
|
||||
|
||||
sendListenerChangeMessage (firstAffectedLine, lastAffectedLine);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class CodeDocumentDeleteAction : public UndoableAction
|
||||
{
|
||||
public:
|
||||
CodeDocumentDeleteAction (CodeDocument& owner_, const int startPos_, const int endPos_) noexcept
|
||||
: owner (owner_),
|
||||
startPos (startPos_),
|
||||
endPos (endPos_)
|
||||
{
|
||||
removedText = owner.getTextBetween (CodeDocument::Position (&owner, startPos),
|
||||
CodeDocument::Position (&owner, endPos));
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
owner.currentActionIndex++;
|
||||
owner.remove (startPos, endPos, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
owner.currentActionIndex--;
|
||||
owner.insert (removedText, startPos, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
int getSizeInUnits() { return removedText.length() + 32; }
|
||||
|
||||
private:
|
||||
CodeDocument& owner;
|
||||
int startPos, endPos;
|
||||
String removedText;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (CodeDocumentDeleteAction);
|
||||
};
|
||||
|
||||
void CodeDocument::remove (const int startPos, const int endPos, const bool undoable)
|
||||
{
|
||||
if (endPos <= startPos)
|
||||
return;
|
||||
|
||||
if (undoable)
|
||||
{
|
||||
undoManager.perform (new CodeDocumentDeleteAction (*this, startPos, endPos));
|
||||
}
|
||||
else
|
||||
{
|
||||
Position startPosition (this, startPos);
|
||||
Position endPosition (this, endPos);
|
||||
|
||||
maximumLineLength = -1;
|
||||
const int firstAffectedLine = startPosition.getLineNumber();
|
||||
const int endLine = endPosition.getLineNumber();
|
||||
int lastAffectedLine = firstAffectedLine + 1;
|
||||
CodeDocumentLine* const firstLine = lines.getUnchecked (firstAffectedLine);
|
||||
|
||||
if (firstAffectedLine == endLine)
|
||||
{
|
||||
firstLine->line = firstLine->line.substring (0, startPosition.getIndexInLine())
|
||||
+ firstLine->line.substring (endPosition.getIndexInLine());
|
||||
firstLine->updateLength();
|
||||
}
|
||||
else
|
||||
{
|
||||
lastAffectedLine = lines.size();
|
||||
|
||||
CodeDocumentLine* const lastLine = lines.getUnchecked (endLine);
|
||||
jassert (lastLine != nullptr);
|
||||
|
||||
firstLine->line = firstLine->line.substring (0, startPosition.getIndexInLine())
|
||||
+ lastLine->line.substring (endPosition.getIndexInLine());
|
||||
firstLine->updateLength();
|
||||
|
||||
int numLinesToRemove = endLine - firstAffectedLine;
|
||||
lines.removeRange (firstAffectedLine + 1, numLinesToRemove);
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = firstAffectedLine + 1; i < lines.size(); ++i)
|
||||
{
|
||||
CodeDocumentLine* const l = lines.getUnchecked (i);
|
||||
const CodeDocumentLine* const previousLine = lines.getUnchecked (i - 1);
|
||||
l->lineStartInFile = previousLine->lineStartInFile + previousLine->lineLength;
|
||||
}
|
||||
|
||||
checkLastLineStatus();
|
||||
|
||||
const int totalChars = getNumCharacters();
|
||||
|
||||
for (i = 0; i < positionsToMaintain.size(); ++i)
|
||||
{
|
||||
CodeDocument::Position* p = positionsToMaintain.getUnchecked(i);
|
||||
|
||||
if (p->getPosition() > startPosition.getPosition())
|
||||
p->setPosition (jmax (startPos, p->getPosition() + startPos - endPos));
|
||||
|
||||
if (p->getPosition() > totalChars)
|
||||
p->setPosition (totalChars);
|
||||
}
|
||||
|
||||
sendListenerChangeMessage (firstAffectedLine, lastAffectedLine);
|
||||
}
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
401
modules/juce_gui_extra/code_editor/juce_CodeDocument.h
Normal file
401
modules/juce_gui_extra/code_editor/juce_CodeDocument.h
Normal file
|
|
@ -0,0 +1,401 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_CODEDOCUMENT_JUCEHEADER__
|
||||
#define __JUCE_CODEDOCUMENT_JUCEHEADER__
|
||||
|
||||
class CodeDocumentLine;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class for storing and manipulating a source code file.
|
||||
|
||||
When using a CodeEditorComponent, it takes one of these as its source object.
|
||||
|
||||
The CodeDocument stores its content as an array of lines, which makes it
|
||||
quick to insert and delete.
|
||||
|
||||
@see CodeEditorComponent
|
||||
*/
|
||||
class JUCE_API CodeDocument
|
||||
{
|
||||
public:
|
||||
/** Creates a new, empty document.
|
||||
*/
|
||||
CodeDocument();
|
||||
|
||||
/** Destructor. */
|
||||
~CodeDocument();
|
||||
|
||||
//==============================================================================
|
||||
/** A position in a code document.
|
||||
|
||||
Using this class you can find a position in a code document and quickly get its
|
||||
character position, line, and index. By calling setPositionMaintained (true), the
|
||||
position is automatically updated when text is inserted or deleted in the document,
|
||||
so that it maintains its original place in the text.
|
||||
*/
|
||||
class JUCE_API Position
|
||||
{
|
||||
public:
|
||||
/** Creates an uninitialised postion.
|
||||
Don't attempt to call any methods on this until you've given it an owner document
|
||||
to refer to!
|
||||
*/
|
||||
Position() noexcept;
|
||||
|
||||
/** Creates a position based on a line and index in a document.
|
||||
|
||||
Note that this index is NOT the column number, it's the number of characters from the
|
||||
start of the line. The "column" number isn't quite the same, because if the line
|
||||
contains any tab characters, the relationship of the index to its visual column depends on
|
||||
the number of spaces per tab being used!
|
||||
|
||||
Lines are numbered from zero, and if the line or index are beyond the bounds of the document,
|
||||
they will be adjusted to keep them within its limits.
|
||||
*/
|
||||
Position (const CodeDocument* ownerDocument,
|
||||
int line, int indexInLine) noexcept;
|
||||
|
||||
/** Creates a position based on a character index in a document.
|
||||
This position is placed at the specified number of characters from the start of the
|
||||
document. The line and column are auto-calculated.
|
||||
|
||||
If the position is beyond the range of the document, it'll be adjusted to keep it
|
||||
inside.
|
||||
*/
|
||||
Position (const CodeDocument* ownerDocument,
|
||||
int charactersFromStartOfDocument) noexcept;
|
||||
|
||||
/** Creates a copy of another position.
|
||||
|
||||
This will copy the position, but the new object will not be set to maintain its position,
|
||||
even if the source object was set to do so.
|
||||
*/
|
||||
Position (const Position& other) noexcept;
|
||||
|
||||
/** Destructor. */
|
||||
~Position();
|
||||
|
||||
Position& operator= (const Position& other);
|
||||
bool operator== (const Position& other) const noexcept;
|
||||
bool operator!= (const Position& other) const noexcept;
|
||||
|
||||
/** Points this object at a new position within the document.
|
||||
|
||||
If the position is beyond the range of the document, it'll be adjusted to keep it
|
||||
inside.
|
||||
@see getPosition, setLineAndIndex
|
||||
*/
|
||||
void setPosition (int charactersFromStartOfDocument);
|
||||
|
||||
/** Returns the position as the number of characters from the start of the document.
|
||||
@see setPosition, getLineNumber, getIndexInLine
|
||||
*/
|
||||
int getPosition() const noexcept { return characterPos; }
|
||||
|
||||
/** Moves the position to a new line and index within the line.
|
||||
|
||||
Note that the index is NOT the column at which the position appears in an editor.
|
||||
If the line contains any tab characters, the relationship of the index to its
|
||||
visual position depends on the number of spaces per tab being used!
|
||||
|
||||
Lines are numbered from zero, and if the line or index are beyond the bounds of the document,
|
||||
they will be adjusted to keep them within its limits.
|
||||
*/
|
||||
void setLineAndIndex (int newLine, int newIndexInLine);
|
||||
|
||||
/** Returns the line number of this position.
|
||||
The first line in the document is numbered zero, not one!
|
||||
*/
|
||||
int getLineNumber() const noexcept { return line; }
|
||||
|
||||
/** Returns the number of characters from the start of the line.
|
||||
|
||||
Note that this value is NOT the column at which the position appears in an editor.
|
||||
If the line contains any tab characters, the relationship of the index to its
|
||||
visual position depends on the number of spaces per tab being used!
|
||||
*/
|
||||
int getIndexInLine() const noexcept { return indexInLine; }
|
||||
|
||||
/** Allows the position to be automatically updated when the document changes.
|
||||
|
||||
If this is set to true, the positon will register with its document so that
|
||||
when the document has text inserted or deleted, this position will be automatically
|
||||
moved to keep it at the same position in the text.
|
||||
*/
|
||||
void setPositionMaintained (bool isMaintained);
|
||||
|
||||
//==============================================================================
|
||||
/** Moves the position forwards or backwards by the specified number of characters.
|
||||
@see movedBy
|
||||
*/
|
||||
void moveBy (int characterDelta);
|
||||
|
||||
/** Returns a position which is the same as this one, moved by the specified number of
|
||||
characters.
|
||||
@see moveBy
|
||||
*/
|
||||
const Position movedBy (int characterDelta) const;
|
||||
|
||||
/** Returns a position which is the same as this one, moved up or down by the specified
|
||||
number of lines.
|
||||
@see movedBy
|
||||
*/
|
||||
const Position movedByLines (int deltaLines) const;
|
||||
|
||||
/** Returns the character in the document at this position.
|
||||
@see getLineText
|
||||
*/
|
||||
const juce_wchar getCharacter() const;
|
||||
|
||||
/** Returns the line from the document that this position is within.
|
||||
@see getCharacter, getLineNumber
|
||||
*/
|
||||
String getLineText() const;
|
||||
|
||||
//==============================================================================
|
||||
private:
|
||||
CodeDocument* owner;
|
||||
int characterPos, line, indexInLine;
|
||||
bool positionMaintained;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the full text of the document. */
|
||||
String getAllContent() const;
|
||||
|
||||
/** Returns a section of the document's text. */
|
||||
String getTextBetween (const Position& start, const Position& end) const;
|
||||
|
||||
/** Returns a line from the document. */
|
||||
String getLine (int lineIndex) const noexcept;
|
||||
|
||||
/** Returns the number of characters in the document. */
|
||||
int getNumCharacters() const noexcept;
|
||||
|
||||
/** Returns the number of lines in the document. */
|
||||
int getNumLines() const noexcept { return lines.size(); }
|
||||
|
||||
/** Returns the number of characters in the longest line of the document. */
|
||||
int getMaximumLineLength() noexcept;
|
||||
|
||||
/** Deletes a section of the text.
|
||||
|
||||
This operation is undoable.
|
||||
*/
|
||||
void deleteSection (const Position& startPosition, const Position& endPosition);
|
||||
|
||||
/** Inserts some text into the document at a given position.
|
||||
|
||||
This operation is undoable.
|
||||
*/
|
||||
void insertText (const Position& position, const String& text);
|
||||
|
||||
/** Clears the document and replaces it with some new text.
|
||||
|
||||
This operation is undoable - if you're trying to completely reset the document, you
|
||||
might want to also call clearUndoHistory() and setSavePoint() after using this method.
|
||||
*/
|
||||
void replaceAllContent (const String& newContent);
|
||||
|
||||
/** Replaces the editor's contents with the contents of a stream.
|
||||
This will also reset the undo history and save point marker.
|
||||
*/
|
||||
bool loadFromStream (InputStream& stream);
|
||||
|
||||
/** Writes the editor's current contents to a stream. */
|
||||
bool writeToStream (OutputStream& stream);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the preferred new-line characters for the document.
|
||||
This will be either "\n", "\r\n", or (rarely) "\r".
|
||||
@see setNewLineCharacters
|
||||
*/
|
||||
String getNewLineCharacters() const noexcept { return newLineChars; }
|
||||
|
||||
/** Sets the new-line characters that the document should use.
|
||||
The string must be either "\n", "\r\n", or (rarely) "\r".
|
||||
@see getNewLineCharacters
|
||||
*/
|
||||
void setNewLineCharacters (const String& newLine) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Begins a new undo transaction.
|
||||
|
||||
The document itself will not call this internally, so relies on whatever is using the
|
||||
document to periodically call this to break up the undo sequence into sensible chunks.
|
||||
@see UndoManager::beginNewTransaction
|
||||
*/
|
||||
void newTransaction();
|
||||
|
||||
/** Undo the last operation.
|
||||
@see UndoManager::undo
|
||||
*/
|
||||
void undo();
|
||||
|
||||
/** Redo the last operation.
|
||||
@see UndoManager::redo
|
||||
*/
|
||||
void redo();
|
||||
|
||||
/** Clears the undo history.
|
||||
@see UndoManager::clearUndoHistory
|
||||
*/
|
||||
void clearUndoHistory();
|
||||
|
||||
/** Returns the document's UndoManager */
|
||||
UndoManager& getUndoManager() noexcept { return undoManager; }
|
||||
|
||||
//==============================================================================
|
||||
/** Makes a note that the document's current state matches the one that is saved.
|
||||
|
||||
After this has been called, hasChangedSinceSavePoint() will return false until
|
||||
the document has been altered, and then it'll start returning true. If the document is
|
||||
altered, but then undone until it gets back to this state, hasChangedSinceSavePoint()
|
||||
will again return false.
|
||||
|
||||
@see hasChangedSinceSavePoint
|
||||
*/
|
||||
void setSavePoint() noexcept;
|
||||
|
||||
/** Returns true if the state of the document differs from the state it was in when
|
||||
setSavePoint() was last called.
|
||||
|
||||
@see setSavePoint
|
||||
*/
|
||||
bool hasChangedSinceSavePoint() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Searches for a word-break. */
|
||||
const Position findWordBreakAfter (const Position& position) const noexcept;
|
||||
|
||||
/** Searches for a word-break. */
|
||||
const Position findWordBreakBefore (const Position& position) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** An object that receives callbacks from the CodeDocument when its text changes.
|
||||
@see CodeDocument::addListener, CodeDocument::removeListener
|
||||
*/
|
||||
class JUCE_API Listener
|
||||
{
|
||||
public:
|
||||
Listener() {}
|
||||
virtual ~Listener() {}
|
||||
|
||||
/** Called by a CodeDocument when it is altered.
|
||||
*/
|
||||
virtual void codeDocumentChanged (const Position& affectedTextStart,
|
||||
const Position& affectedTextEnd) = 0;
|
||||
};
|
||||
|
||||
/** Registers a listener object to receive callbacks when the document changes.
|
||||
If the listener is already registered, this method has no effect.
|
||||
@see removeListener
|
||||
*/
|
||||
void addListener (Listener* listener) noexcept;
|
||||
|
||||
/** Deregisters a listener.
|
||||
@see addListener
|
||||
*/
|
||||
void removeListener (Listener* listener) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Iterates the text in a CodeDocument.
|
||||
|
||||
This class lets you read characters from a CodeDocument. It's designed to be used
|
||||
by a SyntaxAnalyser object.
|
||||
|
||||
@see CodeDocument, SyntaxAnalyser
|
||||
*/
|
||||
class JUCE_API Iterator
|
||||
{
|
||||
public:
|
||||
Iterator (CodeDocument* document);
|
||||
Iterator (const Iterator& other);
|
||||
Iterator& operator= (const Iterator& other) noexcept;
|
||||
~Iterator() noexcept;
|
||||
|
||||
/** Reads the next character and returns it.
|
||||
@see peekNextChar
|
||||
*/
|
||||
juce_wchar nextChar();
|
||||
|
||||
/** Reads the next character without advancing the current position. */
|
||||
juce_wchar peekNextChar() const;
|
||||
|
||||
/** Advances the position by one character. */
|
||||
void skip();
|
||||
|
||||
/** Returns the position of the next character as its position within the
|
||||
whole document.
|
||||
*/
|
||||
int getPosition() const noexcept { return position; }
|
||||
|
||||
/** Skips over any whitespace characters until the next character is non-whitespace. */
|
||||
void skipWhitespace();
|
||||
|
||||
/** Skips forward until the next character will be the first character on the next line */
|
||||
void skipToEndOfLine();
|
||||
|
||||
/** Returns the line number of the next character. */
|
||||
int getLine() const noexcept { return line; }
|
||||
|
||||
/** Returns true if the iterator has reached the end of the document. */
|
||||
bool isEOF() const noexcept;
|
||||
|
||||
private:
|
||||
CodeDocument* document;
|
||||
mutable String::CharPointerType charPointer;
|
||||
int line, position;
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
friend class CodeDocumentInsertAction;
|
||||
friend class CodeDocumentDeleteAction;
|
||||
friend class Iterator;
|
||||
friend class Position;
|
||||
|
||||
OwnedArray <CodeDocumentLine> lines;
|
||||
Array <Position*> positionsToMaintain;
|
||||
UndoManager undoManager;
|
||||
int currentActionIndex, indexOfSavedState;
|
||||
int maximumLineLength;
|
||||
ListenerList <Listener> listeners;
|
||||
String newLineChars;
|
||||
|
||||
void sendListenerChangeMessage (int startLine, int endLine);
|
||||
|
||||
void insert (const String& text, int insertPos, bool undoable);
|
||||
void remove (int startPos, int endPos, bool undoable);
|
||||
void checkLastLineStatus();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CodeDocument);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_CODEDOCUMENT_JUCEHEADER__
|
||||
1193
modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp
Normal file
1193
modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp
Normal file
File diff suppressed because it is too large
Load diff
311
modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.h
Normal file
311
modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.h
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_CODEEDITORCOMPONENT_JUCEHEADER__
|
||||
#define __JUCE_CODEEDITORCOMPONENT_JUCEHEADER__
|
||||
|
||||
#include "juce_CodeDocument.h"
|
||||
#include "juce_CodeTokeniser.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A text editor component designed specifically for source code.
|
||||
|
||||
This is designed to handle syntax highlighting and fast editing of very large
|
||||
files.
|
||||
*/
|
||||
class JUCE_API CodeEditorComponent : public Component,
|
||||
public TextInputTarget,
|
||||
public Timer,
|
||||
public ScrollBar::Listener,
|
||||
public CodeDocument::Listener,
|
||||
public AsyncUpdater
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an editor for a document.
|
||||
|
||||
The tokeniser object is optional - pass 0 to disable syntax highlighting.
|
||||
The object that you pass in is not owned or deleted by the editor - you must
|
||||
make sure that it doesn't get deleted while this component is still using it.
|
||||
|
||||
@see CodeDocument
|
||||
*/
|
||||
CodeEditorComponent (CodeDocument& document,
|
||||
CodeTokeniser* codeTokeniser);
|
||||
|
||||
/** Destructor. */
|
||||
~CodeEditorComponent();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the code document that this component is editing. */
|
||||
CodeDocument& getDocument() const noexcept { return document; }
|
||||
|
||||
/** Loads the given content into the document.
|
||||
This will completely reset the CodeDocument object, clear its undo history,
|
||||
and fill it with this text.
|
||||
*/
|
||||
void loadContent (const String& newContent);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the standard character width. */
|
||||
float getCharWidth() const noexcept { return charWidth; }
|
||||
|
||||
/** Returns the height of a line of text, in pixels. */
|
||||
int getLineHeight() const noexcept { return lineHeight; }
|
||||
|
||||
/** Returns the number of whole lines visible on the screen,
|
||||
This doesn't include a cut-off line that might be visible at the bottom if the
|
||||
component's height isn't an exact multiple of the line-height.
|
||||
*/
|
||||
int getNumLinesOnScreen() const noexcept { return linesOnScreen; }
|
||||
|
||||
/** Returns the number of whole columns visible on the screen.
|
||||
This doesn't include any cut-off columns at the right-hand edge.
|
||||
*/
|
||||
int getNumColumnsOnScreen() const noexcept { return columnsOnScreen; }
|
||||
|
||||
/** Returns the current caret position. */
|
||||
const CodeDocument::Position getCaretPos() const { return caretPos; }
|
||||
|
||||
/** Returns the position of the caret, relative to the editor's origin. */
|
||||
const Rectangle<int> getCaretRectangle();
|
||||
|
||||
/** Moves the caret.
|
||||
If selecting is true, the section of the document between the current
|
||||
caret position and the new one will become selected. If false, any currently
|
||||
selected region will be deselected.
|
||||
*/
|
||||
void moveCaretTo (const CodeDocument::Position& newPos, bool selecting);
|
||||
|
||||
/** Returns the on-screen position of a character in the document.
|
||||
The rectangle returned is relative to this component's top-left origin.
|
||||
*/
|
||||
const Rectangle<int> getCharacterBounds (const CodeDocument::Position& pos) const;
|
||||
|
||||
/** Finds the character at a given on-screen position.
|
||||
The co-ordinates are relative to this component's top-left origin.
|
||||
*/
|
||||
const CodeDocument::Position getPositionAt (int x, int y);
|
||||
|
||||
//==============================================================================
|
||||
bool moveCaretLeft (bool moveInWholeWordSteps, bool selecting);
|
||||
bool moveCaretRight (bool moveInWholeWordSteps, bool selecting);
|
||||
bool moveCaretUp (bool selecting);
|
||||
bool moveCaretDown (bool selecting);
|
||||
bool scrollDown();
|
||||
bool scrollUp();
|
||||
bool pageUp (bool selecting);
|
||||
bool pageDown (bool selecting);
|
||||
bool moveCaretToTop (bool selecting);
|
||||
bool moveCaretToStartOfLine (bool selecting);
|
||||
bool moveCaretToEnd (bool selecting);
|
||||
bool moveCaretToEndOfLine (bool selecting);
|
||||
bool deleteBackwards (bool moveInWholeWordSteps);
|
||||
bool deleteForwards (bool moveInWholeWordSteps);
|
||||
bool copyToClipboard();
|
||||
bool cutToClipboard();
|
||||
bool pasteFromClipboard();
|
||||
bool undo();
|
||||
bool redo();
|
||||
|
||||
bool selectAll();
|
||||
void deselectAll();
|
||||
|
||||
void scrollToLine (int newFirstLineOnScreen);
|
||||
void scrollBy (int deltaLines);
|
||||
void scrollToColumn (int newFirstColumnOnScreen);
|
||||
void scrollToKeepCaretOnScreen();
|
||||
|
||||
void insertTextAtCaret (const String& textToInsert);
|
||||
void insertTabAtCaret();
|
||||
|
||||
//==============================================================================
|
||||
const Range<int> getHighlightedRegion() const;
|
||||
void setHighlightedRegion (const Range<int>& newRange);
|
||||
const String getTextInRange (const Range<int>& range) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the current tab settings.
|
||||
This lets you change the tab size and whether pressing the tab key inserts a
|
||||
tab character, or its equivalent number of spaces.
|
||||
*/
|
||||
void setTabSize (int numSpacesPerTab, bool insertSpacesInsteadOfTabCharacters);
|
||||
|
||||
/** Returns the current number of spaces per tab.
|
||||
@see setTabSize
|
||||
*/
|
||||
int getTabSize() const noexcept { return spacesPerTab; }
|
||||
|
||||
/** Returns true if the tab key will insert spaces instead of actual tab characters.
|
||||
@see setTabSize
|
||||
*/
|
||||
bool areSpacesInsertedForTabs() const { return useSpacesForTabs; }
|
||||
|
||||
/** Changes the font.
|
||||
Make sure you only use a fixed-width font, or this component will look pretty nasty!
|
||||
*/
|
||||
void setFont (const Font& newFont);
|
||||
|
||||
/** Returns the font that the editor is using. */
|
||||
const Font& getFont() const noexcept { return font; }
|
||||
|
||||
/** Resets the syntax highlighting colours to the default ones provided by the
|
||||
code tokeniser.
|
||||
@see CodeTokeniser::getDefaultColour
|
||||
*/
|
||||
void resetToDefaultColours();
|
||||
|
||||
/** Changes one of the syntax highlighting colours.
|
||||
The token type values are dependent on the tokeniser being used - use
|
||||
CodeTokeniser::getTokenTypes() to get a list of the token types.
|
||||
@see getColourForTokenType
|
||||
*/
|
||||
void setColourForTokenType (int tokenType, const Colour& colour);
|
||||
|
||||
/** Returns one of the syntax highlighting colours.
|
||||
The token type values are dependent on the tokeniser being used - use
|
||||
CodeTokeniser::getTokenTypes() to get a list of the token types.
|
||||
@see setColourForTokenType
|
||||
*/
|
||||
const Colour getColourForTokenType (int tokenType) const;
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the editor.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
backgroundColourId = 0x1004500, /**< A colour to use to fill the editor's background. */
|
||||
highlightColourId = 0x1004502, /**< The colour to use for the highlighted background under
|
||||
selected text. */
|
||||
defaultTextColourId = 0x1004503 /**< The colour to use for text when no syntax colouring is
|
||||
enabled. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the size of the scrollbars. */
|
||||
void setScrollbarThickness (int thickness);
|
||||
|
||||
/** Returns the thickness of the scrollbars. */
|
||||
int getScrollbarThickness() const noexcept { return scrollbarThickness; }
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void resized();
|
||||
/** @internal */
|
||||
void paint (Graphics& g);
|
||||
/** @internal */
|
||||
bool keyPressed (const KeyPress& key);
|
||||
/** @internal */
|
||||
void mouseDown (const MouseEvent& e);
|
||||
/** @internal */
|
||||
void mouseDrag (const MouseEvent& e);
|
||||
/** @internal */
|
||||
void mouseUp (const MouseEvent& e);
|
||||
/** @internal */
|
||||
void mouseDoubleClick (const MouseEvent& e);
|
||||
/** @internal */
|
||||
void mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY);
|
||||
/** @internal */
|
||||
void focusGained (FocusChangeType cause);
|
||||
/** @internal */
|
||||
void focusLost (FocusChangeType cause);
|
||||
/** @internal */
|
||||
void timerCallback();
|
||||
/** @internal */
|
||||
void scrollBarMoved (ScrollBar* scrollBarThatHasMoved, double newRangeStart);
|
||||
/** @internal */
|
||||
void handleAsyncUpdate();
|
||||
/** @internal */
|
||||
void codeDocumentChanged (const CodeDocument::Position& affectedTextStart,
|
||||
const CodeDocument::Position& affectedTextEnd);
|
||||
/** @internal */
|
||||
bool isTextInputActive() const;
|
||||
/** @internal */
|
||||
void setTemporaryUnderlining (const Array <Range<int> >&);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
CodeDocument& document;
|
||||
|
||||
Font font;
|
||||
int firstLineOnScreen, gutter, spacesPerTab;
|
||||
float charWidth;
|
||||
int lineHeight, linesOnScreen, columnsOnScreen;
|
||||
int scrollbarThickness, columnToTryToMaintain;
|
||||
bool useSpacesForTabs;
|
||||
double xOffset;
|
||||
|
||||
CodeDocument::Position caretPos;
|
||||
CodeDocument::Position selectionStart, selectionEnd;
|
||||
|
||||
ScopedPointer<CaretComponent> caret;
|
||||
ScrollBar verticalScrollBar, horizontalScrollBar;
|
||||
|
||||
enum DragType
|
||||
{
|
||||
notDragging,
|
||||
draggingSelectionStart,
|
||||
draggingSelectionEnd
|
||||
};
|
||||
|
||||
DragType dragType;
|
||||
|
||||
//==============================================================================
|
||||
CodeTokeniser* codeTokeniser;
|
||||
Array <Colour> coloursForTokenCategories;
|
||||
|
||||
class CodeEditorLine;
|
||||
OwnedArray <CodeEditorLine> lines;
|
||||
void rebuildLineTokens();
|
||||
|
||||
OwnedArray <CodeDocument::Iterator> cachedIterators;
|
||||
void clearCachedIterators (int firstLineToBeInvalid);
|
||||
void updateCachedIterators (int maxLineNum);
|
||||
void getIteratorForPosition (int position, CodeDocument::Iterator& result);
|
||||
void moveLineDelta (int delta, bool selecting);
|
||||
|
||||
//==============================================================================
|
||||
void updateCaretPosition();
|
||||
void updateScrollBars();
|
||||
void scrollToLineInternal (int line);
|
||||
void scrollToColumnInternal (double column);
|
||||
void newTransaction();
|
||||
void cut();
|
||||
|
||||
int indexToColumn (int line, int index) const noexcept;
|
||||
int columnToIndex (int line, int column) const noexcept;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CodeEditorComponent);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_CODEEDITORCOMPONENT_JUCEHEADER__
|
||||
72
modules/juce_gui_extra/code_editor/juce_CodeTokeniser.h
Normal file
72
modules/juce_gui_extra/code_editor/juce_CodeTokeniser.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_CODETOKENISER_JUCEHEADER__
|
||||
#define __JUCE_CODETOKENISER_JUCEHEADER__
|
||||
|
||||
#include "juce_CodeDocument.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A base class for tokenising code so that the syntax can be displayed in a
|
||||
code editor.
|
||||
|
||||
@see CodeDocument, CodeEditorComponent
|
||||
*/
|
||||
class JUCE_API CodeTokeniser
|
||||
{
|
||||
public:
|
||||
CodeTokeniser() {}
|
||||
virtual ~CodeTokeniser() {}
|
||||
|
||||
//==============================================================================
|
||||
/** Reads the next token from the source and returns its token type.
|
||||
|
||||
This must leave the source pointing to the first character in the
|
||||
next token.
|
||||
*/
|
||||
virtual int readNextToken (CodeDocument::Iterator& source) = 0;
|
||||
|
||||
/** Returns a list of the names of the token types this analyser uses.
|
||||
|
||||
The index in this list must match the token type numbers that are
|
||||
returned by readNextToken().
|
||||
*/
|
||||
virtual StringArray getTokenTypes() = 0;
|
||||
|
||||
/** Returns a suggested syntax highlighting colour for a specified
|
||||
token type.
|
||||
*/
|
||||
virtual const Colour getDefaultColour (int tokenType) = 0;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_LEAK_DETECTOR (CodeTokeniser);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_CODETOKENISER_JUCEHEADER__
|
||||
287
modules/juce_gui_extra/documents/juce_FileBasedDocument.cpp
Normal file
287
modules/juce_gui_extra/documents/juce_FileBasedDocument.cpp
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
FileBasedDocument::FileBasedDocument (const String& fileExtension_,
|
||||
const String& fileWildcard_,
|
||||
const String& openFileDialogTitle_,
|
||||
const String& saveFileDialogTitle_)
|
||||
: changedSinceSave (false),
|
||||
fileExtension (fileExtension_),
|
||||
fileWildcard (fileWildcard_),
|
||||
openFileDialogTitle (openFileDialogTitle_),
|
||||
saveFileDialogTitle (saveFileDialogTitle_)
|
||||
{
|
||||
}
|
||||
|
||||
FileBasedDocument::~FileBasedDocument()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FileBasedDocument::setChangedFlag (const bool hasChanged)
|
||||
{
|
||||
if (changedSinceSave != hasChanged)
|
||||
{
|
||||
changedSinceSave = hasChanged;
|
||||
sendChangeMessage();
|
||||
}
|
||||
}
|
||||
|
||||
void FileBasedDocument::changed()
|
||||
{
|
||||
changedSinceSave = true;
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FileBasedDocument::setFile (const File& newFile)
|
||||
{
|
||||
if (documentFile != newFile)
|
||||
{
|
||||
documentFile = newFile;
|
||||
changed();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
bool FileBasedDocument::loadFrom (const File& newFile,
|
||||
const bool showMessageOnFailure)
|
||||
{
|
||||
MouseCursor::showWaitCursor();
|
||||
|
||||
const File oldFile (documentFile);
|
||||
documentFile = newFile;
|
||||
|
||||
String error;
|
||||
|
||||
if (newFile.existsAsFile())
|
||||
{
|
||||
error = loadDocument (newFile);
|
||||
|
||||
if (error.isEmpty())
|
||||
{
|
||||
setChangedFlag (false);
|
||||
MouseCursor::hideWaitCursor();
|
||||
|
||||
setLastDocumentOpened (newFile);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error = "The file doesn't exist";
|
||||
}
|
||||
|
||||
documentFile = oldFile;
|
||||
MouseCursor::hideWaitCursor();
|
||||
|
||||
if (showMessageOnFailure)
|
||||
{
|
||||
AlertWindow::showMessageBox (AlertWindow::WarningIcon,
|
||||
TRANS("Failed to open file..."),
|
||||
TRANS("There was an error while trying to load the file:\n\n")
|
||||
+ newFile.getFullPathName()
|
||||
+ "\n\n"
|
||||
+ error);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileBasedDocument::loadFromUserSpecifiedFile (const bool showMessageOnFailure)
|
||||
{
|
||||
FileChooser fc (openFileDialogTitle,
|
||||
getLastDocumentOpened(),
|
||||
fileWildcard);
|
||||
|
||||
if (fc.browseForFileToOpen())
|
||||
return loadFrom (fc.getResult(), showMessageOnFailure);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
FileBasedDocument::SaveResult FileBasedDocument::save (const bool askUserForFileIfNotSpecified,
|
||||
const bool showMessageOnFailure)
|
||||
{
|
||||
return saveAs (documentFile,
|
||||
false,
|
||||
askUserForFileIfNotSpecified,
|
||||
showMessageOnFailure);
|
||||
}
|
||||
|
||||
FileBasedDocument::SaveResult FileBasedDocument::saveAs (const File& newFile,
|
||||
const bool warnAboutOverwritingExistingFiles,
|
||||
const bool askUserForFileIfNotSpecified,
|
||||
const bool showMessageOnFailure)
|
||||
{
|
||||
if (newFile == File::nonexistent)
|
||||
{
|
||||
if (askUserForFileIfNotSpecified)
|
||||
{
|
||||
return saveAsInteractive (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// can't save to an unspecified file
|
||||
jassertfalse;
|
||||
return failedToWriteToFile;
|
||||
}
|
||||
}
|
||||
|
||||
if (warnAboutOverwritingExistingFiles && newFile.exists())
|
||||
{
|
||||
if (! AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
|
||||
TRANS("File already exists"),
|
||||
TRANS("There's already a file called:\n\n")
|
||||
+ newFile.getFullPathName()
|
||||
+ TRANS("\n\nAre you sure you want to overwrite it?"),
|
||||
TRANS("overwrite"),
|
||||
TRANS("cancel")))
|
||||
{
|
||||
return userCancelledSave;
|
||||
}
|
||||
}
|
||||
|
||||
MouseCursor::showWaitCursor();
|
||||
|
||||
const File oldFile (documentFile);
|
||||
documentFile = newFile;
|
||||
|
||||
String error (saveDocument (newFile));
|
||||
|
||||
if (error.isEmpty())
|
||||
{
|
||||
setChangedFlag (false);
|
||||
MouseCursor::hideWaitCursor();
|
||||
|
||||
return savedOk;
|
||||
}
|
||||
|
||||
documentFile = oldFile;
|
||||
MouseCursor::hideWaitCursor();
|
||||
|
||||
if (showMessageOnFailure)
|
||||
{
|
||||
AlertWindow::showMessageBox (AlertWindow::WarningIcon,
|
||||
TRANS("Error writing to file..."),
|
||||
TRANS("An error occurred while trying to save \"")
|
||||
+ getDocumentTitle()
|
||||
+ TRANS("\" to the file:\n\n")
|
||||
+ newFile.getFullPathName()
|
||||
+ "\n\n"
|
||||
+ error);
|
||||
}
|
||||
|
||||
return failedToWriteToFile;
|
||||
}
|
||||
|
||||
FileBasedDocument::SaveResult FileBasedDocument::saveIfNeededAndUserAgrees()
|
||||
{
|
||||
if (! hasChangedSinceSaved())
|
||||
return savedOk;
|
||||
|
||||
const int r = AlertWindow::showYesNoCancelBox (AlertWindow::QuestionIcon,
|
||||
TRANS("Closing document..."),
|
||||
TRANS("Do you want to save the changes to \"")
|
||||
+ getDocumentTitle() + "\"?",
|
||||
TRANS("save"),
|
||||
TRANS("discard changes"),
|
||||
TRANS("cancel"));
|
||||
|
||||
if (r == 1)
|
||||
{
|
||||
// save changes
|
||||
return save (true, true);
|
||||
}
|
||||
else if (r == 2)
|
||||
{
|
||||
// discard changes
|
||||
return savedOk;
|
||||
}
|
||||
|
||||
return userCancelledSave;
|
||||
}
|
||||
|
||||
FileBasedDocument::SaveResult FileBasedDocument::saveAsInteractive (const bool warnAboutOverwritingExistingFiles)
|
||||
{
|
||||
File f;
|
||||
|
||||
if (documentFile.existsAsFile())
|
||||
f = documentFile;
|
||||
else
|
||||
f = getLastDocumentOpened();
|
||||
|
||||
String legalFilename (File::createLegalFileName (getDocumentTitle()));
|
||||
|
||||
if (legalFilename.isEmpty())
|
||||
legalFilename = "unnamed";
|
||||
|
||||
if (f.existsAsFile() || f.getParentDirectory().isDirectory())
|
||||
f = f.getSiblingFile (legalFilename);
|
||||
else
|
||||
f = File::getSpecialLocation (File::userDocumentsDirectory).getChildFile (legalFilename);
|
||||
|
||||
f = f.withFileExtension (fileExtension)
|
||||
.getNonexistentSibling (true);
|
||||
|
||||
FileChooser fc (saveFileDialogTitle, f, fileWildcard);
|
||||
|
||||
if (fc.browseForFileToSave (warnAboutOverwritingExistingFiles))
|
||||
{
|
||||
File chosen (fc.getResult());
|
||||
if (chosen.getFileExtension().isEmpty())
|
||||
{
|
||||
chosen = chosen.withFileExtension (fileExtension);
|
||||
|
||||
if (chosen.exists())
|
||||
{
|
||||
if (! AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
|
||||
TRANS("File already exists"),
|
||||
TRANS("There's already a file called:")
|
||||
+ "\n\n" + chosen.getFullPathName()
|
||||
+ "\n\n" + TRANS("Are you sure you want to overwrite it?"),
|
||||
TRANS("overwrite"),
|
||||
TRANS("cancel")))
|
||||
{
|
||||
return userCancelledSave;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setLastDocumentOpened (chosen);
|
||||
return saveAs (chosen, false, false, true);
|
||||
}
|
||||
|
||||
return userCancelledSave;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
289
modules/juce_gui_extra/documents/juce_FileBasedDocument.h
Normal file
289
modules/juce_gui_extra/documents/juce_FileBasedDocument.h
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_FILEBASEDDOCUMENT_JUCEHEADER__
|
||||
#define __JUCE_FILEBASEDDOCUMENT_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class to take care of the logic involved with the loading/saving of some kind
|
||||
of document.
|
||||
|
||||
There's quite a lot of tedious logic involved in writing all the load/save/save-as
|
||||
functions you need for documents that get saved to a file, so this class attempts
|
||||
to abstract most of the boring stuff.
|
||||
|
||||
Your subclass should just implement all the pure virtual methods, and you can
|
||||
then use the higher-level public methods to do the load/save dialogs, to warn the user
|
||||
about overwriting files, etc.
|
||||
|
||||
The document object keeps track of whether it has changed since it was last saved or
|
||||
loaded, so when you change something, call its changed() method. This will set a
|
||||
flag so it knows it needs saving, and will also broadcast a change message using the
|
||||
ChangeBroadcaster base class.
|
||||
|
||||
@see ChangeBroadcaster
|
||||
*/
|
||||
class JUCE_API FileBasedDocument : public ChangeBroadcaster
|
||||
{
|
||||
public:
|
||||
/** Creates a FileBasedDocument.
|
||||
|
||||
@param fileExtension the extension to use when loading/saving files, e.g. ".doc"
|
||||
@param fileWildCard the wildcard to use in file dialogs, e.g. "*.doc"
|
||||
@param openFileDialogTitle the title to show on an open-file dialog, e.g. "Choose a file to open.."
|
||||
@param saveFileDialogTitle the title to show on an save-file dialog, e.g. "Choose a file to save as.."
|
||||
*/
|
||||
FileBasedDocument (const String& fileExtension,
|
||||
const String& fileWildCard,
|
||||
const String& openFileDialogTitle,
|
||||
const String& saveFileDialogTitle);
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~FileBasedDocument();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the changed() method has been called since the file was
|
||||
last saved or loaded.
|
||||
|
||||
@see resetChangedFlag, changed
|
||||
*/
|
||||
bool hasChangedSinceSaved() const { return changedSinceSave; }
|
||||
|
||||
/** Called to indicate that the document has changed and needs saving.
|
||||
|
||||
This method will also trigger a change message to be sent out using the
|
||||
ChangeBroadcaster base class.
|
||||
|
||||
After calling the method, the hasChangedSinceSaved() method will return true, until
|
||||
it is reset either by saving to a file or using the resetChangedFlag() method.
|
||||
|
||||
@see hasChangedSinceSaved, resetChangedFlag
|
||||
*/
|
||||
virtual void changed();
|
||||
|
||||
/** Sets the state of the 'changed' flag.
|
||||
|
||||
The 'changed' flag is set to true when the changed() method is called - use this method
|
||||
to reset it or to set it without also broadcasting a change message.
|
||||
|
||||
@see changed, hasChangedSinceSaved
|
||||
*/
|
||||
void setChangedFlag (bool hasChanged);
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to open a file.
|
||||
|
||||
If the file opens correctly, the document's file (see the getFile() method) is set
|
||||
to this new one; if it fails, the document's file is left unchanged, and optionally
|
||||
a message box is shown telling the user there was an error.
|
||||
|
||||
@returns true if the new file loaded successfully
|
||||
@see loadDocument, loadFromUserSpecifiedFile
|
||||
*/
|
||||
bool loadFrom (const File& fileToLoadFrom,
|
||||
bool showMessageOnFailure);
|
||||
|
||||
/** Asks the user for a file and tries to load it.
|
||||
|
||||
This will pop up a dialog box using the title, file extension and
|
||||
wildcard specified in the document's constructor, and asks the user
|
||||
for a file. If they pick one, the loadFrom() method is used to
|
||||
try to load it, optionally showing a message if it fails.
|
||||
|
||||
@returns true if a file was loaded; false if the user cancelled or if they
|
||||
picked a file which failed to load correctly
|
||||
@see loadFrom
|
||||
*/
|
||||
bool loadFromUserSpecifiedFile (bool showMessageOnFailure);
|
||||
|
||||
//==============================================================================
|
||||
/** A set of possible outcomes of one of the save() methods
|
||||
*/
|
||||
enum SaveResult
|
||||
{
|
||||
savedOk = 0, /**< indicates that a file was saved successfully. */
|
||||
userCancelledSave, /**< indicates that the user aborted the save operation. */
|
||||
failedToWriteToFile /**< indicates that it tried to write to a file but this failed. */
|
||||
};
|
||||
|
||||
/** Tries to save the document to the last file it was saved or loaded from.
|
||||
|
||||
This will always try to write to the file, even if the document isn't flagged as
|
||||
having changed.
|
||||
|
||||
@param askUserForFileIfNotSpecified if there's no file currently specified and this is
|
||||
true, it will prompt the user to pick a file, as if
|
||||
saveAsInteractive() was called.
|
||||
@param showMessageOnFailure if true it will show a warning message when if the
|
||||
save operation fails
|
||||
@see saveIfNeededAndUserAgrees, saveAs, saveAsInteractive
|
||||
*/
|
||||
SaveResult save (bool askUserForFileIfNotSpecified,
|
||||
bool showMessageOnFailure);
|
||||
|
||||
/** If the file needs saving, it'll ask the user if that's what they want to do, and save
|
||||
it if they say yes.
|
||||
|
||||
If you've got a document open and want to close it (e.g. to quit the app), this is the
|
||||
method to call.
|
||||
|
||||
If the document doesn't need saving it'll return the value savedOk so
|
||||
you can go ahead and delete the document.
|
||||
|
||||
If it does need saving it'll prompt the user, and if they say "discard changes" it'll
|
||||
return savedOk, so again, you can safely delete the document.
|
||||
|
||||
If the user clicks "cancel", it'll return userCancelledSave, so if you can abort the
|
||||
close-document operation.
|
||||
|
||||
And if they click "save changes", it'll try to save and either return savedOk, or
|
||||
failedToWriteToFile if there was a problem.
|
||||
|
||||
@see save, saveAs, saveAsInteractive
|
||||
*/
|
||||
SaveResult saveIfNeededAndUserAgrees();
|
||||
|
||||
/** Tries to save the document to a specified file.
|
||||
|
||||
If this succeeds, it'll also change the document's internal file (as returned by
|
||||
the getFile() method). If it fails, the file will be left unchanged.
|
||||
|
||||
@param newFile the file to try to write to
|
||||
@param warnAboutOverwritingExistingFiles if true and the file exists, it'll ask
|
||||
the user first if they want to overwrite it
|
||||
@param askUserForFileIfNotSpecified if the file is non-existent and this is true, it'll
|
||||
use the saveAsInteractive() method to ask the user for a
|
||||
filename
|
||||
@param showMessageOnFailure if true and the write operation fails, it'll show
|
||||
a message box to warn the user
|
||||
@see saveIfNeededAndUserAgrees, save, saveAsInteractive
|
||||
*/
|
||||
SaveResult saveAs (const File& newFile,
|
||||
bool warnAboutOverwritingExistingFiles,
|
||||
bool askUserForFileIfNotSpecified,
|
||||
bool showMessageOnFailure);
|
||||
|
||||
/** Prompts the user for a filename and tries to save to it.
|
||||
|
||||
This will pop up a dialog box using the title, file extension and
|
||||
wildcard specified in the document's constructor, and asks the user
|
||||
for a file. If they pick one, the saveAs() method is used to try to save
|
||||
to this file.
|
||||
|
||||
@param warnAboutOverwritingExistingFiles if true and the file exists, it'll ask
|
||||
the user first if they want to overwrite it
|
||||
@see saveIfNeededAndUserAgrees, save, saveAs
|
||||
*/
|
||||
SaveResult saveAsInteractive (bool warnAboutOverwritingExistingFiles);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the file that this document was last successfully saved or loaded from.
|
||||
|
||||
When the document object is created, this will be set to File::nonexistent.
|
||||
|
||||
It is changed when one of the load or save methods is used, or when setFile()
|
||||
is used to explicitly set it.
|
||||
*/
|
||||
const File& getFile() const { return documentFile; }
|
||||
|
||||
/** Sets the file that this document thinks it was loaded from.
|
||||
|
||||
This won't actually load anything - it just changes the file stored internally.
|
||||
|
||||
@see getFile
|
||||
*/
|
||||
void setFile (const File& newFile);
|
||||
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Overload this to return the title of the document.
|
||||
|
||||
This is used in message boxes, filenames and file choosers, so it should be
|
||||
something sensible.
|
||||
*/
|
||||
virtual const String getDocumentTitle() = 0;
|
||||
|
||||
/** This method should try to load your document from the given file.
|
||||
|
||||
If it fails, it should return an error message. If it succeeds, it should return
|
||||
an empty string.
|
||||
*/
|
||||
virtual const String loadDocument (const File& file) = 0;
|
||||
|
||||
/** This method should try to write your document to the given file.
|
||||
|
||||
If it fails, it should return an error message. If it succeeds, it should return
|
||||
an empty string.
|
||||
*/
|
||||
virtual const String saveDocument (const File& file) = 0;
|
||||
|
||||
/** This is used for dialog boxes to make them open at the last folder you
|
||||
were using.
|
||||
|
||||
getLastDocumentOpened() and setLastDocumentOpened() are used to store
|
||||
the last document that was used - you might want to store this value
|
||||
in a static variable, or even in your application's properties. It should
|
||||
be a global setting rather than a property of this object.
|
||||
|
||||
This method works very well in conjunction with a RecentlyOpenedFilesList
|
||||
object to manage your recent-files list.
|
||||
|
||||
As a default value, it's ok to return File::nonexistent, and the document
|
||||
object will use a sensible one instead.
|
||||
|
||||
@see RecentlyOpenedFilesList
|
||||
*/
|
||||
virtual const File getLastDocumentOpened() = 0;
|
||||
|
||||
/** This is used for dialog boxes to make them open at the last folder you
|
||||
were using.
|
||||
|
||||
getLastDocumentOpened() and setLastDocumentOpened() are used to store
|
||||
the last document that was used - you might want to store this value
|
||||
in a static variable, or even in your application's properties. It should
|
||||
be a global setting rather than a property of this object.
|
||||
|
||||
This method works very well in conjunction with a RecentlyOpenedFilesList
|
||||
object to manage your recent-files list.
|
||||
|
||||
@see RecentlyOpenedFilesList
|
||||
*/
|
||||
virtual void setLastDocumentOpened (const File& file) = 0;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
File documentFile;
|
||||
bool changedSinceSave;
|
||||
String fileExtension, fileWildcard, openFileDialogTitle, saveFileDialogTitle;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileBasedDocument);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_FILEBASEDDOCUMENT_JUCEHEADER__
|
||||
129
modules/juce_gui_extra/embedding/juce_ActiveXControlComponent.h
Normal file
129
modules/juce_gui_extra/embedding/juce_ActiveXControlComponent.h
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_ACTIVEXCONTROLCOMPONENT_JUCEHEADER__
|
||||
#define __JUCE_ACTIVEXCONTROLCOMPONENT_JUCEHEADER__
|
||||
|
||||
#if JUCE_WINDOWS || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A Windows-specific class that can create and embed an ActiveX control inside
|
||||
itself.
|
||||
|
||||
To use it, create one of these, put it in place and make sure it's visible in a
|
||||
window, then use createControl() to instantiate an ActiveX control. The control
|
||||
will then be moved and resized to follow the movements of this component.
|
||||
|
||||
Of course, since the control is a heavyweight window, it'll obliterate any
|
||||
juce components that may overlap this component, but that's life.
|
||||
*/
|
||||
class JUCE_API ActiveXControlComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Create an initially-empty container. */
|
||||
ActiveXControlComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~ActiveXControlComponent();
|
||||
|
||||
/** Tries to create an ActiveX control and embed it in this peer.
|
||||
|
||||
The peer controlIID is a pointer to an IID structure - it's treated
|
||||
as a void* because when including the Juce headers, you might not always
|
||||
have included windows.h first, in which case IID wouldn't be defined.
|
||||
|
||||
e.g. @code
|
||||
const IID myIID = __uuidof (QTControl);
|
||||
myControlComp->createControl (&myIID);
|
||||
@endcode
|
||||
*/
|
||||
bool createControl (const void* controlIID);
|
||||
|
||||
/** Deletes the ActiveX control, if one has been created.
|
||||
*/
|
||||
void deleteControl();
|
||||
|
||||
/** Returns true if a control is currently in use. */
|
||||
bool isControlOpen() const noexcept { return control != nullptr; }
|
||||
|
||||
/** Does a QueryInterface call on the embedded control object.
|
||||
|
||||
This allows you to cast the control to whatever type of COM object you need.
|
||||
|
||||
The iid parameter is a pointer to an IID structure - it's treated
|
||||
as a void* because when including the Juce headers, you might not always
|
||||
have included windows.h first, in which case IID wouldn't be defined, but
|
||||
you should just pass a pointer to an IID.
|
||||
|
||||
e.g. @code
|
||||
const IID iid = __uuidof (IOleWindow);
|
||||
|
||||
IOleWindow* oleWindow = (IOleWindow*) myControlComp->queryInterface (&iid);
|
||||
|
||||
if (oleWindow != nullptr)
|
||||
{
|
||||
HWND hwnd;
|
||||
oleWindow->GetWindow (&hwnd);
|
||||
|
||||
...
|
||||
|
||||
oleWindow->Release();
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
void* queryInterface (const void* iid) const;
|
||||
|
||||
/** Set this to false to stop mouse events being allowed through to the control.
|
||||
*/
|
||||
void setMouseEventsAllowed (bool eventsCanReachControl);
|
||||
|
||||
/** Returns true if mouse events are allowed to get through to the control.
|
||||
*/
|
||||
bool areMouseEventsAllowed() const noexcept { return mouseEventsAllowed; }
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics& g);
|
||||
/** @internal */
|
||||
void* originalWndProc;
|
||||
|
||||
private:
|
||||
class Pimpl;
|
||||
friend class Pimpl;
|
||||
friend class ScopedPointer <Pimpl>;
|
||||
ScopedPointer <Pimpl> control;
|
||||
bool mouseEventsAllowed;
|
||||
|
||||
void setControlBounds (const Rectangle<int>& bounds) const;
|
||||
void setControlVisible (bool b) const;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ActiveXControlComponent);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // __JUCE_ACTIVEXCONTROLCOMPONENT_JUCEHEADER__
|
||||
87
modules/juce_gui_extra/embedding/juce_NSViewComponent.h
Normal file
87
modules/juce_gui_extra/embedding/juce_NSViewComponent.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_NSVIEWCOMPONENT_JUCEHEADER__
|
||||
#define __JUCE_NSVIEWCOMPONENT_JUCEHEADER__
|
||||
|
||||
#if JUCE_MAC || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A Mac-specific class that can create and embed an NSView inside itself.
|
||||
|
||||
To use it, create one of these, put it in place and make sure it's visible in a
|
||||
window, then use setView() to assign an NSView to it. The view will then be
|
||||
moved and resized to follow the movements of this component.
|
||||
|
||||
Of course, since the view is a native object, it'll obliterate any
|
||||
juce components that may overlap this component, but that's life.
|
||||
*/
|
||||
class JUCE_API NSViewComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Create an initially-empty container. */
|
||||
NSViewComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~NSViewComponent();
|
||||
|
||||
/** Assigns an NSView to this peer.
|
||||
|
||||
The view will be retained and released by this component for as long as
|
||||
it is needed. To remove the current view, just call setView (nullptr).
|
||||
|
||||
Note: a void* is used here to avoid including the cocoa headers as
|
||||
part of the juce.h, but the method expects an NSView*.
|
||||
*/
|
||||
void setView (void* nsView);
|
||||
|
||||
/** Returns the current NSView.
|
||||
|
||||
Note: a void* is returned here to avoid the needing to include the cocoa
|
||||
headers, so you should just cast the return value to an NSView*.
|
||||
*/
|
||||
void* getView() const;
|
||||
|
||||
|
||||
/** Resizes this component to fit the view that it contains. */
|
||||
void resizeToFitView();
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics& g);
|
||||
|
||||
|
||||
private:
|
||||
class Pimpl;
|
||||
friend class Pimpl;
|
||||
ScopedPointer<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NSViewComponent);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // __JUCE_NSVIEWCOMPONENT_JUCEHEADER__
|
||||
88
modules/juce_gui_extra/embedding/juce_UIViewComponent.h
Normal file
88
modules/juce_gui_extra/embedding/juce_UIViewComponent.h
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_UIVIEWCOMPONENT_JUCEHEADER__
|
||||
#define __JUCE_UIVIEWCOMPONENT_JUCEHEADER__
|
||||
|
||||
#if JUCE_IOS || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An iOS-specific class that can create and embed an UIView inside itself.
|
||||
|
||||
To use it, create one of these, put it in place and make sure it's visible in a
|
||||
window, then use setView() to assign an NSView to it. The view will then be
|
||||
moved and resized to follow the movements of this component.
|
||||
|
||||
Of course, since the view is a native object, it'll obliterate any
|
||||
juce components that may overlap this component, but that's life.
|
||||
*/
|
||||
class JUCE_API UIViewComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Create an initially-empty container. */
|
||||
UIViewComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~UIViewComponent();
|
||||
|
||||
/** Assigns an UIView to this peer.
|
||||
|
||||
The view will be retained and released by this component for as long as
|
||||
it is needed. To remove the current view, just call setView (nullptr).
|
||||
|
||||
Note: a void* is used here to avoid including the cocoa headers as
|
||||
part of the juce.h, but the method expects an UIView*.
|
||||
*/
|
||||
void setView (void* uiView);
|
||||
|
||||
/** Returns the current UIView.
|
||||
|
||||
Note: a void* is returned here to avoid the needing to include the cocoa
|
||||
headers, so you should just cast the return value to an UIView*.
|
||||
*/
|
||||
void* getView() const;
|
||||
|
||||
|
||||
/** Resizes this component to fit the view that it contains. */
|
||||
void resizeToFitView();
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics& g);
|
||||
|
||||
|
||||
private:
|
||||
class Pimpl;
|
||||
friend class Pimpl;
|
||||
ScopedPointer<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIViewComponent);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // __JUCE_UIVIEWCOMPONENT_JUCEHEADER__
|
||||
142
modules/juce_gui_extra/juce_gui_extra.cpp
Normal file
142
modules/juce_gui_extra/juce_gui_extra.cpp
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#if defined (__JUCE_GUI_EXTRA_JUCEHEADER__) && ! JUCE_AMALGAMATED_INCLUDE
|
||||
/* When you add this cpp file to your project, you mustn't include it in a file where you've
|
||||
already included any other headers - just put it inside a file on its own, possibly with your config
|
||||
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
|
||||
header files that the compiler may be using.
|
||||
*/
|
||||
#error "Incorrect use of JUCE cpp file"
|
||||
#endif
|
||||
|
||||
#define JUCE_DONT_DEFINE_MACROS 1
|
||||
#include "../juce_core/native/juce_BasicNativeHeaders.h"
|
||||
#include "juce_gui_extra.h"
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC
|
||||
#define Point CarbonDummyPointName
|
||||
#define Component CarbonDummyCompName
|
||||
#import <WebKit/WebKit.h>
|
||||
#import <IOKit/IOKitLib.h>
|
||||
#import <IOKit/IOCFPlugIn.h>
|
||||
#import <IOKit/hid/IOHIDLib.h>
|
||||
#import <IOKit/hid/IOHIDKeys.h>
|
||||
#import <IOKit/pwr_mgt/IOPMLib.h>
|
||||
#import <Carbon/Carbon.h> // still needed for SetSystemUIMode()
|
||||
#undef Point
|
||||
#undef Component
|
||||
|
||||
#elif JUCE_IOS
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_WINDOWS
|
||||
#include <windowsx.h>
|
||||
#include <vfw.h>
|
||||
#include <commdlg.h>
|
||||
|
||||
#if JUCE_WEB_BROWSER
|
||||
#include <Exdisp.h>
|
||||
#include <exdispid.h>
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_LINUX
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xutil.h>
|
||||
#undef SIZEOF
|
||||
#undef KeyPress
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
// START_AUTOINCLUDE documents/*.cpp, code_editor/*.cpp, embedding/*.cpp, lookandfeel/*.cpp, misc/*.cpp
|
||||
#include "documents/juce_FileBasedDocument.cpp"
|
||||
#include "code_editor/juce_CodeDocument.cpp"
|
||||
#include "code_editor/juce_CodeEditorComponent.cpp"
|
||||
#include "code_editor/juce_CPlusPlusCodeTokeniser.cpp"
|
||||
#include "lookandfeel/juce_OldSchoolLookAndFeel.cpp"
|
||||
#include "misc/juce_BubbleMessageComponent.cpp"
|
||||
#include "misc/juce_ColourSelector.cpp"
|
||||
#include "misc/juce_KeyMappingEditorComponent.cpp"
|
||||
#include "misc/juce_PreferencesPanel.cpp"
|
||||
#include "misc/juce_RecentlyOpenedFilesList.cpp"
|
||||
#include "misc/juce_SplashScreen.cpp"
|
||||
#include "misc/juce_SystemTrayIconComponent.cpp"
|
||||
// END_AUTOINCLUDE
|
||||
|
||||
using namespace JUCE_NAMESPACE;
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
#include "../juce_core/native/juce_osx_ObjCHelpers.h"
|
||||
#include "../juce_core/native/juce_mac_ObjCSuffix.h"
|
||||
#include "../juce_graphics/native/juce_mac_CoreGraphicsHelpers.h"
|
||||
|
||||
#if JUCE_MAC
|
||||
#include "native/juce_mac_NSViewComponent.mm"
|
||||
#include "native/juce_mac_AppleRemote.mm"
|
||||
#endif
|
||||
|
||||
#if JUCE_IOS
|
||||
#include "native/juce_ios_UIViewComponent.mm"
|
||||
#endif
|
||||
|
||||
#if JUCE_WEB_BROWSER
|
||||
#if JUCE_MAC
|
||||
#include "native/juce_mac_WebBrowserComponent.mm"
|
||||
#else
|
||||
#include "native/juce_ios_WebBrowserComponent.mm"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_WINDOWS
|
||||
#include "../juce_core/native/juce_win32_ComSmartPtr.h"
|
||||
#include "../juce_events/native/juce_win32_HiddenMessageWindow.h"
|
||||
#include "native/juce_win32_ActiveXComponent.cpp"
|
||||
#if JUCE_WEB_BROWSER
|
||||
#include "native/juce_win32_WebBrowserComponent.cpp"
|
||||
#endif
|
||||
#include "native/juce_win32_SystemTrayIcon.cpp"
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_LINUX
|
||||
#if JUCE_WEB_BROWSER
|
||||
#include "native/juce_linux_WebBrowserComponent.cpp"
|
||||
#endif
|
||||
#include "native/juce_linux_SystemTrayIcon.cpp"
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_ANDROID
|
||||
#if JUCE_WEB_BROWSER
|
||||
#include "native/juce_android_WebBrowserComponent.cpp"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
103
modules/juce_gui_extra/juce_gui_extra.h
Normal file
103
modules/juce_gui_extra/juce_gui_extra.h
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_GUI_EXTRA_JUCEHEADER__
|
||||
#define __JUCE_GUI_EXTRA_JUCEHEADER__
|
||||
|
||||
#include "../juce_gui_basics/juce_gui_basics.h"
|
||||
|
||||
|
||||
//=============================================================================
|
||||
/** Config: JUCE_WEB_BROWSER
|
||||
This lets you disable the WebBrowserComponent class (Mac and Windows).
|
||||
If you're not using any embedded web-pages, turning this off may reduce your code size.
|
||||
*/
|
||||
#ifndef JUCE_WEB_BROWSER
|
||||
#define JUCE_WEB_BROWSER 1
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
// START_AUTOINCLUDE documents, code_editor, embedding, lookandfeel, misc
|
||||
#ifndef __JUCE_FILEBASEDDOCUMENT_JUCEHEADER__
|
||||
#include "documents/juce_FileBasedDocument.h"
|
||||
#endif
|
||||
#ifndef __JUCE_CODEDOCUMENT_JUCEHEADER__
|
||||
#include "code_editor/juce_CodeDocument.h"
|
||||
#endif
|
||||
#ifndef __JUCE_CODEEDITORCOMPONENT_JUCEHEADER__
|
||||
#include "code_editor/juce_CodeEditorComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_CODETOKENISER_JUCEHEADER__
|
||||
#include "code_editor/juce_CodeTokeniser.h"
|
||||
#endif
|
||||
#ifndef __JUCE_CPLUSPLUSCODETOKENISER_JUCEHEADER__
|
||||
#include "code_editor/juce_CPlusPlusCodeTokeniser.h"
|
||||
#endif
|
||||
#ifndef __JUCE_ACTIVEXCONTROLCOMPONENT_JUCEHEADER__
|
||||
#include "embedding/juce_ActiveXControlComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_NSVIEWCOMPONENT_JUCEHEADER__
|
||||
#include "embedding/juce_NSViewComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_UIVIEWCOMPONENT_JUCEHEADER__
|
||||
#include "embedding/juce_UIViewComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_OLDSCHOOLLOOKANDFEEL_JUCEHEADER__
|
||||
#include "lookandfeel/juce_OldSchoolLookAndFeel.h"
|
||||
#endif
|
||||
#ifndef __JUCE_APPLEREMOTE_JUCEHEADER__
|
||||
#include "misc/juce_AppleRemote.h"
|
||||
#endif
|
||||
#ifndef __JUCE_BUBBLEMESSAGECOMPONENT_JUCEHEADER__
|
||||
#include "misc/juce_BubbleMessageComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_COLOURSELECTOR_JUCEHEADER__
|
||||
#include "misc/juce_ColourSelector.h"
|
||||
#endif
|
||||
#ifndef __JUCE_KEYMAPPINGEDITORCOMPONENT_JUCEHEADER__
|
||||
#include "misc/juce_KeyMappingEditorComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_PREFERENCESPANEL_JUCEHEADER__
|
||||
#include "misc/juce_PreferencesPanel.h"
|
||||
#endif
|
||||
#ifndef __JUCE_RECENTLYOPENEDFILESLIST_JUCEHEADER__
|
||||
#include "misc/juce_RecentlyOpenedFilesList.h"
|
||||
#endif
|
||||
#ifndef __JUCE_SPLASHSCREEN_JUCEHEADER__
|
||||
#include "misc/juce_SplashScreen.h"
|
||||
#endif
|
||||
#ifndef __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__
|
||||
#include "misc/juce_SystemTrayIconComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_WEBBROWSERCOMPONENT_JUCEHEADER__
|
||||
#include "misc/juce_WebBrowserComponent.h"
|
||||
#endif
|
||||
// END_AUTOINCLUDE
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
|
||||
#endif // __JUCE_GUI_EXTRA_JUCEHEADER__
|
||||
21
modules/juce_gui_extra/juce_module_info
Normal file
21
modules/juce_gui_extra/juce_module_info
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"id": "juce_gui_extra",
|
||||
"name": "JUCE extended GUI classes",
|
||||
"version": "2.0.0",
|
||||
"description": "Miscellaneous GUI classes for specialised tasks.",
|
||||
"website": "http://www.juce.com/juce",
|
||||
"license": "GPL/Commercial",
|
||||
|
||||
"dependencies": [ { "id": "juce_gui_basics", "version": "matching" } ],
|
||||
|
||||
"include": "juce_gui_extra.h",
|
||||
|
||||
"compile": [ { "file": "juce_gui_extra.cpp" } ],
|
||||
|
||||
"browse": [ "code_editor/*",
|
||||
"documents/*",
|
||||
"embedding/*",
|
||||
"lookandfeel/*",
|
||||
"misc/*",
|
||||
"native/*" ]
|
||||
}
|
||||
599
modules/juce_gui_extra/lookandfeel/juce_OldSchoolLookAndFeel.cpp
Normal file
599
modules/juce_gui_extra/lookandfeel/juce_OldSchoolLookAndFeel.cpp
Normal file
|
|
@ -0,0 +1,599 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
OldSchoolLookAndFeel::OldSchoolLookAndFeel()
|
||||
{
|
||||
setColour (TextButton::buttonColourId, Colour (0xffbbbbff));
|
||||
setColour (ListBox::outlineColourId, findColour (ComboBox::outlineColourId));
|
||||
setColour (ScrollBar::thumbColourId, Colour (0xffbbbbdd));
|
||||
setColour (ScrollBar::backgroundColourId, Colours::transparentBlack);
|
||||
setColour (Slider::thumbColourId, Colours::white);
|
||||
setColour (Slider::trackColourId, Colour (0x7f000000));
|
||||
setColour (Slider::textBoxOutlineColourId, Colours::grey);
|
||||
setColour (ProgressBar::backgroundColourId, Colours::white.withAlpha (0.6f));
|
||||
setColour (ProgressBar::foregroundColourId, Colours::green.withAlpha (0.7f));
|
||||
setColour (PopupMenu::backgroundColourId, Colour (0xffeef5f8));
|
||||
setColour (PopupMenu::highlightedBackgroundColourId, Colour (0xbfa4c2ce));
|
||||
setColour (PopupMenu::highlightedTextColourId, Colours::black);
|
||||
setColour (TextEditor::focusedOutlineColourId, findColour (TextButton::buttonColourId));
|
||||
|
||||
scrollbarShadow.setShadowProperties (2.2f, 0.5f, 0, 0);
|
||||
}
|
||||
|
||||
OldSchoolLookAndFeel::~OldSchoolLookAndFeel()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void OldSchoolLookAndFeel::drawButtonBackground (Graphics& g,
|
||||
Button& button,
|
||||
const Colour& backgroundColour,
|
||||
bool isMouseOverButton,
|
||||
bool isButtonDown)
|
||||
{
|
||||
const int width = button.getWidth();
|
||||
const int height = button.getHeight();
|
||||
|
||||
const float indent = 2.0f;
|
||||
const int cornerSize = jmin (roundToInt (width * 0.4f),
|
||||
roundToInt (height * 0.4f));
|
||||
|
||||
Path p;
|
||||
p.addRoundedRectangle (indent, indent,
|
||||
width - indent * 2.0f,
|
||||
height - indent * 2.0f,
|
||||
(float) cornerSize);
|
||||
|
||||
Colour bc (backgroundColour.withMultipliedSaturation (0.3f));
|
||||
|
||||
if (isMouseOverButton)
|
||||
{
|
||||
if (isButtonDown)
|
||||
bc = bc.brighter();
|
||||
else if (bc.getBrightness() > 0.5f)
|
||||
bc = bc.darker (0.1f);
|
||||
else
|
||||
bc = bc.brighter (0.1f);
|
||||
}
|
||||
|
||||
g.setColour (bc);
|
||||
g.fillPath (p);
|
||||
|
||||
g.setColour (bc.contrasting().withAlpha ((isMouseOverButton) ? 0.6f : 0.4f));
|
||||
g.strokePath (p, PathStrokeType ((isMouseOverButton) ? 2.0f : 1.4f));
|
||||
}
|
||||
|
||||
void OldSchoolLookAndFeel::drawTickBox (Graphics& g,
|
||||
Component& /*component*/,
|
||||
float x, float y, float w, float h,
|
||||
const bool ticked,
|
||||
const bool isEnabled,
|
||||
const bool /*isMouseOverButton*/,
|
||||
const bool isButtonDown)
|
||||
{
|
||||
Path box;
|
||||
box.addRoundedRectangle (0.0f, 2.0f, 6.0f, 6.0f, 1.0f);
|
||||
|
||||
g.setColour (isEnabled ? Colours::blue.withAlpha (isButtonDown ? 0.3f : 0.1f)
|
||||
: Colours::lightgrey.withAlpha (0.1f));
|
||||
|
||||
AffineTransform trans (AffineTransform::scale (w / 9.0f, h / 9.0f).translated (x, y));
|
||||
|
||||
g.fillPath (box, trans);
|
||||
|
||||
g.setColour (Colours::black.withAlpha (0.6f));
|
||||
g.strokePath (box, PathStrokeType (0.9f), trans);
|
||||
|
||||
if (ticked)
|
||||
{
|
||||
Path tick;
|
||||
tick.startNewSubPath (1.5f, 3.0f);
|
||||
tick.lineTo (3.0f, 6.0f);
|
||||
tick.lineTo (6.0f, 0.0f);
|
||||
|
||||
g.setColour (isEnabled ? Colours::black : Colours::grey);
|
||||
g.strokePath (tick, PathStrokeType (2.5f), trans);
|
||||
}
|
||||
}
|
||||
|
||||
void OldSchoolLookAndFeel::drawToggleButton (Graphics& g,
|
||||
ToggleButton& button,
|
||||
bool isMouseOverButton,
|
||||
bool isButtonDown)
|
||||
{
|
||||
if (button.hasKeyboardFocus (true))
|
||||
{
|
||||
g.setColour (button.findColour (TextEditor::focusedOutlineColourId));
|
||||
g.drawRect (0, 0, button.getWidth(), button.getHeight());
|
||||
}
|
||||
|
||||
const int tickWidth = jmin (20, button.getHeight() - 4);
|
||||
|
||||
drawTickBox (g, button, 4.0f, (button.getHeight() - tickWidth) * 0.5f,
|
||||
(float) tickWidth, (float) tickWidth,
|
||||
button.getToggleState(),
|
||||
button.isEnabled(),
|
||||
isMouseOverButton,
|
||||
isButtonDown);
|
||||
|
||||
g.setColour (button.findColour (ToggleButton::textColourId));
|
||||
g.setFont (jmin (15.0f, button.getHeight() * 0.6f));
|
||||
|
||||
if (! button.isEnabled())
|
||||
g.setOpacity (0.5f);
|
||||
|
||||
const int textX = tickWidth + 5;
|
||||
|
||||
g.drawFittedText (button.getButtonText(),
|
||||
textX, 4,
|
||||
button.getWidth() - textX - 2, button.getHeight() - 8,
|
||||
Justification::centredLeft, 10);
|
||||
}
|
||||
|
||||
void OldSchoolLookAndFeel::drawProgressBar (Graphics& g, ProgressBar& progressBar,
|
||||
int width, int height,
|
||||
double progress, const String& textToShow)
|
||||
{
|
||||
if (progress < 0 || progress >= 1.0)
|
||||
{
|
||||
LookAndFeel::drawProgressBar (g, progressBar, width, height, progress, textToShow);
|
||||
}
|
||||
else
|
||||
{
|
||||
const Colour background (progressBar.findColour (ProgressBar::backgroundColourId));
|
||||
const Colour foreground (progressBar.findColour (ProgressBar::foregroundColourId));
|
||||
|
||||
g.fillAll (background);
|
||||
g.setColour (foreground);
|
||||
|
||||
g.fillRect (1, 1,
|
||||
jlimit (0, width - 2, roundToInt (progress * (width - 2))),
|
||||
height - 2);
|
||||
|
||||
if (textToShow.isNotEmpty())
|
||||
{
|
||||
g.setColour (Colour::contrasting (background, foreground));
|
||||
g.setFont (height * 0.6f);
|
||||
|
||||
g.drawText (textToShow, 0, 0, width, height, Justification::centred, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OldSchoolLookAndFeel::drawScrollbarButton (Graphics& g,
|
||||
ScrollBar& bar,
|
||||
int width, int height,
|
||||
int buttonDirection,
|
||||
bool isScrollbarVertical,
|
||||
bool isMouseOverButton,
|
||||
bool isButtonDown)
|
||||
{
|
||||
if (isScrollbarVertical)
|
||||
width -= 2;
|
||||
else
|
||||
height -= 2;
|
||||
|
||||
Path p;
|
||||
|
||||
if (buttonDirection == 0)
|
||||
p.addTriangle (width * 0.5f, height * 0.2f,
|
||||
width * 0.1f, height * 0.7f,
|
||||
width * 0.9f, height * 0.7f);
|
||||
else if (buttonDirection == 1)
|
||||
p.addTriangle (width * 0.8f, height * 0.5f,
|
||||
width * 0.3f, height * 0.1f,
|
||||
width * 0.3f, height * 0.9f);
|
||||
else if (buttonDirection == 2)
|
||||
p.addTriangle (width * 0.5f, height * 0.8f,
|
||||
width * 0.1f, height * 0.3f,
|
||||
width * 0.9f, height * 0.3f);
|
||||
else if (buttonDirection == 3)
|
||||
p.addTriangle (width * 0.2f, height * 0.5f,
|
||||
width * 0.7f, height * 0.1f,
|
||||
width * 0.7f, height * 0.9f);
|
||||
|
||||
if (isButtonDown)
|
||||
g.setColour (Colours::white);
|
||||
else if (isMouseOverButton)
|
||||
g.setColour (Colours::white.withAlpha (0.7f));
|
||||
else
|
||||
g.setColour (bar.findColour (ScrollBar::thumbColourId).withAlpha (0.5f));
|
||||
|
||||
g.fillPath (p);
|
||||
|
||||
g.setColour (Colours::black.withAlpha (0.5f));
|
||||
g.strokePath (p, PathStrokeType (0.5f));
|
||||
}
|
||||
|
||||
void OldSchoolLookAndFeel::drawScrollbar (Graphics& g,
|
||||
ScrollBar& bar,
|
||||
int x, int y,
|
||||
int width, int height,
|
||||
bool isScrollbarVertical,
|
||||
int thumbStartPosition,
|
||||
int thumbSize,
|
||||
bool isMouseOver,
|
||||
bool isMouseDown)
|
||||
{
|
||||
g.fillAll (bar.findColour (ScrollBar::backgroundColourId));
|
||||
|
||||
g.setColour (bar.findColour (ScrollBar::thumbColourId)
|
||||
.withAlpha ((isMouseOver || isMouseDown) ? 0.4f : 0.15f));
|
||||
|
||||
if (thumbSize > 0.0f)
|
||||
{
|
||||
Rectangle<int> thumb;
|
||||
|
||||
if (isScrollbarVertical)
|
||||
{
|
||||
width -= 2;
|
||||
g.fillRect (x + roundToInt (width * 0.35f), y,
|
||||
roundToInt (width * 0.3f), height);
|
||||
|
||||
thumb.setBounds (x + 1, thumbStartPosition,
|
||||
width - 2, thumbSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
height -= 2;
|
||||
g.fillRect (x, y + roundToInt (height * 0.35f),
|
||||
width, roundToInt (height * 0.3f));
|
||||
|
||||
thumb.setBounds (thumbStartPosition, y + 1,
|
||||
thumbSize, height - 2);
|
||||
}
|
||||
|
||||
g.setColour (bar.findColour (ScrollBar::thumbColourId)
|
||||
.withAlpha ((isMouseOver || isMouseDown) ? 0.95f : 0.7f));
|
||||
|
||||
g.fillRect (thumb);
|
||||
|
||||
g.setColour (Colours::black.withAlpha ((isMouseOver || isMouseDown) ? 0.4f : 0.25f));
|
||||
g.drawRect (thumb.getX(), thumb.getY(), thumb.getWidth(), thumb.getHeight());
|
||||
|
||||
if (thumbSize > 16)
|
||||
{
|
||||
for (int i = 3; --i >= 0;)
|
||||
{
|
||||
const float linePos = thumbStartPosition + thumbSize / 2 + (i - 1) * 4.0f;
|
||||
g.setColour (Colours::black.withAlpha (0.15f));
|
||||
|
||||
if (isScrollbarVertical)
|
||||
{
|
||||
g.drawLine (x + width * 0.2f, linePos, width * 0.8f, linePos);
|
||||
g.setColour (Colours::white.withAlpha (0.15f));
|
||||
g.drawLine (width * 0.2f, linePos - 1, width * 0.8f, linePos - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
g.drawLine (linePos, height * 0.2f, linePos, height * 0.8f);
|
||||
g.setColour (Colours::white.withAlpha (0.15f));
|
||||
g.drawLine (linePos - 1, height * 0.2f, linePos - 1, height * 0.8f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImageEffectFilter* OldSchoolLookAndFeel::getScrollbarEffect()
|
||||
{
|
||||
return &scrollbarShadow;
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void OldSchoolLookAndFeel::drawPopupMenuBackground (Graphics& g, int width, int height)
|
||||
{
|
||||
g.fillAll (findColour (PopupMenu::backgroundColourId));
|
||||
|
||||
g.setColour (Colours::black.withAlpha (0.6f));
|
||||
g.drawRect (0, 0, width, height);
|
||||
}
|
||||
|
||||
void OldSchoolLookAndFeel::drawMenuBarBackground (Graphics& g, int /*width*/, int /*height*/,
|
||||
bool, MenuBarComponent& menuBar)
|
||||
{
|
||||
g.fillAll (menuBar.findColour (PopupMenu::backgroundColourId));
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void OldSchoolLookAndFeel::drawTextEditorOutline (Graphics& g, int width, int height, TextEditor& textEditor)
|
||||
{
|
||||
if (textEditor.isEnabled())
|
||||
{
|
||||
g.setColour (textEditor.findColour (TextEditor::outlineColourId));
|
||||
g.drawRect (0, 0, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void OldSchoolLookAndFeel::drawComboBox (Graphics& g, int width, int height,
|
||||
const bool isButtonDown,
|
||||
int buttonX, int buttonY,
|
||||
int buttonW, int buttonH,
|
||||
ComboBox& box)
|
||||
{
|
||||
g.fillAll (box.findColour (ComboBox::backgroundColourId));
|
||||
|
||||
g.setColour (box.findColour ((isButtonDown) ? ComboBox::buttonColourId
|
||||
: ComboBox::backgroundColourId));
|
||||
g.fillRect (buttonX, buttonY, buttonW, buttonH);
|
||||
|
||||
g.setColour (box.findColour (ComboBox::outlineColourId));
|
||||
g.drawRect (0, 0, width, height);
|
||||
|
||||
const float arrowX = 0.2f;
|
||||
const float arrowH = 0.3f;
|
||||
|
||||
if (box.isEnabled())
|
||||
{
|
||||
Path p;
|
||||
p.addTriangle (buttonX + buttonW * 0.5f, buttonY + buttonH * (0.45f - arrowH),
|
||||
buttonX + buttonW * (1.0f - arrowX), buttonY + buttonH * 0.45f,
|
||||
buttonX + buttonW * arrowX, buttonY + buttonH * 0.45f);
|
||||
|
||||
p.addTriangle (buttonX + buttonW * 0.5f, buttonY + buttonH * (0.55f + arrowH),
|
||||
buttonX + buttonW * (1.0f - arrowX), buttonY + buttonH * 0.55f,
|
||||
buttonX + buttonW * arrowX, buttonY + buttonH * 0.55f);
|
||||
|
||||
g.setColour (box.findColour ((isButtonDown) ? ComboBox::backgroundColourId
|
||||
: ComboBox::buttonColourId));
|
||||
g.fillPath (p);
|
||||
}
|
||||
}
|
||||
|
||||
const Font OldSchoolLookAndFeel::getComboBoxFont (ComboBox& box)
|
||||
{
|
||||
Font f (jmin (15.0f, box.getHeight() * 0.85f));
|
||||
f.setHorizontalScale (0.9f);
|
||||
return f;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static void drawTriangle (Graphics& g, float x1, float y1, float x2, float y2, float x3, float y3, const Colour& fill, const Colour& outline) noexcept
|
||||
{
|
||||
Path p;
|
||||
p.addTriangle (x1, y1, x2, y2, x3, y3);
|
||||
g.setColour (fill);
|
||||
g.fillPath (p);
|
||||
|
||||
g.setColour (outline);
|
||||
g.strokePath (p, PathStrokeType (0.3f));
|
||||
}
|
||||
|
||||
void OldSchoolLookAndFeel::drawLinearSlider (Graphics& g,
|
||||
int x, int y,
|
||||
int w, int h,
|
||||
float sliderPos,
|
||||
float minSliderPos,
|
||||
float maxSliderPos,
|
||||
const Slider::SliderStyle style,
|
||||
Slider& slider)
|
||||
{
|
||||
g.fillAll (slider.findColour (Slider::backgroundColourId));
|
||||
|
||||
if (style == Slider::LinearBar)
|
||||
{
|
||||
g.setColour (slider.findColour (Slider::thumbColourId));
|
||||
g.fillRect (x, y, (int) sliderPos - x, h);
|
||||
|
||||
g.setColour (slider.findColour (Slider::textBoxTextColourId).withMultipliedAlpha (0.5f));
|
||||
g.drawRect (x, y, (int) sliderPos - x, h);
|
||||
}
|
||||
else
|
||||
{
|
||||
g.setColour (slider.findColour (Slider::trackColourId)
|
||||
.withMultipliedAlpha (slider.isEnabled() ? 1.0f : 0.3f));
|
||||
|
||||
if (slider.isHorizontal())
|
||||
{
|
||||
g.fillRect (x, y + roundToInt (h * 0.6f),
|
||||
w, roundToInt (h * 0.2f));
|
||||
}
|
||||
else
|
||||
{
|
||||
g.fillRect (x + roundToInt (w * 0.5f - jmin (3.0f, w * 0.1f)), y,
|
||||
jmin (4, roundToInt (w * 0.2f)), h);
|
||||
}
|
||||
|
||||
float alpha = 0.35f;
|
||||
|
||||
if (slider.isEnabled())
|
||||
alpha = slider.isMouseOverOrDragging() ? 1.0f : 0.7f;
|
||||
|
||||
const Colour fill (slider.findColour (Slider::thumbColourId).withAlpha (alpha));
|
||||
const Colour outline (Colours::black.withAlpha (slider.isEnabled() ? 0.7f : 0.35f));
|
||||
|
||||
if (style == Slider::TwoValueVertical || style == Slider::ThreeValueVertical)
|
||||
{
|
||||
drawTriangle (g, x + w * 0.5f + jmin (4.0f, w * 0.3f), minSliderPos,
|
||||
x + w * 0.5f - jmin (8.0f, w * 0.4f), minSliderPos - 7.0f,
|
||||
x + w * 0.5f - jmin (8.0f, w * 0.4f), minSliderPos,
|
||||
fill, outline);
|
||||
|
||||
drawTriangle (g, x + w * 0.5f + jmin (4.0f, w * 0.3f), maxSliderPos,
|
||||
x + w * 0.5f - jmin (8.0f, w * 0.4f), maxSliderPos,
|
||||
x + w * 0.5f - jmin (8.0f, w * 0.4f), maxSliderPos + 7.0f,
|
||||
fill, outline);
|
||||
}
|
||||
else if (style == Slider::TwoValueHorizontal || style == Slider::ThreeValueHorizontal)
|
||||
{
|
||||
drawTriangle (g, minSliderPos, y + h * 0.6f - jmin (4.0f, h * 0.3f),
|
||||
minSliderPos - 7.0f, y + h * 0.9f ,
|
||||
minSliderPos, y + h * 0.9f,
|
||||
fill, outline);
|
||||
|
||||
drawTriangle (g, maxSliderPos, y + h * 0.6f - jmin (4.0f, h * 0.3f),
|
||||
maxSliderPos, y + h * 0.9f,
|
||||
maxSliderPos + 7.0f, y + h * 0.9f,
|
||||
fill, outline);
|
||||
}
|
||||
|
||||
if (style == Slider::LinearHorizontal || style == Slider::ThreeValueHorizontal)
|
||||
{
|
||||
drawTriangle (g, sliderPos, y + h * 0.9f,
|
||||
sliderPos - 7.0f, y + h * 0.2f,
|
||||
sliderPos + 7.0f, y + h * 0.2f,
|
||||
fill, outline);
|
||||
}
|
||||
else if (style == Slider::LinearVertical || style == Slider::ThreeValueVertical)
|
||||
{
|
||||
drawTriangle (g, x + w * 0.5f - jmin (4.0f, w * 0.3f), sliderPos,
|
||||
x + w * 0.5f + jmin (8.0f, w * 0.4f), sliderPos - 7.0f,
|
||||
x + w * 0.5f + jmin (8.0f, w * 0.4f), sliderPos + 7.0f,
|
||||
fill, outline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button* OldSchoolLookAndFeel::createSliderButton (const bool isIncrement)
|
||||
{
|
||||
if (isIncrement)
|
||||
return new ArrowButton ("u", 0.75f, Colours::white.withAlpha (0.8f));
|
||||
else
|
||||
return new ArrowButton ("d", 0.25f, Colours::white.withAlpha (0.8f));
|
||||
}
|
||||
|
||||
ImageEffectFilter* OldSchoolLookAndFeel::getSliderEffect()
|
||||
{
|
||||
return &scrollbarShadow;
|
||||
}
|
||||
|
||||
int OldSchoolLookAndFeel::getSliderThumbRadius (Slider&)
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void OldSchoolLookAndFeel::drawCornerResizer (Graphics& g,
|
||||
int w, int h,
|
||||
bool isMouseOver,
|
||||
bool isMouseDragging)
|
||||
{
|
||||
g.setColour ((isMouseOver || isMouseDragging) ? Colours::lightgrey
|
||||
: Colours::darkgrey);
|
||||
|
||||
const float lineThickness = jmin (w, h) * 0.1f;
|
||||
|
||||
for (float i = 0.0f; i < 1.0f; i += 0.3f)
|
||||
{
|
||||
g.drawLine (w * i,
|
||||
h + 1.0f,
|
||||
w + 1.0f,
|
||||
h * i,
|
||||
lineThickness);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Button* OldSchoolLookAndFeel::createDocumentWindowButton (int buttonType)
|
||||
{
|
||||
Path shape;
|
||||
|
||||
if (buttonType == DocumentWindow::closeButton)
|
||||
{
|
||||
shape.addLineSegment (Line<float> (0.0f, 0.0f, 1.0f, 1.0f), 0.35f);
|
||||
shape.addLineSegment (Line<float> (1.0f, 0.0f, 0.0f, 1.0f), 0.35f);
|
||||
|
||||
ShapeButton* const b = new ShapeButton ("close",
|
||||
Colour (0x7fff3333),
|
||||
Colour (0xd7ff3333),
|
||||
Colour (0xf7ff3333));
|
||||
|
||||
b->setShape (shape, true, true, true);
|
||||
return b;
|
||||
}
|
||||
else if (buttonType == DocumentWindow::minimiseButton)
|
||||
{
|
||||
shape.addLineSegment (Line<float> (0.0f, 0.5f, 1.0f, 0.5f), 0.25f);
|
||||
|
||||
DrawableButton* b = new DrawableButton ("minimise", DrawableButton::ImageFitted);
|
||||
DrawablePath dp;
|
||||
dp.setPath (shape);
|
||||
dp.setFill (Colours::black.withAlpha (0.3f));
|
||||
b->setImages (&dp);
|
||||
return b;
|
||||
}
|
||||
else if (buttonType == DocumentWindow::maximiseButton)
|
||||
{
|
||||
shape.addLineSegment (Line<float> (0.5f, 0.0f, 0.5f, 1.0f), 0.25f);
|
||||
shape.addLineSegment (Line<float> (0.0f, 0.5f, 1.0f, 0.5f), 0.25f);
|
||||
|
||||
DrawableButton* b = new DrawableButton ("maximise", DrawableButton::ImageFitted);
|
||||
DrawablePath dp;
|
||||
dp.setPath (shape);
|
||||
dp.setFill (Colours::black.withAlpha (0.3f));
|
||||
b->setImages (&dp);
|
||||
return b;
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void OldSchoolLookAndFeel::positionDocumentWindowButtons (DocumentWindow&,
|
||||
int titleBarX,
|
||||
int titleBarY,
|
||||
int titleBarW,
|
||||
int titleBarH,
|
||||
Button* minimiseButton,
|
||||
Button* maximiseButton,
|
||||
Button* closeButton,
|
||||
bool positionTitleBarButtonsOnLeft)
|
||||
{
|
||||
titleBarY += titleBarH / 8;
|
||||
titleBarH -= titleBarH / 4;
|
||||
|
||||
const int buttonW = titleBarH;
|
||||
|
||||
int x = positionTitleBarButtonsOnLeft ? titleBarX + 4
|
||||
: titleBarX + titleBarW - buttonW - 4;
|
||||
|
||||
if (closeButton != nullptr)
|
||||
{
|
||||
closeButton->setBounds (x, titleBarY, buttonW, titleBarH);
|
||||
x += positionTitleBarButtonsOnLeft ? buttonW + buttonW / 5
|
||||
: -(buttonW + buttonW / 5);
|
||||
}
|
||||
|
||||
if (positionTitleBarButtonsOnLeft)
|
||||
std::swap (minimiseButton, maximiseButton);
|
||||
|
||||
if (maximiseButton != nullptr)
|
||||
{
|
||||
maximiseButton->setBounds (x, titleBarY - 2, buttonW, titleBarH);
|
||||
x += positionTitleBarButtonsOnLeft ? buttonW : -buttonW;
|
||||
}
|
||||
|
||||
if (minimiseButton != nullptr)
|
||||
minimiseButton->setBounds (x, titleBarY - 2, buttonW, titleBarH);
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
157
modules/juce_gui_extra/lookandfeel/juce_OldSchoolLookAndFeel.h
Normal file
157
modules/juce_gui_extra/lookandfeel/juce_OldSchoolLookAndFeel.h
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_OLDSCHOOLLOOKANDFEEL_JUCEHEADER__
|
||||
#define __JUCE_OLDSCHOOLLOOKANDFEEL_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
The original Juce look-and-feel.
|
||||
|
||||
*/
|
||||
class JUCE_API OldSchoolLookAndFeel : public LookAndFeel
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates the default JUCE look and feel. */
|
||||
OldSchoolLookAndFeel();
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~OldSchoolLookAndFeel();
|
||||
|
||||
//==============================================================================
|
||||
/** Draws the lozenge-shaped background for a standard button. */
|
||||
virtual void drawButtonBackground (Graphics& g,
|
||||
Button& button,
|
||||
const Colour& backgroundColour,
|
||||
bool isMouseOverButton,
|
||||
bool isButtonDown);
|
||||
|
||||
|
||||
/** Draws the contents of a standard ToggleButton. */
|
||||
virtual void drawToggleButton (Graphics& g,
|
||||
ToggleButton& button,
|
||||
bool isMouseOverButton,
|
||||
bool isButtonDown);
|
||||
|
||||
virtual void drawTickBox (Graphics& g,
|
||||
Component& component,
|
||||
float x, float y, float w, float h,
|
||||
bool ticked,
|
||||
bool isEnabled,
|
||||
bool isMouseOverButton,
|
||||
bool isButtonDown);
|
||||
|
||||
//==============================================================================
|
||||
virtual void drawProgressBar (Graphics& g, ProgressBar& progressBar,
|
||||
int width, int height,
|
||||
double progress, const String& textToShow);
|
||||
|
||||
//==============================================================================
|
||||
virtual void drawScrollbarButton (Graphics& g,
|
||||
ScrollBar& scrollbar,
|
||||
int width, int height,
|
||||
int buttonDirection,
|
||||
bool isScrollbarVertical,
|
||||
bool isMouseOverButton,
|
||||
bool isButtonDown);
|
||||
|
||||
virtual void drawScrollbar (Graphics& g,
|
||||
ScrollBar& scrollbar,
|
||||
int x, int y,
|
||||
int width, int height,
|
||||
bool isScrollbarVertical,
|
||||
int thumbStartPosition,
|
||||
int thumbSize,
|
||||
bool isMouseOver,
|
||||
bool isMouseDown);
|
||||
|
||||
virtual ImageEffectFilter* getScrollbarEffect();
|
||||
|
||||
//==============================================================================
|
||||
virtual void drawTextEditorOutline (Graphics& g,
|
||||
int width, int height,
|
||||
TextEditor& textEditor);
|
||||
|
||||
//==============================================================================
|
||||
/** Fills the background of a popup menu component. */
|
||||
virtual void drawPopupMenuBackground (Graphics& g, int width, int height);
|
||||
|
||||
virtual void drawMenuBarBackground (Graphics& g, int width, int height,
|
||||
bool isMouseOverBar,
|
||||
MenuBarComponent& menuBar);
|
||||
|
||||
//==============================================================================
|
||||
virtual void drawComboBox (Graphics& g, int width, int height,
|
||||
bool isButtonDown,
|
||||
int buttonX, int buttonY,
|
||||
int buttonW, int buttonH,
|
||||
ComboBox& box);
|
||||
|
||||
virtual const Font getComboBoxFont (ComboBox& box);
|
||||
|
||||
//==============================================================================
|
||||
virtual void drawLinearSlider (Graphics& g,
|
||||
int x, int y,
|
||||
int width, int height,
|
||||
float sliderPos,
|
||||
float minSliderPos,
|
||||
float maxSliderPos,
|
||||
const Slider::SliderStyle style,
|
||||
Slider& slider);
|
||||
|
||||
virtual int getSliderThumbRadius (Slider& slider);
|
||||
|
||||
virtual Button* createSliderButton (bool isIncrement);
|
||||
|
||||
virtual ImageEffectFilter* getSliderEffect();
|
||||
|
||||
//==============================================================================
|
||||
virtual void drawCornerResizer (Graphics& g,
|
||||
int w, int h,
|
||||
bool isMouseOver,
|
||||
bool isMouseDragging);
|
||||
|
||||
virtual Button* createDocumentWindowButton (int buttonType);
|
||||
|
||||
virtual void positionDocumentWindowButtons (DocumentWindow& window,
|
||||
int titleBarX, int titleBarY,
|
||||
int titleBarW, int titleBarH,
|
||||
Button* minimiseButton,
|
||||
Button* maximiseButton,
|
||||
Button* closeButton,
|
||||
bool positionTitleBarButtonsOnLeft);
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
DropShadowEffect scrollbarShadow;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OldSchoolLookAndFeel);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_OLDSCHOOLLOOKANDFEEL_JUCEHEADER__
|
||||
115
modules/juce_gui_extra/misc/juce_AppleRemote.h
Normal file
115
modules/juce_gui_extra/misc/juce_AppleRemote.h
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_APPLEREMOTE_JUCEHEADER__
|
||||
#define __JUCE_APPLEREMOTE_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC || DOXYGEN
|
||||
/**
|
||||
Receives events from an Apple IR remote control device (Only available in OSX!).
|
||||
|
||||
To use it, just create a subclass of this class, implementing the buttonPressed()
|
||||
callback, then call start() and stop() to start or stop receiving events.
|
||||
*/
|
||||
class JUCE_API AppleRemoteDevice
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
AppleRemoteDevice();
|
||||
virtual ~AppleRemoteDevice();
|
||||
|
||||
//==============================================================================
|
||||
/** The set of buttons that may be pressed.
|
||||
@see buttonPressed
|
||||
*/
|
||||
enum ButtonType
|
||||
{
|
||||
menuButton = 0, /**< The menu button (if it's held for a short time). */
|
||||
playButton, /**< The play button. */
|
||||
plusButton, /**< The plus or volume-up button. */
|
||||
minusButton, /**< The minus or volume-down button. */
|
||||
rightButton, /**< The right button (if it's held for a short time). */
|
||||
leftButton, /**< The left button (if it's held for a short time). */
|
||||
rightButton_Long, /**< The right button (if it's held for a long time). */
|
||||
leftButton_Long, /**< The menu button (if it's held for a long time). */
|
||||
menuButton_Long, /**< The menu button (if it's held for a long time). */
|
||||
playButtonSleepMode,
|
||||
switched
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Override this method to receive the callback about a button press.
|
||||
|
||||
The callback will happen on the application's message thread.
|
||||
|
||||
Some buttons trigger matching up and down events, in which the isDown parameter
|
||||
will be true and then false. Others only send a single event when the
|
||||
button is pressed.
|
||||
*/
|
||||
virtual void buttonPressed (ButtonType buttonId, bool isDown) = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Starts the device running and responding to events.
|
||||
|
||||
Returns true if it managed to open the device.
|
||||
|
||||
@param inExclusiveMode if true, the remote will be grabbed exclusively for this app,
|
||||
and will not be available to any other part of the system. If
|
||||
false, it will be shared with other apps.
|
||||
@see stop
|
||||
*/
|
||||
bool start (bool inExclusiveMode);
|
||||
|
||||
/** Stops the device running.
|
||||
@see start
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/** Returns true if the device has been started successfully.
|
||||
*/
|
||||
bool isActive() const;
|
||||
|
||||
/** Returns the ID number of the remote, if it has sent one.
|
||||
*/
|
||||
int getRemoteId() const { return remoteId; }
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void handleCallbackInternal();
|
||||
|
||||
private:
|
||||
void* device;
|
||||
void* queue;
|
||||
int remoteId;
|
||||
|
||||
bool open (bool openInExclusiveMode);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AppleRemoteDevice);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // __JUCE_APPLEREMOTE_JUCEHEADER__
|
||||
127
modules/juce_gui_extra/misc/juce_BubbleMessageComponent.cpp
Normal file
127
modules/juce_gui_extra/misc/juce_BubbleMessageComponent.cpp
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
BubbleMessageComponent::BubbleMessageComponent (int fadeOutLengthMs)
|
||||
: fadeOutLength (fadeOutLengthMs),
|
||||
deleteAfterUse (false)
|
||||
{
|
||||
}
|
||||
|
||||
BubbleMessageComponent::~BubbleMessageComponent()
|
||||
{
|
||||
Desktop::getInstance().getAnimator().fadeOut (this, fadeOutLength);
|
||||
}
|
||||
|
||||
void BubbleMessageComponent::showAt (int x, int y,
|
||||
const String& text,
|
||||
const int numMillisecondsBeforeRemoving,
|
||||
const bool removeWhenMouseClicked,
|
||||
const bool deleteSelfAfterUse)
|
||||
{
|
||||
textLayout.clear();
|
||||
textLayout.setText (text, Font (14.0f));
|
||||
textLayout.layout (256, Justification::centredLeft, true);
|
||||
|
||||
setPosition (x, y);
|
||||
|
||||
init (numMillisecondsBeforeRemoving, removeWhenMouseClicked, deleteSelfAfterUse);
|
||||
}
|
||||
|
||||
void BubbleMessageComponent::showAt (Component* const component,
|
||||
const String& text,
|
||||
const int numMillisecondsBeforeRemoving,
|
||||
const bool removeWhenMouseClicked,
|
||||
const bool deleteSelfAfterUse)
|
||||
{
|
||||
textLayout.clear();
|
||||
textLayout.setText (text, Font (14.0f));
|
||||
textLayout.layout (256, Justification::centredLeft, true);
|
||||
|
||||
setPosition (component);
|
||||
|
||||
init (numMillisecondsBeforeRemoving, removeWhenMouseClicked, deleteSelfAfterUse);
|
||||
}
|
||||
|
||||
void BubbleMessageComponent::init (const int numMillisecondsBeforeRemoving,
|
||||
const bool removeWhenMouseClicked,
|
||||
const bool deleteSelfAfterUse)
|
||||
{
|
||||
setVisible (true);
|
||||
|
||||
deleteAfterUse = deleteSelfAfterUse;
|
||||
|
||||
if (numMillisecondsBeforeRemoving > 0)
|
||||
expiryTime = Time::getMillisecondCounter() + numMillisecondsBeforeRemoving;
|
||||
else
|
||||
expiryTime = 0;
|
||||
|
||||
startTimer (77);
|
||||
|
||||
mouseClickCounter = Desktop::getInstance().getMouseButtonClickCounter();
|
||||
|
||||
if (! (removeWhenMouseClicked && isShowing()))
|
||||
mouseClickCounter += 0xfffff;
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
void BubbleMessageComponent::getContentSize (int& w, int& h)
|
||||
{
|
||||
w = textLayout.getWidth() + 16;
|
||||
h = textLayout.getHeight() + 16;
|
||||
}
|
||||
|
||||
void BubbleMessageComponent::paintContent (Graphics& g, int w, int h)
|
||||
{
|
||||
g.setColour (findColour (TooltipWindow::textColourId));
|
||||
|
||||
textLayout.drawWithin (g, 0, 0, w, h, Justification::centred);
|
||||
}
|
||||
|
||||
void BubbleMessageComponent::timerCallback()
|
||||
{
|
||||
if (Desktop::getInstance().getMouseButtonClickCounter() > mouseClickCounter)
|
||||
{
|
||||
stopTimer();
|
||||
setVisible (false);
|
||||
|
||||
if (deleteAfterUse)
|
||||
delete this;
|
||||
}
|
||||
else if (expiryTime != 0 && Time::getMillisecondCounter() > expiryTime)
|
||||
{
|
||||
stopTimer();
|
||||
|
||||
if (deleteAfterUse)
|
||||
delete this;
|
||||
else
|
||||
Desktop::getInstance().getAnimator().fadeOut (this, fadeOutLength);
|
||||
}
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
132
modules/juce_gui_extra/misc/juce_BubbleMessageComponent.h
Normal file
132
modules/juce_gui_extra/misc/juce_BubbleMessageComponent.h
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_BUBBLEMESSAGECOMPONENT_JUCEHEADER__
|
||||
#define __JUCE_BUBBLEMESSAGECOMPONENT_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A speech-bubble component that displays a short message.
|
||||
|
||||
This can be used to show a message with the tail of the speech bubble
|
||||
pointing to a particular component or location on the screen.
|
||||
|
||||
@see BubbleComponent
|
||||
*/
|
||||
class JUCE_API BubbleMessageComponent : public BubbleComponent,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a bubble component.
|
||||
|
||||
After creating one a BubbleComponent, do the following:
|
||||
- add it to an appropriate parent component, or put it on the
|
||||
desktop with Component::addToDesktop (0).
|
||||
- use the showAt() method to show a message.
|
||||
- it will make itself invisible after it times-out (and can optionally
|
||||
also delete itself), or you can reuse it somewhere else by calling
|
||||
showAt() again.
|
||||
*/
|
||||
BubbleMessageComponent (int fadeOutLengthMs = 150);
|
||||
|
||||
/** Destructor. */
|
||||
~BubbleMessageComponent();
|
||||
|
||||
//==============================================================================
|
||||
/** Shows a message bubble at a particular position.
|
||||
|
||||
This shows the bubble with its stem pointing to the given location
|
||||
(co-ordinates being relative to its parent component).
|
||||
|
||||
For details about exactly how it decides where to position itself, see
|
||||
BubbleComponent::updatePosition().
|
||||
|
||||
@param x the x co-ordinate of end of the bubble's tail
|
||||
@param y the y co-ordinate of end of the bubble's tail
|
||||
@param message the text to display
|
||||
@param numMillisecondsBeforeRemoving how long to leave it on the screen before removing itself
|
||||
from its parent compnent. If this is 0 or less, it
|
||||
will stay there until manually removed.
|
||||
@param removeWhenMouseClicked if this is true, the bubble will disappear as soon as a
|
||||
mouse button is pressed (anywhere on the screen)
|
||||
@param deleteSelfAfterUse if true, then the component will delete itself after
|
||||
it becomes invisible
|
||||
*/
|
||||
void showAt (int x, int y,
|
||||
const String& message,
|
||||
int numMillisecondsBeforeRemoving,
|
||||
bool removeWhenMouseClicked = true,
|
||||
bool deleteSelfAfterUse = false);
|
||||
|
||||
/** Shows a message bubble next to a particular component.
|
||||
|
||||
This shows the bubble with its stem pointing at the given component.
|
||||
|
||||
For details about exactly how it decides where to position itself, see
|
||||
BubbleComponent::updatePosition().
|
||||
|
||||
@param component the component that you want to point at
|
||||
@param message the text to display
|
||||
@param numMillisecondsBeforeRemoving how long to leave it on the screen before removing itself
|
||||
from its parent compnent. If this is 0 or less, it
|
||||
will stay there until manually removed.
|
||||
@param removeWhenMouseClicked if this is true, the bubble will disappear as soon as a
|
||||
mouse button is pressed (anywhere on the screen)
|
||||
@param deleteSelfAfterUse if true, then the component will delete itself after
|
||||
it becomes invisible
|
||||
*/
|
||||
void showAt (Component* component,
|
||||
const String& message,
|
||||
int numMillisecondsBeforeRemoving,
|
||||
bool removeWhenMouseClicked = true,
|
||||
bool deleteSelfAfterUse = false);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void getContentSize (int& w, int& h);
|
||||
/** @internal */
|
||||
void paintContent (Graphics& g, int w, int h);
|
||||
/** @internal */
|
||||
void timerCallback();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
int fadeOutLength, mouseClickCounter;
|
||||
TextLayout textLayout;
|
||||
int64 expiryTime;
|
||||
bool deleteAfterUse;
|
||||
|
||||
void init (int numMillisecondsBeforeRemoving,
|
||||
bool removeWhenMouseClicked,
|
||||
bool deleteSelfAfterUse);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BubbleMessageComponent);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_BUBBLEMESSAGECOMPONENT_JUCEHEADER__
|
||||
585
modules/juce_gui_extra/misc/juce_ColourSelector.cpp
Normal file
585
modules/juce_gui_extra/misc/juce_ColourSelector.cpp
Normal file
|
|
@ -0,0 +1,585 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
class ColourComponentSlider : public Slider
|
||||
{
|
||||
public:
|
||||
ColourComponentSlider (const String& name)
|
||||
: Slider (name)
|
||||
{
|
||||
setRange (0.0, 255.0, 1.0);
|
||||
}
|
||||
|
||||
const String getTextFromValue (double value)
|
||||
{
|
||||
return String::toHexString ((int) value).toUpperCase().paddedLeft ('0', 2);
|
||||
}
|
||||
|
||||
double getValueFromText (const String& text)
|
||||
{
|
||||
return (double) text.getHexValue32();
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE (ColourComponentSlider);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ColourSpaceMarker : public Component
|
||||
{
|
||||
public:
|
||||
ColourSpaceMarker()
|
||||
{
|
||||
setInterceptsMouseClicks (false, false);
|
||||
}
|
||||
|
||||
void paint (Graphics& g)
|
||||
{
|
||||
g.setColour (Colour::greyLevel (0.1f));
|
||||
g.drawEllipse (1.0f, 1.0f, getWidth() - 2.0f, getHeight() - 2.0f, 1.0f);
|
||||
g.setColour (Colour::greyLevel (0.9f));
|
||||
g.drawEllipse (2.0f, 2.0f, getWidth() - 4.0f, getHeight() - 4.0f, 1.0f);
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE (ColourSpaceMarker);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ColourSelector::ColourSpaceView : public Component
|
||||
{
|
||||
public:
|
||||
ColourSpaceView (ColourSelector& owner_,
|
||||
float& h_, float& s_, float& v_,
|
||||
const int edgeSize)
|
||||
: owner (owner_),
|
||||
h (h_), s (s_), v (v_),
|
||||
lastHue (0.0f),
|
||||
edge (edgeSize)
|
||||
{
|
||||
addAndMakeVisible (&marker);
|
||||
setMouseCursor (MouseCursor::CrosshairCursor);
|
||||
}
|
||||
|
||||
void paint (Graphics& g)
|
||||
{
|
||||
if (colours.isNull())
|
||||
{
|
||||
const int width = getWidth() / 2;
|
||||
const int height = getHeight() / 2;
|
||||
colours = Image (Image::RGB, width, height, false);
|
||||
|
||||
Image::BitmapData pixels (colours, Image::BitmapData::writeOnly);
|
||||
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
const float val = 1.0f - y / (float) height;
|
||||
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
const float sat = x / (float) width;
|
||||
|
||||
pixels.setPixelColour (x, y, Colour (h, sat, val, 1.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g.setOpacity (1.0f);
|
||||
g.drawImage (colours, edge, edge, getWidth() - edge * 2, getHeight() - edge * 2,
|
||||
0, 0, colours.getWidth(), colours.getHeight());
|
||||
}
|
||||
|
||||
void mouseDown (const MouseEvent& e)
|
||||
{
|
||||
mouseDrag (e);
|
||||
}
|
||||
|
||||
void mouseDrag (const MouseEvent& e)
|
||||
{
|
||||
const float sat = (e.x - edge) / (float) (getWidth() - edge * 2);
|
||||
const float val = 1.0f - (e.y - edge) / (float) (getHeight() - edge * 2);
|
||||
|
||||
owner.setSV (sat, val);
|
||||
}
|
||||
|
||||
void updateIfNeeded()
|
||||
{
|
||||
if (lastHue != h)
|
||||
{
|
||||
lastHue = h;
|
||||
colours = Image::null;
|
||||
repaint();
|
||||
}
|
||||
|
||||
updateMarker();
|
||||
}
|
||||
|
||||
void resized()
|
||||
{
|
||||
colours = Image::null;
|
||||
updateMarker();
|
||||
}
|
||||
|
||||
private:
|
||||
ColourSelector& owner;
|
||||
float& h;
|
||||
float& s;
|
||||
float& v;
|
||||
float lastHue;
|
||||
ColourSpaceMarker marker;
|
||||
const int edge;
|
||||
Image colours;
|
||||
|
||||
void updateMarker()
|
||||
{
|
||||
marker.setBounds (roundToInt ((getWidth() - edge * 2) * s),
|
||||
roundToInt ((getHeight() - edge * 2) * (1.0f - v)),
|
||||
edge * 2, edge * 2);
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ColourSpaceView);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class HueSelectorMarker : public Component
|
||||
{
|
||||
public:
|
||||
HueSelectorMarker()
|
||||
{
|
||||
setInterceptsMouseClicks (false, false);
|
||||
}
|
||||
|
||||
void paint (Graphics& g)
|
||||
{
|
||||
Path p;
|
||||
p.addTriangle (1.0f, 1.0f,
|
||||
getWidth() * 0.3f, getHeight() * 0.5f,
|
||||
1.0f, getHeight() - 1.0f);
|
||||
|
||||
p.addTriangle (getWidth() - 1.0f, 1.0f,
|
||||
getWidth() * 0.7f, getHeight() * 0.5f,
|
||||
getWidth() - 1.0f, getHeight() - 1.0f);
|
||||
|
||||
g.setColour (Colours::white.withAlpha (0.75f));
|
||||
g.fillPath (p);
|
||||
|
||||
g.setColour (Colours::black.withAlpha (0.75f));
|
||||
g.strokePath (p, PathStrokeType (1.2f));
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE (HueSelectorMarker);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ColourSelector::HueSelectorComp : public Component
|
||||
{
|
||||
public:
|
||||
HueSelectorComp (ColourSelector& owner_,
|
||||
float& h_, float& s_, float& v_,
|
||||
const int edgeSize)
|
||||
: owner (owner_),
|
||||
h (h_), s (s_), v (v_),
|
||||
lastHue (0.0f),
|
||||
edge (edgeSize)
|
||||
{
|
||||
addAndMakeVisible (&marker);
|
||||
}
|
||||
|
||||
void paint (Graphics& g)
|
||||
{
|
||||
const float yScale = 1.0f / (getHeight() - edge * 2);
|
||||
|
||||
const Rectangle<int> clip (g.getClipBounds());
|
||||
|
||||
for (int y = jmin (clip.getBottom(), getHeight() - edge); --y >= jmax (edge, clip.getY());)
|
||||
{
|
||||
g.setColour (Colour ((y - edge) * yScale, 1.0f, 1.0f, 1.0f));
|
||||
g.fillRect (edge, y, getWidth() - edge * 2, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void resized()
|
||||
{
|
||||
marker.setBounds (0, roundToInt ((getHeight() - edge * 2) * h),
|
||||
getWidth(), edge * 2);
|
||||
}
|
||||
|
||||
void mouseDown (const MouseEvent& e)
|
||||
{
|
||||
mouseDrag (e);
|
||||
}
|
||||
|
||||
void mouseDrag (const MouseEvent& e)
|
||||
{
|
||||
owner.setHue ((e.y - edge) / (float) (getHeight() - edge * 2));
|
||||
}
|
||||
|
||||
void updateIfNeeded()
|
||||
{
|
||||
resized();
|
||||
}
|
||||
|
||||
private:
|
||||
ColourSelector& owner;
|
||||
float& h;
|
||||
float& s;
|
||||
float& v;
|
||||
float lastHue;
|
||||
HueSelectorMarker marker;
|
||||
const int edge;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (HueSelectorComp);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ColourSelector::SwatchComponent : public Component
|
||||
{
|
||||
public:
|
||||
SwatchComponent (ColourSelector& owner_, int index_)
|
||||
: owner (owner_), index (index_)
|
||||
{
|
||||
}
|
||||
|
||||
void paint (Graphics& g)
|
||||
{
|
||||
const Colour colour (owner.getSwatchColour (index));
|
||||
|
||||
g.fillCheckerBoard (getLocalBounds(), 6, 6,
|
||||
Colour (0xffdddddd).overlaidWith (colour),
|
||||
Colour (0xffffffff).overlaidWith (colour));
|
||||
}
|
||||
|
||||
void mouseDown (const MouseEvent&)
|
||||
{
|
||||
PopupMenu m;
|
||||
m.addItem (1, TRANS("Use this swatch as the current colour"));
|
||||
m.addSeparator();
|
||||
m.addItem (2, TRANS("Set this swatch to the current colour"));
|
||||
|
||||
m.showMenuAsync (PopupMenu::Options().withTargetComponent (this),
|
||||
ModalCallbackFunction::forComponent (menuStaticCallback, this));
|
||||
}
|
||||
|
||||
private:
|
||||
ColourSelector& owner;
|
||||
const int index;
|
||||
|
||||
static void menuStaticCallback (int result, SwatchComponent* comp)
|
||||
{
|
||||
if (comp != nullptr)
|
||||
{
|
||||
if (result == 1)
|
||||
comp->setColourFromSwatch();
|
||||
else if (result == 2)
|
||||
comp->setSwatchFromColour();
|
||||
}
|
||||
}
|
||||
|
||||
void setColourFromSwatch()
|
||||
{
|
||||
owner.setCurrentColour (owner.getSwatchColour (index));
|
||||
}
|
||||
|
||||
void setSwatchFromColour()
|
||||
{
|
||||
if (owner.getSwatchColour (index) != owner.getCurrentColour())
|
||||
{
|
||||
owner.setSwatchColour (index, owner.getCurrentColour());
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (SwatchComponent);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ColourSelector::ColourSelector (const int flags_,
|
||||
const int edgeGap_,
|
||||
const int gapAroundColourSpaceComponent)
|
||||
: colour (Colours::white),
|
||||
flags (flags_),
|
||||
edgeGap (edgeGap_)
|
||||
{
|
||||
// not much point having a selector with no components in it!
|
||||
jassert ((flags_ & (showColourAtTop | showSliders | showColourspace)) != 0);
|
||||
|
||||
updateHSV();
|
||||
|
||||
if ((flags & showSliders) != 0)
|
||||
{
|
||||
addAndMakeVisible (sliders[0] = new ColourComponentSlider (TRANS ("red")));
|
||||
addAndMakeVisible (sliders[1] = new ColourComponentSlider (TRANS ("green")));
|
||||
addAndMakeVisible (sliders[2] = new ColourComponentSlider (TRANS ("blue")));
|
||||
addChildComponent (sliders[3] = new ColourComponentSlider (TRANS ("alpha")));
|
||||
|
||||
sliders[3]->setVisible ((flags & showAlphaChannel) != 0);
|
||||
|
||||
for (int i = 4; --i >= 0;)
|
||||
sliders[i]->addListener (this);
|
||||
}
|
||||
|
||||
if ((flags & showColourspace) != 0)
|
||||
{
|
||||
addAndMakeVisible (colourSpace = new ColourSpaceView (*this, h, s, v, gapAroundColourSpaceComponent));
|
||||
addAndMakeVisible (hueSelector = new HueSelectorComp (*this, h, s, v, gapAroundColourSpaceComponent));
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
ColourSelector::~ColourSelector()
|
||||
{
|
||||
dispatchPendingMessages();
|
||||
swatchComponents.clear();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const Colour ColourSelector::getCurrentColour() const
|
||||
{
|
||||
return ((flags & showAlphaChannel) != 0) ? colour
|
||||
: colour.withAlpha ((uint8) 0xff);
|
||||
}
|
||||
|
||||
void ColourSelector::setCurrentColour (const Colour& c)
|
||||
{
|
||||
if (c != colour)
|
||||
{
|
||||
colour = ((flags & showAlphaChannel) != 0) ? c : c.withAlpha ((uint8) 0xff);
|
||||
|
||||
updateHSV();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void ColourSelector::setHue (float newH)
|
||||
{
|
||||
newH = jlimit (0.0f, 1.0f, newH);
|
||||
|
||||
if (h != newH)
|
||||
{
|
||||
h = newH;
|
||||
colour = Colour (h, s, v, colour.getFloatAlpha());
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void ColourSelector::setSV (float newS, float newV)
|
||||
{
|
||||
newS = jlimit (0.0f, 1.0f, newS);
|
||||
newV = jlimit (0.0f, 1.0f, newV);
|
||||
|
||||
if (s != newS || v != newV)
|
||||
{
|
||||
s = newS;
|
||||
v = newV;
|
||||
colour = Colour (h, s, v, colour.getFloatAlpha());
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ColourSelector::updateHSV()
|
||||
{
|
||||
colour.getHSB (h, s, v);
|
||||
}
|
||||
|
||||
void ColourSelector::update()
|
||||
{
|
||||
if (sliders[0] != nullptr)
|
||||
{
|
||||
sliders[0]->setValue ((int) colour.getRed());
|
||||
sliders[1]->setValue ((int) colour.getGreen());
|
||||
sliders[2]->setValue ((int) colour.getBlue());
|
||||
sliders[3]->setValue ((int) colour.getAlpha());
|
||||
}
|
||||
|
||||
if (colourSpace != nullptr)
|
||||
{
|
||||
colourSpace->updateIfNeeded();
|
||||
hueSelector->updateIfNeeded();
|
||||
}
|
||||
|
||||
if ((flags & showColourAtTop) != 0)
|
||||
repaint (previewArea);
|
||||
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ColourSelector::paint (Graphics& g)
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
|
||||
if ((flags & showColourAtTop) != 0)
|
||||
{
|
||||
const Colour currentColour (getCurrentColour());
|
||||
|
||||
g.fillCheckerBoard (previewArea, 10, 10,
|
||||
Colour (0xffdddddd).overlaidWith (currentColour),
|
||||
Colour (0xffffffff).overlaidWith (currentColour));
|
||||
|
||||
g.setColour (Colours::white.overlaidWith (currentColour).contrasting());
|
||||
g.setFont (14.0f, true);
|
||||
g.drawText (currentColour.toDisplayString ((flags & showAlphaChannel) != 0),
|
||||
previewArea.getX(), previewArea.getY(), previewArea.getWidth(), previewArea.getHeight(),
|
||||
Justification::centred, false);
|
||||
}
|
||||
|
||||
if ((flags & showSliders) != 0)
|
||||
{
|
||||
g.setColour (findColour (labelTextColourId));
|
||||
g.setFont (11.0f);
|
||||
|
||||
for (int i = 4; --i >= 0;)
|
||||
{
|
||||
if (sliders[i]->isVisible())
|
||||
g.drawText (sliders[i]->getName() + ":",
|
||||
0, sliders[i]->getY(),
|
||||
sliders[i]->getX() - 8, sliders[i]->getHeight(),
|
||||
Justification::centredRight, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ColourSelector::resized()
|
||||
{
|
||||
const int swatchesPerRow = 8;
|
||||
const int swatchHeight = 22;
|
||||
|
||||
const int numSliders = ((flags & showAlphaChannel) != 0) ? 4 : 3;
|
||||
const int numSwatches = getNumSwatches();
|
||||
|
||||
const int swatchSpace = numSwatches > 0 ? edgeGap + swatchHeight * ((numSwatches + 7) / swatchesPerRow) : 0;
|
||||
const int sliderSpace = ((flags & showSliders) != 0) ? jmin (22 * numSliders + edgeGap, proportionOfHeight (0.3f)) : 0;
|
||||
const int topSpace = ((flags & showColourAtTop) != 0) ? jmin (30 + edgeGap * 2, proportionOfHeight (0.2f)) : edgeGap;
|
||||
|
||||
previewArea.setBounds (edgeGap, edgeGap, getWidth() - edgeGap * 2, topSpace - edgeGap * 2);
|
||||
|
||||
int y = topSpace;
|
||||
|
||||
if ((flags & showColourspace) != 0)
|
||||
{
|
||||
const int hueWidth = jmin (50, proportionOfWidth (0.15f));
|
||||
|
||||
colourSpace->setBounds (edgeGap, y,
|
||||
getWidth() - hueWidth - edgeGap - 4,
|
||||
getHeight() - topSpace - sliderSpace - swatchSpace - edgeGap);
|
||||
|
||||
hueSelector->setBounds (colourSpace->getRight() + 4, y,
|
||||
getWidth() - edgeGap - (colourSpace->getRight() + 4),
|
||||
colourSpace->getHeight());
|
||||
|
||||
y = getHeight() - sliderSpace - swatchSpace - edgeGap;
|
||||
}
|
||||
|
||||
if ((flags & showSliders) != 0)
|
||||
{
|
||||
const int sliderHeight = jmax (4, sliderSpace / numSliders);
|
||||
|
||||
for (int i = 0; i < numSliders; ++i)
|
||||
{
|
||||
sliders[i]->setBounds (proportionOfWidth (0.2f), y,
|
||||
proportionOfWidth (0.72f), sliderHeight - 2);
|
||||
|
||||
y += sliderHeight;
|
||||
}
|
||||
}
|
||||
|
||||
if (numSwatches > 0)
|
||||
{
|
||||
const int startX = 8;
|
||||
const int xGap = 4;
|
||||
const int yGap = 4;
|
||||
const int swatchWidth = (getWidth() - startX * 2) / swatchesPerRow;
|
||||
y += edgeGap;
|
||||
|
||||
if (swatchComponents.size() != numSwatches)
|
||||
{
|
||||
swatchComponents.clear();
|
||||
|
||||
for (int i = 0; i < numSwatches; ++i)
|
||||
{
|
||||
SwatchComponent* const sc = new SwatchComponent (*this, i);
|
||||
swatchComponents.add (sc);
|
||||
addAndMakeVisible (sc);
|
||||
}
|
||||
}
|
||||
|
||||
int x = startX;
|
||||
|
||||
for (int i = 0; i < swatchComponents.size(); ++i)
|
||||
{
|
||||
SwatchComponent* const sc = swatchComponents.getUnchecked(i);
|
||||
|
||||
sc->setBounds (x + xGap / 2,
|
||||
y + yGap / 2,
|
||||
swatchWidth - xGap,
|
||||
swatchHeight - yGap);
|
||||
|
||||
if (((i + 1) % swatchesPerRow) == 0)
|
||||
{
|
||||
x = startX;
|
||||
y += swatchHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
x += swatchWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ColourSelector::sliderValueChanged (Slider*)
|
||||
{
|
||||
if (sliders[0] != nullptr)
|
||||
setCurrentColour (Colour ((uint8) sliders[0]->getValue(),
|
||||
(uint8) sliders[1]->getValue(),
|
||||
(uint8) sliders[2]->getValue(),
|
||||
(uint8) sliders[3]->getValue()));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int ColourSelector::getNumSwatches() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const Colour ColourSelector::getSwatchColour (const int) const
|
||||
{
|
||||
jassertfalse; // if you've overridden getNumSwatches(), you also need to implement this method
|
||||
return Colours::black;
|
||||
}
|
||||
|
||||
void ColourSelector::setSwatchColour (const int, const Colour&) const
|
||||
{
|
||||
jassertfalse; // if you've overridden getNumSwatches(), you also need to implement this method
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
170
modules/juce_gui_extra/misc/juce_ColourSelector.h
Normal file
170
modules/juce_gui_extra/misc/juce_ColourSelector.h
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_COLOURSELECTOR_JUCEHEADER__
|
||||
#define __JUCE_COLOURSELECTOR_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component that lets the user choose a colour.
|
||||
|
||||
This shows RGB sliders and a colourspace that the user can pick colours from.
|
||||
|
||||
This class is also a ChangeBroadcaster, so listeners can register to be told
|
||||
when the colour changes.
|
||||
*/
|
||||
class JUCE_API ColourSelector : public Component,
|
||||
public ChangeBroadcaster,
|
||||
protected SliderListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Options for the type of selector to show. These are passed into the constructor. */
|
||||
enum ColourSelectorOptions
|
||||
{
|
||||
showAlphaChannel = 1 << 0, /**< if set, the colour's alpha channel can be changed as well as its RGB. */
|
||||
|
||||
showColourAtTop = 1 << 1, /**< if set, a swatch of the colour is shown at the top of the component. */
|
||||
showSliders = 1 << 2, /**< if set, RGB sliders are shown at the bottom of the component. */
|
||||
showColourspace = 1 << 3 /**< if set, a big HSV selector is shown. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a ColourSelector object.
|
||||
|
||||
The flags are a combination of values from the ColourSelectorOptions enum, specifying
|
||||
which of the selector's features should be visible.
|
||||
|
||||
The edgeGap value specifies the amount of space to leave around the edge.
|
||||
|
||||
gapAroundColourSpaceComponent indicates how much of a gap to put around the
|
||||
colourspace and hue selector components.
|
||||
*/
|
||||
ColourSelector (int sectionsToShow = (showAlphaChannel | showColourAtTop | showSliders | showColourspace),
|
||||
int edgeGap = 4,
|
||||
int gapAroundColourSpaceComponent = 7);
|
||||
|
||||
/** Destructor. */
|
||||
~ColourSelector();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the colour that the user has currently selected.
|
||||
|
||||
The ColourSelector class is also a ChangeBroadcaster, so listeners can
|
||||
register to be told when the colour changes.
|
||||
|
||||
@see setCurrentColour
|
||||
*/
|
||||
const Colour getCurrentColour() const;
|
||||
|
||||
/** Changes the colour that is currently being shown.
|
||||
*/
|
||||
void setCurrentColour (const Colour& newColour);
|
||||
|
||||
//==============================================================================
|
||||
/** Tells the selector how many preset colour swatches you want to have on the component.
|
||||
|
||||
To enable swatches, you'll need to override getNumSwatches(), getSwatchColour(), and
|
||||
setSwatchColour(), to return the number of colours you want, and to set and retrieve
|
||||
their values.
|
||||
*/
|
||||
virtual int getNumSwatches() const;
|
||||
|
||||
/** Called by the selector to find out the colour of one of the swatches.
|
||||
|
||||
Your subclass should return the colour of the swatch with the given index.
|
||||
|
||||
To enable swatches, you'll need to override getNumSwatches(), getSwatchColour(), and
|
||||
setSwatchColour(), to return the number of colours you want, and to set and retrieve
|
||||
their values.
|
||||
*/
|
||||
virtual const Colour getSwatchColour (int index) const;
|
||||
|
||||
/** Called by the selector when the user puts a new colour into one of the swatches.
|
||||
|
||||
Your subclass should change the colour of the swatch with the given index.
|
||||
|
||||
To enable swatches, you'll need to override getNumSwatches(), getSwatchColour(), and
|
||||
setSwatchColour(), to return the number of colours you want, and to set and retrieve
|
||||
their values.
|
||||
*/
|
||||
virtual void setSwatchColour (int index, const Colour& newColour) const;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the keyboard.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
backgroundColourId = 0x1007000, /**< the colour used to fill the component's background. */
|
||||
labelTextColourId = 0x1007001 /**< the colour used for the labels next to the sliders. */
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class ColourSpaceView;
|
||||
class HueSelectorComp;
|
||||
class SwatchComponent;
|
||||
friend class ColourSpaceView;
|
||||
friend class ScopedPointer<ColourSpaceView>;
|
||||
friend class HueSelectorComp;
|
||||
friend class ScopedPointer<HueSelectorComp>;
|
||||
|
||||
Colour colour;
|
||||
float h, s, v;
|
||||
ScopedPointer<Slider> sliders[4];
|
||||
ScopedPointer<ColourSpaceView> colourSpace;
|
||||
ScopedPointer<HueSelectorComp> hueSelector;
|
||||
OwnedArray <SwatchComponent> swatchComponents;
|
||||
const int flags;
|
||||
int edgeGap;
|
||||
Rectangle<int> previewArea;
|
||||
|
||||
void setHue (float newH);
|
||||
void setSV (float newS, float newV);
|
||||
void updateHSV();
|
||||
void update();
|
||||
void sliderValueChanged (Slider*);
|
||||
void paint (Graphics& g);
|
||||
void resized();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ColourSelector);
|
||||
|
||||
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
|
||||
// This constructor is here temporarily to prevent old code compiling, because the parameters
|
||||
// have changed - if you get an error here, update your code to use the new constructor instead..
|
||||
ColourSelector (bool);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_COLOURSELECTOR_JUCEHEADER__
|
||||
492
modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp
Normal file
492
modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp
Normal file
|
|
@ -0,0 +1,492 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
class KeyMappingEditorComponent::ChangeKeyButton : public Button
|
||||
{
|
||||
public:
|
||||
ChangeKeyButton (KeyMappingEditorComponent& owner_,
|
||||
const CommandID commandID_,
|
||||
const String& keyName,
|
||||
const int keyNum_)
|
||||
: Button (keyName),
|
||||
owner (owner_),
|
||||
commandID (commandID_),
|
||||
keyNum (keyNum_)
|
||||
{
|
||||
setWantsKeyboardFocus (false);
|
||||
setTriggeredOnMouseDown (keyNum >= 0);
|
||||
|
||||
setTooltip (keyNum_ < 0 ? TRANS("adds a new key-mapping")
|
||||
: TRANS("click to change this key-mapping"));
|
||||
}
|
||||
|
||||
void paintButton (Graphics& g, bool /*isOver*/, bool /*isDown*/)
|
||||
{
|
||||
getLookAndFeel().drawKeymapChangeButton (g, getWidth(), getHeight(), *this,
|
||||
keyNum >= 0 ? getName() : String::empty);
|
||||
}
|
||||
|
||||
static void menuCallback (int result, ChangeKeyButton* button)
|
||||
{
|
||||
if (button != nullptr)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case 1: button->assignNewKey(); break;
|
||||
case 2: button->owner.getMappings().removeKeyPress (button->commandID, button->keyNum); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clicked()
|
||||
{
|
||||
if (keyNum >= 0)
|
||||
{
|
||||
// existing key clicked..
|
||||
PopupMenu m;
|
||||
m.addItem (1, TRANS("change this key-mapping"));
|
||||
m.addSeparator();
|
||||
m.addItem (2, TRANS("remove this key-mapping"));
|
||||
|
||||
m.showMenuAsync (PopupMenu::Options(),
|
||||
ModalCallbackFunction::forComponent (menuCallback, this));
|
||||
}
|
||||
else
|
||||
{
|
||||
assignNewKey(); // + button pressed..
|
||||
}
|
||||
}
|
||||
|
||||
void fitToContent (const int h) noexcept
|
||||
{
|
||||
if (keyNum < 0)
|
||||
{
|
||||
setSize (h, h);
|
||||
}
|
||||
else
|
||||
{
|
||||
Font f (h * 0.6f);
|
||||
setSize (jlimit (h * 4, h * 8, 6 + f.getStringWidth (getName())), h);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class KeyEntryWindow : public AlertWindow
|
||||
{
|
||||
public:
|
||||
KeyEntryWindow (KeyMappingEditorComponent& owner_)
|
||||
: AlertWindow (TRANS("New key-mapping"),
|
||||
TRANS("Please press a key combination now..."),
|
||||
AlertWindow::NoIcon),
|
||||
owner (owner_)
|
||||
{
|
||||
addButton (TRANS("Ok"), 1);
|
||||
addButton (TRANS("Cancel"), 0);
|
||||
|
||||
// (avoid return + escape keys getting processed by the buttons..)
|
||||
for (int i = getNumChildComponents(); --i >= 0;)
|
||||
getChildComponent (i)->setWantsKeyboardFocus (false);
|
||||
|
||||
setWantsKeyboardFocus (true);
|
||||
grabKeyboardFocus();
|
||||
}
|
||||
|
||||
bool keyPressed (const KeyPress& key)
|
||||
{
|
||||
lastPress = key;
|
||||
String message (TRANS("Key: ") + owner.getDescriptionForKeyPress (key));
|
||||
|
||||
const CommandID previousCommand = owner.getMappings().findCommandForKeyPress (key);
|
||||
|
||||
if (previousCommand != 0)
|
||||
message << "\n\n" << TRANS("(Currently assigned to \"")
|
||||
<< owner.getMappings().getCommandManager()->getNameOfCommand (previousCommand) << "\")";
|
||||
|
||||
setMessage (message);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool keyStateChanged (bool)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
KeyPress lastPress;
|
||||
|
||||
private:
|
||||
KeyMappingEditorComponent& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (KeyEntryWindow);
|
||||
};
|
||||
|
||||
static void assignNewKeyCallback (int result, ChangeKeyButton* button, KeyPress newKey)
|
||||
{
|
||||
if (result != 0 && button != nullptr)
|
||||
button->setNewKey (newKey, true);
|
||||
}
|
||||
|
||||
void setNewKey (const KeyPress& newKey, bool dontAskUser)
|
||||
{
|
||||
if (newKey.isValid())
|
||||
{
|
||||
const CommandID previousCommand = owner.getMappings().findCommandForKeyPress (newKey);
|
||||
|
||||
if (previousCommand == 0 || dontAskUser)
|
||||
{
|
||||
owner.getMappings().removeKeyPress (newKey);
|
||||
|
||||
if (keyNum >= 0)
|
||||
owner.getMappings().removeKeyPress (commandID, keyNum);
|
||||
|
||||
owner.getMappings().addKeyPress (commandID, newKey, keyNum);
|
||||
}
|
||||
else
|
||||
{
|
||||
AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
|
||||
TRANS("Change key-mapping"),
|
||||
TRANS("This key is already assigned to the command \"")
|
||||
+ owner.getMappings().getCommandManager()->getNameOfCommand (previousCommand)
|
||||
+ TRANS("\"\n\nDo you want to re-assign it to this new command instead?"),
|
||||
TRANS("Re-assign"),
|
||||
TRANS("Cancel"),
|
||||
this,
|
||||
ModalCallbackFunction::forComponent (assignNewKeyCallback,
|
||||
this, KeyPress (newKey)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void keyChosen (int result, ChangeKeyButton* button)
|
||||
{
|
||||
if (result != 0 && button != nullptr && button->currentKeyEntryWindow != nullptr)
|
||||
{
|
||||
button->currentKeyEntryWindow->setVisible (false);
|
||||
button->setNewKey (button->currentKeyEntryWindow->lastPress, false);
|
||||
}
|
||||
|
||||
button->currentKeyEntryWindow = nullptr;
|
||||
}
|
||||
|
||||
void assignNewKey()
|
||||
{
|
||||
currentKeyEntryWindow = new KeyEntryWindow (owner);
|
||||
currentKeyEntryWindow->enterModalState (true, ModalCallbackFunction::forComponent (keyChosen, this));
|
||||
}
|
||||
|
||||
private:
|
||||
KeyMappingEditorComponent& owner;
|
||||
const CommandID commandID;
|
||||
const int keyNum;
|
||||
ScopedPointer<KeyEntryWindow> currentKeyEntryWindow;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChangeKeyButton);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class KeyMappingEditorComponent::ItemComponent : public Component
|
||||
{
|
||||
public:
|
||||
ItemComponent (KeyMappingEditorComponent& owner_, const CommandID commandID_)
|
||||
: owner (owner_), commandID (commandID_)
|
||||
{
|
||||
setInterceptsMouseClicks (false, true);
|
||||
|
||||
const bool isReadOnly = owner.isCommandReadOnly (commandID);
|
||||
|
||||
const Array <KeyPress> keyPresses (owner.getMappings().getKeyPressesAssignedToCommand (commandID));
|
||||
|
||||
for (int i = 0; i < jmin ((int) maxNumAssignments, keyPresses.size()); ++i)
|
||||
addKeyPressButton (owner.getDescriptionForKeyPress (keyPresses.getReference (i)), i, isReadOnly);
|
||||
|
||||
addKeyPressButton (String::empty, -1, isReadOnly);
|
||||
}
|
||||
|
||||
void addKeyPressButton (const String& desc, const int index, const bool isReadOnly)
|
||||
{
|
||||
ChangeKeyButton* const b = new ChangeKeyButton (owner, commandID, desc, index);
|
||||
keyChangeButtons.add (b);
|
||||
|
||||
b->setEnabled (! isReadOnly);
|
||||
b->setVisible (keyChangeButtons.size() <= (int) maxNumAssignments);
|
||||
addChildComponent (b);
|
||||
}
|
||||
|
||||
void paint (Graphics& g)
|
||||
{
|
||||
g.setFont (getHeight() * 0.7f);
|
||||
g.setColour (findColour (KeyMappingEditorComponent::textColourId));
|
||||
|
||||
g.drawFittedText (owner.getMappings().getCommandManager()->getNameOfCommand (commandID),
|
||||
4, 0, jmax (40, getChildComponent (0)->getX() - 5), getHeight(),
|
||||
Justification::centredLeft, true);
|
||||
}
|
||||
|
||||
void resized()
|
||||
{
|
||||
int x = getWidth() - 4;
|
||||
|
||||
for (int i = keyChangeButtons.size(); --i >= 0;)
|
||||
{
|
||||
ChangeKeyButton* const b = keyChangeButtons.getUnchecked(i);
|
||||
|
||||
b->fitToContent (getHeight() - 2);
|
||||
b->setTopRightPosition (x, 1);
|
||||
x = b->getX() - 5;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
KeyMappingEditorComponent& owner;
|
||||
OwnedArray<ChangeKeyButton> keyChangeButtons;
|
||||
const CommandID commandID;
|
||||
|
||||
enum { maxNumAssignments = 3 };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemComponent);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class KeyMappingEditorComponent::MappingItem : public TreeViewItem
|
||||
{
|
||||
public:
|
||||
MappingItem (KeyMappingEditorComponent& owner_, const CommandID commandID_)
|
||||
: owner (owner_), commandID (commandID_)
|
||||
{
|
||||
}
|
||||
|
||||
const String getUniqueName() const { return String ((int) commandID) + "_id"; }
|
||||
bool mightContainSubItems() { return false; }
|
||||
int getItemHeight() const { return 20; }
|
||||
|
||||
Component* createItemComponent()
|
||||
{
|
||||
return new ItemComponent (owner, commandID);
|
||||
}
|
||||
|
||||
private:
|
||||
KeyMappingEditorComponent& owner;
|
||||
const CommandID commandID;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MappingItem);
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class KeyMappingEditorComponent::CategoryItem : public TreeViewItem
|
||||
{
|
||||
public:
|
||||
CategoryItem (KeyMappingEditorComponent& owner_, const String& name)
|
||||
: owner (owner_), categoryName (name)
|
||||
{
|
||||
}
|
||||
|
||||
const String getUniqueName() const { return categoryName + "_cat"; }
|
||||
bool mightContainSubItems() { return true; }
|
||||
int getItemHeight() const { return 28; }
|
||||
|
||||
void paintItem (Graphics& g, int width, int height)
|
||||
{
|
||||
g.setFont (height * 0.6f, Font::bold);
|
||||
g.setColour (owner.findColour (KeyMappingEditorComponent::textColourId));
|
||||
|
||||
g.drawText (categoryName,
|
||||
2, 0, width - 2, height,
|
||||
Justification::centredLeft, true);
|
||||
}
|
||||
|
||||
void itemOpennessChanged (bool isNowOpen)
|
||||
{
|
||||
if (isNowOpen)
|
||||
{
|
||||
if (getNumSubItems() == 0)
|
||||
{
|
||||
Array <CommandID> commands (owner.getMappings().getCommandManager()->getCommandsInCategory (categoryName));
|
||||
|
||||
for (int i = 0; i < commands.size(); ++i)
|
||||
{
|
||||
if (owner.shouldCommandBeIncluded (commands[i]))
|
||||
addSubItem (new MappingItem (owner, commands[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
clearSubItems();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
KeyMappingEditorComponent& owner;
|
||||
String categoryName;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CategoryItem);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class KeyMappingEditorComponent::TopLevelItem : public TreeViewItem,
|
||||
public ChangeListener,
|
||||
public ButtonListener
|
||||
{
|
||||
public:
|
||||
TopLevelItem (KeyMappingEditorComponent& owner_)
|
||||
: owner (owner_)
|
||||
{
|
||||
setLinesDrawnForSubItems (false);
|
||||
owner.getMappings().addChangeListener (this);
|
||||
}
|
||||
|
||||
~TopLevelItem()
|
||||
{
|
||||
owner.getMappings().removeChangeListener (this);
|
||||
}
|
||||
|
||||
bool mightContainSubItems() { return true; }
|
||||
const String getUniqueName() const { return "keys"; }
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster*)
|
||||
{
|
||||
const OpennessRestorer opennessRestorer (*this);
|
||||
clearSubItems();
|
||||
|
||||
const StringArray categories (owner.getMappings().getCommandManager()->getCommandCategories());
|
||||
|
||||
for (int i = 0; i < categories.size(); ++i)
|
||||
{
|
||||
const Array <CommandID> commands (owner.getMappings().getCommandManager()->getCommandsInCategory (categories[i]));
|
||||
int count = 0;
|
||||
|
||||
for (int j = 0; j < commands.size(); ++j)
|
||||
if (owner.shouldCommandBeIncluded (commands[j]))
|
||||
++count;
|
||||
|
||||
if (count > 0)
|
||||
addSubItem (new CategoryItem (owner, categories[i]));
|
||||
}
|
||||
}
|
||||
|
||||
static void resetToDefaultsCallback (int result, KeyMappingEditorComponent* owner)
|
||||
{
|
||||
if (result != 0 && owner != nullptr)
|
||||
owner->getMappings().resetToDefaultMappings();
|
||||
}
|
||||
|
||||
void buttonClicked (Button*)
|
||||
{
|
||||
AlertWindow::showOkCancelBox (AlertWindow::QuestionIcon,
|
||||
TRANS("Reset to defaults"),
|
||||
TRANS("Are you sure you want to reset all the key-mappings to their default state?"),
|
||||
TRANS("Reset"),
|
||||
String::empty,
|
||||
&owner,
|
||||
ModalCallbackFunction::forComponent (resetToDefaultsCallback, &owner));
|
||||
}
|
||||
|
||||
private:
|
||||
KeyMappingEditorComponent& owner;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
KeyMappingEditorComponent::KeyMappingEditorComponent (KeyPressMappingSet& mappingManager,
|
||||
const bool showResetToDefaultButton)
|
||||
: mappings (mappingManager),
|
||||
resetButton (TRANS ("reset to defaults"))
|
||||
{
|
||||
treeItem = new TopLevelItem (*this);
|
||||
|
||||
if (showResetToDefaultButton)
|
||||
{
|
||||
addAndMakeVisible (&resetButton);
|
||||
resetButton.addListener (treeItem);
|
||||
}
|
||||
|
||||
addAndMakeVisible (&tree);
|
||||
tree.setColour (TreeView::backgroundColourId, findColour (backgroundColourId));
|
||||
tree.setRootItemVisible (false);
|
||||
tree.setDefaultOpenness (true);
|
||||
tree.setRootItem (treeItem);
|
||||
}
|
||||
|
||||
KeyMappingEditorComponent::~KeyMappingEditorComponent()
|
||||
{
|
||||
tree.setRootItem (nullptr);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void KeyMappingEditorComponent::setColours (const Colour& mainBackground,
|
||||
const Colour& textColour)
|
||||
{
|
||||
setColour (backgroundColourId, mainBackground);
|
||||
setColour (textColourId, textColour);
|
||||
tree.setColour (TreeView::backgroundColourId, mainBackground);
|
||||
}
|
||||
|
||||
void KeyMappingEditorComponent::parentHierarchyChanged()
|
||||
{
|
||||
treeItem->changeListenerCallback (nullptr);
|
||||
}
|
||||
|
||||
void KeyMappingEditorComponent::resized()
|
||||
{
|
||||
int h = getHeight();
|
||||
|
||||
if (resetButton.isVisible())
|
||||
{
|
||||
const int buttonHeight = 20;
|
||||
h -= buttonHeight + 8;
|
||||
int x = getWidth() - 8;
|
||||
|
||||
resetButton.changeWidthToFitText (buttonHeight);
|
||||
resetButton.setTopRightPosition (x, h + 6);
|
||||
}
|
||||
|
||||
tree.setBounds (0, 0, getWidth(), h);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool KeyMappingEditorComponent::shouldCommandBeIncluded (const CommandID commandID)
|
||||
{
|
||||
const ApplicationCommandInfo* const ci = mappings.getCommandManager()->getCommandForID (commandID);
|
||||
|
||||
return ci != nullptr && (ci->flags & ApplicationCommandInfo::hiddenFromKeyEditor) == 0;
|
||||
}
|
||||
|
||||
bool KeyMappingEditorComponent::isCommandReadOnly (const CommandID commandID)
|
||||
{
|
||||
const ApplicationCommandInfo* const ci = mappings.getCommandManager()->getCommandForID (commandID);
|
||||
|
||||
return ci != nullptr && (ci->flags & ApplicationCommandInfo::readOnlyInKeyEditor) != 0;
|
||||
}
|
||||
|
||||
const String KeyMappingEditorComponent::getDescriptionForKeyPress (const KeyPress& key)
|
||||
{
|
||||
return key.getTextDescription();
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
134
modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.h
Normal file
134
modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.h
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_KEYMAPPINGEDITORCOMPONENT_JUCEHEADER__
|
||||
#define __JUCE_KEYMAPPINGEDITORCOMPONENT_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component to allow editing of the keymaps stored by a KeyPressMappingSet
|
||||
object.
|
||||
|
||||
@see KeyPressMappingSet
|
||||
*/
|
||||
class JUCE_API KeyMappingEditorComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a KeyMappingEditorComponent.
|
||||
|
||||
@param mappingSet this is the set of mappings to display and edit. Make sure the
|
||||
mappings object is not deleted before this component!
|
||||
@param showResetToDefaultButton if true, then at the bottom of the list, the
|
||||
component will include a 'reset to defaults' button.
|
||||
*/
|
||||
KeyMappingEditorComponent (KeyPressMappingSet& mappingSet,
|
||||
bool showResetToDefaultButton);
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~KeyMappingEditorComponent();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets up the colours to use for parts of the component.
|
||||
|
||||
@param mainBackground colour to use for most of the background
|
||||
@param textColour colour to use for the text
|
||||
*/
|
||||
void setColours (const Colour& mainBackground,
|
||||
const Colour& textColour);
|
||||
|
||||
/** Returns the KeyPressMappingSet that this component is acting upon. */
|
||||
KeyPressMappingSet& getMappings() const noexcept { return mappings; }
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Can be overridden if some commands need to be excluded from the list.
|
||||
|
||||
By default this will use the KeyPressMappingSet's shouldCommandBeVisibleInEditor()
|
||||
method to decide what to return, but you can override it to handle special cases.
|
||||
*/
|
||||
virtual bool shouldCommandBeIncluded (CommandID commandID);
|
||||
|
||||
/** Can be overridden to indicate that some commands are shown as read-only.
|
||||
|
||||
By default this will use the KeyPressMappingSet's shouldCommandBeReadOnlyInEditor()
|
||||
method to decide what to return, but you can override it to handle special cases.
|
||||
*/
|
||||
virtual bool isCommandReadOnly (CommandID commandID);
|
||||
|
||||
/** This can be overridden to let you change the format of the string used
|
||||
to describe a keypress.
|
||||
|
||||
This is handy if you're using non-standard KeyPress objects, e.g. for custom
|
||||
keys that are triggered by something else externally. If you override the
|
||||
method, be sure to let the base class's method handle keys you're not
|
||||
interested in.
|
||||
*/
|
||||
virtual const String getDescriptionForKeyPress (const KeyPress& key);
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the editor.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
To change the colours of the menu that pops up
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
backgroundColourId = 0x100ad00, /**< The background colour to fill the editor background. */
|
||||
textColourId = 0x100ad01, /**< The colour for the text. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void parentHierarchyChanged();
|
||||
/** @internal */
|
||||
void resized();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
KeyPressMappingSet& mappings;
|
||||
TreeView tree;
|
||||
TextButton resetButton;
|
||||
|
||||
class TopLevelItem;
|
||||
class ChangeKeyButton;
|
||||
class MappingItem;
|
||||
class CategoryItem;
|
||||
class ItemComponent;
|
||||
friend class TopLevelItem;
|
||||
friend class OwnedArray <ChangeKeyButton>;
|
||||
friend class ScopedPointer<TopLevelItem>;
|
||||
ScopedPointer<TopLevelItem> treeItem;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KeyMappingEditorComponent);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_KEYMAPPINGEDITORCOMPONENT_JUCEHEADER__
|
||||
147
modules/juce_gui_extra/misc/juce_PreferencesPanel.cpp
Normal file
147
modules/juce_gui_extra/misc/juce_PreferencesPanel.cpp
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
PreferencesPanel::PreferencesPanel()
|
||||
: buttonSize (70)
|
||||
{
|
||||
}
|
||||
|
||||
PreferencesPanel::~PreferencesPanel()
|
||||
{
|
||||
}
|
||||
|
||||
int PreferencesPanel::getButtonSize() const noexcept
|
||||
{
|
||||
return buttonSize;
|
||||
}
|
||||
|
||||
void PreferencesPanel::setButtonSize (int newSize)
|
||||
{
|
||||
buttonSize = newSize;
|
||||
resized();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void PreferencesPanel::addSettingsPage (const String& title,
|
||||
const Drawable* icon,
|
||||
const Drawable* overIcon,
|
||||
const Drawable* downIcon)
|
||||
{
|
||||
DrawableButton* const button = new DrawableButton (title, DrawableButton::ImageAboveTextLabel);
|
||||
buttons.add (button);
|
||||
|
||||
button->setImages (icon, overIcon, downIcon);
|
||||
button->setRadioGroupId (1);
|
||||
button->addListener (this);
|
||||
button->setClickingTogglesState (true);
|
||||
button->setWantsKeyboardFocus (false);
|
||||
addAndMakeVisible (button);
|
||||
|
||||
resized();
|
||||
|
||||
if (currentPage == nullptr)
|
||||
setCurrentPage (title);
|
||||
}
|
||||
|
||||
void PreferencesPanel::addSettingsPage (const String& title, const void* imageData, const int imageDataSize)
|
||||
{
|
||||
DrawableImage icon, iconOver, iconDown;
|
||||
icon.setImage (ImageCache::getFromMemory (imageData, imageDataSize));
|
||||
|
||||
iconOver.setImage (ImageCache::getFromMemory (imageData, imageDataSize));
|
||||
iconOver.setOverlayColour (Colours::black.withAlpha (0.12f));
|
||||
|
||||
iconDown.setImage (ImageCache::getFromMemory (imageData, imageDataSize));
|
||||
iconDown.setOverlayColour (Colours::black.withAlpha (0.25f));
|
||||
|
||||
addSettingsPage (title, &icon, &iconOver, &iconDown);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void PreferencesPanel::showInDialogBox (const String& dialogTitle, int dialogWidth, int dialogHeight, const Colour& backgroundColour)
|
||||
{
|
||||
setSize (dialogWidth, dialogHeight);
|
||||
DialogWindow::showDialog (dialogTitle, this, 0, backgroundColour, false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void PreferencesPanel::resized()
|
||||
{
|
||||
for (int i = 0; i < buttons.size(); ++i)
|
||||
buttons.getUnchecked(i)->setBounds (i * buttonSize, 0, buttonSize, buttonSize);
|
||||
|
||||
if (currentPage != nullptr)
|
||||
currentPage->setBounds (getLocalBounds().withTop (buttonSize + 5));
|
||||
}
|
||||
|
||||
void PreferencesPanel::paint (Graphics& g)
|
||||
{
|
||||
g.setColour (Colours::grey);
|
||||
g.fillRect (0, buttonSize + 2, getWidth(), 1);
|
||||
}
|
||||
|
||||
void PreferencesPanel::setCurrentPage (const String& pageName)
|
||||
{
|
||||
if (currentPageName != pageName)
|
||||
{
|
||||
currentPageName = pageName;
|
||||
|
||||
currentPage = nullptr;
|
||||
currentPage = createComponentForPage (pageName);
|
||||
|
||||
if (currentPage != nullptr)
|
||||
{
|
||||
addAndMakeVisible (currentPage);
|
||||
currentPage->toBack();
|
||||
resized();
|
||||
}
|
||||
|
||||
for (int i = 0; i < buttons.size(); ++i)
|
||||
{
|
||||
if (buttons.getUnchecked(i)->getName() == pageName)
|
||||
{
|
||||
buttons.getUnchecked(i)->setToggleState (true, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PreferencesPanel::buttonClicked (Button*)
|
||||
{
|
||||
for (int i = 0; i < buttons.size(); ++i)
|
||||
{
|
||||
if (buttons.getUnchecked(i)->getToggleState())
|
||||
{
|
||||
setCurrentPage (buttons.getUnchecked(i)->getName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
149
modules/juce_gui_extra/misc/juce_PreferencesPanel.h
Normal file
149
modules/juce_gui_extra/misc/juce_PreferencesPanel.h
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_PREFERENCESPANEL_JUCEHEADER__
|
||||
#define __JUCE_PREFERENCESPANEL_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component with a set of buttons at the top for changing between pages of
|
||||
preferences.
|
||||
|
||||
This is just a handy way of writing a Mac-style preferences panel where you
|
||||
have a row of buttons along the top for the different preference categories,
|
||||
each button having an icon above its name. Clicking these will show an
|
||||
appropriate prefs page below it.
|
||||
|
||||
You can either put one of these inside your own component, or just use the
|
||||
showInDialogBox() method to show it in a window and run it modally.
|
||||
|
||||
To use it, just add a set of named pages with the addSettingsPage() method,
|
||||
and implement the createComponentForPage() method to create suitable components
|
||||
for each of these pages.
|
||||
*/
|
||||
class JUCE_API PreferencesPanel : public Component,
|
||||
private ButtonListener // (can't use Button::Listener due to idiotic VC2005 bug)
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty panel.
|
||||
|
||||
Use addSettingsPage() to add some pages to it in your constructor.
|
||||
*/
|
||||
PreferencesPanel();
|
||||
|
||||
/** Destructor. */
|
||||
~PreferencesPanel();
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a page using a set of drawables to define the page's icon.
|
||||
|
||||
Note that the other version of this method is much easier if you're using
|
||||
an image instead of a custom drawable.
|
||||
|
||||
@param pageTitle the name of this preferences page - you'll need to
|
||||
make sure your createComponentForPage() method creates
|
||||
a suitable component when it is passed this name
|
||||
@param normalIcon the drawable to display in the page's button normally
|
||||
@param overIcon the drawable to display in the page's button when the mouse is over
|
||||
@param downIcon the drawable to display in the page's button when the button is down
|
||||
@see DrawableButton
|
||||
*/
|
||||
void addSettingsPage (const String& pageTitle,
|
||||
const Drawable* normalIcon,
|
||||
const Drawable* overIcon,
|
||||
const Drawable* downIcon);
|
||||
|
||||
/** Creates a page using a set of drawables to define the page's icon.
|
||||
|
||||
The other version of this method gives you more control over the icon, but this
|
||||
one is much easier if you're just loading it from a file.
|
||||
|
||||
@param pageTitle the name of this preferences page - you'll need to
|
||||
make sure your createComponentForPage() method creates
|
||||
a suitable component when it is passed this name
|
||||
@param imageData a block of data containing an image file, e.g. a jpeg, png or gif.
|
||||
For this to look good, you'll probably want to use a nice
|
||||
transparent png file.
|
||||
@param imageDataSize the size of the image data, in bytes
|
||||
*/
|
||||
void addSettingsPage (const String& pageTitle,
|
||||
const void* imageData,
|
||||
int imageDataSize);
|
||||
|
||||
/** Utility method to display this panel in a DialogWindow.
|
||||
|
||||
Calling this will create a DialogWindow containing this panel with the
|
||||
given size and title, and will run it modally, returning when the user
|
||||
closes the dialog box.
|
||||
*/
|
||||
void showInDialogBox (const String& dialogTitle,
|
||||
int dialogWidth,
|
||||
int dialogHeight,
|
||||
const Colour& backgroundColour = Colours::white);
|
||||
|
||||
//==============================================================================
|
||||
/** Subclasses must override this to return a component for each preferences page.
|
||||
|
||||
The subclass should return a pointer to a new component representing the named
|
||||
page, which the panel will then display.
|
||||
|
||||
The panel will delete the component later when the user goes to another page
|
||||
or deletes the panel.
|
||||
*/
|
||||
virtual Component* createComponentForPage (const String& pageName) = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the current page being displayed. */
|
||||
void setCurrentPage (const String& pageName);
|
||||
|
||||
/** Returns the size of the buttons shown along the top. */
|
||||
int getButtonSize() const noexcept;
|
||||
|
||||
/** Changes the size of the buttons shown along the top. */
|
||||
void setButtonSize (int newSize);
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void resized();
|
||||
/** @internal */
|
||||
void paint (Graphics& g);
|
||||
/** @internal */
|
||||
void buttonClicked (Button* button);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
String currentPageName;
|
||||
ScopedPointer <Component> currentPage;
|
||||
OwnedArray<DrawableButton> buttons;
|
||||
int buttonSize;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PreferencesPanel);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // __JUCE_PREFERENCESPANEL_JUCEHEADER__
|
||||
138
modules/juce_gui_extra/misc/juce_RecentlyOpenedFilesList.cpp
Normal file
138
modules/juce_gui_extra/misc/juce_RecentlyOpenedFilesList.cpp
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
RecentlyOpenedFilesList::RecentlyOpenedFilesList()
|
||||
: maxNumberOfItems (10)
|
||||
{
|
||||
}
|
||||
|
||||
RecentlyOpenedFilesList::~RecentlyOpenedFilesList()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void RecentlyOpenedFilesList::setMaxNumberOfItems (const int newMaxNumber)
|
||||
{
|
||||
maxNumberOfItems = jmax (1, newMaxNumber);
|
||||
|
||||
files.removeRange (maxNumberOfItems, getNumFiles());
|
||||
}
|
||||
|
||||
int RecentlyOpenedFilesList::getNumFiles() const
|
||||
{
|
||||
return files.size();
|
||||
}
|
||||
|
||||
File RecentlyOpenedFilesList::getFile (const int index) const
|
||||
{
|
||||
return File (files [index]);
|
||||
}
|
||||
|
||||
void RecentlyOpenedFilesList::clear()
|
||||
{
|
||||
files.clear();
|
||||
}
|
||||
|
||||
void RecentlyOpenedFilesList::addFile (const File& file)
|
||||
{
|
||||
removeFile (file);
|
||||
files.insert (0, file.getFullPathName());
|
||||
|
||||
setMaxNumberOfItems (maxNumberOfItems);
|
||||
}
|
||||
|
||||
void RecentlyOpenedFilesList::removeFile (const File& file)
|
||||
{
|
||||
files.removeString (file.getFullPathName());
|
||||
}
|
||||
|
||||
void RecentlyOpenedFilesList::removeNonExistentFiles()
|
||||
{
|
||||
for (int i = getNumFiles(); --i >= 0;)
|
||||
if (! getFile(i).exists())
|
||||
files.remove (i);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int RecentlyOpenedFilesList::createPopupMenuItems (PopupMenu& menuToAddTo,
|
||||
const int baseItemId,
|
||||
const bool showFullPaths,
|
||||
const bool dontAddNonExistentFiles,
|
||||
const File** filesToAvoid)
|
||||
{
|
||||
int num = 0;
|
||||
|
||||
for (int i = 0; i < getNumFiles(); ++i)
|
||||
{
|
||||
const File f (getFile(i));
|
||||
|
||||
if ((! dontAddNonExistentFiles) || f.exists())
|
||||
{
|
||||
bool needsAvoiding = false;
|
||||
|
||||
if (filesToAvoid != nullptr)
|
||||
{
|
||||
for (const File** avoid = filesToAvoid; *avoid != nullptr; ++avoid)
|
||||
{
|
||||
if (f == **avoid)
|
||||
{
|
||||
needsAvoiding = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! needsAvoiding)
|
||||
{
|
||||
menuToAddTo.addItem (baseItemId + i,
|
||||
showFullPaths ? f.getFullPathName()
|
||||
: f.getFileName());
|
||||
++num;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String RecentlyOpenedFilesList::toString() const
|
||||
{
|
||||
return files.joinIntoString ("\n");
|
||||
}
|
||||
|
||||
void RecentlyOpenedFilesList::restoreFromString (const String& stringifiedVersion)
|
||||
{
|
||||
clear();
|
||||
files.addLines (stringifiedVersion);
|
||||
|
||||
setMaxNumberOfItems (maxNumberOfItems);
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
159
modules/juce_gui_extra/misc/juce_RecentlyOpenedFilesList.h
Normal file
159
modules/juce_gui_extra/misc/juce_RecentlyOpenedFilesList.h
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_RECENTLYOPENEDFILESLIST_JUCEHEADER__
|
||||
#define __JUCE_RECENTLYOPENEDFILESLIST_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Manages a set of files for use as a list of recently-opened documents.
|
||||
|
||||
This is a handy class for holding your list of recently-opened documents, with
|
||||
helpful methods for things like purging any non-existent files, automatically
|
||||
adding them to a menu, and making persistence easy.
|
||||
|
||||
@see File, FileBasedDocument
|
||||
*/
|
||||
class JUCE_API RecentlyOpenedFilesList
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty list.
|
||||
*/
|
||||
RecentlyOpenedFilesList();
|
||||
|
||||
/** Destructor. */
|
||||
~RecentlyOpenedFilesList();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets a limit for the number of files that will be stored in the list.
|
||||
|
||||
When addFile() is called, then if there is no more space in the list, the
|
||||
least-recently added file will be dropped.
|
||||
|
||||
@see getMaxNumberOfItems
|
||||
*/
|
||||
void setMaxNumberOfItems (int newMaxNumber);
|
||||
|
||||
/** Returns the number of items that this list will store.
|
||||
@see setMaxNumberOfItems
|
||||
*/
|
||||
int getMaxNumberOfItems() const noexcept { return maxNumberOfItems; }
|
||||
|
||||
/** Returns the number of files in the list.
|
||||
|
||||
The most recently added file is always at index 0.
|
||||
*/
|
||||
int getNumFiles() const;
|
||||
|
||||
/** Returns one of the files in the list.
|
||||
|
||||
The most recently added file is always at index 0.
|
||||
*/
|
||||
File getFile (int index) const;
|
||||
|
||||
/** Returns an array of all the absolute pathnames in the list.
|
||||
*/
|
||||
const StringArray& getAllFilenames() const noexcept { return files; }
|
||||
|
||||
/** Clears all the files from the list. */
|
||||
void clear();
|
||||
|
||||
/** Adds a file to the list.
|
||||
|
||||
The file will be added at index 0. If this file is already in the list, it will
|
||||
be moved up to index 0, but a file can only appear once in the list.
|
||||
|
||||
If the list already contains the maximum number of items that is permitted, the
|
||||
least-recently added file will be dropped from the end.
|
||||
*/
|
||||
void addFile (const File& file);
|
||||
|
||||
/** Removes a file from the list. */
|
||||
void removeFile (const File& file);
|
||||
|
||||
/** Checks each of the files in the list, removing any that don't exist.
|
||||
|
||||
You might want to call this after reloading a list of files, or before putting them
|
||||
on a menu.
|
||||
*/
|
||||
void removeNonExistentFiles();
|
||||
|
||||
//==============================================================================
|
||||
/** Adds entries to a menu, representing each of the files in the list.
|
||||
|
||||
This is handy for creating an "open recent file..." menu in your app. The
|
||||
menu items are numbered consecutively starting with the baseItemId value,
|
||||
and can either be added as complete pathnames, or just the last part of the
|
||||
filename.
|
||||
|
||||
If dontAddNonExistentFiles is true, then each file will be checked and only those
|
||||
that exist will be added.
|
||||
|
||||
If filesToAvoid is non-zero, then it is considered to be a zero-terminated array of
|
||||
pointers to file objects. Any files that appear in this list will not be added to the
|
||||
menu - the reason for this is that you might have a number of files already open, so
|
||||
might not want these to be shown in the menu.
|
||||
|
||||
It returns the number of items that were added.
|
||||
*/
|
||||
int createPopupMenuItems (PopupMenu& menuToAddItemsTo,
|
||||
int baseItemId,
|
||||
bool showFullPaths,
|
||||
bool dontAddNonExistentFiles,
|
||||
const File** filesToAvoid = nullptr);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a string that encapsulates all the files in the list.
|
||||
|
||||
The string that is returned can later be passed into restoreFromString() in
|
||||
order to recreate the list. This is handy for persisting your list, e.g. in
|
||||
a PropertiesFile object.
|
||||
|
||||
@see restoreFromString
|
||||
*/
|
||||
String toString() const;
|
||||
|
||||
/** Restores the list from a previously stringified version of the list.
|
||||
|
||||
Pass in a stringified version created with toString() in order to persist/restore
|
||||
your list.
|
||||
|
||||
@see toString
|
||||
*/
|
||||
void restoreFromString (const String& stringifiedVersion);
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
StringArray files;
|
||||
int maxNumberOfItems;
|
||||
|
||||
JUCE_LEAK_DETECTOR (RecentlyOpenedFilesList);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_RECENTLYOPENEDFILESLIST_JUCEHEADER__
|
||||
111
modules/juce_gui_extra/misc/juce_SplashScreen.cpp
Normal file
111
modules/juce_gui_extra/misc/juce_SplashScreen.cpp
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
SplashScreen::SplashScreen()
|
||||
{
|
||||
setOpaque (true);
|
||||
}
|
||||
|
||||
SplashScreen::~SplashScreen()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void SplashScreen::show (const String& title,
|
||||
const Image& backgroundImage_,
|
||||
const int minimumTimeToDisplayFor,
|
||||
const bool useDropShadow,
|
||||
const bool removeOnMouseClick)
|
||||
{
|
||||
backgroundImage = backgroundImage_;
|
||||
|
||||
jassert (backgroundImage_.isValid());
|
||||
|
||||
if (backgroundImage_.isValid())
|
||||
{
|
||||
setOpaque (! backgroundImage_.hasAlphaChannel());
|
||||
|
||||
show (title,
|
||||
backgroundImage_.getWidth(),
|
||||
backgroundImage_.getHeight(),
|
||||
minimumTimeToDisplayFor,
|
||||
useDropShadow,
|
||||
removeOnMouseClick);
|
||||
}
|
||||
}
|
||||
|
||||
void SplashScreen::show (const String& title,
|
||||
const int width,
|
||||
const int height,
|
||||
const int minimumTimeToDisplayFor,
|
||||
const bool useDropShadow,
|
||||
const bool removeOnMouseClick)
|
||||
{
|
||||
setName (title);
|
||||
setAlwaysOnTop (true);
|
||||
setVisible (true);
|
||||
centreWithSize (width, height);
|
||||
|
||||
addToDesktop (useDropShadow ? ComponentPeer::windowHasDropShadow : 0);
|
||||
toFront (false);
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
MessageManager::getInstance()->runDispatchLoopUntil (300);
|
||||
#endif
|
||||
|
||||
repaint();
|
||||
|
||||
originalClickCounter = removeOnMouseClick
|
||||
? Desktop::getMouseButtonClickCounter()
|
||||
: std::numeric_limits<int>::max();
|
||||
|
||||
earliestTimeToDelete = Time::getCurrentTime() + RelativeTime::milliseconds (minimumTimeToDisplayFor);
|
||||
|
||||
startTimer (50);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void SplashScreen::paint (Graphics& g)
|
||||
{
|
||||
g.setOpacity (1.0f);
|
||||
|
||||
g.drawImage (backgroundImage,
|
||||
0, 0, getWidth(), getHeight(),
|
||||
0, 0, backgroundImage.getWidth(), backgroundImage.getHeight());
|
||||
}
|
||||
|
||||
void SplashScreen::timerCallback()
|
||||
{
|
||||
if (Time::getCurrentTime() > earliestTimeToDelete
|
||||
|| Desktop::getMouseButtonClickCounter() > originalClickCounter)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
146
modules/juce_gui_extra/misc/juce_SplashScreen.h
Normal file
146
modules/juce_gui_extra/misc/juce_SplashScreen.h
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_SPLASHSCREEN_JUCEHEADER__
|
||||
#define __JUCE_SPLASHSCREEN_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** A component for showing a splash screen while your app starts up.
|
||||
|
||||
This will automatically position itself, and delete itself when the app has
|
||||
finished initialising (it uses the JUCEApplication::isInitialising() to detect
|
||||
this).
|
||||
|
||||
To use it, just create one of these in your JUCEApplication::initialise() method,
|
||||
call its show() method and let the object delete itself later.
|
||||
|
||||
E.g. @code
|
||||
|
||||
void MyApp::initialise (const String& commandLine)
|
||||
{
|
||||
SplashScreen* splash = new SplashScreen();
|
||||
|
||||
splash->show ("welcome to my app",
|
||||
ImageCache::getFromFile (File ("/foobar/splash.jpg")),
|
||||
4000, false);
|
||||
|
||||
.. no need to delete the splash screen - it'll do that itself.
|
||||
}
|
||||
|
||||
@endcode
|
||||
*/
|
||||
class JUCE_API SplashScreen : public Component,
|
||||
public Timer,
|
||||
private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a SplashScreen object.
|
||||
|
||||
After creating one of these (or your subclass of it), call one of the show()
|
||||
methods to display it.
|
||||
*/
|
||||
SplashScreen();
|
||||
|
||||
/** Destructor. */
|
||||
~SplashScreen();
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a SplashScreen object that will display an image.
|
||||
|
||||
As soon as this is called, the SplashScreen will be displayed in the centre of the
|
||||
screen. This method will also dispatch any pending messages to make sure that when
|
||||
it returns, the splash screen has been completely drawn, and your initialisation
|
||||
code can carry on.
|
||||
|
||||
@param title the name to give the component
|
||||
@param backgroundImage an image to draw on the component. The component's size
|
||||
will be set to the size of this image, and if the image is
|
||||
semi-transparent, the component will be made semi-transparent
|
||||
too. This image will be deleted (or released from the ImageCache
|
||||
if that's how it was created) by the splash screen object when
|
||||
it is itself deleted.
|
||||
@param minimumTimeToDisplayFor how long (in milliseconds) the splash screen
|
||||
should stay visible for. If the initialisation takes longer than
|
||||
this time, the splash screen will wait for it to finish before
|
||||
disappearing, but if initialisation is very quick, this lets
|
||||
you make sure that people get a good look at your splash.
|
||||
@param useDropShadow if true, the window will have a drop shadow
|
||||
@param removeOnMouseClick if true, the window will go away as soon as the user clicks
|
||||
the mouse (anywhere)
|
||||
*/
|
||||
void show (const String& title,
|
||||
const Image& backgroundImage,
|
||||
int minimumTimeToDisplayFor,
|
||||
bool useDropShadow,
|
||||
bool removeOnMouseClick = true);
|
||||
|
||||
/** Creates a SplashScreen object with a specified size.
|
||||
|
||||
For a custom splash screen, you can use this method to display it at a certain size
|
||||
and then override the paint() method yourself to do whatever's necessary.
|
||||
|
||||
As soon as this is called, the SplashScreen will be displayed in the centre of the
|
||||
screen. This method will also dispatch any pending messages to make sure that when
|
||||
it returns, the splash screen has been completely drawn, and your initialisation
|
||||
code can carry on.
|
||||
|
||||
@param title the name to give the component
|
||||
@param width the width to use
|
||||
@param height the height to use
|
||||
@param minimumTimeToDisplayFor how long (in milliseconds) the splash screen
|
||||
should stay visible for. If the initialisation takes longer than
|
||||
this time, the splash screen will wait for it to finish before
|
||||
disappearing, but if initialisation is very quick, this lets
|
||||
you make sure that people get a good look at your splash.
|
||||
@param useDropShadow if true, the window will have a drop shadow
|
||||
@param removeOnMouseClick if true, the window will go away as soon as the user clicks
|
||||
the mouse (anywhere)
|
||||
*/
|
||||
void show (const String& title,
|
||||
int width,
|
||||
int height,
|
||||
int minimumTimeToDisplayFor,
|
||||
bool useDropShadow,
|
||||
bool removeOnMouseClick = true);
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics& g);
|
||||
/** @internal */
|
||||
void timerCallback();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Image backgroundImage;
|
||||
Time earliestTimeToDelete;
|
||||
int originalClickCounter;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SplashScreen);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_SPLASHSCREEN_JUCEHEADER__
|
||||
42
modules/juce_gui_extra/misc/juce_SystemTrayIconComponent.cpp
Normal file
42
modules/juce_gui_extra/misc/juce_SystemTrayIconComponent.cpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#if JUCE_WINDOWS || JUCE_LINUX
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
SystemTrayIconComponent::SystemTrayIconComponent()
|
||||
{
|
||||
addToDesktop (0);
|
||||
}
|
||||
|
||||
SystemTrayIconComponent::~SystemTrayIconComponent()
|
||||
{
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
|
||||
#endif
|
||||
81
modules/juce_gui_extra/misc/juce_SystemTrayIconComponent.h
Normal file
81
modules/juce_gui_extra/misc/juce_SystemTrayIconComponent.h
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__
|
||||
#define __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__
|
||||
|
||||
#if JUCE_WINDOWS || JUCE_LINUX || DOXYGEN
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
On Windows and Linux only, this component sits in the taskbar tray as a small icon.
|
||||
|
||||
To use it, just create one of these components, but don't attempt to make it
|
||||
visible, add it to a parent, or put it on the desktop.
|
||||
|
||||
You can then call setIconImage() to create an icon for it in the taskbar.
|
||||
|
||||
To change the icon's tooltip, you can use setIconTooltip().
|
||||
|
||||
To respond to mouse-events, you can override the normal mouseDown(),
|
||||
mouseUp(), mouseDoubleClick() and mouseMove() methods, and although the x, y
|
||||
position will not be valid, you can use this to respond to clicks. Traditionally
|
||||
you'd use a left-click to show your application's window, and a right-click
|
||||
to show a pop-up menu.
|
||||
*/
|
||||
class JUCE_API SystemTrayIconComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
SystemTrayIconComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~SystemTrayIconComponent();
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the image shown in the taskbar.
|
||||
*/
|
||||
void setIconImage (const Image& newImage);
|
||||
|
||||
/** Changes the tooltip that Windows shows above the icon. */
|
||||
void setIconTooltip (const String& tooltip);
|
||||
|
||||
#if JUCE_LINUX
|
||||
/** @internal */
|
||||
void paint (Graphics& g);
|
||||
#endif
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class Pimpl;
|
||||
ScopedPointer<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SystemTrayIconComponent);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
#endif // __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__
|
||||
128
modules/juce_gui_extra/misc/juce_WebBrowserComponent.h
Normal file
128
modules/juce_gui_extra/misc/juce_WebBrowserComponent.h
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_WEBBROWSERCOMPONENT_JUCEHEADER__
|
||||
#define __JUCE_WEBBROWSERCOMPONENT_JUCEHEADER__
|
||||
|
||||
#if JUCE_WEB_BROWSER || DOXYGEN
|
||||
|
||||
#if ! DOXYGEN
|
||||
class WebBrowserComponentInternal;
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component that displays an embedded web browser.
|
||||
|
||||
The browser itself will be platform-dependent. On the Mac, probably Safari, on
|
||||
Windows, probably IE.
|
||||
|
||||
*/
|
||||
class JUCE_API WebBrowserComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a WebBrowserComponent.
|
||||
|
||||
Once it's created and visible, send the browser to a URL using goToURL().
|
||||
|
||||
@param unloadPageWhenBrowserIsHidden if this is true, then when the browser
|
||||
component is taken offscreen, it'll clear the current page
|
||||
and replace it with a blank page - this can be handy to stop
|
||||
the browser using resources in the background when it's not
|
||||
actually being used.
|
||||
*/
|
||||
explicit WebBrowserComponent (bool unloadPageWhenBrowserIsHidden = true);
|
||||
|
||||
/** Destructor. */
|
||||
~WebBrowserComponent();
|
||||
|
||||
//==============================================================================
|
||||
/** Sends the browser to a particular URL.
|
||||
|
||||
@param url the URL to go to.
|
||||
@param headers an optional set of parameters to put in the HTTP header. If
|
||||
you supply this, it should be a set of string in the form
|
||||
"HeaderKey: HeaderValue"
|
||||
@param postData an optional block of data that will be attached to the HTTP
|
||||
POST request
|
||||
*/
|
||||
void goToURL (const String& url,
|
||||
const StringArray* headers = nullptr,
|
||||
const MemoryBlock* postData = nullptr);
|
||||
|
||||
/** Stops the current page loading.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/** Sends the browser back one page.
|
||||
*/
|
||||
void goBack();
|
||||
|
||||
/** Sends the browser forward one page.
|
||||
*/
|
||||
void goForward();
|
||||
|
||||
/** Refreshes the browser.
|
||||
*/
|
||||
void refresh();
|
||||
|
||||
//==============================================================================
|
||||
/** This callback is called when the browser is about to navigate
|
||||
to a new location.
|
||||
|
||||
You can override this method to perform some action when the user
|
||||
tries to go to a particular URL. To allow the operation to carry on,
|
||||
return true, or return false to stop the navigation happening.
|
||||
*/
|
||||
virtual bool pageAboutToLoad (const String& newURL);
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics& g);
|
||||
/** @internal */
|
||||
void resized();
|
||||
/** @internal */
|
||||
void parentHierarchyChanged();
|
||||
/** @internal */
|
||||
void visibilityChanged();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
WebBrowserComponentInternal* browser;
|
||||
bool blankPageShown, unloadPageWhenBrowserIsHidden;
|
||||
String lastURL;
|
||||
StringArray lastHeaders;
|
||||
MemoryBlock lastPostData;
|
||||
|
||||
void reloadLastURL();
|
||||
void checkWindowAssociation();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebBrowserComponent);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
#endif // __JUCE_WEBBROWSERCOMPONENT_JUCEHEADER__
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
WebBrowserComponent::WebBrowserComponent (const bool unloadPageWhenBrowserIsHidden_)
|
||||
: browser (nullptr),
|
||||
blankPageShown (false),
|
||||
unloadPageWhenBrowserIsHidden (unloadPageWhenBrowserIsHidden_)
|
||||
{
|
||||
setOpaque (true);
|
||||
}
|
||||
|
||||
WebBrowserComponent::~WebBrowserComponent()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void WebBrowserComponent::goToURL (const String& url,
|
||||
const StringArray* headers,
|
||||
const MemoryBlock* postData)
|
||||
{
|
||||
lastURL = url;
|
||||
|
||||
lastHeaders.clear();
|
||||
if (headers != nullptr)
|
||||
lastHeaders = *headers;
|
||||
|
||||
lastPostData.setSize (0);
|
||||
if (postData != nullptr)
|
||||
lastPostData = *postData;
|
||||
|
||||
blankPageShown = false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
void WebBrowserComponent::stop()
|
||||
{
|
||||
}
|
||||
|
||||
void WebBrowserComponent::goBack()
|
||||
{
|
||||
lastURL = String::empty;
|
||||
blankPageShown = false;
|
||||
|
||||
}
|
||||
|
||||
void WebBrowserComponent::goForward()
|
||||
{
|
||||
lastURL = String::empty;
|
||||
|
||||
}
|
||||
|
||||
void WebBrowserComponent::refresh()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void WebBrowserComponent::paint (Graphics& g)
|
||||
{
|
||||
g.fillAll (Colours::white);
|
||||
}
|
||||
|
||||
void WebBrowserComponent::checkWindowAssociation()
|
||||
{
|
||||
}
|
||||
|
||||
void WebBrowserComponent::reloadLastURL()
|
||||
{
|
||||
if (lastURL.isNotEmpty())
|
||||
{
|
||||
goToURL (lastURL, &lastHeaders, &lastPostData);
|
||||
lastURL = String::empty;
|
||||
}
|
||||
}
|
||||
|
||||
void WebBrowserComponent::parentHierarchyChanged()
|
||||
{
|
||||
checkWindowAssociation();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::resized()
|
||||
{
|
||||
}
|
||||
|
||||
void WebBrowserComponent::visibilityChanged()
|
||||
{
|
||||
checkWindowAssociation();
|
||||
}
|
||||
|
||||
bool WebBrowserComponent::pageAboutToLoad (const String& url)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
128
modules/juce_gui_extra/native/juce_ios_UIViewComponent.mm
Normal file
128
modules/juce_gui_extra/native/juce_ios_UIViewComponent.mm
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
class UIViewComponent::Pimpl : public ComponentMovementWatcher
|
||||
{
|
||||
public:
|
||||
Pimpl (UIView* const view_, Component& owner_)
|
||||
: ComponentMovementWatcher (&owner_),
|
||||
view (view_),
|
||||
owner (owner_),
|
||||
currentPeer (nullptr)
|
||||
{
|
||||
[view_ retain];
|
||||
|
||||
if (owner.isShowing())
|
||||
componentPeerChanged();
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
[view removeFromSuperview];
|
||||
[view release];
|
||||
}
|
||||
|
||||
void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/)
|
||||
{
|
||||
Component* const topComp = owner.getTopLevelComponent();
|
||||
|
||||
if (topComp->getPeer() != nullptr)
|
||||
{
|
||||
const Point<int> pos (topComp->getLocalPoint (&owner, Point<int>()));
|
||||
|
||||
[view setFrame: CGRectMake ((float) pos.getX(), (float) pos.getY(),
|
||||
(float) owner.getWidth(), (float) owner.getHeight())];
|
||||
}
|
||||
}
|
||||
|
||||
void componentPeerChanged()
|
||||
{
|
||||
ComponentPeer* const peer = owner.getPeer();
|
||||
|
||||
if (currentPeer != peer)
|
||||
{
|
||||
if ([view superview] != nil)
|
||||
[view removeFromSuperview]; // Must be careful not to call this unless it's required - e.g. some Apple AU views
|
||||
// override the call and use it as a sign that they're being deleted, which breaks everything..
|
||||
currentPeer = peer;
|
||||
|
||||
if (peer != nullptr)
|
||||
{
|
||||
UIView* peerView = (UIView*) peer->getNativeHandle();
|
||||
[peerView addSubview: view];
|
||||
componentMovedOrResized (false, false);
|
||||
}
|
||||
}
|
||||
|
||||
[view setHidden: ! owner.isShowing()];
|
||||
}
|
||||
|
||||
void componentVisibilityChanged()
|
||||
{
|
||||
componentPeerChanged();
|
||||
}
|
||||
|
||||
Rectangle<int> getViewBounds() const
|
||||
{
|
||||
CGRect r = [view frame];
|
||||
return Rectangle<int> (0, 0, (int) r.size.width, (int) r.size.height);
|
||||
}
|
||||
|
||||
UIView* const view;
|
||||
|
||||
private:
|
||||
Component& owner;
|
||||
ComponentPeer* currentPeer;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
UIViewComponent::UIViewComponent() {}
|
||||
UIViewComponent::~UIViewComponent() {}
|
||||
|
||||
void UIViewComponent::setView (void* const view)
|
||||
{
|
||||
if (view != getView())
|
||||
{
|
||||
pimpl = nullptr;
|
||||
|
||||
if (view != nullptr)
|
||||
pimpl = new Pimpl ((UIView*) view, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void* UIViewComponent::getView() const
|
||||
{
|
||||
return pimpl == nullptr ? nullptr : pimpl->view;
|
||||
}
|
||||
|
||||
void UIViewComponent::resizeToFitView()
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
setBounds (pimpl->getViewBounds());
|
||||
}
|
||||
|
||||
void UIViewComponent::paint (Graphics&) {}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
WebBrowserComponent::WebBrowserComponent (const bool unloadPageWhenBrowserIsHidden_)
|
||||
{
|
||||
}
|
||||
|
||||
WebBrowserComponent::~WebBrowserComponent()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void WebBrowserComponent::goToURL (const String& url,
|
||||
const StringArray* headers,
|
||||
const MemoryBlock* postData)
|
||||
{
|
||||
}
|
||||
|
||||
void WebBrowserComponent::stop()
|
||||
{
|
||||
}
|
||||
|
||||
void WebBrowserComponent::goBack()
|
||||
{
|
||||
}
|
||||
|
||||
void WebBrowserComponent::goForward()
|
||||
{
|
||||
}
|
||||
|
||||
void WebBrowserComponent::refresh()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void WebBrowserComponent::paint (Graphics& g)
|
||||
{
|
||||
}
|
||||
|
||||
void WebBrowserComponent::checkWindowAssociation()
|
||||
{
|
||||
}
|
||||
|
||||
void WebBrowserComponent::reloadLastURL()
|
||||
{
|
||||
}
|
||||
|
||||
void WebBrowserComponent::parentHierarchyChanged()
|
||||
{
|
||||
}
|
||||
|
||||
void WebBrowserComponent::resized()
|
||||
{
|
||||
}
|
||||
|
||||
void WebBrowserComponent::visibilityChanged()
|
||||
{
|
||||
}
|
||||
|
||||
bool WebBrowserComponent::pageAboutToLoad (const String& url)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
124
modules/juce_gui_extra/native/juce_linux_SystemTrayIcon.cpp
Normal file
124
modules/juce_gui_extra/native/juce_linux_SystemTrayIcon.cpp
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
extern Display* display;
|
||||
|
||||
//==============================================================================
|
||||
class SystemTrayIconComponent::Pimpl
|
||||
{
|
||||
public:
|
||||
Pimpl (const Image& image_, Window windowH)
|
||||
: image (image_)
|
||||
{
|
||||
ScopedXLock xlock;
|
||||
|
||||
Screen* const screen = XDefaultScreenOfDisplay (display);
|
||||
const int screenNumber = XScreenNumberOfScreen (screen);
|
||||
|
||||
String screenAtom ("_NET_SYSTEM_TRAY_S");
|
||||
screenAtom << screenNumber;
|
||||
Atom selectionAtom = XInternAtom (display, screenAtom.toUTF8(), false);
|
||||
|
||||
XGrabServer (display);
|
||||
Window managerWin = XGetSelectionOwner (display, selectionAtom);
|
||||
|
||||
if (managerWin != None)
|
||||
XSelectInput (display, managerWin, StructureNotifyMask);
|
||||
|
||||
XUngrabServer (display);
|
||||
XFlush (display);
|
||||
|
||||
if (managerWin != None)
|
||||
{
|
||||
XEvent ev = { 0 };
|
||||
ev.xclient.type = ClientMessage;
|
||||
ev.xclient.window = managerWin;
|
||||
ev.xclient.message_type = XInternAtom (display, "_NET_SYSTEM_TRAY_OPCODE", False);
|
||||
ev.xclient.format = 32;
|
||||
ev.xclient.data.l[0] = CurrentTime;
|
||||
ev.xclient.data.l[1] = 0 /*SYSTEM_TRAY_REQUEST_DOCK*/;
|
||||
ev.xclient.data.l[2] = windowH;
|
||||
ev.xclient.data.l[3] = 0;
|
||||
ev.xclient.data.l[4] = 0;
|
||||
|
||||
XSendEvent (display, managerWin, False, NoEventMask, &ev);
|
||||
XSync (display, False);
|
||||
}
|
||||
|
||||
// For older KDE's ...
|
||||
long atomData = 1;
|
||||
Atom trayAtom = XInternAtom (display, "KWM_DOCKWINDOW", false);
|
||||
XChangeProperty (display, windowH, trayAtom, trayAtom, 32, PropModeReplace, (unsigned char*) &atomData, 1);
|
||||
|
||||
// For more recent KDE's...
|
||||
trayAtom = XInternAtom (display, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", false);
|
||||
XChangeProperty (display, windowH, trayAtom, XA_WINDOW, 32, PropModeReplace, (unsigned char*) &windowH, 1);
|
||||
|
||||
// A minimum size must be specified for GNOME and Xfce, otherwise the icon is displayed with a width of 1
|
||||
XSizeHints* hints = XAllocSizeHints();
|
||||
hints->flags = PMinSize;
|
||||
hints->min_width = 22;
|
||||
hints->min_height = 22;
|
||||
XSetWMNormalHints (display, windowH, hints);
|
||||
XFree (hints);
|
||||
}
|
||||
|
||||
Image image;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl);
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void SystemTrayIconComponent::setIconImage (const Image& newImage)
|
||||
{
|
||||
pimpl = nullptr;
|
||||
|
||||
if (newImage.isValid())
|
||||
{
|
||||
if (! isOnDesktop())
|
||||
addToDesktop (0);
|
||||
|
||||
pimpl = new Pimpl (newImage, (Window) getWindowHandle());
|
||||
|
||||
setVisible (true);
|
||||
toFront (false);
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::paint (Graphics& g)
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
g.drawImageWithin (pimpl->image, 0, 0, getWidth(), getHeight(),
|
||||
RectanglePlacement::xLeft | RectanglePlacement::yTop | RectanglePlacement::onlyReduceInSize, false);
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::setIconTooltip (const String& tooltip)
|
||||
{
|
||||
// xxx not yet implemented!
|
||||
}
|
||||
120
modules/juce_gui_extra/native/juce_linux_WebBrowserComponent.cpp
Normal file
120
modules/juce_gui_extra/native/juce_linux_WebBrowserComponent.cpp
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
Sorry.. This class isn't implemented on Linux!
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
WebBrowserComponent::WebBrowserComponent (const bool unloadPageWhenBrowserIsHidden_)
|
||||
: browser (0),
|
||||
blankPageShown (false),
|
||||
unloadPageWhenBrowserIsHidden (unloadPageWhenBrowserIsHidden_)
|
||||
{
|
||||
setOpaque (true);
|
||||
}
|
||||
|
||||
WebBrowserComponent::~WebBrowserComponent()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void WebBrowserComponent::goToURL (const String& url,
|
||||
const StringArray* headers,
|
||||
const MemoryBlock* postData)
|
||||
{
|
||||
lastURL = url;
|
||||
|
||||
lastHeaders.clear();
|
||||
if (headers != nullptr)
|
||||
lastHeaders = *headers;
|
||||
|
||||
lastPostData.setSize (0);
|
||||
if (postData != nullptr)
|
||||
lastPostData = *postData;
|
||||
|
||||
blankPageShown = false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
void WebBrowserComponent::stop()
|
||||
{
|
||||
}
|
||||
|
||||
void WebBrowserComponent::goBack()
|
||||
{
|
||||
lastURL = String::empty;
|
||||
blankPageShown = false;
|
||||
|
||||
}
|
||||
|
||||
void WebBrowserComponent::goForward()
|
||||
{
|
||||
lastURL = String::empty;
|
||||
|
||||
}
|
||||
|
||||
void WebBrowserComponent::refresh()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void WebBrowserComponent::paint (Graphics& g)
|
||||
{
|
||||
g.fillAll (Colours::white);
|
||||
}
|
||||
|
||||
void WebBrowserComponent::checkWindowAssociation()
|
||||
{
|
||||
}
|
||||
|
||||
void WebBrowserComponent::reloadLastURL()
|
||||
{
|
||||
if (lastURL.isNotEmpty())
|
||||
{
|
||||
goToURL (lastURL, &lastHeaders, &lastPostData);
|
||||
lastURL = String::empty;
|
||||
}
|
||||
}
|
||||
|
||||
void WebBrowserComponent::parentHierarchyChanged()
|
||||
{
|
||||
checkWindowAssociation();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::resized()
|
||||
{
|
||||
}
|
||||
|
||||
void WebBrowserComponent::visibilityChanged()
|
||||
{
|
||||
checkWindowAssociation();
|
||||
}
|
||||
|
||||
bool WebBrowserComponent::pageAboutToLoad (const String& url)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
264
modules/juce_gui_extra/native/juce_mac_AppleRemote.mm
Normal file
264
modules/juce_gui_extra/native/juce_mac_AppleRemote.mm
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
AppleRemoteDevice::AppleRemoteDevice()
|
||||
: device (0),
|
||||
queue (0),
|
||||
remoteId (0)
|
||||
{
|
||||
}
|
||||
|
||||
AppleRemoteDevice::~AppleRemoteDevice()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
io_object_t getAppleRemoteDevice()
|
||||
{
|
||||
CFMutableDictionaryRef dict = IOServiceMatching ("AppleIRController");
|
||||
|
||||
io_iterator_t iter = 0;
|
||||
io_object_t iod = 0;
|
||||
|
||||
if (IOServiceGetMatchingServices (kIOMasterPortDefault, dict, &iter) == kIOReturnSuccess
|
||||
&& iter != 0)
|
||||
{
|
||||
iod = IOIteratorNext (iter);
|
||||
}
|
||||
|
||||
IOObjectRelease (iter);
|
||||
return iod;
|
||||
}
|
||||
|
||||
bool createAppleRemoteInterface (io_object_t iod, void** device)
|
||||
{
|
||||
jassert (*device == nullptr);
|
||||
io_name_t classname;
|
||||
|
||||
if (IOObjectGetClass (iod, classname) == kIOReturnSuccess)
|
||||
{
|
||||
IOCFPlugInInterface** cfPlugInInterface = nullptr;
|
||||
SInt32 score = 0;
|
||||
|
||||
if (IOCreatePlugInInterfaceForService (iod,
|
||||
kIOHIDDeviceUserClientTypeID,
|
||||
kIOCFPlugInInterfaceID,
|
||||
&cfPlugInInterface,
|
||||
&score) == kIOReturnSuccess)
|
||||
{
|
||||
HRESULT hr = (*cfPlugInInterface)->QueryInterface (cfPlugInInterface,
|
||||
CFUUIDGetUUIDBytes (kIOHIDDeviceInterfaceID),
|
||||
device);
|
||||
|
||||
(void) hr;
|
||||
|
||||
(*cfPlugInInterface)->Release (cfPlugInInterface);
|
||||
}
|
||||
}
|
||||
|
||||
return *device != 0;
|
||||
}
|
||||
|
||||
void appleRemoteQueueCallback (void* const target, const IOReturn result, void*, void*)
|
||||
{
|
||||
if (result == kIOReturnSuccess)
|
||||
((AppleRemoteDevice*) target)->handleCallbackInternal();
|
||||
}
|
||||
}
|
||||
|
||||
bool AppleRemoteDevice::start (const bool inExclusiveMode)
|
||||
{
|
||||
if (queue != 0)
|
||||
return true;
|
||||
|
||||
stop();
|
||||
|
||||
bool result = false;
|
||||
io_object_t iod = getAppleRemoteDevice();
|
||||
|
||||
if (iod != 0)
|
||||
{
|
||||
if (createAppleRemoteInterface (iod, &device) && open (inExclusiveMode))
|
||||
result = true;
|
||||
else
|
||||
stop();
|
||||
|
||||
IOObjectRelease (iod);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void AppleRemoteDevice::stop()
|
||||
{
|
||||
if (queue != 0)
|
||||
{
|
||||
(*(IOHIDQueueInterface**) queue)->stop ((IOHIDQueueInterface**) queue);
|
||||
(*(IOHIDQueueInterface**) queue)->dispose ((IOHIDQueueInterface**) queue);
|
||||
(*(IOHIDQueueInterface**) queue)->Release ((IOHIDQueueInterface**) queue);
|
||||
queue = 0;
|
||||
}
|
||||
|
||||
if (device != 0)
|
||||
{
|
||||
(*(IOHIDDeviceInterface**) device)->close ((IOHIDDeviceInterface**) device);
|
||||
(*(IOHIDDeviceInterface**) device)->Release ((IOHIDDeviceInterface**) device);
|
||||
device = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool AppleRemoteDevice::isActive() const
|
||||
{
|
||||
return queue != 0;
|
||||
}
|
||||
|
||||
bool AppleRemoteDevice::open (const bool openInExclusiveMode)
|
||||
{
|
||||
Array <int> cookies;
|
||||
|
||||
CFArrayRef elements;
|
||||
IOHIDDeviceInterface122** const device122 = (IOHIDDeviceInterface122**) device;
|
||||
|
||||
if ((*device122)->copyMatchingElements (device122, 0, &elements) != kIOReturnSuccess)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < CFArrayGetCount (elements); ++i)
|
||||
{
|
||||
CFDictionaryRef element = (CFDictionaryRef) CFArrayGetValueAtIndex (elements, i);
|
||||
|
||||
// get the cookie
|
||||
CFTypeRef object = CFDictionaryGetValue (element, CFSTR (kIOHIDElementCookieKey));
|
||||
|
||||
if (object == 0 || CFGetTypeID (object) != CFNumberGetTypeID())
|
||||
continue;
|
||||
|
||||
long number;
|
||||
if (! CFNumberGetValue ((CFNumberRef) object, kCFNumberLongType, &number))
|
||||
continue;
|
||||
|
||||
cookies.add ((int) number);
|
||||
}
|
||||
|
||||
CFRelease (elements);
|
||||
|
||||
if ((*(IOHIDDeviceInterface**) device)
|
||||
->open ((IOHIDDeviceInterface**) device,
|
||||
openInExclusiveMode ? kIOHIDOptionsTypeSeizeDevice
|
||||
: kIOHIDOptionsTypeNone) == KERN_SUCCESS)
|
||||
{
|
||||
queue = (*(IOHIDDeviceInterface**) device)->allocQueue ((IOHIDDeviceInterface**) device);
|
||||
|
||||
if (queue != 0)
|
||||
{
|
||||
(*(IOHIDQueueInterface**) queue)->create ((IOHIDQueueInterface**) queue, 0, 12);
|
||||
|
||||
for (int i = 0; i < cookies.size(); ++i)
|
||||
{
|
||||
IOHIDElementCookie cookie = (IOHIDElementCookie) cookies.getUnchecked(i);
|
||||
(*(IOHIDQueueInterface**) queue)->addElement ((IOHIDQueueInterface**) queue, cookie, 0);
|
||||
}
|
||||
|
||||
CFRunLoopSourceRef eventSource;
|
||||
|
||||
if ((*(IOHIDQueueInterface**) queue)
|
||||
->createAsyncEventSource ((IOHIDQueueInterface**) queue, &eventSource) == KERN_SUCCESS)
|
||||
{
|
||||
if ((*(IOHIDQueueInterface**) queue)->setEventCallout ((IOHIDQueueInterface**) queue,
|
||||
appleRemoteQueueCallback, this, 0) == KERN_SUCCESS)
|
||||
{
|
||||
CFRunLoopAddSource (CFRunLoopGetCurrent(), eventSource, kCFRunLoopDefaultMode);
|
||||
|
||||
(*(IOHIDQueueInterface**) queue)->start ((IOHIDQueueInterface**) queue);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AppleRemoteDevice::handleCallbackInternal()
|
||||
{
|
||||
int totalValues = 0;
|
||||
AbsoluteTime nullTime = { 0, 0 };
|
||||
char cookies [12];
|
||||
int numCookies = 0;
|
||||
|
||||
while (numCookies < numElementsInArray (cookies))
|
||||
{
|
||||
IOHIDEventStruct e;
|
||||
|
||||
if ((*(IOHIDQueueInterface**) queue)->getNextEvent ((IOHIDQueueInterface**) queue, &e, nullTime, 0) != kIOReturnSuccess)
|
||||
break;
|
||||
|
||||
if ((int) e.elementCookie == 19)
|
||||
{
|
||||
remoteId = e.value;
|
||||
buttonPressed (switched, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
totalValues += e.value;
|
||||
cookies [numCookies++] = (char) (pointer_sized_int) e.elementCookie;
|
||||
}
|
||||
}
|
||||
|
||||
cookies [numCookies++] = 0;
|
||||
//DBG (String::toHexString ((uint8*) cookies, numCookies, 1) + " " + String (totalValues));
|
||||
|
||||
static const char buttonPatterns[] =
|
||||
{
|
||||
0x1f, 0x14, 0x12, 0x1f, 0x14, 0x12, 0,
|
||||
0x1f, 0x15, 0x12, 0x1f, 0x15, 0x12, 0,
|
||||
0x1f, 0x1d, 0x1c, 0x12, 0,
|
||||
0x1f, 0x1e, 0x1c, 0x12, 0,
|
||||
0x1f, 0x16, 0x12, 0x1f, 0x16, 0x12, 0,
|
||||
0x1f, 0x17, 0x12, 0x1f, 0x17, 0x12, 0,
|
||||
0x1f, 0x12, 0x04, 0x02, 0,
|
||||
0x1f, 0x12, 0x03, 0x02, 0,
|
||||
0x1f, 0x12, 0x1f, 0x12, 0,
|
||||
0x23, 0x1f, 0x12, 0x23, 0x1f, 0x12, 0,
|
||||
19, 0
|
||||
};
|
||||
|
||||
int buttonNum = (int) menuButton;
|
||||
int i = 0;
|
||||
|
||||
while (i < numElementsInArray (buttonPatterns))
|
||||
{
|
||||
if (strcmp (cookies, buttonPatterns + i) == 0)
|
||||
{
|
||||
buttonPressed ((ButtonType) buttonNum, totalValues > 0);
|
||||
break;
|
||||
}
|
||||
|
||||
i += (int) strlen (buttonPatterns + i) + 1;
|
||||
++buttonNum;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_JUCEHEADER__
|
||||
#define __JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Creates a floating carbon window that can be used to hold a carbon UI.
|
||||
|
||||
This is a handy class that's designed to be inlined where needed, e.g.
|
||||
in the audio plugin hosting code.
|
||||
*/
|
||||
class CarbonViewWrapperComponent : public Component,
|
||||
public ComponentMovementWatcher,
|
||||
public Timer
|
||||
{
|
||||
public:
|
||||
CarbonViewWrapperComponent()
|
||||
: ComponentMovementWatcher (this),
|
||||
wrapperWindow (0),
|
||||
carbonWindow (0),
|
||||
embeddedView (0),
|
||||
recursiveResize (false)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~CarbonViewWrapperComponent()
|
||||
{
|
||||
jassert (embeddedView == 0); // must call deleteWindow() in the subclass's destructor!
|
||||
}
|
||||
|
||||
virtual HIViewRef attachView (WindowRef windowRef, HIViewRef rootView) = 0;
|
||||
virtual void removeView (HIViewRef embeddedView) = 0;
|
||||
virtual void mouseDown (int, int) {}
|
||||
virtual void paint() {}
|
||||
|
||||
virtual bool getEmbeddedViewSize (int& w, int& h)
|
||||
{
|
||||
if (embeddedView == 0)
|
||||
return false;
|
||||
|
||||
HIRect bounds;
|
||||
HIViewGetBounds (embeddedView, &bounds);
|
||||
w = jmax (1, roundToInt (bounds.size.width));
|
||||
h = jmax (1, roundToInt (bounds.size.height));
|
||||
return true;
|
||||
}
|
||||
|
||||
void createWindow()
|
||||
{
|
||||
if (wrapperWindow == 0)
|
||||
{
|
||||
Rect r;
|
||||
r.left = getScreenX();
|
||||
r.top = getScreenY();
|
||||
r.right = r.left + getWidth();
|
||||
r.bottom = r.top + getHeight();
|
||||
|
||||
CreateNewWindow (kDocumentWindowClass,
|
||||
(WindowAttributes) (kWindowStandardHandlerAttribute | kWindowCompositingAttribute
|
||||
| kWindowNoShadowAttribute | kWindowNoTitleBarAttribute),
|
||||
&r, &wrapperWindow);
|
||||
|
||||
jassert (wrapperWindow != 0);
|
||||
if (wrapperWindow == 0)
|
||||
return;
|
||||
|
||||
carbonWindow = [[NSWindow alloc] initWithWindowRef: wrapperWindow];
|
||||
NSWindow* ownerWindow = [((NSView*) getWindowHandle()) window];
|
||||
|
||||
[ownerWindow addChildWindow: carbonWindow
|
||||
ordered: NSWindowAbove];
|
||||
|
||||
embeddedView = attachView (wrapperWindow, HIViewGetRoot (wrapperWindow));
|
||||
|
||||
EventTypeSpec windowEventTypes[] =
|
||||
{
|
||||
{ kEventClassWindow, kEventWindowGetClickActivation },
|
||||
{ kEventClassWindow, kEventWindowHandleDeactivate },
|
||||
{ kEventClassWindow, kEventWindowBoundsChanging },
|
||||
{ kEventClassMouse, kEventMouseDown },
|
||||
{ kEventClassMouse, kEventMouseMoved },
|
||||
{ kEventClassMouse, kEventMouseDragged },
|
||||
{ kEventClassMouse, kEventMouseUp},
|
||||
{ kEventClassWindow, kEventWindowDrawContent },
|
||||
{ kEventClassWindow, kEventWindowShown },
|
||||
{ kEventClassWindow, kEventWindowHidden }
|
||||
};
|
||||
|
||||
EventHandlerUPP upp = NewEventHandlerUPP (carbonEventCallback);
|
||||
InstallWindowEventHandler (wrapperWindow, upp,
|
||||
sizeof (windowEventTypes) / sizeof (EventTypeSpec),
|
||||
windowEventTypes, this, &eventHandlerRef);
|
||||
|
||||
setOurSizeToEmbeddedViewSize();
|
||||
setEmbeddedWindowToOurSize();
|
||||
|
||||
creationTime = Time::getCurrentTime();
|
||||
}
|
||||
}
|
||||
|
||||
void deleteWindow()
|
||||
{
|
||||
removeView (embeddedView);
|
||||
embeddedView = 0;
|
||||
|
||||
if (wrapperWindow != 0)
|
||||
{
|
||||
RemoveEventHandler (eventHandlerRef);
|
||||
DisposeWindow (wrapperWindow);
|
||||
wrapperWindow = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void setOurSizeToEmbeddedViewSize()
|
||||
{
|
||||
int w, h;
|
||||
if (getEmbeddedViewSize (w, h))
|
||||
{
|
||||
if (w != getWidth() || h != getHeight())
|
||||
{
|
||||
startTimer (50);
|
||||
|
||||
setSize (w, h);
|
||||
if (getParentComponent() != nullptr)
|
||||
getParentComponent()->setSize (w, h);
|
||||
}
|
||||
else
|
||||
{
|
||||
startTimer (jlimit (50, 500, getTimerInterval() + 20));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stopTimer();
|
||||
}
|
||||
}
|
||||
|
||||
void setEmbeddedWindowToOurSize()
|
||||
{
|
||||
if (! recursiveResize)
|
||||
{
|
||||
recursiveResize = true;
|
||||
|
||||
if (embeddedView != 0)
|
||||
{
|
||||
HIRect r;
|
||||
r.origin.x = 0;
|
||||
r.origin.y = 0;
|
||||
r.size.width = (float) getWidth();
|
||||
r.size.height = (float) getHeight();
|
||||
HIViewSetFrame (embeddedView, &r);
|
||||
}
|
||||
|
||||
if (wrapperWindow != 0)
|
||||
{
|
||||
Rect wr;
|
||||
wr.left = getScreenX();
|
||||
wr.top = getScreenY();
|
||||
wr.right = wr.left + getWidth();
|
||||
wr.bottom = wr.top + getHeight();
|
||||
|
||||
SetWindowBounds (wrapperWindow, kWindowContentRgn, &wr);
|
||||
ShowWindow (wrapperWindow);
|
||||
}
|
||||
|
||||
recursiveResize = false;
|
||||
}
|
||||
}
|
||||
|
||||
void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/)
|
||||
{
|
||||
setEmbeddedWindowToOurSize();
|
||||
}
|
||||
|
||||
void componentPeerChanged()
|
||||
{
|
||||
deleteWindow();
|
||||
createWindow();
|
||||
}
|
||||
|
||||
void componentVisibilityChanged()
|
||||
{
|
||||
if (isShowing())
|
||||
createWindow();
|
||||
else
|
||||
deleteWindow();
|
||||
|
||||
setEmbeddedWindowToOurSize();
|
||||
}
|
||||
|
||||
static void recursiveHIViewRepaint (HIViewRef view)
|
||||
{
|
||||
HIViewSetNeedsDisplay (view, true);
|
||||
HIViewRef child = HIViewGetFirstSubview (view);
|
||||
|
||||
while (child != 0)
|
||||
{
|
||||
recursiveHIViewRepaint (child);
|
||||
child = HIViewGetNextView (child);
|
||||
}
|
||||
}
|
||||
|
||||
void timerCallback()
|
||||
{
|
||||
setOurSizeToEmbeddedViewSize();
|
||||
|
||||
// To avoid strange overpainting problems when the UI is first opened, we'll
|
||||
// repaint it a few times during the first second that it's on-screen..
|
||||
if ((Time::getCurrentTime() - creationTime).inMilliseconds() < 1000)
|
||||
recursiveHIViewRepaint (HIViewGetRoot (wrapperWindow));
|
||||
}
|
||||
|
||||
OSStatus carbonEventHandler (EventHandlerCallRef /*nextHandlerRef*/, EventRef event)
|
||||
{
|
||||
switch (GetEventKind (event))
|
||||
{
|
||||
case kEventWindowHandleDeactivate:
|
||||
ActivateWindow (wrapperWindow, TRUE);
|
||||
return noErr;
|
||||
|
||||
case kEventWindowGetClickActivation:
|
||||
{
|
||||
getTopLevelComponent()->toFront (false);
|
||||
[carbonWindow makeKeyAndOrderFront: nil];
|
||||
|
||||
ClickActivationResult howToHandleClick = kActivateAndHandleClick;
|
||||
|
||||
SetEventParameter (event, kEventParamClickActivation, typeClickActivationResult,
|
||||
sizeof (ClickActivationResult), &howToHandleClick);
|
||||
|
||||
HIViewSetNeedsDisplay (embeddedView, true);
|
||||
return noErr;
|
||||
}
|
||||
}
|
||||
|
||||
return eventNotHandledErr;
|
||||
}
|
||||
|
||||
static pascal OSStatus carbonEventCallback (EventHandlerCallRef nextHandlerRef, EventRef event, void* userData)
|
||||
{
|
||||
return ((CarbonViewWrapperComponent*) userData)->carbonEventHandler (nextHandlerRef, event);
|
||||
}
|
||||
|
||||
protected:
|
||||
WindowRef wrapperWindow;
|
||||
NSWindow* carbonWindow;
|
||||
HIViewRef embeddedView;
|
||||
bool recursiveResize;
|
||||
Time creationTime;
|
||||
|
||||
EventHandlerRef eventHandlerRef;
|
||||
};
|
||||
|
||||
#endif // __JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_JUCEHEADER__
|
||||
143
modules/juce_gui_extra/native/juce_mac_NSViewComponent.mm
Normal file
143
modules/juce_gui_extra/native/juce_mac_NSViewComponent.mm
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
class NSViewComponent::Pimpl : public ComponentMovementWatcher
|
||||
{
|
||||
public:
|
||||
Pimpl (NSView* const view_, Component& owner_)
|
||||
: ComponentMovementWatcher (&owner_),
|
||||
view (view_),
|
||||
owner (owner_),
|
||||
currentPeer (nullptr)
|
||||
{
|
||||
[view_ retain];
|
||||
|
||||
if (owner.isShowing())
|
||||
componentPeerChanged();
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
[view removeFromSuperview];
|
||||
[view release];
|
||||
}
|
||||
|
||||
void componentMovedOrResized (Component& comp, bool wasMoved, bool wasResized)
|
||||
{
|
||||
ComponentMovementWatcher::componentMovedOrResized (comp, wasMoved, wasResized);
|
||||
|
||||
// The ComponentMovementWatcher version of this method avoids calling
|
||||
// us when the top-level comp is resized, but for an NSView we need to know this
|
||||
// because with inverted co-ords, we need to update the position even if the
|
||||
// top-left pos hasn't changed
|
||||
if (comp.isOnDesktop() && wasResized)
|
||||
componentMovedOrResized (wasMoved, wasResized);
|
||||
}
|
||||
|
||||
void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/)
|
||||
{
|
||||
Component* const topComp = owner.getTopLevelComponent();
|
||||
|
||||
if (topComp->getPeer() != nullptr)
|
||||
{
|
||||
const Point<int> pos (topComp->getLocalPoint (&owner, Point<int>()));
|
||||
|
||||
NSRect r = NSMakeRect ((float) pos.getX(), (float) pos.getY(), (float) owner.getWidth(), (float) owner.getHeight());
|
||||
r.origin.y = [[view superview] frame].size.height - (r.origin.y + r.size.height);
|
||||
|
||||
[view setFrame: r];
|
||||
}
|
||||
}
|
||||
|
||||
void componentPeerChanged()
|
||||
{
|
||||
ComponentPeer* const peer = owner.getPeer();
|
||||
|
||||
if (currentPeer != peer)
|
||||
{
|
||||
if ([view superview] != nil)
|
||||
[view removeFromSuperview]; // Must be careful not to call this unless it's required - e.g. some Apple AU views
|
||||
// override the call and use it as a sign that they're being deleted, which breaks everything..
|
||||
|
||||
currentPeer = peer;
|
||||
|
||||
if (peer != nullptr)
|
||||
{
|
||||
NSView* peerView = (NSView*) peer->getNativeHandle();
|
||||
[peerView addSubview: view];
|
||||
componentMovedOrResized (false, false);
|
||||
}
|
||||
}
|
||||
|
||||
[view setHidden: ! owner.isShowing()];
|
||||
}
|
||||
|
||||
void componentVisibilityChanged()
|
||||
{
|
||||
componentPeerChanged();
|
||||
}
|
||||
|
||||
Rectangle<int> getViewBounds() const
|
||||
{
|
||||
NSRect r = [view frame];
|
||||
return Rectangle<int> (0, 0, (int) r.size.width, (int) r.size.height);
|
||||
}
|
||||
|
||||
NSView* const view;
|
||||
|
||||
private:
|
||||
Component& owner;
|
||||
ComponentPeer* currentPeer;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
NSViewComponent::NSViewComponent() {}
|
||||
NSViewComponent::~NSViewComponent() {}
|
||||
|
||||
void NSViewComponent::setView (void* const view)
|
||||
{
|
||||
if (view != getView())
|
||||
{
|
||||
pimpl = nullptr;
|
||||
|
||||
if (view != nullptr)
|
||||
pimpl = new Pimpl ((NSView*) view, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void* NSViewComponent::getView() const
|
||||
{
|
||||
return pimpl == nullptr ? nullptr : pimpl->view;
|
||||
}
|
||||
|
||||
void NSViewComponent::resizeToFitView()
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
setBounds (pimpl->getViewBounds());
|
||||
}
|
||||
|
||||
void NSViewComponent::paint (Graphics&) {}
|
||||
273
modules/juce_gui_extra/native/juce_mac_WebBrowserComponent.mm
Normal file
273
modules/juce_gui_extra/native/juce_mac_WebBrowserComponent.mm
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
|
||||
#define DownloadClickDetector MakeObjCClassName(DownloadClickDetector)
|
||||
|
||||
@interface DownloadClickDetector : NSObject
|
||||
{
|
||||
JUCE_NAMESPACE::WebBrowserComponent* ownerComponent;
|
||||
}
|
||||
|
||||
- (DownloadClickDetector*) initWithWebBrowserOwner: (JUCE_NAMESPACE::WebBrowserComponent*) ownerComponent;
|
||||
|
||||
- (void) webView: (WebView*) webView decidePolicyForNavigationAction: (NSDictionary*) actionInformation
|
||||
request: (NSURLRequest*) request
|
||||
frame: (WebFrame*) frame
|
||||
decisionListener: (id<WebPolicyDecisionListener>) listener;
|
||||
@end
|
||||
|
||||
//==============================================================================
|
||||
@implementation DownloadClickDetector
|
||||
|
||||
- (DownloadClickDetector*) initWithWebBrowserOwner: (JUCE_NAMESPACE::WebBrowserComponent*) ownerComponent_
|
||||
{
|
||||
[super init];
|
||||
ownerComponent = ownerComponent_;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) webView: (WebView*) sender decidePolicyForNavigationAction: (NSDictionary*) actionInformation
|
||||
request: (NSURLRequest*) request
|
||||
frame: (WebFrame*) frame
|
||||
decisionListener: (id <WebPolicyDecisionListener>) listener
|
||||
{
|
||||
(void) sender;
|
||||
(void) request;
|
||||
(void) frame;
|
||||
|
||||
NSURL* url = [actionInformation valueForKey: nsStringLiteral ("WebActionOriginalURLKey")];
|
||||
|
||||
if (ownerComponent->pageAboutToLoad (nsStringToJuce ([url absoluteString])))
|
||||
[listener use];
|
||||
else
|
||||
[listener ignore];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
class WebBrowserComponentInternal : public NSViewComponent
|
||||
{
|
||||
public:
|
||||
WebBrowserComponentInternal (WebBrowserComponent* owner)
|
||||
{
|
||||
webView = [[WebView alloc] initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f)
|
||||
frameName: nsEmptyString()
|
||||
groupName: nsEmptyString()];
|
||||
setView (webView);
|
||||
|
||||
clickListener = [[DownloadClickDetector alloc] initWithWebBrowserOwner: owner];
|
||||
[webView setPolicyDelegate: clickListener];
|
||||
}
|
||||
|
||||
~WebBrowserComponentInternal()
|
||||
{
|
||||
[webView setPolicyDelegate: nil];
|
||||
[clickListener release];
|
||||
setView (nil);
|
||||
}
|
||||
|
||||
void goToURL (const String& url,
|
||||
const StringArray* headers,
|
||||
const MemoryBlock* postData)
|
||||
{
|
||||
NSMutableURLRequest* r
|
||||
= [NSMutableURLRequest requestWithURL: [NSURL URLWithString: juceStringToNS (url)]
|
||||
cachePolicy: NSURLRequestUseProtocolCachePolicy
|
||||
timeoutInterval: 30.0];
|
||||
|
||||
if (postData != nullptr && postData->getSize() > 0)
|
||||
{
|
||||
[r setHTTPMethod: nsStringLiteral ("POST")];
|
||||
[r setHTTPBody: [NSData dataWithBytes: postData->getData()
|
||||
length: postData->getSize()]];
|
||||
}
|
||||
|
||||
if (headers != nullptr)
|
||||
{
|
||||
for (int i = 0; i < headers->size(); ++i)
|
||||
{
|
||||
const String headerName ((*headers)[i].upToFirstOccurrenceOf (":", false, false).trim());
|
||||
const String headerValue ((*headers)[i].fromFirstOccurrenceOf (":", false, false).trim());
|
||||
|
||||
[r setValue: juceStringToNS (headerValue)
|
||||
forHTTPHeaderField: juceStringToNS (headerName)];
|
||||
}
|
||||
}
|
||||
|
||||
stop();
|
||||
[[webView mainFrame] loadRequest: r];
|
||||
}
|
||||
|
||||
void goBack()
|
||||
{
|
||||
[webView goBack];
|
||||
}
|
||||
|
||||
void goForward()
|
||||
{
|
||||
[webView goForward];
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
[webView stopLoading: nil];
|
||||
}
|
||||
|
||||
void refresh()
|
||||
{
|
||||
[webView reload: nil];
|
||||
}
|
||||
|
||||
void mouseMove (const MouseEvent&)
|
||||
{
|
||||
// WebKit doesn't capture mouse-moves itself, so it seems the only way to make
|
||||
// them work is to push them via this non-public method..
|
||||
if ([webView respondsToSelector: @selector (_updateMouseoverWithFakeEvent)])
|
||||
[webView performSelector: @selector (_updateMouseoverWithFakeEvent)];
|
||||
}
|
||||
|
||||
private:
|
||||
WebView* webView;
|
||||
DownloadClickDetector* clickListener;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
WebBrowserComponent::WebBrowserComponent (const bool unloadPageWhenBrowserIsHidden_)
|
||||
: browser (nullptr),
|
||||
blankPageShown (false),
|
||||
unloadPageWhenBrowserIsHidden (unloadPageWhenBrowserIsHidden_)
|
||||
{
|
||||
setOpaque (true);
|
||||
|
||||
addAndMakeVisible (browser = new WebBrowserComponentInternal (this));
|
||||
}
|
||||
|
||||
WebBrowserComponent::~WebBrowserComponent()
|
||||
{
|
||||
deleteAndZero (browser);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void WebBrowserComponent::goToURL (const String& url,
|
||||
const StringArray* headers,
|
||||
const MemoryBlock* postData)
|
||||
{
|
||||
lastURL = url;
|
||||
|
||||
lastHeaders.clear();
|
||||
if (headers != nullptr)
|
||||
lastHeaders = *headers;
|
||||
|
||||
lastPostData.setSize (0);
|
||||
if (postData != nullptr)
|
||||
lastPostData = *postData;
|
||||
|
||||
blankPageShown = false;
|
||||
|
||||
browser->goToURL (url, headers, postData);
|
||||
}
|
||||
|
||||
void WebBrowserComponent::stop()
|
||||
{
|
||||
browser->stop();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::goBack()
|
||||
{
|
||||
lastURL = String::empty;
|
||||
blankPageShown = false;
|
||||
browser->goBack();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::goForward()
|
||||
{
|
||||
lastURL = String::empty;
|
||||
browser->goForward();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::refresh()
|
||||
{
|
||||
browser->refresh();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void WebBrowserComponent::paint (Graphics&)
|
||||
{
|
||||
}
|
||||
|
||||
void WebBrowserComponent::checkWindowAssociation()
|
||||
{
|
||||
if (isShowing())
|
||||
{
|
||||
if (blankPageShown)
|
||||
goBack();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (unloadPageWhenBrowserIsHidden && ! blankPageShown)
|
||||
{
|
||||
// when the component becomes invisible, some stuff like flash
|
||||
// carries on playing audio, so we need to force it onto a blank
|
||||
// page to avoid this, (and send it back when it's made visible again).
|
||||
|
||||
blankPageShown = true;
|
||||
browser->goToURL ("about:blank", 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebBrowserComponent::reloadLastURL()
|
||||
{
|
||||
if (lastURL.isNotEmpty())
|
||||
{
|
||||
goToURL (lastURL, &lastHeaders, &lastPostData);
|
||||
lastURL = String::empty;
|
||||
}
|
||||
}
|
||||
|
||||
void WebBrowserComponent::parentHierarchyChanged()
|
||||
{
|
||||
checkWindowAssociation();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::resized()
|
||||
{
|
||||
browser->setSize (getWidth(), getHeight());
|
||||
}
|
||||
|
||||
void WebBrowserComponent::visibilityChanged()
|
||||
{
|
||||
checkWindowAssociation();
|
||||
}
|
||||
|
||||
bool WebBrowserComponent::pageAboutToLoad (const String&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
414
modules/juce_gui_extra/native/juce_win32_ActiveXComponent.cpp
Normal file
414
modules/juce_gui_extra/native/juce_win32_ActiveXComponent.cpp
Normal file
|
|
@ -0,0 +1,414 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
extern int64 getMouseEventTime();
|
||||
|
||||
namespace ActiveXHelpers
|
||||
{
|
||||
//==============================================================================
|
||||
class JuceIStorage : public ComBaseClassHelper <IStorage>
|
||||
{
|
||||
public:
|
||||
JuceIStorage() {}
|
||||
|
||||
JUCE_COMRESULT CreateStream (const WCHAR*, DWORD, DWORD, DWORD, IStream**) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT OpenStream (const WCHAR*, void*, DWORD, DWORD, IStream**) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT CreateStorage (const WCHAR*, DWORD, DWORD, DWORD, IStorage**) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT OpenStorage (const WCHAR*, IStorage*, DWORD, SNB, DWORD, IStorage**) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT CopyTo (DWORD, IID const*, SNB, IStorage*) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT MoveElementTo (const OLECHAR*,IStorage*, const OLECHAR*, DWORD) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT Commit (DWORD) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT Revert() { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT EnumElements (DWORD, void*, DWORD, IEnumSTATSTG**) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT DestroyElement (const OLECHAR*) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT RenameElement (const WCHAR*, const WCHAR*) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT SetElementTimes (const WCHAR*, FILETIME const*, FILETIME const*, FILETIME const*) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT SetClass (REFCLSID) { return S_OK; }
|
||||
JUCE_COMRESULT SetStateBits (DWORD, DWORD) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT Stat (STATSTG*, DWORD) { return E_NOTIMPL; }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class JuceOleInPlaceFrame : public ComBaseClassHelper <IOleInPlaceFrame>
|
||||
{
|
||||
public:
|
||||
JuceOleInPlaceFrame (HWND window_) : window (window_) {}
|
||||
|
||||
JUCE_COMRESULT GetWindow (HWND* lphwnd) { *lphwnd = window; return S_OK; }
|
||||
JUCE_COMRESULT ContextSensitiveHelp (BOOL) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT GetBorder (LPRECT) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT RequestBorderSpace (LPCBORDERWIDTHS) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT SetBorderSpace (LPCBORDERWIDTHS) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT SetActiveObject (IOleInPlaceActiveObject*, LPCOLESTR) { return S_OK; }
|
||||
JUCE_COMRESULT InsertMenus (HMENU, LPOLEMENUGROUPWIDTHS) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT SetMenu (HMENU, HOLEMENU, HWND) { return S_OK; }
|
||||
JUCE_COMRESULT RemoveMenus (HMENU) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT SetStatusText (LPCOLESTR) { return S_OK; }
|
||||
JUCE_COMRESULT EnableModeless (BOOL) { return S_OK; }
|
||||
JUCE_COMRESULT TranslateAccelerator (LPMSG, WORD) { return E_NOTIMPL; }
|
||||
|
||||
private:
|
||||
HWND window;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class JuceIOleInPlaceSite : public ComBaseClassHelper <IOleInPlaceSite>
|
||||
{
|
||||
public:
|
||||
JuceIOleInPlaceSite (HWND window_)
|
||||
: window (window_),
|
||||
frame (new JuceOleInPlaceFrame (window))
|
||||
{}
|
||||
|
||||
~JuceIOleInPlaceSite()
|
||||
{
|
||||
frame->Release();
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetWindow (HWND* lphwnd) { *lphwnd = window; return S_OK; }
|
||||
JUCE_COMRESULT ContextSensitiveHelp (BOOL) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT CanInPlaceActivate() { return S_OK; }
|
||||
JUCE_COMRESULT OnInPlaceActivate() { return S_OK; }
|
||||
JUCE_COMRESULT OnUIActivate() { return S_OK; }
|
||||
|
||||
JUCE_COMRESULT GetWindowContext (LPOLEINPLACEFRAME* lplpFrame, LPOLEINPLACEUIWINDOW* lplpDoc, LPRECT, LPRECT, LPOLEINPLACEFRAMEINFO lpFrameInfo)
|
||||
{
|
||||
/* Note: if you call AddRef on the frame here, then some types of object (e.g. web browser control) cause leaks..
|
||||
If you don't call AddRef then others crash (e.g. QuickTime).. Bit of a catch-22, so letting it leak is probably preferable.
|
||||
*/
|
||||
if (lplpFrame != nullptr) { frame->AddRef(); *lplpFrame = frame; }
|
||||
if (lplpDoc != nullptr) *lplpDoc = 0;
|
||||
lpFrameInfo->fMDIApp = FALSE;
|
||||
lpFrameInfo->hwndFrame = window;
|
||||
lpFrameInfo->haccel = 0;
|
||||
lpFrameInfo->cAccelEntries = 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT Scroll (SIZE) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT OnUIDeactivate (BOOL) { return S_OK; }
|
||||
JUCE_COMRESULT OnInPlaceDeactivate() { return S_OK; }
|
||||
JUCE_COMRESULT DiscardUndoState() { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT DeactivateAndUndo() { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT OnPosRectChange (LPCRECT) { return S_OK; }
|
||||
|
||||
private:
|
||||
HWND window;
|
||||
JuceOleInPlaceFrame* frame;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class JuceIOleClientSite : public ComBaseClassHelper <IOleClientSite>
|
||||
{
|
||||
public:
|
||||
JuceIOleClientSite (HWND window)
|
||||
: inplaceSite (new JuceIOleInPlaceSite (window))
|
||||
{}
|
||||
|
||||
~JuceIOleClientSite()
|
||||
{
|
||||
inplaceSite->Release();
|
||||
}
|
||||
|
||||
JUCE_COMRESULT QueryInterface (REFIID type, void** result)
|
||||
{
|
||||
if (type == IID_IOleInPlaceSite)
|
||||
{
|
||||
inplaceSite->AddRef();
|
||||
*result = static_cast <IOleInPlaceSite*> (inplaceSite);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return ComBaseClassHelper <IOleClientSite>::QueryInterface (type, result);
|
||||
}
|
||||
|
||||
JUCE_COMRESULT SaveObject() { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT GetMoniker (DWORD, DWORD, IMoniker**) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT GetContainer (LPOLECONTAINER* ppContainer) { *ppContainer = 0; return E_NOINTERFACE; }
|
||||
JUCE_COMRESULT ShowObject() { return S_OK; }
|
||||
JUCE_COMRESULT OnShowWindow (BOOL) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT RequestNewObjectLayout() { return E_NOTIMPL; }
|
||||
|
||||
private:
|
||||
JuceIOleInPlaceSite* inplaceSite;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
static Array<ActiveXControlComponent*> activeXComps;
|
||||
|
||||
HWND getHWND (const ActiveXControlComponent* const component)
|
||||
{
|
||||
HWND hwnd = 0;
|
||||
|
||||
const IID iid = IID_IOleWindow;
|
||||
IOleWindow* const window = (IOleWindow*) component->queryInterface (&iid);
|
||||
|
||||
if (window != nullptr)
|
||||
{
|
||||
window->GetWindow (&hwnd);
|
||||
window->Release();
|
||||
}
|
||||
|
||||
return hwnd;
|
||||
}
|
||||
|
||||
void offerActiveXMouseEventToPeer (ComponentPeer* const peer, HWND hwnd, UINT message, LPARAM lParam)
|
||||
{
|
||||
RECT activeXRect, peerRect;
|
||||
GetWindowRect (hwnd, &activeXRect);
|
||||
GetWindowRect ((HWND) peer->getNativeHandle(), &peerRect);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case WM_MOUSEMOVE:
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_LBUTTONUP:
|
||||
case WM_MBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
peer->handleMouseEvent (0, Point<int> (GET_X_LPARAM (lParam) + activeXRect.left - peerRect.left,
|
||||
GET_Y_LPARAM (lParam) + activeXRect.top - peerRect.top),
|
||||
ModifierKeys::getCurrentModifiersRealtime(),
|
||||
getMouseEventTime());
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ActiveXControlComponent::Pimpl : public ComponentMovementWatcher
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
Pimpl (HWND hwnd, ActiveXControlComponent& owner_)
|
||||
: ComponentMovementWatcher (&owner_),
|
||||
owner (owner_),
|
||||
controlHWND (0),
|
||||
storage (new ActiveXHelpers::JuceIStorage()),
|
||||
clientSite (new ActiveXHelpers::JuceIOleClientSite (hwnd)),
|
||||
control (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
if (control != nullptr)
|
||||
{
|
||||
control->Close (OLECLOSE_NOSAVE);
|
||||
control->Release();
|
||||
}
|
||||
|
||||
clientSite->Release();
|
||||
storage->Release();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/)
|
||||
{
|
||||
Component* const topComp = owner.getTopLevelComponent();
|
||||
|
||||
if (topComp->getPeer() != nullptr)
|
||||
{
|
||||
const Point<int> pos (topComp->getLocalPoint (&owner, Point<int>()));
|
||||
owner.setControlBounds (Rectangle<int> (pos.getX(), pos.getY(), owner.getWidth(), owner.getHeight()));
|
||||
}
|
||||
}
|
||||
|
||||
void componentPeerChanged()
|
||||
{
|
||||
componentMovedOrResized (true, true);
|
||||
}
|
||||
|
||||
void componentVisibilityChanged()
|
||||
{
|
||||
owner.setControlVisible (owner.isShowing());
|
||||
componentPeerChanged();
|
||||
}
|
||||
|
||||
// intercepts events going to an activeX control, so we can sneakily use the mouse events
|
||||
static LRESULT CALLBACK activeXHookWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
for (int i = ActiveXHelpers::activeXComps.size(); --i >= 0;)
|
||||
{
|
||||
const ActiveXControlComponent* const ax = ActiveXHelpers::activeXComps.getUnchecked(i);
|
||||
|
||||
if (ax->control != nullptr && ax->control->controlHWND == hwnd)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_MOUSEMOVE:
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_LBUTTONUP:
|
||||
case WM_MBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
case WM_LBUTTONDBLCLK:
|
||||
case WM_MBUTTONDBLCLK:
|
||||
case WM_RBUTTONDBLCLK:
|
||||
if (ax->isShowing())
|
||||
{
|
||||
ComponentPeer* const peer = ax->getPeer();
|
||||
|
||||
if (peer != nullptr)
|
||||
{
|
||||
ActiveXHelpers::offerActiveXMouseEventToPeer (peer, hwnd, message, lParam);
|
||||
|
||||
if (! ax->areMouseEventsAllowed())
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return CallWindowProc ((WNDPROC) ax->originalWndProc, hwnd, message, wParam, lParam);
|
||||
}
|
||||
}
|
||||
|
||||
return DefWindowProc (hwnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
private:
|
||||
ActiveXControlComponent& owner;
|
||||
|
||||
public:
|
||||
HWND controlHWND;
|
||||
IStorage* storage;
|
||||
IOleClientSite* clientSite;
|
||||
IOleObject* control;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ActiveXControlComponent::ActiveXControlComponent()
|
||||
: originalWndProc (0),
|
||||
mouseEventsAllowed (true)
|
||||
{
|
||||
ActiveXHelpers::activeXComps.add (this);
|
||||
}
|
||||
|
||||
ActiveXControlComponent::~ActiveXControlComponent()
|
||||
{
|
||||
deleteControl();
|
||||
ActiveXHelpers::activeXComps.removeValue (this);
|
||||
}
|
||||
|
||||
void ActiveXControlComponent::paint (Graphics& g)
|
||||
{
|
||||
if (control == nullptr)
|
||||
g.fillAll (Colours::lightgrey);
|
||||
}
|
||||
|
||||
bool ActiveXControlComponent::createControl (const void* controlIID)
|
||||
{
|
||||
deleteControl();
|
||||
ComponentPeer* const peer = getPeer();
|
||||
|
||||
// the component must have already been added to a real window when you call this!
|
||||
jassert (peer != nullptr);
|
||||
|
||||
if (peer != nullptr)
|
||||
{
|
||||
const Point<int> pos (getTopLevelComponent()->getLocalPoint (this, Point<int>()));
|
||||
HWND hwnd = (HWND) peer->getNativeHandle();
|
||||
|
||||
ScopedPointer<Pimpl> newControl (new Pimpl (hwnd, *this));
|
||||
|
||||
HRESULT hr;
|
||||
if ((hr = OleCreate (*(const IID*) controlIID, IID_IOleObject, 1 /*OLERENDER_DRAW*/, 0,
|
||||
newControl->clientSite, newControl->storage,
|
||||
(void**) &(newControl->control))) == S_OK)
|
||||
{
|
||||
newControl->control->SetHostNames (L"Juce", 0);
|
||||
|
||||
if (OleSetContainedObject (newControl->control, TRUE) == S_OK)
|
||||
{
|
||||
RECT rect;
|
||||
rect.left = pos.getX();
|
||||
rect.top = pos.getY();
|
||||
rect.right = pos.getX() + getWidth();
|
||||
rect.bottom = pos.getY() + getHeight();
|
||||
|
||||
if (newControl->control->DoVerb (OLEIVERB_SHOW, 0, newControl->clientSite, 0, hwnd, &rect) == S_OK)
|
||||
{
|
||||
control = newControl;
|
||||
setControlBounds (Rectangle<int> (pos.getX(), pos.getY(), getWidth(), getHeight()));
|
||||
|
||||
control->controlHWND = ActiveXHelpers::getHWND (this);
|
||||
|
||||
if (control->controlHWND != 0)
|
||||
{
|
||||
originalWndProc = (void*) (pointer_sized_int) GetWindowLongPtr ((HWND) control->controlHWND, GWLP_WNDPROC);
|
||||
SetWindowLongPtr ((HWND) control->controlHWND, GWLP_WNDPROC, (LONG_PTR) Pimpl::activeXHookWndProc);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ActiveXControlComponent::deleteControl()
|
||||
{
|
||||
control = nullptr;
|
||||
originalWndProc = 0;
|
||||
}
|
||||
|
||||
void* ActiveXControlComponent::queryInterface (const void* iid) const
|
||||
{
|
||||
void* result = nullptr;
|
||||
|
||||
if (control != nullptr && control->control != nullptr
|
||||
&& SUCCEEDED (control->control->QueryInterface (*(const IID*) iid, &result)))
|
||||
return result;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ActiveXControlComponent::setControlBounds (const Rectangle<int>& newBounds) const
|
||||
{
|
||||
if (control->controlHWND != 0)
|
||||
MoveWindow (control->controlHWND, newBounds.getX(), newBounds.getY(), newBounds.getWidth(), newBounds.getHeight(), TRUE);
|
||||
}
|
||||
|
||||
void ActiveXControlComponent::setControlVisible (const bool shouldBeVisible) const
|
||||
{
|
||||
if (control->controlHWND != 0)
|
||||
ShowWindow (control->controlHWND, shouldBeVisible ? SW_SHOWNA : SW_HIDE);
|
||||
}
|
||||
|
||||
void ActiveXControlComponent::setMouseEventsAllowed (const bool eventsCanReachControl)
|
||||
{
|
||||
mouseEventsAllowed = eventsCanReachControl;
|
||||
}
|
||||
204
modules/juce_gui_extra/native/juce_win32_SystemTrayIcon.cpp
Normal file
204
modules/juce_gui_extra/native/juce_win32_SystemTrayIcon.cpp
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace IconConverters
|
||||
{
|
||||
extern HICON createHICONFromImage (const Image& image, const BOOL isIcon, int hotspotX, int hotspotY);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class SystemTrayIconComponent::Pimpl
|
||||
{
|
||||
public:
|
||||
Pimpl (SystemTrayIconComponent& owner_, HICON hicon, HWND hwnd)
|
||||
: owner (owner_),
|
||||
originalWndProc ((WNDPROC) GetWindowLongPtr (hwnd, GWLP_WNDPROC)),
|
||||
taskbarCreatedMessage (RegisterWindowMessage (TEXT ("TaskbarCreated")))
|
||||
{
|
||||
SetWindowLongPtr (hwnd, GWLP_WNDPROC, (LONG_PTR) hookedWndProc);
|
||||
|
||||
zerostruct (iconData);
|
||||
iconData.cbSize = sizeof (iconData);
|
||||
iconData.hWnd = hwnd;
|
||||
iconData.uID = (UINT) (pointer_sized_int) hwnd;
|
||||
iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
||||
iconData.uCallbackMessage = WM_TRAYNOTIFY;
|
||||
iconData.hIcon = hicon;
|
||||
|
||||
Shell_NotifyIcon (NIM_ADD, &iconData);
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
SetWindowLongPtr (iconData.hWnd, GWLP_WNDPROC, (LONG_PTR) originalWndProc);
|
||||
|
||||
iconData.uFlags = 0;
|
||||
Shell_NotifyIcon (NIM_DELETE, &iconData);
|
||||
DestroyIcon (iconData.hIcon);
|
||||
}
|
||||
|
||||
void updateIcon (HICON hicon)
|
||||
{
|
||||
HICON oldIcon = iconData.hIcon;
|
||||
|
||||
iconData.hIcon = hicon;
|
||||
iconData.uFlags = NIF_ICON;
|
||||
Shell_NotifyIcon (NIM_MODIFY, &iconData);
|
||||
|
||||
DestroyIcon (oldIcon);
|
||||
}
|
||||
|
||||
void setToolTip (const String& toolTip)
|
||||
{
|
||||
iconData.uFlags = NIF_TIP;
|
||||
toolTip.copyToUTF16 (iconData.szTip, sizeof (iconData.szTip) - 1);
|
||||
Shell_NotifyIcon (NIM_MODIFY, &iconData);
|
||||
}
|
||||
|
||||
void handleTaskBarEvent (const LPARAM lParam)
|
||||
{
|
||||
if (owner.isCurrentlyBlockedByAnotherModalComponent())
|
||||
{
|
||||
if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN
|
||||
|| lParam == WM_LBUTTONDBLCLK || lParam == WM_LBUTTONDBLCLK)
|
||||
{
|
||||
Component* const current = Component::getCurrentlyModalComponent();
|
||||
|
||||
if (current != nullptr)
|
||||
current->inputAttemptWhenModal();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ModifierKeys eventMods (ModifierKeys::getCurrentModifiersRealtime());
|
||||
|
||||
if (lParam == WM_LBUTTONDOWN || lParam == WM_LBUTTONDBLCLK)
|
||||
eventMods = eventMods.withFlags (ModifierKeys::leftButtonModifier);
|
||||
else if (lParam == WM_RBUTTONDOWN || lParam == WM_RBUTTONDBLCLK)
|
||||
eventMods = eventMods.withFlags (ModifierKeys::rightButtonModifier);
|
||||
else if (lParam == WM_LBUTTONUP || lParam == WM_RBUTTONUP)
|
||||
eventMods = eventMods.withoutMouseButtons();
|
||||
|
||||
const MouseEvent e (Desktop::getInstance().getMainMouseSource(),
|
||||
Point<int>(), eventMods, &owner, &owner, Time (getMouseEventTime()),
|
||||
Point<int>(), Time (getMouseEventTime()), 1, false);
|
||||
|
||||
if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN)
|
||||
{
|
||||
SetFocus (iconData.hWnd);
|
||||
SetForegroundWindow (iconData.hWnd);
|
||||
owner.mouseDown (e);
|
||||
}
|
||||
else if (lParam == WM_LBUTTONUP || lParam == WM_RBUTTONUP)
|
||||
{
|
||||
owner.mouseUp (e);
|
||||
}
|
||||
else if (lParam == WM_LBUTTONDBLCLK || lParam == WM_LBUTTONDBLCLK)
|
||||
{
|
||||
owner.mouseDoubleClick (e);
|
||||
}
|
||||
else if (lParam == WM_MOUSEMOVE)
|
||||
{
|
||||
owner.mouseMove (e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Pimpl* getPimpl (HWND hwnd)
|
||||
{
|
||||
if (JuceWindowIdentifier::isJUCEWindow (hwnd))
|
||||
{
|
||||
ComponentPeer* peer = (ComponentPeer*) GetWindowLongPtr (hwnd, 8);
|
||||
|
||||
if (peer != nullptr)
|
||||
{
|
||||
SystemTrayIconComponent* const iconComp = dynamic_cast<SystemTrayIconComponent*> (peer->getComponent());
|
||||
|
||||
if (iconComp != nullptr)
|
||||
return iconComp->pimpl;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK hookedWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
Pimpl* const p = getPimpl (hwnd);
|
||||
|
||||
if (p != nullptr)
|
||||
return p->windowProc (hwnd, message, wParam, lParam);
|
||||
else
|
||||
return DefWindowProcW (hwnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
LRESULT windowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (message == WM_TRAYNOTIFY)
|
||||
{
|
||||
handleTaskBarEvent (lParam);
|
||||
}
|
||||
else if (message == taskbarCreatedMessage)
|
||||
{
|
||||
iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
||||
Shell_NotifyIcon (NIM_ADD, &iconData);
|
||||
}
|
||||
|
||||
return CallWindowProc (originalWndProc, hwnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
private:
|
||||
SystemTrayIconComponent& owner;
|
||||
NOTIFYICONDATA iconData;
|
||||
WNDPROC originalWndProc;
|
||||
const DWORD taskbarCreatedMessage;
|
||||
enum { WM_TRAYNOTIFY = WM_USER + 100 };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void SystemTrayIconComponent::setIconImage (const Image& newImage)
|
||||
{
|
||||
if (newImage.isValid())
|
||||
{
|
||||
HICON hicon = IconConverters::createHICONFromImage (newImage, TRUE, 0, 0);
|
||||
|
||||
if (pimpl == nullptr)
|
||||
pimpl = new Pimpl (*this, hicon, (HWND) getWindowHandle());
|
||||
else
|
||||
pimpl->updateIcon (hicon);
|
||||
}
|
||||
else
|
||||
{
|
||||
pimpl = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::setIconTooltip (const String& tooltip)
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
pimpl->setToolTip (tooltip);
|
||||
}
|
||||
318
modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp
Normal file
318
modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
class WebBrowserComponentInternal : public ActiveXControlComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
WebBrowserComponentInternal()
|
||||
: browser (nullptr),
|
||||
connectionPoint (nullptr),
|
||||
adviseCookie (0)
|
||||
{
|
||||
}
|
||||
|
||||
~WebBrowserComponentInternal()
|
||||
{
|
||||
if (connectionPoint != nullptr)
|
||||
connectionPoint->Unadvise (adviseCookie);
|
||||
|
||||
if (browser != nullptr)
|
||||
browser->Release();
|
||||
}
|
||||
|
||||
void createBrowser()
|
||||
{
|
||||
createControl (&CLSID_WebBrowser);
|
||||
browser = (IWebBrowser2*) queryInterface (&IID_IWebBrowser2);
|
||||
|
||||
IConnectionPointContainer* connectionPointContainer = (IConnectionPointContainer*) queryInterface (&IID_IConnectionPointContainer);
|
||||
|
||||
if (connectionPointContainer != nullptr)
|
||||
{
|
||||
connectionPointContainer->FindConnectionPoint (DIID_DWebBrowserEvents2,
|
||||
&connectionPoint);
|
||||
|
||||
if (connectionPoint != nullptr)
|
||||
{
|
||||
WebBrowserComponent* const owner = dynamic_cast <WebBrowserComponent*> (getParentComponent());
|
||||
jassert (owner != nullptr);
|
||||
|
||||
EventHandler* handler = new EventHandler (*owner);
|
||||
connectionPoint->Advise (handler, &adviseCookie);
|
||||
handler->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void goToURL (const String& url,
|
||||
const StringArray* headers,
|
||||
const MemoryBlock* postData)
|
||||
{
|
||||
if (browser != nullptr)
|
||||
{
|
||||
LPSAFEARRAY sa = nullptr;
|
||||
|
||||
VARIANT flags, frame, postDataVar, headersVar; // (_variant_t isn't available in all compilers)
|
||||
VariantInit (&flags);
|
||||
VariantInit (&frame);
|
||||
VariantInit (&postDataVar);
|
||||
VariantInit (&headersVar);
|
||||
|
||||
if (headers != nullptr)
|
||||
{
|
||||
V_VT (&headersVar) = VT_BSTR;
|
||||
V_BSTR (&headersVar) = SysAllocString ((const OLECHAR*) headers->joinIntoString ("\r\n").toWideCharPointer());
|
||||
}
|
||||
|
||||
if (postData != nullptr && postData->getSize() > 0)
|
||||
{
|
||||
LPSAFEARRAY sa = SafeArrayCreateVector (VT_UI1, 0, (ULONG) postData->getSize());
|
||||
|
||||
if (sa != 0)
|
||||
{
|
||||
void* data = nullptr;
|
||||
SafeArrayAccessData (sa, &data);
|
||||
jassert (data != nullptr);
|
||||
|
||||
if (data != nullptr)
|
||||
{
|
||||
postData->copyTo (data, 0, postData->getSize());
|
||||
SafeArrayUnaccessData (sa);
|
||||
|
||||
VARIANT postDataVar2;
|
||||
VariantInit (&postDataVar2);
|
||||
V_VT (&postDataVar2) = VT_ARRAY | VT_UI1;
|
||||
V_ARRAY (&postDataVar2) = sa;
|
||||
|
||||
postDataVar = postDataVar2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
browser->Navigate ((BSTR) (const OLECHAR*) url.toWideCharPointer(),
|
||||
&flags, &frame,
|
||||
&postDataVar, &headersVar);
|
||||
|
||||
if (sa != 0)
|
||||
SafeArrayDestroy (sa);
|
||||
|
||||
VariantClear (&flags);
|
||||
VariantClear (&frame);
|
||||
VariantClear (&postDataVar);
|
||||
VariantClear (&headersVar);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
IWebBrowser2* browser;
|
||||
|
||||
private:
|
||||
IConnectionPoint* connectionPoint;
|
||||
DWORD adviseCookie;
|
||||
|
||||
//==============================================================================
|
||||
class EventHandler : public ComBaseClassHelper <IDispatch>,
|
||||
public ComponentMovementWatcher
|
||||
{
|
||||
public:
|
||||
EventHandler (WebBrowserComponent& owner_)
|
||||
: ComponentMovementWatcher (&owner_),
|
||||
owner (owner_)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT GetTypeInfoCount (UINT*) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT GetTypeInfo (UINT, LCID, ITypeInfo**) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT GetIDsOfNames (REFIID, LPOLESTR*, UINT, LCID, DISPID*) { return E_NOTIMPL; }
|
||||
|
||||
JUCE_COMRESULT Invoke (DISPID dispIdMember, REFIID /*riid*/, LCID /*lcid*/, WORD /*wFlags*/, DISPPARAMS* pDispParams,
|
||||
VARIANT* /*pVarResult*/, EXCEPINFO* /*pExcepInfo*/, UINT* /*puArgErr*/)
|
||||
{
|
||||
if (dispIdMember == DISPID_BEFORENAVIGATE2)
|
||||
{
|
||||
VARIANT* const vurl = pDispParams->rgvarg[5].pvarVal;
|
||||
String url;
|
||||
|
||||
if ((vurl->vt & VT_BYREF) != 0)
|
||||
url = *vurl->pbstrVal;
|
||||
else
|
||||
url = vurl->bstrVal;
|
||||
|
||||
*pDispParams->rgvarg->pboolVal
|
||||
= owner.pageAboutToLoad (url) ? VARIANT_FALSE
|
||||
: VARIANT_TRUE;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
void componentMovedOrResized (bool, bool ) {}
|
||||
void componentPeerChanged() {}
|
||||
void componentVisibilityChanged() { owner.visibilityChanged(); }
|
||||
|
||||
//==============================================================================
|
||||
private:
|
||||
WebBrowserComponent& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EventHandler);
|
||||
};
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebBrowserComponentInternal);
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
WebBrowserComponent::WebBrowserComponent (const bool unloadPageWhenBrowserIsHidden_)
|
||||
: browser (nullptr),
|
||||
blankPageShown (false),
|
||||
unloadPageWhenBrowserIsHidden (unloadPageWhenBrowserIsHidden_)
|
||||
{
|
||||
setOpaque (true);
|
||||
addAndMakeVisible (browser = new WebBrowserComponentInternal());
|
||||
}
|
||||
|
||||
WebBrowserComponent::~WebBrowserComponent()
|
||||
{
|
||||
delete browser;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void WebBrowserComponent::goToURL (const String& url,
|
||||
const StringArray* headers,
|
||||
const MemoryBlock* postData)
|
||||
{
|
||||
lastURL = url;
|
||||
|
||||
lastHeaders.clear();
|
||||
if (headers != nullptr)
|
||||
lastHeaders = *headers;
|
||||
|
||||
lastPostData.setSize (0);
|
||||
if (postData != nullptr)
|
||||
lastPostData = *postData;
|
||||
|
||||
blankPageShown = false;
|
||||
|
||||
browser->goToURL (url, headers, postData);
|
||||
}
|
||||
|
||||
void WebBrowserComponent::stop()
|
||||
{
|
||||
if (browser->browser != nullptr)
|
||||
browser->browser->Stop();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::goBack()
|
||||
{
|
||||
lastURL = String::empty;
|
||||
blankPageShown = false;
|
||||
|
||||
if (browser->browser != nullptr)
|
||||
browser->browser->GoBack();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::goForward()
|
||||
{
|
||||
lastURL = String::empty;
|
||||
|
||||
if (browser->browser != nullptr)
|
||||
browser->browser->GoForward();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::refresh()
|
||||
{
|
||||
if (browser->browser != nullptr)
|
||||
browser->browser->Refresh();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void WebBrowserComponent::paint (Graphics& g)
|
||||
{
|
||||
if (browser->browser == nullptr)
|
||||
g.fillAll (Colours::white);
|
||||
}
|
||||
|
||||
void WebBrowserComponent::checkWindowAssociation()
|
||||
{
|
||||
if (isShowing())
|
||||
{
|
||||
if (browser->browser == nullptr && getPeer() != nullptr)
|
||||
{
|
||||
browser->createBrowser();
|
||||
reloadLastURL();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (blankPageShown)
|
||||
goBack();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (browser != nullptr && unloadPageWhenBrowserIsHidden && ! blankPageShown)
|
||||
{
|
||||
// when the component becomes invisible, some stuff like flash
|
||||
// carries on playing audio, so we need to force it onto a blank
|
||||
// page to avoid this..
|
||||
|
||||
blankPageShown = true;
|
||||
browser->goToURL ("about:blank", 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebBrowserComponent::reloadLastURL()
|
||||
{
|
||||
if (lastURL.isNotEmpty())
|
||||
{
|
||||
goToURL (lastURL, &lastHeaders, &lastPostData);
|
||||
lastURL = String::empty;
|
||||
}
|
||||
}
|
||||
|
||||
void WebBrowserComponent::parentHierarchyChanged()
|
||||
{
|
||||
checkWindowAssociation();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::resized()
|
||||
{
|
||||
browser->setSize (getWidth(), getHeight());
|
||||
}
|
||||
|
||||
void WebBrowserComponent::visibilityChanged()
|
||||
{
|
||||
checkWindowAssociation();
|
||||
}
|
||||
|
||||
bool WebBrowserComponent::pageAboutToLoad (const String&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue