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.

293 lines
9.3KB

  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 == nullptr)
  68. return {};
  69. if (valueWithDefault->isUsingDefault())
  70. return -1;
  71. auto targetValue = sourceValue.getValue();
  72. for (auto map : mappings)
  73. if (map.equalsWithSameType (targetValue))
  74. return mappings.indexOf (map) + 1;
  75. return mappings.indexOf (targetValue) + 1;
  76. }
  77. void setValue (const var& newValue) override
  78. {
  79. if (valueWithDefault == nullptr)
  80. return;
  81. auto newValueInt = static_cast<int> (newValue);
  82. if (newValueInt == -1)
  83. {
  84. valueWithDefault->resetToDefault();
  85. }
  86. else
  87. {
  88. auto remappedVal = mappings [newValueInt - 1];
  89. if (! remappedVal.equalsWithSameType (sourceValue))
  90. *valueWithDefault = remappedVal;
  91. }
  92. }
  93. private:
  94. void valueChanged (Value&) override { sendChangeMessage (true); }
  95. WeakReference<ValueWithDefault> valueWithDefault;
  96. Value sourceValue;
  97. Array<var> mappings;
  98. //==============================================================================
  99. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RemapperValueSourceWithDefault)
  100. };
  101. //==============================================================================
  102. ChoicePropertyComponent::ChoicePropertyComponent (const String& name)
  103. : PropertyComponent (name),
  104. isCustomClass (true)
  105. {
  106. }
  107. ChoicePropertyComponent::ChoicePropertyComponent (const String& name,
  108. const StringArray& choiceList,
  109. const Array<var>& correspondingValues)
  110. : PropertyComponent (name),
  111. choices (choiceList)
  112. {
  113. // The array of corresponding values must contain one value for each of the items in
  114. // the choices array!
  115. jassert (correspondingValues.size() == choices.size());
  116. ignoreUnused (correspondingValues);
  117. }
  118. ChoicePropertyComponent::ChoicePropertyComponent (const Value& valueToControl,
  119. const String& name,
  120. const StringArray& choiceList,
  121. const Array<var>& correspondingValues)
  122. : ChoicePropertyComponent (name, choiceList, correspondingValues)
  123. {
  124. createComboBox();
  125. comboBox.getSelectedIdAsValue().referTo (Value (new RemapperValueSource (valueToControl,
  126. correspondingValues)));
  127. }
  128. ChoicePropertyComponent::ChoicePropertyComponent (ValueWithDefault& valueToControl,
  129. const String& name,
  130. const StringArray& choiceList,
  131. const Array<var>& correspondingValues)
  132. : ChoicePropertyComponent (name, choiceList, correspondingValues)
  133. {
  134. valueWithDefault = &valueToControl;
  135. createComboBoxWithDefault (choiceList [correspondingValues.indexOf (valueWithDefault->getDefault())]);
  136. comboBox.getSelectedIdAsValue().referTo (Value (new RemapperValueSourceWithDefault (valueWithDefault,
  137. correspondingValues)));
  138. valueWithDefault->onDefaultChange = [this, choiceList, correspondingValues]
  139. {
  140. auto selectedId = comboBox.getSelectedId();
  141. comboBox.clear();
  142. createComboBoxWithDefault (choiceList [correspondingValues.indexOf (valueWithDefault->getDefault())]);
  143. comboBox.setSelectedId (selectedId);
  144. };
  145. }
  146. ChoicePropertyComponent::ChoicePropertyComponent (ValueWithDefault& valueToControl,
  147. const String& name)
  148. : PropertyComponent (name),
  149. choices ({ "Enabled", "Disabled" })
  150. {
  151. valueWithDefault = &valueToControl;
  152. createComboBoxWithDefault (valueWithDefault->getDefault() ? "Enabled" : "Disabled");
  153. comboBox.getSelectedIdAsValue().referTo (Value (new RemapperValueSourceWithDefault (valueWithDefault,
  154. { true, false })));
  155. valueWithDefault->onDefaultChange = [this]
  156. {
  157. auto selectedId = comboBox.getSelectedId();
  158. comboBox.clear();
  159. createComboBoxWithDefault (valueWithDefault->getDefault() ? "Enabled" : "Disabled");
  160. comboBox.setSelectedId (selectedId);
  161. };
  162. }
  163. ChoicePropertyComponent::~ChoicePropertyComponent()
  164. {
  165. if (valueWithDefault != nullptr)
  166. valueWithDefault->onDefaultChange = nullptr;
  167. }
  168. //==============================================================================
  169. void ChoicePropertyComponent::createComboBox()
  170. {
  171. addAndMakeVisible (comboBox);
  172. for (auto choice : choices)
  173. {
  174. if (choice.isNotEmpty())
  175. comboBox.addItem (choice, choices.indexOf (choice) + 1);
  176. else
  177. comboBox.addSeparator();
  178. }
  179. comboBox.setEditableText (false);
  180. }
  181. void ChoicePropertyComponent::createComboBoxWithDefault (const String& defaultString)
  182. {
  183. addAndMakeVisible (comboBox);
  184. comboBox.addItem ("Default" + (defaultString.isNotEmpty() ? " (" + defaultString + ")" : ""), -1);
  185. for (auto choice : choices)
  186. {
  187. if (choice.isNotEmpty())
  188. comboBox.addItem (choice, choices.indexOf (choice) + 1);
  189. else
  190. comboBox.addSeparator();
  191. }
  192. comboBox.setEditableText (false);
  193. }
  194. void ChoicePropertyComponent::setIndex (const int /*newIndex*/)
  195. {
  196. jassertfalse; // you need to override this method in your subclass!
  197. }
  198. int ChoicePropertyComponent::getIndex() const
  199. {
  200. jassertfalse; // you need to override this method in your subclass!
  201. return -1;
  202. }
  203. const StringArray& ChoicePropertyComponent::getChoices() const
  204. {
  205. return choices;
  206. }
  207. //==============================================================================
  208. void ChoicePropertyComponent::refresh()
  209. {
  210. if (isCustomClass)
  211. {
  212. if (! comboBox.isVisible())
  213. {
  214. createComboBox();
  215. comboBox.onChange = [this] { changeIndex(); };
  216. }
  217. comboBox.setSelectedId (getIndex() + 1, dontSendNotification);
  218. }
  219. }
  220. void ChoicePropertyComponent::changeIndex()
  221. {
  222. if (isCustomClass)
  223. {
  224. auto newIndex = comboBox.getSelectedId() - 1;
  225. if (newIndex != getIndex())
  226. setIndex (newIndex);
  227. }
  228. }
  229. } // namespace juce