| @@ -35,16 +35,22 @@ class JucerComponent : public Component | |||
| public: | |||
| JucerComponent() | |||
| { | |||
| addAndMakeVisible (comp = new ComponentDocument::TestComponent (0, File::nonexistent)); | |||
| } | |||
| void paint (Graphics& g) | |||
| void resized() | |||
| { | |||
| g.setColour (Colours::grey); | |||
| g.drawRect (getLocalBounds()); | |||
| comp->setBounds (getLocalBounds()); | |||
| } | |||
| g.drawLine (0.5f, 0.5f, getWidth() - 0.5f, getHeight() - 0.5f); | |||
| g.drawLine (0.5f, getHeight() - 0.5f, getWidth() - 0.5f, 0.5f); | |||
| void setJucerComp (ComponentDocument& document, const File& cppFile) | |||
| { | |||
| addAndMakeVisible (comp = new ComponentDocument::TestComponent (document.getProject(), cppFile)); | |||
| resized(); | |||
| } | |||
| private: | |||
| ScopedPointer<ComponentDocument::TestComponent> comp; | |||
| }; | |||
| //============================================================================== | |||
| @@ -71,17 +71,7 @@ public: | |||
| props.add (new TextPropertyComponent (getValue ("text", state, document), "Text", 16384, true)); | |||
| props.getLast()->setTooltip ("The label's text."); | |||
| const char* const layouts[] = { "Centred", "Centred-left", "Centred-right", "Centred-top", "Centred-bottom", "Top-left", | |||
| "Top-right", "Bottom-left", "Bottom-right", 0 }; | |||
| const int justifications[] = { Justification::centred, Justification::centredLeft, Justification::centredRight, | |||
| Justification::centredTop, Justification::centredBottom, Justification::topLeft, | |||
| Justification::topRight, Justification::bottomLeft, Justification::bottomRight, 0 }; | |||
| ValueRemapperSource* remapper = new ValueRemapperSource (state.getPropertyAsValue ("justification", document.getUndoManager())); | |||
| for (int i = 0; i < numElementsInArray (justifications) - 1; ++i) | |||
| remapper->addMapping (justifications[i], i + 1); | |||
| props.add (new ChoicePropertyComponent (Value (remapper), "Layout", StringArray (layouts))); | |||
| props.add (createJustificationProperty ("Layout", state.getPropertyAsValue ("justification", document.getUndoManager()), false)); | |||
| const char* const editModes[] = { "Read-only", "Edit on Single-Click", "Edit on Double-Click", 0 }; | |||
| props.add (new ChoicePropertyComponent (state.getPropertyAsValue ("editMode", document.getUndoManager()), | |||
| @@ -52,18 +52,50 @@ public: | |||
| Component* createComponent() { return new Slider (String::empty); } | |||
| const Rectangle<int> getDefaultSize() { return Rectangle<int> (0, 0, 200, 24); } | |||
| void update (ComponentDocument& document, Slider* comp, const ValueTree& state) | |||
| void initialiseNew (ComponentDocument& document, ValueTree& state) | |||
| { | |||
| state.setProperty ("min", 0, 0); | |||
| state.setProperty ("max", 100, 0); | |||
| state.setProperty ("interval", 1, 0); | |||
| state.setProperty ("type", 1 + Slider::LinearHorizontal, 0); | |||
| state.setProperty ("textBoxPos", 2, 0); | |||
| state.setProperty ("editable", true, 0); | |||
| state.setProperty ("textBoxWidth", 80, 0); | |||
| state.setProperty ("textBoxHeight", 20, 0); | |||
| state.setProperty ("skew", 1, 0); | |||
| } | |||
| void initialiseNew (ComponentDocument& document, ValueTree& state) | |||
| void update (ComponentDocument& document, Slider* comp, const ValueTree& state) | |||
| { | |||
| comp->setRange ((double) state ["min"], (double) state ["max"], (double) state ["interval"]); | |||
| comp->setSliderStyle ((Slider::SliderStyle) ((int) state ["type"] - 1)); | |||
| comp->setTextBoxStyle ((Slider::TextEntryBoxPosition) ((int) state ["textBoxPos"] - 1), | |||
| ! (bool) state ["editable"], | |||
| (int) state ["textBoxWidth"], (int) state ["textBoxHeight"]); | |||
| comp->setSkewFactor ((double) state ["skew"]); | |||
| } | |||
| void createProperties (ComponentDocument& document, ValueTree& state, Array <PropertyComponent*>& props) | |||
| { | |||
| addTooltipProperty (document, state, props); | |||
| addFocusOrderProperty (document, state, props); | |||
| props.add (new TextPropertyComponent (Value (new NumericValueSource<double> (getValue ("min", state, document))), "Minimum", 16, false)); | |||
| props.add (new TextPropertyComponent (Value (new NumericValueSource<double> (getValue ("max", state, document))), "Maximum", 16, false)); | |||
| props.add (new TextPropertyComponent (Value (new NumericValueSource<double> (getValue ("interval", state, document))), "Interval", 16, false)); | |||
| const char* const types[] = { "LinearHorizontal", "LinearVertical", "LinearBar", "Rotary", "RotaryHorizontalDrag", "RotaryVerticalDrag", | |||
| "IncDecButtons", "TwoValueHorizontal", "TwoValueVertical", "ThreeValueHorizontal", "ThreeValueVertical", 0 }; | |||
| props.add (new ChoicePropertyComponent (state.getPropertyAsValue ("type", document.getUndoManager()), "Type", StringArray (types))); | |||
| const char* const textBoxPositions[] = { "NoTextBox", "TextBoxLeft", "TextBoxRight", "TextBoxAbove", "TextBoxBelow", 0 }; | |||
| props.add (new ChoicePropertyComponent (state.getPropertyAsValue ("textBoxPos", document.getUndoManager()), "Text Box", StringArray (textBoxPositions))); | |||
| props.add (new BooleanPropertyComponent (getValue ("editable", state, document), "Editable", "Value can be edited")); | |||
| props.add (new TextPropertyComponent (Value (new NumericValueSource<int> (getValue ("textBoxWidth", state, document))), "Text Box Width", 8, false)); | |||
| props.add (new TextPropertyComponent (Value (new NumericValueSource<int> (getValue ("textBoxHeight", state, document))), "Text Box Height", 8, false)); | |||
| props.add (new TextPropertyComponent (Value (new NumericValueSource<double> (getValue ("skew", state, document))), "Skew Factor", 16, false)); | |||
| addEditableColourProperties (document, state, props); | |||
| } | |||
| }; | |||
| @@ -45,20 +45,6 @@ public: | |||
| Component* createComponent() { return new TextButton (String::empty); } | |||
| const Rectangle<int> getDefaultSize() { return Rectangle<int> (0, 0, 150, 24); } | |||
| void update (ComponentDocument& document, TextButton* comp, const ValueTree& state) | |||
| { | |||
| comp->setButtonText (state ["text"].toString()); | |||
| comp->setRadioGroupId (state ["radioGroup"]); | |||
| int connected = 0; | |||
| if (state ["connectedLeft"]) connected |= TextButton::ConnectedOnLeft; | |||
| if (state ["connectedRight"]) connected |= TextButton::ConnectedOnRight; | |||
| if (state ["connectedTop"]) connected |= TextButton::ConnectedOnTop; | |||
| if (state ["connectedBottom"]) connected |= TextButton::ConnectedOnBottom; | |||
| comp->setConnectedEdges (connected); | |||
| } | |||
| void initialiseNew (ComponentDocument& document, ValueTree& state) | |||
| { | |||
| state.setProperty ("text", "New Button", 0); | |||
| @@ -69,6 +55,20 @@ public: | |||
| state.setProperty ("connectedBottom", false, 0); | |||
| } | |||
| void update (ComponentDocument& document, TextButton* comp, const ValueTree& state) | |||
| { | |||
| comp->setButtonText (state ["text"].toString()); | |||
| comp->setRadioGroupId (state ["radioGroup"]); | |||
| int connected = 0; | |||
| if (state ["connectedLeft"]) connected |= TextButton::ConnectedOnLeft; | |||
| if (state ["connectedRight"]) connected |= TextButton::ConnectedOnRight; | |||
| if (state ["connectedTop"]) connected |= TextButton::ConnectedOnTop; | |||
| if (state ["connectedBottom"]) connected |= TextButton::ConnectedOnBottom; | |||
| comp->setConnectedEdges (connected); | |||
| } | |||
| void createProperties (ComponentDocument& document, ValueTree& state, Array <PropertyComponent*>& props) | |||
| { | |||
| addTooltipProperty (document, state, props); | |||
| @@ -77,7 +77,7 @@ public: | |||
| props.add (new TextPropertyComponent (getValue ("text", state, document), "Button Text", 1024, false)); | |||
| props.getLast()->setTooltip ("The button's text."); | |||
| props.add (new TextPropertyComponent (Value (new IntegerValueSource (getValue ("radioGroup", state, document))), "Radio Group", 8, false)); | |||
| props.add (new TextPropertyComponent (Value (new NumericValueSource<int> (getValue ("radioGroup", state, document))), "Radio Group", 8, false)); | |||
| props.getLast()->setTooltip ("The radio group that this button is a member of."); | |||
| props.add (new BooleanPropertyComponent (getValue ("connectedLeft", state, document), "Connected left", "Connected")); | |||
| @@ -51,14 +51,26 @@ public: | |||
| Component* createComponent() { return new TextEditor(); } | |||
| const Rectangle<int> getDefaultSize() { return Rectangle<int> (0, 0, 180, 24); } | |||
| void update (ComponentDocument& document, TextEditor* comp, const ValueTree& state) | |||
| void initialiseNew (ComponentDocument& document, ValueTree& state) | |||
| { | |||
| comp->setText (state ["text"].toString()); | |||
| state.setProperty ("text", "Text Editor Content", 0); | |||
| state.setProperty ("readOnly", false, 0); | |||
| state.setProperty ("scrollbarsShown", true, 0); | |||
| state.setProperty ("caretVisible", true, 0); | |||
| state.setProperty ("popupMenuEnabled", true, 0); | |||
| state.setProperty ("mode", 1, 0); | |||
| } | |||
| void initialiseNew (ComponentDocument& document, ValueTree& state) | |||
| void update (ComponentDocument& document, TextEditor* comp, const ValueTree& state) | |||
| { | |||
| state.setProperty ("text", "Text Editor Content", 0); | |||
| comp->setReadOnly (state["readOnly"]); | |||
| comp->setScrollbarsShown (state ["scrollbarsShown"]); | |||
| comp->setCaretVisible (state ["caretVisible"]); | |||
| comp->setPopupMenuEnabled (state ["popupMenuEnabled"]); | |||
| int mode = state ["mode"]; | |||
| comp->setMultiLine (mode > 1, true); | |||
| comp->setReturnKeyStartsNewLine (mode != 3); | |||
| comp->setText (state ["text"].toString()); | |||
| } | |||
| void createProperties (ComponentDocument& document, ValueTree& state, Array <PropertyComponent*>& props) | |||
| @@ -69,6 +81,14 @@ public: | |||
| props.add (new TextPropertyComponent (getValue ("text", state, document), "Text", 16384, true)); | |||
| props.getLast()->setTooltip ("The editor's initial content."); | |||
| const char* const modes[] = { "Single-Line", "Multi-Line (Return key starts new line)", "Multi-Line (Return key disabled)", 0 }; | |||
| props.add (new ChoicePropertyComponent (getValue ("mode", state, document), "Mode", StringArray (modes))); | |||
| props.add (new BooleanPropertyComponent (getValue ("readOnly", state, document), "Read-Only", "Read-Only")); | |||
| props.add (new BooleanPropertyComponent (getValue ("scrollbarsShown", state, document), "Scrollbars", "Scrollbars Shown")); | |||
| props.add (new BooleanPropertyComponent (getValue ("caretVisible", state, document), "Caret", "Caret Visible")); | |||
| props.add (new BooleanPropertyComponent (getValue ("popupMenuEnabled", state, document), "Popup Menu", "Popup Menu Enabled")); | |||
| addEditableColourProperties (document, state, props); | |||
| } | |||
| }; | |||
| @@ -44,14 +44,16 @@ public: | |||
| Component* createComponent() { return new ToggleButton (String::empty); } | |||
| const Rectangle<int> getDefaultSize() { return Rectangle<int> (0, 0, 180, 24); } | |||
| void update (ComponentDocument& document, ToggleButton* comp, const ValueTree& state) | |||
| void initialiseNew (ComponentDocument& document, ValueTree& state) | |||
| { | |||
| comp->setButtonText (state ["text"].toString()); | |||
| state.setProperty ("text", "New Toggle Button", 0); | |||
| state.setProperty ("initialState", false, 0); | |||
| } | |||
| void initialiseNew (ComponentDocument& document, ValueTree& state) | |||
| void update (ComponentDocument& document, ToggleButton* comp, const ValueTree& state) | |||
| { | |||
| state.setProperty ("text", "New Toggle Button", 0); | |||
| comp->setButtonText (state ["text"].toString()); | |||
| comp->setToggleState (state ["initialState"], false); | |||
| } | |||
| void createProperties (ComponentDocument& document, ValueTree& state, Array <PropertyComponent*>& props) | |||
| @@ -62,6 +64,8 @@ public: | |||
| props.add (new TextPropertyComponent (getValue ("text", state, document), "Button Text", 1024, false)); | |||
| props.getLast()->setTooltip ("The button's text."); | |||
| props.add (new BooleanPropertyComponent (getValue ("initialState", state, document), "Initial State", "Enabled initially")); | |||
| addEditableColourProperties (document, state, props); | |||
| } | |||
| }; | |||
| @@ -37,19 +37,47 @@ public: | |||
| ViewportHandler() : ComponentTypeHelper<Viewport> ("Viewport", "VIEWPORT", "viewport") {} | |||
| ~ViewportHandler() {} | |||
| Component* createComponent() { return new Viewport(); } | |||
| class DemoContentComponent : public Component | |||
| { | |||
| public: | |||
| DemoContentComponent() { setSize (1000, 1000); } | |||
| ~DemoContentComponent() {} | |||
| void paint (Graphics& g) | |||
| { | |||
| g.fillCheckerBoard (0, 0, getWidth(), getHeight(), 40, 40, | |||
| Colours::grey.withAlpha (0.7f), | |||
| Colours::white.withAlpha (0.7f)); | |||
| } | |||
| }; | |||
| Component* createComponent() | |||
| { | |||
| Viewport* v = new Viewport(); | |||
| v->setViewedComponent (new DemoContentComponent()); | |||
| return v; | |||
| } | |||
| const Rectangle<int> getDefaultSize() { return Rectangle<int> (0, 0, 300, 200); } | |||
| void update (ComponentDocument& document, Viewport* comp, const ValueTree& state) | |||
| void initialiseNew (ComponentDocument& document, ValueTree& state) | |||
| { | |||
| state.setProperty ("scrollBarV", true, 0); | |||
| state.setProperty ("scrollBarH", true, 0); | |||
| state.setProperty ("scrollbarWidth", 18, 0); | |||
| } | |||
| void initialiseNew (ComponentDocument& document, ValueTree& state) | |||
| void update (ComponentDocument& document, Viewport* comp, const ValueTree& state) | |||
| { | |||
| comp->setScrollBarsShown (state ["scrollBarV"], state ["scrollBarH"]); | |||
| comp->setScrollBarThickness (state ["scrollbarWidth"]); | |||
| } | |||
| void createProperties (ComponentDocument& document, ValueTree& state, Array <PropertyComponent*>& props) | |||
| { | |||
| props.add (new BooleanPropertyComponent (getValue ("scrollBarV", state, document), "Scrollbar V", "Vertical scrollbar shown")); | |||
| props.add (new BooleanPropertyComponent (getValue ("scrollBarH", state, document), "Scrollbar H", "Horizontal scrollbar shown")); | |||
| props.add (new SliderPropertyComponent (getValue ("scrollbarWidth", state, document), "Scrollbar Thickness", 3, 40, 1)); | |||
| } | |||
| }; | |||
| @@ -74,7 +74,11 @@ public: | |||
| return false; | |||
| out = 0; | |||
| return temp.overwriteTargetFileWithTemporary(); | |||
| if (! temp.overwriteTargetFileWithTemporary()) | |||
| return false; | |||
| modDetector.updateHash(); | |||
| return true; | |||
| } | |||
| Component* createEditor() | |||
| @@ -141,7 +145,13 @@ public: | |||
| bool save() | |||
| { | |||
| return componentDoc->save(); | |||
| if (componentDoc->save()) | |||
| { | |||
| modDetector.updateHash(); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| Component* createEditor() | |||
| @@ -202,7 +212,13 @@ public: | |||
| bool save() | |||
| { | |||
| return drawableDoc->save (modDetector.getFile()); | |||
| if (drawableDoc->save (modDetector.getFile())) | |||
| { | |||
| modDetector.updateHash(); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| Component* createEditor() | |||
| @@ -351,27 +351,8 @@ const String makeValidCppIdentifier (String s, | |||
| if (CharacterFunctions::isDigit (n[0])) | |||
| n = "_" + n; | |||
| // make sure it's not a reserved c++ keyword.. | |||
| static const char* const reservedWords[] = | |||
| { | |||
| "auto", "const", "double", "float", "int", "short", "struct", | |||
| "return", "static", "union", "while", "asm", "dynamic_cast", | |||
| "unsigned", "break", "continue", "else", "for", "long", "signed", | |||
| "switch", "void", "case", "default", "enum", "goto", "register", | |||
| "sizeof", "typedef", "volatile", "char", "do", "extern", "if", | |||
| "namespace", "reinterpret_cast", "try", "bool", "explicit", "new", | |||
| "static_cast", "typeid", "catch", "false", "operator", "template", | |||
| "typename", "class", "friend", "private", "this", "using", "const_cast", | |||
| "inline", "public", "throw", "virtual", "delete", "mutable", "protected", | |||
| "true", "wchar_t", "and", "bitand", "compl", "not_eq", "or_eq", | |||
| "xor_eq", "and_eq", "bitor", "not", "or", "xor", "cin", "endl", | |||
| "INT_MIN", "iomanip", "main", "npos", "std", "cout", "include", | |||
| "INT_MAX", "iostream", "MAX_RAND", "NULL", "string", "id", "std" | |||
| }; | |||
| for (i = 0; i < numElementsInArray (reservedWords); ++i) | |||
| if (n == reservedWords[i]) | |||
| n << '_'; | |||
| if (CPlusPlusCodeTokeniser::isReservedKeyword (n)) | |||
| n << '_'; | |||
| return n; | |||
| } | |||
| @@ -387,6 +368,20 @@ void autoScrollForMouseEvent (const MouseEvent& e) | |||
| } | |||
| } | |||
| void drawComponentPlaceholder (Graphics& g, int w, int h, const String& text) | |||
| { | |||
| g.fillAll (Colours::white.withAlpha (0.4f)); | |||
| g.setColour (Colours::grey); | |||
| g.drawRect (0, 0, w, h); | |||
| g.drawLine (0.5f, 0.5f, w - 0.5f, h - 0.5f); | |||
| g.drawLine (0.5f, h - 0.5f, w - 0.5f, 0.5f); | |||
| g.setColour (Colours::black); | |||
| g.setFont (11.0f); | |||
| g.drawFittedText (text, 2, 2, w - 4, h - 4, Justification::centredTop, 2); | |||
| } | |||
| //============================================================================== | |||
| const String floatToCode (const float v) | |||
| { | |||
| @@ -740,3 +735,35 @@ void createFontProperties (Array <PropertyComponent*>& props, const ValueTree& s | |||
| props.add (StringListValueSource::create ("Font Style", state.getPropertyAsValue (fontStyle, undoManager), StringArray (fontStyles))); | |||
| } | |||
| PropertyComponent* createJustificationProperty (const String& name, const Value& value, bool onlyHorizontal) | |||
| { | |||
| ValueRemapperSource* remapper = new ValueRemapperSource (value); | |||
| StringArray strings; | |||
| if (onlyHorizontal) | |||
| { | |||
| const char* const layouts[] = { "Left", "Centred", "Right", 0 }; | |||
| const int justifications[] = { Justification::left, Justification::centred, Justification::right, 0 }; | |||
| for (int i = 0; i < numElementsInArray (justifications) - 1; ++i) | |||
| remapper->addMapping (justifications[i], i + 1); | |||
| strings = StringArray (layouts); | |||
| } | |||
| else | |||
| { | |||
| const char* const layouts[] = { "Centred", "Centred-left", "Centred-right", "Centred-top", "Centred-bottom", "Top-left", | |||
| "Top-right", "Bottom-left", "Bottom-right", 0 }; | |||
| const int justifications[] = { Justification::centred, Justification::centredLeft, Justification::centredRight, | |||
| Justification::centredTop, Justification::centredBottom, Justification::topLeft, | |||
| Justification::topRight, Justification::bottomLeft, Justification::bottomRight, 0 }; | |||
| for (int i = 0; i < numElementsInArray (justifications) - 1; ++i) | |||
| remapper->addMapping (justifications[i], i + 1); | |||
| strings = StringArray (layouts); | |||
| } | |||
| return new ChoicePropertyComponent (Value (remapper), name, strings); | |||
| } | |||
| @@ -80,6 +80,8 @@ int indexOfLineStartingWith (const StringArray& lines, const String& text, int s | |||
| void autoScrollForMouseEvent (const MouseEvent& e); | |||
| void drawComponentPlaceholder (Graphics& g, int w, int h, const String& text); | |||
| //============================================================================== | |||
| const Font getFontFromState (const ValueTree& state, | |||
| const var::identifier& fontName, | |||
| @@ -92,6 +94,8 @@ void createFontProperties (Array <PropertyComponent*>& props, const ValueTree& s | |||
| const var::identifier& fontStyle, | |||
| UndoManager* undoManager); | |||
| PropertyComponent* createJustificationProperty (const String& name, const Value& value, bool onlyHorizontal); | |||
| //============================================================================== | |||
| class FileModificationDetector | |||
| { | |||
| @@ -153,44 +153,38 @@ protected: | |||
| //============================================================================== | |||
| /** | |||
| */ | |||
| class IntegerValueSource : public Value::ValueSource, | |||
| template <typename Type> | |||
| class NumericValueSource : public Value::ValueSource, | |||
| public Value::Listener | |||
| { | |||
| public: | |||
| IntegerValueSource (const Value& sourceValue_) | |||
| NumericValueSource (const Value& sourceValue_) | |||
| : sourceValue (sourceValue_) | |||
| { | |||
| sourceValue.addListener (this); | |||
| } | |||
| ~IntegerValueSource() {} | |||
| ~NumericValueSource() {} | |||
| const var getValue() const | |||
| { | |||
| return (int) sourceValue.getValue(); | |||
| } | |||
| void valueChanged (Value&) { sendChangeMessage (true); } | |||
| const var getValue() const { return (Type) sourceValue.getValue(); } | |||
| void setValue (const var& newValue) | |||
| { | |||
| const int newVal = (int) newValue; | |||
| const Type newVal = (Type) newValue; | |||
| if (newVal != (int) getValue()) // this test is important, because if a property is missing, it won't | |||
| if (newVal != (Type) getValue()) // this test is important, because if a property is missing, it won't | |||
| sourceValue = newVal; // create it (causing an unwanted undo action) when a control sets it to 0 | |||
| } | |||
| void valueChanged (Value&) | |||
| { | |||
| sendChangeMessage (true); | |||
| } | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| protected: | |||
| Value sourceValue; | |||
| IntegerValueSource (const IntegerValueSource&); | |||
| const IntegerValueSource& operator= (const IntegerValueSource&); | |||
| NumericValueSource (const NumericValueSource&); | |||
| const NumericValueSource& operator= (const NumericValueSource&); | |||
| }; | |||