The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

287 lines
9.2KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. //==============================================================================
  22. class ChoicePropertyComponent::RemapperValueSource : public Value::ValueSource,
  23. private Value::Listener
  24. {
  25. public:
  26. RemapperValueSource (const Value& source, const Array<var>& map)
  27. : sourceValue (source),
  28. mappings (map)
  29. {
  30. sourceValue.addListener (this);
  31. }
  32. var getValue() const override
  33. {
  34. auto targetValue = sourceValue.getValue();
  35. for (auto& map : mappings)
  36. if (map.equalsWithSameType (targetValue))
  37. return mappings.indexOf (map) + 1;
  38. return mappings.indexOf (targetValue) + 1;
  39. }
  40. void setValue (const var& newValue) override
  41. {
  42. auto remappedVal = mappings [static_cast<int> (newValue) - 1];
  43. if (! remappedVal.equalsWithSameType (sourceValue))
  44. sourceValue = remappedVal;
  45. }
  46. protected:
  47. Value sourceValue;
  48. Array<var> mappings;
  49. void valueChanged (Value&) override { sendChangeMessage (true); }
  50. //==============================================================================
  51. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RemapperValueSource)
  52. };
  53. //==============================================================================
  54. class ChoicePropertyComponent::RemapperValueSourceWithDefault : public Value::ValueSource,
  55. private Value::Listener
  56. {
  57. public:
  58. RemapperValueSourceWithDefault (ValueWithDefault* vwd, const Array<var>& map)
  59. : valueWithDefault (vwd),
  60. sourceValue (valueWithDefault->getPropertyAsValue()),
  61. mappings (map)
  62. {
  63. sourceValue.addListener (this);
  64. }
  65. var getValue() const override
  66. {
  67. if (valueWithDefault->isUsingDefault())
  68. return -1;
  69. auto targetValue = sourceValue.getValue();
  70. for (auto map : mappings)
  71. if (map.equalsWithSameType (targetValue))
  72. return mappings.indexOf (map) + 1;
  73. return mappings.indexOf (targetValue) + 1;
  74. }
  75. void setValue (const var& newValue) override
  76. {
  77. auto newValueInt = static_cast<int> (newValue);
  78. if (newValueInt == -1)
  79. {
  80. valueWithDefault->resetToDefault();
  81. }
  82. else
  83. {
  84. auto remappedVal = mappings [newValueInt - 1];
  85. if (! remappedVal.equalsWithSameType (sourceValue))
  86. *valueWithDefault = remappedVal;
  87. }
  88. }
  89. private:
  90. void valueChanged (Value&) override { sendChangeMessage (true); }
  91. ValueWithDefault* valueWithDefault;
  92. Value sourceValue;
  93. Array<var> mappings;
  94. //==============================================================================
  95. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RemapperValueSourceWithDefault)
  96. };
  97. //==============================================================================
  98. ChoicePropertyComponent::ChoicePropertyComponent (const String& name)
  99. : PropertyComponent (name),
  100. isCustomClass (true)
  101. {
  102. }
  103. ChoicePropertyComponent::ChoicePropertyComponent (const String& name,
  104. const StringArray& choiceList,
  105. const Array<var>& correspondingValues)
  106. : PropertyComponent (name),
  107. choices (choiceList)
  108. {
  109. // The array of corresponding values must contain one value for each of the items in
  110. // the choices array!
  111. jassert (correspondingValues.size() == choices.size());
  112. ignoreUnused (correspondingValues);
  113. }
  114. ChoicePropertyComponent::ChoicePropertyComponent (const Value& valueToControl,
  115. const String& name,
  116. const StringArray& choiceList,
  117. const Array<var>& correspondingValues)
  118. : ChoicePropertyComponent (name, choiceList, correspondingValues)
  119. {
  120. createComboBox();
  121. comboBox.getSelectedIdAsValue().referTo (Value (new RemapperValueSource (valueToControl,
  122. correspondingValues)));
  123. }
  124. ChoicePropertyComponent::ChoicePropertyComponent (ValueWithDefault& valueToControl,
  125. const String& name,
  126. const StringArray& choiceList,
  127. const Array<var>& correspondingValues)
  128. : ChoicePropertyComponent (name, choiceList, correspondingValues)
  129. {
  130. valueWithDefault = &valueToControl;
  131. createComboBoxWithDefault (choiceList [correspondingValues.indexOf (valueWithDefault->getDefault())]);
  132. comboBox.getSelectedIdAsValue().referTo (Value (new RemapperValueSourceWithDefault (valueWithDefault,
  133. correspondingValues)));
  134. valueWithDefault->onDefaultChange = [this, choiceList, correspondingValues]
  135. {
  136. auto selectedId = comboBox.getSelectedId();
  137. comboBox.clear();
  138. createComboBoxWithDefault (choiceList [correspondingValues.indexOf (valueWithDefault->getDefault())]);
  139. comboBox.setSelectedId (selectedId);
  140. };
  141. }
  142. ChoicePropertyComponent::ChoicePropertyComponent (ValueWithDefault& valueToControl,
  143. const String& name)
  144. : PropertyComponent (name),
  145. choices ({ "Enabled", "Disabled" })
  146. {
  147. valueWithDefault = &valueToControl;
  148. createComboBoxWithDefault (valueWithDefault->getDefault() ? "Enabled" : "Disabled");
  149. comboBox.getSelectedIdAsValue().referTo (Value (new RemapperValueSourceWithDefault (valueWithDefault,
  150. { true, false })));
  151. valueWithDefault->onDefaultChange = [this]
  152. {
  153. auto selectedId = comboBox.getSelectedId();
  154. comboBox.clear();
  155. createComboBoxWithDefault (valueWithDefault->getDefault() ? "Enabled" : "Disabled");
  156. comboBox.setSelectedId (selectedId);
  157. };
  158. }
  159. ChoicePropertyComponent::~ChoicePropertyComponent()
  160. {
  161. if (valueWithDefault != nullptr)
  162. valueWithDefault->onDefaultChange = nullptr;
  163. }
  164. //==============================================================================
  165. void ChoicePropertyComponent::createComboBox()
  166. {
  167. addAndMakeVisible (comboBox);
  168. for (auto choice : choices)
  169. {
  170. if (choice.isNotEmpty())
  171. comboBox.addItem (choice, choices.indexOf (choice) + 1);
  172. else
  173. comboBox.addSeparator();
  174. }
  175. comboBox.setEditableText (false);
  176. }
  177. void ChoicePropertyComponent::createComboBoxWithDefault (const String& defaultString)
  178. {
  179. addAndMakeVisible (comboBox);
  180. comboBox.addItem ("Default" + (defaultString.isNotEmpty() ? " (" + defaultString + ")" : ""), -1);
  181. for (auto choice : choices)
  182. {
  183. if (choice.isNotEmpty())
  184. comboBox.addItem (choice, choices.indexOf (choice) + 1);
  185. else
  186. comboBox.addSeparator();
  187. }
  188. comboBox.setEditableText (false);
  189. }
  190. void ChoicePropertyComponent::setIndex (const int /*newIndex*/)
  191. {
  192. jassertfalse; // you need to override this method in your subclass!
  193. }
  194. int ChoicePropertyComponent::getIndex() const
  195. {
  196. jassertfalse; // you need to override this method in your subclass!
  197. return -1;
  198. }
  199. const StringArray& ChoicePropertyComponent::getChoices() const
  200. {
  201. return choices;
  202. }
  203. //==============================================================================
  204. void ChoicePropertyComponent::refresh()
  205. {
  206. if (isCustomClass)
  207. {
  208. if (! comboBox.isVisible())
  209. {
  210. createComboBox();
  211. comboBox.onChange = [this] { changeIndex(); };
  212. }
  213. comboBox.setSelectedId (getIndex() + 1, dontSendNotification);
  214. }
  215. }
  216. void ChoicePropertyComponent::changeIndex()
  217. {
  218. if (isCustomClass)
  219. {
  220. auto newIndex = comboBox.getSelectedId() - 1;
  221. if (newIndex != getIndex())
  222. setIndex (newIndex);
  223. }
  224. }
  225. } // namespace juce