Vital
Loading...
Searching...
No Matches
modulation_matrix.cpp
Go to the documentation of this file.
1#include "modulation_matrix.h"
2#include "bar_renderer.h"
3#include "fonts.h"
5#include "line_map_editor.h"
6#include "lfo_section.h"
7#include "load_save.h"
9#include "paths.h"
10#include "synth_strings.h"
11#include "synth_gui_interface.h"
12#include "synth_module.h"
13#include "text_look_and_feel.h"
14#include "text_selector.h"
15#include "shaders.h"
16#include "synth_slider.h"
17#include "synth_parameters.h"
18
19namespace {
20 constexpr float kNumberWidthPercent = 0.2f;
21 constexpr float kSourceWidthPercent = 0.2f;
22 constexpr float kDestinationWidthPercent = 0.2f;
23 constexpr float kPaddingWidthPercent = 0.04f;
24 constexpr float kMatrixHeightInRows = 12.0f;
25
26 struct SubMenu {
27 std::string prefix;
28 std::string name;
29 bool local_description = false;
30 };
31
32 const std::string kNoConnectionString = "-";
33 const SubMenu kDestinationSubMenuPrefixes[] = {
34 { "", "" },
35 { "osc_1_", "Oscillator 1", true },
36 { "osc_2_", "Oscillator 2", true },
37 { "osc_3_", "Oscillator 3", true },
38 { "sample_", "Sample" },
39 { "filter_1_", "Filter 1", true },
40 { "filter_2_", "Filter 2", true },
41 { "filter_fx_", "Filter FX", true },
42 { "", "" },
43 { "lfo_", "LFOs" },
44 { "random_", "Randoms" },
45 { "env_", "Envelopes" },
46 { "modulation_", "Mod Matrix" },
47 { "", "" },
48 { "chorus_", "Chorus" },
49 { "compressor_", "Compressor" },
50 { "delay_", "Delay" },
51 { "distortion_", "Distortion" },
52 { "phaser_", "Phaser" },
53 { "flanger_", "Flanger" },
54 { "reverb_", "Reverb" },
55 { "delay_", "Delay" },
56 { "eq_", "Equalizer" },
57 };
58
59 PopupItems createSubMenuForParameterPrefix(const std::string& name, const String& prefix,
60 const std::vector<String>& parameter_names, bool local) {
61 PopupItems items(name);
62 int prefix_length = static_cast<int>(prefix.length());
63 int index = 0;
64 for (const String& parameter_name : parameter_names) {
65 if (parameter_name.substring(0, prefix_length) == prefix) {
66 std::string display_name;
67 if (local)
68 display_name = vital::Parameters::getDetails(parameter_name.toStdString()).local_description;
69 else
70 display_name = vital::Parameters::getDetails(parameter_name.toStdString()).display_name;
71 items.addItem(index, display_name);
72 }
73
74 index++;
75 }
76 return items;
77 }
78
79 PopupItems createMiscSubMenu(const std::string& name, const std::vector<String>& parameter_names) {
80 int index = 0;
81 PopupItems items(name);
82 for (const String& parameter_name : parameter_names) {
83 bool prefix_match = false;
84 for (const auto& prefix : kDestinationSubMenuPrefixes) {
85 int prefix_length = static_cast<int>(prefix.prefix.length());
86 if (prefix_length && parameter_name.substring(0, prefix_length) == String(prefix.prefix))
87 prefix_match = true;
88 }
89
90 if (!prefix_match && parameter_name != String(kNoConnectionString)) {
91 std::string display_name = vital::Parameters::getDetails(parameter_name.toStdString()).display_name;
92 items.addItem(index, display_name);
93 }
94 index++;
95 }
96 return items;
97 }
98
99 bool compareIndexAscending(const ModulationMatrixRow* left, const ModulationMatrixRow* right) {
100 return left->index() < right->index();
101 }
102
103 bool compareIndexDescending(const ModulationMatrixRow* left, const ModulationMatrixRow* right) {
104 return left->index() > right->index();
105 }
106
107 bool compareSourceAscending(const ModulationMatrixRow* left, const ModulationMatrixRow* right) {
108 return left->source() < right->source();
109 }
110
111 bool compareSourceDescending(const ModulationMatrixRow* left, const ModulationMatrixRow* right) {
112 return left->source() > right->source();
113 }
114
115 bool compareBipolarAscending(const ModulationMatrixRow* left, const ModulationMatrixRow* right) {
116 return left->bipolar() < right->bipolar();
117 }
118
119 bool compareBipolarDescending(const ModulationMatrixRow* left, const ModulationMatrixRow* right) {
120 return left->bipolar() > right->bipolar();
121 }
122
123 bool compareStereoAscending(const ModulationMatrixRow* left, const ModulationMatrixRow* right) {
124 return left->stereo() < right->stereo();
125 }
126
127 bool compareStereoDescending(const ModulationMatrixRow* left, const ModulationMatrixRow* right) {
128 return left->stereo() > right->stereo();
129 }
130
131 bool compareMorphAscending(const ModulationMatrixRow* left, const ModulationMatrixRow* right) {
132 return left->morph() < right->morph();
133 }
134
135 bool compareMorphDescending(const ModulationMatrixRow* left, const ModulationMatrixRow* right) {
136 return left->morph() > right->morph();
137 }
138
139 bool compareAmountAscending(const ModulationMatrixRow* left, const ModulationMatrixRow* right) {
140 return left->amount() < right->amount();
141 }
142
143 bool compareAmountDescending(const ModulationMatrixRow* left, const ModulationMatrixRow* right) {
144 return left->amount() > right->amount();
145 }
146
147 bool compareDestinationAscending(const ModulationMatrixRow* left, const ModulationMatrixRow* right) {
148 return left->destination() < right->destination();
149 }
150
151 bool compareDestinationDescending(const ModulationMatrixRow* left, const ModulationMatrixRow* right) {
152 return left->destination() > right->destination();
153 }
154
155 bool compareConnected(const ModulationMatrixRow* left, const ModulationMatrixRow* right) {
156 return left->connected() > right->connected();
157 }
158}
159
161 bool operator()(const String& first, const String& second) const {
162 return first.compareNatural(second) < 0;
163 }
164};
165
166class BypassButton : public SynthButton {
167 public:
168 BypassButton(String name, String on, String off) : SynthButton(std::move(name)),
169 on_(std::move(on)), off_(std::move(off)) { }
170
171 void buttonStateChanged() override {
172 if (getToggleState())
173 setText(on_);
174 else
175 setText(off_);
176 ToggleButton::buttonStateChanged();
177 }
178
179 private:
180 String on_;
181 String off_;
182};
183
185 public:
186 ModulationMeterReadouts() : BarRenderer(vital::kMaxModulationConnections, false),
187 parent_(nullptr), modulation_amounts_(), scroll_offset_(0), modulation_active_() { }
188
190 std::string modulation_prefix = "modulation_amount_";
191 for (int i = 0; i < vital::kMaxModulationConnections; ++i) {
192 std::string number = std::to_string(i + 1);
193 modulation_amounts_[i] = parent_->getSynth()->getStatusOutput(modulation_prefix + number);
194 }
195 }
196
197 void parentHierarchyChanged() override {
198 if (parent_)
199 return;
200
201 parent_ = findParentComponentOfClass<SynthGuiInterface>();
202
203 if (parent_ != nullptr)
205 }
206
207 void updatePositions(int index) {
208 if (parent_ == nullptr)
209 return;
210
211 float width = getWidth();
212 float height = getHeight();
213 setBarWidth(vital::kMaxModulationConnections * modulation_bounds_[0].getHeight() / height);
214
215 for (int i = 0; i < vital::kMaxModulationConnections; ++i) {
216 if (modulation_active_[i]) {
217 float min_x = 2.0f * modulation_bounds_[i].getX() / width - 1.0f;
218 float max_x = 2.0f * modulation_bounds_[i].getRight() / width - 1.0f;
219 float y = 1.0f - 2.0f * (modulation_bounds_[i].getBottom() - scroll_offset_) / height;
220
221 float value = modulation_amounts_[i]->value()[index];
223 value = 0.0f;
224 float t = vital::utils::clamp(0.5f * (value + 1.0f), 0.0f, 1.0f);
225 float x = vital::utils::interpolate(min_x, max_x, t);
226 float center_x = (max_x + min_x) / 2.0f;
227 positionBar(i, center_x, y, x - center_x, 0.0f);
228 }
229 else
230 positionBar(i, 0.0f, 0.0f, 0.0f, 0.0f);
231 }
232 }
233
234 void render(OpenGlWrapper& open_gl, bool animate) override {
235 if (animate) {
237 setColor(findColour(Skin::kModulationMeterLeft, true));
238 BarRenderer::render(open_gl, animate);
239
241 setColor(findColour(Skin::kModulationMeterRight, true));
242 BarRenderer::render(open_gl, animate);
243 }
244 }
245
246 void paintBackground(Graphics& g) override { }
247
248 void setMeterBounds(int i, Rectangle<int> bounds) {
249 modulation_bounds_[i] = bounds;
250 }
251
252 void setMeterActive(int i, bool active) {
253 modulation_active_[i] = active;
254 }
255
256 void setScrollOffset(int offset) {
257 scroll_offset_ = offset;
258 }
259
260 private:
261 SynthGuiInterface* parent_;
262 const vital::StatusOutput* modulation_amounts_[vital::kMaxModulationConnections];
263 Rectangle<int> modulation_bounds_[vital::kMaxModulationConnections];
264 int scroll_offset_;
265 bool modulation_active_[vital::kMaxModulationConnections];
266};
267
269 // Returns the user-friendly name for the given index.
270 int index = std::round(value);
271 return ModulationMatrix::getMenuSourceDisplayName(selections_->at(index));
272}
273
274void ModulationSelector::setValueFromName(const String& name, NotificationType notification_type) {
275 // Sets the selector's value based on a given name.
276 int value = getValue();
277 for (int i = 0; i < selections_->size(); ++i) {
278 if (selections_->at(i) == name) {
279 if (value != i)
280 setValue(i, notification_type);
281
282 redoImage();
283 return;
284 }
285 }
286 if (value != 0) {
287 setValue(0, notification_type);
288 redoImage();
289 }
290}
291
292void ModulationSelector::mouseDown(const juce::MouseEvent &e) {
293 // Shows popup menu on mouse down.
294 static constexpr int kWideWidth = 420;
295 if (dual_menu_) {
296 parent_->showDualPopupSelector(this, Point<int>(0, getHeight()), kWideWidth * parent_->getSizeRatio(),
297 *popup_items_, [=](int selection) { setValue(selection); });
298 }
299 else {
300 parent_->showPopupSelector(this, Point<int>(0, getHeight()), *popup_items_,
301 [=](int selection) { setValue(selection); });
302 }
303}
304
305ModulationMatrixRow::ModulationMatrixRow(int index, PopupItems* source_items, PopupItems* destination_items,
306 const std::vector<String>* sources, const std::vector<String>* destinations) :
307 SynthSection(String("MOD ") + String(index)), index_(index), connection_(nullptr), parent_(nullptr),
308 last_source_value_(0.0f), last_destination_value_(0.0f), last_amount_value_(0.0),
309 updating_(false), selected_(false) {
312 highlight_.setVisible(false);
313
314 source_ = std::make_unique<ModulationSelector>("source", sources, source_items, false);
315 addAndMakeVisible(source_.get());
316 addOpenGlComponent(source_->getImageComponent());
317 source_->setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
318 source_->setLookAndFeel(TextLookAndFeel::instance());
319 source_->addListener(this);
320 source_->setScrollWheelEnabled(false);
321
322 destination_ = std::make_unique<ModulationSelector>("destination", destinations, destination_items, true);
323 addAndMakeVisible(destination_.get());
324 addOpenGlComponent(destination_->getImageComponent());
325 destination_->setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
326 destination_->setLookAndFeel(TextLookAndFeel::instance());
327 destination_->addListener(this);
328 destination_->setScrollWheelEnabled(false);
329
330 amount_slider_ = std::make_unique<SynthSlider>(String("modulation_") + String(index + 1) + String("_amount"));
332 amount_slider_->setBipolar(true);
333 amount_slider_->setSliderStyle(Slider::LinearBar);
334
335 power_slider_ = std::make_unique<SynthSlider>(String("modulation_") + String(index + 1) + String("_power"));
337 power_slider_->setLookAndFeel(CurveLookAndFeel::instance());
338 power_slider_->setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
339
340 String bipolar_text = String("modulation_") + String(index + 1) + String("_bipolar");
341 bipolar_ = std::make_unique<OpenGlShapeButton>(bipolar_text);
342 bipolar_->useOnColors(true);
343 bipolar_->setClickingTogglesState(true);
344 addAndMakeVisible(bipolar_.get());
345 addOpenGlComponent(bipolar_->getGlComponent());
346 bipolar_->addListener(this);
347 bipolar_->setShape(Paths::bipolar());
348
349 stereo_ = std::make_unique<SynthButton>(String("modulation_") + String(index + 1) + String("_stereo"));
350 stereo_->setText("L/R");
351 stereo_->setNoBackground();
352 stereo_->setLookAndFeel(TextLookAndFeel::instance());
353 addButton(stereo_.get());
354
355 String bypass_string = String("modulation_") + String(index + 1) + String("_bypass");
356 bypass_ = std::make_unique<BypassButton>(bypass_string, "X", String(index + 1));
357 bypass_->setText(String(index + 1));
358 bypass_->setNoBackground();
359 bypass_->setLookAndFeel(TextLookAndFeel::instance());
360 addButton(bypass_.get());
361
363}
364
367 highlight_.setBounds(getLocalBounds());
368 highlight_.setColor(findColour(Skin::kLightenScreen, true));
369
370 int width = getWidth();
371 int x_padding = width * kPaddingWidthPercent;
372 int y_padding = size_ratio_ * 3;
373 int source_width = width * kSourceWidthPercent;
374 int destination_width = width * kDestinationWidthPercent;
375 int component_height = getHeight() - 2 * y_padding;
376 int slider_height = getSliderWidth();
377 int text_component_height = component_height * 0.7f;
378 int text_y = (getHeight() - text_component_height) / 2.0f;
379
380 bypass_->setBounds(y_padding, text_y, getHeight() - 2 * y_padding, text_component_height);
381 source_->setBounds(bypass_->getRight() + y_padding, y_padding, source_width, component_height);
382 source_->redoImage();
383
384 int bipolar_x = source_->getRight() + x_padding / 2.0f;
385 bipolar_->setBounds(bipolar_x, y_padding, component_height, component_height);
386
387 int stereo_x = bipolar_->getRight() + x_padding;
388 int expand = x_padding / 2;
389 stereo_->setBounds(stereo_x - expand, text_y, component_height + 2 * expand, text_component_height);
390
391 int power_x = stereo_x + component_height + x_padding;
392 power_slider_->setBounds(power_x, y_padding, component_height, component_height);
393
394 int destination_x = width - x_padding - destination_width;
395 destination_->setBounds(destination_x, y_padding, destination_width + x_padding / 2.0f, component_height);
396 destination_->redoImage();
397
398 int widget_margin = findValue(Skin::kWidgetMargin);
399 int amount_x = power_slider_->getRight() + x_padding - widget_margin;
400 int amount_width = destination_x - amount_x - x_padding / 2.0f + 2 * widget_margin;
401 amount_slider_->setBounds(amount_x, (getHeight() - slider_height + 1) / 2, amount_width, slider_height);
402}
403
405 g.setColour(findColour(Skin::kBody, true));
406 g.fillRect(getLocalBounds());
407
408 g.setColour(findColour(Skin::kTextComponentBackground, true));
410 g.fillRoundedRectangle(source_->getBounds().toFloat(), rounding);
411 g.fillRoundedRectangle(destination_->getBounds().toFloat(), rounding);
412
415}
416
419 if (button == bipolar_.get())
420 power_slider_->setBipolar(bipolar_->getToggleState());
421
422 for (Listener* listener : listeners_)
423 listener->rowSelected(this);
424}
425
427 if (updating_ || connection_ == nullptr)
428 return;
429
430 source_->setValueFromName(connection_->source_name, dontSendNotification);
431 source_->redoImage();
432 destination_->setValueFromName(connection_->destination_name, dontSendNotification);
433 destination_->redoImage();
434
436}
437
439 bipolar_->setToggleState(connection_->modulation_processor->isBipolar(), dontSendNotification);
440 stereo_->setToggleState(connection_->modulation_processor->isStereo(), dontSendNotification);
441 power_slider_->setBipolar(connection_->modulation_processor->isBipolar());
442 bypass_->setToggleState(connection_->modulation_processor->isBypassed(), dontSendNotification);
443
444 last_source_value_ = source_->getValue();
446
447 amount_slider_->setDisplayMultiply(1.0f);
451 amount_slider_->setDisplayMultiply(details.max - details.min);
452
453 float current_value = connection_->modulation_processor->currentBaseValue();
454 if (current_value != last_amount_value_) {
455 amount_slider_->setValue(current_value, dontSendNotification);
456 amount_slider_->redoImage();
457 last_amount_value_ = current_value;
458 }
459 }
460}
461
463 return source_->connected() && destination_->connected();
464}
465
466bool ModulationMatrixRow::matchesSourceAndDestination(const std::string& source, const std::string& destination) const {
467 String source_name = source_->getSelection();
468 String destination_name = destination_->getSelection();
469 return source == source_name && destination == destination_name;
470}
471
472void ModulationMatrixRow::sliderValueChanged(Slider* changed_slider) {
473 updating_ = true;
474 String source_name = source_->getSelection();
475 String destination_name = destination_->getSelection();
476
477 if (changed_slider == source_.get() || changed_slider == destination_.get()) {
478 if (last_source_value_ > 0.5f && last_destination_value_ > 0.5f)
480 if (source_->getValue() > 0.5 && destination_->getValue() > 0.5) {
481 connection_->source_name = source_name.toStdString();
482 connection_->destination_name = destination_name.toStdString();
484 }
485 }
486 else {
487 SynthSection::sliderValueChanged(changed_slider);
489 }
490
491 last_source_value_ = source_->getValue();
493 updating_ = false;
494
495 for (Listener* listener : listeners_)
496 listener->rowSelected(this);
497}
498
500 Rectangle<int> bounds = destination_->getBounds();
501 bounds.setHeight(2);
502 return bounds;
503}
504
506 if (select == selected_)
507 return;
508
510 highlight_.setVisible(selected_);
511}
512
513String ModulationMatrix::getMenuSourceDisplayName(const String& original) {
514 if (original == "aftertouch")
515 return "After Touch";
516
517 String modified = original.replaceFirstOccurrenceOf("control_", "");
518 StringArray tokens;
519 tokens.addTokens(modified, "_", "");
520 String result;
521 for (const String& token : tokens) {
522 String capitalized = String(token.substring(0, 1)).toUpperCase() + token.substring(1);
523 result += capitalized + " ";
524 }
525
526 return result.trim();
527}
528
529String ModulationMatrix::getUiSourceDisplayName(const String& original) {
530 return getMenuSourceDisplayName(original).toUpperCase();
531}
532
534 SynthSection("MODULATION MATRIX"), container_("Container") {
535 const NaturalStringComparator comparator;
536
537 sort_column_ = kNumber;
538 sort_ascending_ = true;
539 selected_index_ = 0;
540 num_shown_ = 1;
541
542 addAndMakeVisible(viewport_);
543 viewport_.setViewedComponent(&container_);
544 viewport_.addListener(this);
545 viewport_.setScrollBarsShown(false, false, true, false);
546
547 source_strings_.push_back(kNoConnectionString);
548 for (const auto& source : sources)
549 source_strings_.push_back(source.first);
550
551 std::sort(source_strings_.begin(), source_strings_.end(), comparator);
552
553 destination_strings_.push_back(kNoConnectionString);
554 for (const auto& destination : destinations)
555 destination_strings_.push_back(destination.first);
556
557 std::sort(destination_strings_.begin(), destination_strings_.end(), comparator);
558
559 source_popup_items_.addItem(0, "-");
560 for (int i = 1; i < source_strings_.size(); ++i) {
561 std::string display_name = getMenuSourceDisplayName(source_strings_[i]).toStdString();
562 source_popup_items_.addItem(i, display_name);
563 }
564
565 destination_popup_items_.addItem(0, kNoConnectionString);
566 destination_popup_items_.addItem(createMiscSubMenu("Global", destination_strings_));
567 for (const auto& sub_menu_prefix : kDestinationSubMenuPrefixes) {
568 if (sub_menu_prefix.prefix.empty())
569 destination_popup_items_.addItem(-1, "");
570 else {
571 bool local = sub_menu_prefix.local_description;
572 PopupItems sub_items = createSubMenuForParameterPrefix(sub_menu_prefix.name, sub_menu_prefix.prefix,
573 destination_strings_, local);
574 destination_popup_items_.addItem(sub_items);
575 }
576 }
577
578 for (int i = 0; i < vital::kMaxModulationConnections; ++i) {
579 rows_[i] = std::make_unique<ModulationMatrixRow>(i, &source_popup_items_, &destination_popup_items_,
580 &source_strings_, &destination_strings_);
581 rows_[i]->addListener(this);
582 row_order_.push_back(rows_[i].get());
583 addSubSection(rows_[i].get(), false);
584 container_.addAndMakeVisible(rows_[i].get());
585 }
586
587 scroll_bar_ = std::make_unique<OpenGlScrollBar>();
588 addAndMakeVisible(scroll_bar_.get());
589 addOpenGlComponent(scroll_bar_->getGlComponent());
590 scroll_bar_->addListener(this);
591 scroll_bar_->setAlwaysOnTop(true);
592
593 readouts_ = std::make_unique<ModulationMeterReadouts>();
594 addOpenGlComponent(readouts_.get());
595 readouts_->setInterceptsMouseClicks(false, false);
596
597 grid_size_x_ = std::make_unique<SynthSlider>("grid_size_x");
598 grid_size_x_->setRange(1.0, LineEditor::kMaxGridSizeX, 1.0);
599 grid_size_x_->setValue(kDefaultGridSizeX);
600 grid_size_x_->setLookAndFeel(TextLookAndFeel::instance());
601 grid_size_x_->setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
602 addSlider(grid_size_x_.get());
603 grid_size_x_->addListener(this);
604 grid_size_x_->setDoubleClickReturnValue(true, kDefaultGridSizeX);
605 grid_size_x_->setMaxDecimalPlaces(0);
606 grid_size_x_->setTextHeightPercentage(0.6f);
607 grid_size_x_->setSensitivity(0.2f);
608 grid_size_x_->overrideValue(Skin::kTextComponentOffset, 0.0f);
609
610 grid_size_y_ = std::make_unique<SynthSlider>("grid_size_y");
611 grid_size_y_->setRange(1.0, LineEditor::kMaxGridSizeY, 1.0);
612 grid_size_y_->setValue(kDefaultGridSizeY);
613 grid_size_y_->setLookAndFeel(TextLookAndFeel::instance());
614 grid_size_y_->setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
615 addSlider(grid_size_y_.get());
616 grid_size_y_->addListener(this);
617 grid_size_y_->setDoubleClickReturnValue(true, kDefaultGridSizeY);
618 grid_size_y_->setMaxDecimalPlaces(0);
619 grid_size_y_->setTextHeightPercentage(0.6f);
620 grid_size_y_->setSensitivity(0.2f);
621 grid_size_y_->overrideValue(Skin::kTextComponentOffset, 0.0f);
622
623 paint_ = std::make_unique<OpenGlShapeButton>("paint");
624 paint_->useOnColors(true);
625 paint_->setClickingTogglesState(true);
626 addAndMakeVisible(paint_.get());
627 addOpenGlComponent(paint_->getGlComponent());
628 paint_->addListener(this);
629 paint_->setShape(Paths::paintBrush());
630
631 remap_name_ = std::make_unique<PlainTextComponent>("remap_name", String("MOD REMAP ") + String(1));
632 addOpenGlComponent(remap_name_.get());
633 remap_name_->setFontType(PlainTextComponent::kTitle);
634
635 paint_pattern_ = std::make_unique<PaintPatternSelector>("paint_pattern");
636 addSlider(paint_pattern_.get());
637 paint_pattern_->addListener(this);
638 paint_pattern_->setRange(0.0f, LfoSection::kNumPaintPatterns - 1.0f, 1.0f);
639 paint_pattern_->setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
640 paint_pattern_->setStringLookup(strings::kPaintPatternNames);
641 paint_pattern_->setLookAndFeel(TextLookAndFeel::instance());
642 paint_pattern_->setLongStringLookup(strings::kPaintPatternNames);
643 paint_pattern_->setTextHeightPercentage(0.45f);
644 paint_pattern_->setActive(false);
645 paint_pattern_->overrideValue(Skin::kTextComponentOffset, 0.0f);
646
647 smooth_ = std::make_unique<OpenGlShapeButton>("smooth");
648 smooth_->useOnColors(true);
649 smooth_->setClickingTogglesState(true);
650 addAndMakeVisible(smooth_.get());
651 addOpenGlComponent(smooth_->getGlComponent());
652 smooth_->addListener(this);
653 smooth_->setShape(Paths::halfSinCurve());
654
655 preset_selector_ = std::make_unique<PresetSelector>();
656 addSubSection(preset_selector_.get());
657 preset_selector_->addListener(this);
658 setPresetSelector(preset_selector_.get());
659 preset_selector_->setText("Linear");
660
662}
663
665
667 if (getWidth() <= 0)
668 return;
669
670 int row_height = getRowHeight();
671 int total_height = kRowPadding + num_shown_ * (row_height + kRowPadding);
672 total_height = std::max(total_height, viewport_.getHeight());
673 container_.setBounds(container_.getX(), container_.getY(), getWidth(), total_height);
674
675 int mult = getPixelMultiple();
676 Image background_image = Image(Image::ARGB, getWidth() * mult, total_height * mult, true);
677 Graphics background_graphics(background_image);
678 background_graphics.addTransform(AffineTransform::scale(mult));
679
680 for (int i = 0; i < num_shown_; ++i) {
681 ModulationMatrixRow* row = row_order_[i];
682
683 background_graphics.saveState();
684 Rectangle<int> bounds = row->getBounds();
685 background_graphics.reduceClipRegion(bounds);
686 background_graphics.setOrigin(bounds.getTopLeft());
687 row->paintBackground(background_graphics);
688 background_graphics.restoreState();
689 }
690 background_.setOwnImage(background_image);
691}
692
694 int padding = getPadding();
695
696 Rectangle<int> matrix_bounds(0, 0, getWidth(), viewport_.getBottom());
697 int remap_y = matrix_bounds.getBottom() + padding;
698 Rectangle<int> remap_bounds(0, remap_y, getWidth(), getHeight() - remap_y);
699 paintBody(g, matrix_bounds);
700 paintBody(g, remap_bounds);
701
702 int title_width = getTitleWidth();
703 int row_height = getRowHeight();
704
705 int width = getWidth();
706 int x_padding = width * kPaddingWidthPercent;
707 int y_padding = size_ratio_ * 3;
708 int source_width = width * kSourceWidthPercent;
709 int destination_width = width * kDestinationWidthPercent;
710 int component_height = row_height - 2 * y_padding;
711 int bipolar_x = source_width + row_height;
712 int stereo_x = bipolar_x + component_height + x_padding;
713 int morph_x = stereo_x + component_height + x_padding;
714 int amount_x = morph_x + component_height + x_padding;
715 int destination_x = getWidth() - destination_width - x_padding;
716
717 g.setColour(findColour(Skin::kLightenScreen, true));
718 g.fillRect(row_height, 0, 1, title_width);
719 g.fillRect(morph_x, 0, 1, title_width);
720 g.fillRect(bipolar_x, 0, 1, title_width);
721 g.fillRect(stereo_x, 0, 1, title_width);
722 g.fillRect(amount_x, 0, 1, title_width);
723 g.fillRect(destination_x, 0, 1, title_width);
724
725 g.setColour(findColour(Skin::kTextComponentText, true));
726 Font regular = Fonts::instance()->proportional_light().withPointHeight(title_width * 0.4f);
727 Font sorted = Fonts::instance()->proportional_regular().withPointHeight(title_width * 0.4f);
728 g.setFont(sort_column_ == kNumber ? sorted : regular);
729 g.drawText("#", 0, 0, row_height, title_width, Justification::centred);
730 g.setFont(sort_column_ == kSource ? sorted : regular);
731 g.drawText("SOURCE", row_height, 0, bipolar_x - row_height, title_width, Justification::centred);
732 g.setFont(sort_column_ == kBipolar ? sorted : regular);
733 g.drawText("BIPOLAR", bipolar_x, 0, stereo_x - bipolar_x, title_width, Justification::centred);
734 g.setFont(sort_column_ == kStereo ? sorted : regular);
735 g.drawText("STEREO", stereo_x, 0, morph_x - stereo_x, title_width, Justification::centred);
736 g.setFont(sort_column_ == kMorph ? sorted : regular);
737 g.drawText("MORPH", morph_x, 0, amount_x - morph_x, title_width, Justification::centred);
738 g.setFont(sort_column_ == kAmount ? sorted : regular);
739 g.drawText("AMOUNT", amount_x, 0, destination_x - amount_x, title_width, Justification::centred);
740 g.setFont(sort_column_ == kDestination ? sorted : regular);
741 g.drawText("DESTINATION", destination_x - 0.5f * x_padding, 0,
742 getWidth() - destination_x + 0.5f * x_padding, title_width, Justification::centred);
743
744 int rounding = findValue(Skin::kBodyRounding);
745 int widget_rounding = getWidgetRounding();
746 g.setColour(findColour(Skin::kBackground, true));
747
748 g.saveState();
749 g.reduceClipRegion(0, title_width, getWidth(), getHeight());
750 g.fillRoundedRectangle(0, 0, getWidth(), viewport_.getBottom(), rounding);
751 g.restoreState();
752
753 paintBorder(g, matrix_bounds);
754 paintBorder(g, remap_bounds);
755 viewport_.setColour(ScrollBar::thumbColourId, findColour(Skin::kLightenScreen, true));
756
757 if (map_editors_[selected_index_] && map_editors_[0]) {
758 g.saveState();
759 Rectangle<int> bounds = getLocalArea(map_editors_[0].get(), map_editors_[0]->getLocalBounds());
760 g.reduceClipRegion(bounds);
761 g.setOrigin(bounds.getTopLeft());
762 map_editors_[selected_index_]->paintBackground(g);
763 g.restoreState();
764 }
765
766 g.saveState();
767 Rectangle<int> preset_bounds = getLocalArea(preset_selector_.get(), preset_selector_->getLocalBounds());
768 g.reduceClipRegion(preset_bounds);
769 g.setOrigin(preset_bounds.getTopLeft());
770 preset_selector_->paintBackground(g);
771 g.restoreState();
772
773 g.setColour(findColour(Skin::kPopupSelectorBackground, true));
774 g.fillRoundedRectangle(paint_->getX(), paint_->getY(),
775 paint_pattern_->getRight() - paint_->getX(), paint_->getHeight(), widget_rounding);
776 g.fillRoundedRectangle(grid_size_x_->getX(), grid_size_x_->getY(),
777 grid_size_y_->getRight() - grid_size_x_->getX(), grid_size_x_->getHeight(), widget_rounding);
778
779 int grid_label_x = grid_size_x_->getX();
780 int grid_size_width = std::max(1, grid_size_y_->getRight() - grid_label_x);
781 setLabelFont(g);
782 g.setColour(findColour(Skin::kBodyText, true));
783 g.drawText("-", grid_label_x, grid_size_x_->getY(), grid_size_width, grid_size_x_->getHeight(),
784 Justification::centred, false);
785
789}
790
792 Rectangle<int> matrix_bounds(0, 0, getWidth(), viewport_.getBottom());
793 paintTabShadow(g, matrix_bounds);
794
795 int remap_y = viewport_.getBottom() + getPadding();
796 Rectangle<int> remap_bounds(0, remap_y, getWidth(), getHeight() - remap_y);
797 paintTabShadow(g, remap_bounds);
798}
799
801 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
802 if (parent == nullptr)
803 return;
804
806 for (int i = 0; i < vital::kMaxModulationConnections; ++i) {
807 rows_[i]->setGuiParent(parent);
808 vital::ModulationConnection* connection = bank.atIndex(i);
809 rows_[i]->setConnection(connection);
810
811 if (map_editors_[i] == nullptr) {
812 LineGenerator* map_generator = connection->modulation_processor->lineMapGenerator();
813 std::string name = "modulation_source_" + std::to_string(i + 1);
814 map_editors_[i] = std::make_unique<LineMapEditor>(map_generator, name);
815 map_editors_[i]->setPaintPattern(LfoSection::getPaintPattern(paint_pattern_->getValue()));
816 map_editors_[i]->addListener(this);
817 addOpenGlComponent(map_editors_[i].get());
818 addOpenGlComponent(map_editors_[i]->getTextEditorComponent());
819 map_editors_[i]->setVisible(false);
820 }
821 }
822 rows_[0]->select(true);
823 map_editors_[0]->setVisible(true);
824}
825
827 int row_height = getRowHeight();
828 int matrix_width = getWidth();
829 int widget_margin = getWidgetMargin();
830 int title_width = getTitleWidth();
831
832 int remap_section_y = viewport_.getBottom() + getPadding();
833 int remap_y = remap_section_y + title_width;
834 Rectangle<int> mapping_bounds(widget_margin, remap_y,
835 getWidth() - 2 * widget_margin, getHeight() - remap_y - widget_margin);
836
837 for (int i = 0; i < vital::kMaxModulationConnections; ++i) {
838 row_order_[i]->setBounds(0, kRowPadding + i * (row_height + kRowPadding), matrix_width, row_height);
839
840 float size_ratio = getSizeRatio();
841 if (map_editors_[i]) {
842 map_editors_[i]->setBounds(mapping_bounds);
843 map_editors_[i]->setSizeRatio(size_ratio);
844 }
845 }
846}
847
849 static constexpr float kScrollBarWidth = 13.0f;
850
852
853 int row_height = getRowHeight();
854 int title_width = getTitleWidth();
855 int widget_margin = getWidgetMargin();
856
857 int matrix_height = (row_height + kRowPadding) * kMatrixHeightInRows + kRowPadding;
858 int matrix_width = getWidth();
859 viewport_.setBounds(0, title_width, matrix_width, matrix_height);
861
862 int preset_x = getWidth() / 2;
863 int top_bar_height = title_width - 2 * widget_margin;
864 int top_bar_y = viewport_.getBottom() + getPadding() + widget_margin;
865 preset_selector_->setBounds(preset_x, top_bar_y, getWidth() - preset_x - widget_margin, top_bar_height);
866
867 smooth_->setBounds(preset_x - title_width - widget_margin, top_bar_y, title_width, top_bar_height);
868 int grid_y_x = smooth_->getX() - title_width - widget_margin;
869 int grid_x_x = grid_y_x - title_width - widget_margin;
870 grid_size_y_->setBounds(grid_y_x, top_bar_y, title_width, top_bar_height);
871 grid_size_x_->setBounds(grid_x_x, top_bar_y, title_width, top_bar_height);
872
873 paint_pattern_->setPadding(getWidgetMargin());
874 int paint_pattern_width = 3 * top_bar_height;
875 paint_pattern_->setBounds(grid_x_x - paint_pattern_width - widget_margin, top_bar_y,
876 paint_pattern_width, top_bar_height);
877
878 paint_->setBounds(paint_pattern_->getX() - top_bar_height, top_bar_y, top_bar_height, top_bar_height);
879
880 remap_name_->setBounds(widget_margin, top_bar_y, paint_->getX() - 2 * widget_margin, top_bar_height);
881 remap_name_->setTextSize(title_width * 0.45f);
882 remap_name_->setColor(findColour(Skin::kHeadingText, true));
883
885
886 int container_height = kRowPadding + num_shown_ * (row_height + kRowPadding);
887 container_.setBounds(0, title_width, matrix_width, container_height);
888
889 int scroll_bar_width = size_ratio_ * kScrollBarWidth;
890 scroll_bar_->setBounds(getWidth() - scroll_bar_width - 1, title_width, scroll_bar_width, matrix_height);
891 scroll_bar_->setColor(findColour(Skin::kLightenScreen, true));
893
895}
896
898 readouts_->setBounds(viewport_.getBounds());
899 for (int i = 0; i < vital::kMaxModulationConnections; ++i)
900 readouts_->setMeterBounds(i, rows_[i]->getMeterBounds() + rows_[i]->getPosition());
901}
902
903void ModulationMatrix::sliderValueChanged(Slider* changed_slider) {
904 if (changed_slider == grid_size_x_.get()) {
905 if (map_editors_[selected_index_])
906 map_editors_[selected_index_]->setGridSizeX(grid_size_x_->getValue());
907 }
908 else if (changed_slider == grid_size_y_.get()) {
909 if (map_editors_[selected_index_])
910 map_editors_[selected_index_]->setGridSizeY(grid_size_y_->getValue());
911 }
912 else if (changed_slider == paint_pattern_.get()) {
913 if (map_editors_[selected_index_])
914 map_editors_[selected_index_]->setPaintPattern(LfoSection::getPaintPattern(paint_pattern_->getValue()));
915 }
916 else
917 SynthSection::sliderValueChanged(changed_slider);
918}
919
920void ModulationMatrix::buttonClicked(Button* clicked_button) {
921 if (clicked_button == paint_.get()) {
922 if (map_editors_[selected_index_])
923 map_editors_[selected_index_]->setPaint(paint_->getToggleState());
924 paint_pattern_->setActive(paint_->getToggleState());
925 }
926 else if (clicked_button == smooth_.get()) {
927 if (map_editors_[selected_index_])
928 map_editors_[selected_index_]->setSmooth(smooth_->getToggleState());
929 }
930 else
931 SynthSection::buttonClicked(clicked_button);
932}
933
936 if (map_editors_[selected_index_])
937 smooth_->setToggleState(map_editors_[selected_index_]->getSmooth(), dontSendNotification);
938}
939
941 if (getWidth() <= 0 || getHeight() <= 0)
942 return;
943
944 int i = 0;
945 for (auto& row : rows_) {
946 row->updateDisplay();
947 row->setActive(row->connected());
948 readouts_->setMeterActive(i++, row->connected());
949 }
950
951 if (map_editors_[selected_index_])
952 map_editors_[selected_index_]->setActive(rows_[selected_index_]->connected());
953
954 sort();
955}
956
958 rows_[index]->updateDisplayValue();
959 rowSelected(rows_[index].get());
960}
961
963 if (row_order_.size() != vital::kMaxModulationConnections)
964 return;
965
966 int num_show = 1;
967 for (int i = 0; i < vital::kMaxModulationConnections; ++i) {
968 if (row_order_[i]->isActive())
969 num_show = i + 2;
970 }
971 num_show = std::min(vital::kMaxModulationConnections, num_show);
972
973 for (int i = 0; i < vital::kMaxModulationConnections; ++i)
974 row_order_[i]->setVisible(i < num_show);
975
976 if (num_shown_ != num_show) {
977 num_shown_ = num_show;
979 }
980}
981
983 if (rows_[selected_row->index()]->selected())
984 return;
985
986 for (int i = 0; i < vital::kMaxModulationConnections; ++i) {
987 bool selected = rows_[i].get() == selected_row;
988 rows_[i]->select(selected);
989 if (map_editors_[i]) {
990 map_editors_[i]->setVisible(selected);
991
992 if (selected) {
993 map_editors_[i]->setActive(rows_[i]->connected());
994
995 selected_index_ = i;
996 smooth_->setToggleState(map_editors_[i]->getModel()->smooth(), dontSendNotification);
997 map_editors_[i]->setGridSizeX(grid_size_x_->getValue());
998 map_editors_[i]->setGridSizeY(grid_size_y_->getValue());
999 map_editors_[i]->setPaintPattern(LfoSection::getPaintPattern(paint_pattern_->getValue()));
1000 map_editors_[i]->setPaint(paint_->getToggleState());
1001 remap_name_->setText(String("MOD REMAP ") + String(i + 1));
1002 }
1003 }
1004 }
1005}
1006
1007void ModulationMatrix::mouseDown(const MouseEvent& e) {
1008 if (e.position.y > getTitleWidth())
1009 return;
1010
1011 int x = e.position.x;
1012 int width = getWidth();
1013 int x_padding = width * kPaddingWidthPercent;
1014 int row_height = getRowHeight();
1015 int y_padding = size_ratio_ * 3;
1016 int component_height = row_height - 2 * y_padding;
1017 int source_width = width * kSourceWidthPercent;
1018 int destination_width = width * kDestinationWidthPercent;
1019 int bipolar_x = source_width + 1.5f * x_padding;
1020 int stereo_x = bipolar_x + component_height + x_padding;
1021 int morph_x = stereo_x + component_height + x_padding;
1022 int amount_x = morph_x + component_height + x_padding;
1023 int destination_x = getWidth() - destination_width - 1.5f * x_padding;
1024
1025 SortColumn sort_column = kNumColumns;
1026 if (x < x_padding)
1027 sort_column = kNumber;
1028 else if (x < bipolar_x)
1029 sort_column = kSource;
1030 else if (x < stereo_x)
1031 sort_column = kBipolar;
1032 else if (x < morph_x)
1033 sort_column = kStereo;
1034 else if (x < amount_x)
1035 sort_column = kMorph;
1036 else if (x < destination_x)
1037 sort_column = kAmount;
1038 else
1039 sort_column = kDestination;
1040
1041 if (sort_column == sort_column_)
1042 sort_ascending_ = !sort_ascending_;
1043 else
1044 sort_ascending_ = true;
1045
1046 sort_column_ = sort_column;
1047 sort();
1048}
1049
1050void ModulationMatrix::sort() {
1051 if (sort_column_ == kNumber) {
1052 if (sort_ascending_)
1053 std::stable_sort(row_order_.begin(), row_order_.end(), compareIndexAscending);
1054 else
1055 std::stable_sort(row_order_.begin(), row_order_.end(), compareIndexDescending);
1056 }
1057 else if (sort_column_ == kSource) {
1058 if (sort_ascending_)
1059 std::stable_sort(row_order_.begin(), row_order_.end(), compareSourceAscending);
1060 else
1061 std::stable_sort(row_order_.begin(), row_order_.end(), compareSourceDescending);
1062 }
1063 else if (sort_column_ == kBipolar) {
1064 if (sort_ascending_)
1065 std::stable_sort(row_order_.begin(), row_order_.end(), compareBipolarAscending);
1066 else
1067 std::stable_sort(row_order_.begin(), row_order_.end(), compareBipolarDescending);
1068 }
1069 else if (sort_column_ == kStereo) {
1070 if (sort_ascending_)
1071 std::stable_sort(row_order_.begin(), row_order_.end(), compareStereoAscending);
1072 else
1073 std::stable_sort(row_order_.begin(), row_order_.end(), compareStereoDescending);
1074 }
1075 else if (sort_column_ == kMorph) {
1076 if (sort_ascending_)
1077 std::stable_sort(row_order_.begin(), row_order_.end(), compareMorphAscending);
1078 else
1079 std::stable_sort(row_order_.begin(), row_order_.end(), compareMorphDescending);
1080 }
1081 else if (sort_column_ == kAmount) {
1082 if (sort_ascending_)
1083 std::stable_sort(row_order_.begin(), row_order_.end(), compareAmountAscending);
1084 else
1085 std::stable_sort(row_order_.begin(), row_order_.end(), compareAmountDescending);
1086 }
1087 else if (sort_column_ == kDestination) {
1088 if (sort_ascending_)
1089 std::stable_sort(row_order_.begin(), row_order_.end(), compareDestinationAscending);
1090 else
1091 std::stable_sort(row_order_.begin(), row_order_.end(), compareDestinationDescending);
1092 }
1093
1094 std::stable_sort(row_order_.begin(), row_order_.end(), compareConnected);
1095
1100}
1101
1103 background_.init(open_gl);
1105}
1106
1108 OpenGlComponent::setViewPort(&viewport_, open_gl);
1109 ScopedLock open_gl_lock(open_gl_critical_section_);
1110
1111 float image_width = vital::utils::nextPowerOfTwo(background_.getImageWidth());
1112 float image_height = vital::utils::nextPowerOfTwo(background_.getImageHeight());
1113 int mult = getPixelMultiple();
1114 float width_ratio = image_width / (mult * viewport_.getWidth());
1115 float height_ratio = image_height / (mult * viewport_.getHeight());
1116
1117 float y_offset = (2.0f * viewport_.getViewPositionY()) / viewport_.getHeight();
1118
1119 background_.setTopLeft(-1.0f, 1.0f + y_offset);
1120 background_.setTopRight(-1.0 + 2.0f * width_ratio, 1.0f + y_offset);
1121 background_.setBottomLeft(-1.0f, 1.0f - 2.0f * height_ratio + y_offset);
1122 background_.setBottomRight(-1.0 + 2.0f * width_ratio, 1.0f - 2.0f * height_ratio + y_offset);
1123
1124 background_.setColor(Colours::white);
1125 background_.drawImage(open_gl);
1126 readouts_->setScrollOffset(viewport_.getViewPositionY());
1127 map_editors_[selected_index_]->setAnimate(rows_[selected_index_]->isActive());
1129}
1130
1132 background_.destroy(open_gl);
1134}
1135
1138 "", getCurrentFile(), -1);
1139 if (lfo_file.exists())
1140 loadFile(lfo_file);
1141
1142 updatePopupBrowser(this);
1143}
1144
1147 "", getCurrentFile(), 1);
1148 if (lfo_file.exists())
1149 loadFile(lfo_file);
1150
1151 updatePopupBrowser(this);
1152}
1153
1154void ModulationMatrix::textMouseDown(const MouseEvent& e) {
1155 static constexpr int kBrowserWidth = 500;
1156 static constexpr int kBrowserHeight = 250;
1157
1158 int browser_width = kBrowserWidth * size_ratio_;
1159 int browser_height = kBrowserHeight * size_ratio_;
1160 Rectangle<int> bounds(preset_selector_->getRight(), preset_selector_->getY(), browser_width, browser_height);
1161 bounds = getLocalArea(this, bounds);
1164}
1165
1166void ModulationMatrix::lineEditorScrolled(const MouseEvent& e, const MouseWheelDetails& wheel) {
1167 if (paint_->getToggleState())
1168 paint_pattern_->mouseWheelMove(e, wheel);
1169 else
1170 grid_size_x_->mouseWheelMove(e, wheel);
1171}
1172
1173void ModulationMatrix::togglePaintMode(bool enabled, bool temporary_switch) {
1174 paint_->setToggleState(enabled != temporary_switch, dontSendNotification);
1175 paint_pattern_->setActive(enabled != temporary_switch);
1176}
1177
1179 FileChooser import_box("Import LFO", LoadSave::getUserLfoDirectory(), String("*.") + vital::kLfoExtension);
1180 if (!import_box.browseForFileToOpen())
1181 return;
1182
1183 File choice = import_box.getResult();
1184 loadFile(choice.withFileExtension(vital::kLfoExtension));
1185}
1186
1188 FileChooser export_box("Export LFO", LoadSave::getUserLfoDirectory(), String("*.") + vital::kLfoExtension);
1189 if (!export_box.browseForFileToSave(true))
1190 return;
1191
1192 File choice = export_box.getResult();
1193 choice = choice.withFileExtension(vital::kLfoExtension);
1194 if (!choice.exists())
1195 choice.create();
1196 choice.replaceWithText(map_editors_[selected_index_]->getModel()->stateToJson().dump());
1197
1198 String name = choice.getFileNameWithoutExtension();
1199 map_editors_[selected_index_]->getModel()->setName(name.toStdString());
1200 preset_selector_->setText(name);
1201}
1202
1204 smooth_->setToggleState(map_editors_[selected_index_]->getModel()->smooth(), dontSendNotification);
1205 preset_selector_->setText(map_editors_[selected_index_]->getModel()->getName());
1206}
1207
1208void ModulationMatrix::loadFile(const File& file) {
1209 if (!file.exists())
1210 return;
1211
1212 current_file_ = file;
1213 LineMapEditor* current_editor = map_editors_[selected_index_].get();
1214
1215 try {
1216 json parsed_file = json::parse(file.loadFileAsString().toStdString(), nullptr, false);
1217 current_editor->getModel()->jsonToState(parsed_file);
1218 }
1219 catch (const json::exception& e) {
1220 return;
1221 }
1222
1223 String name = file.getFileNameWithoutExtension();
1224 current_editor->getModel()->setName(name.toStdString());
1225 current_editor->getModel()->setLastBrowsedFile(file.getFullPathName().toStdString());
1226 preset_selector_->setText(name);
1227
1228 current_editor->resetPositions();
1229 smooth_->setToggleState(current_editor->getModel()->smooth(), dontSendNotification);
1230}
1231
1232void ModulationMatrix::scrollBarMoved(ScrollBar* scroll_bar, double range_start) {
1233 ScopedLock open_gl_lock(open_gl_critical_section_);
1234 viewport_.setViewPosition(Point<int>(0, range_start));
1235}
1236
1238 scroll_bar_->setRangeLimits(0.0, container_.getHeight());
1239 scroll_bar_->setCurrentRange(scroll_bar_->getCurrentRangeStart(), viewport_.getHeight(), dontSendNotification);
1240}
A renderer for drawing a series of bars using OpenGL.
Definition bar_renderer.h:18
void setColor(const Colour &color)
Sets the color of the bars.
Definition bar_renderer.h:76
void positionBar(int index, float x, float y, float width, float height)
Positions a bar at a specific rectangle.
Definition bar_renderer.h:179
void setBarWidth(float bar_width)
Sets the relative width of each bar.
Definition bar_renderer.h:94
virtual void render(OpenGlWrapper &open_gl, bool animate) override
Renders the bars using the current OpenGL context.
Definition bar_renderer.cpp:149
Definition modulation_matrix.cpp:166
BypassButton(String name, String on, String off)
Definition modulation_matrix.cpp:168
void buttonStateChanged() override
Definition modulation_matrix.cpp:171
static CurveLookAndFeel * instance()
Gets the singleton instance of CurveLookAndFeel.
Definition curve_look_and_feel.h:52
Font & proportional_regular()
Returns a reference to the proportional regular font.
Definition fonts.h:22
Font & proportional_light()
Returns a reference to the proportional light font.
Definition fonts.h:28
static Fonts * instance()
Gets the singleton instance of the Fonts class.
Definition fonts.h:52
static std::vector< std::pair< float, float > > getPaintPattern(int pattern)
Retrieves a paint pattern as a vector of (x, y) pairs.
Definition lfo_section.h:54
@ kNumPaintPatterns
The number of available paint patterns.
Definition lfo_section.h:45
static constexpr int kMaxGridSizeY
Definition line_editor.h:51
LineGenerator * getModel()
Gets the current LineGenerator model.
Definition line_editor.h:357
force_inline void resetPositions()
Marks positions as needing recalculation on next render.
Definition line_editor.h:399
static constexpr int kMaxGridSizeX
Maximum grid sizes for horizontal and vertical lines.
Definition line_editor.h:50
A class for generating and storing a line shape, defined by a series of points and associated powers.
Definition line_generator.h:20
void setLastBrowsedFile(const std::string &path)
Stores the last browsed file path associated with this line.
Definition line_generator.h:86
void setName(const std::string &name)
Sets a name identifier for the line.
Definition line_generator.h:79
force_inline bool smooth() const
Indicates whether smoothing is enabled.
Definition line_generator.h:280
void jsonToState(json data)
Restores the line state from a given JSON object.
Definition line_generator.cpp:125
A specialized LineEditor that visualizes and optionally animates a line-to-value mapping.
Definition line_map_editor.h:20
static std::vector< File > getLfoDirectories()
Gets directories that should contain LFO shapes.
Definition load_save.cpp:1780
static File getShiftedFile(const String directory_name, const String &extensions, const std::string &additional_folders_name, const File &current_file, int shift)
Given a directory name and extensions, returns a file shifted by some offset from the current file.
Definition load_save.cpp:1921
static const std::string kLfoFolderName
Definition load_save.h:78
static File getUserLfoDirectory()
Retrieves the user's LFO directory.
Definition load_save.cpp:1819
SortColumn
Columns available for sorting the modulation matrix rows.
Definition modulation_matrix.h:387
@ kAmount
Definition modulation_matrix.h:393
@ kDestination
Definition modulation_matrix.h:394
@ kNumber
Definition modulation_matrix.h:388
@ kSource
Definition modulation_matrix.h:389
@ kBipolar
Definition modulation_matrix.h:390
@ kMorph
Definition modulation_matrix.h:392
@ kStereo
Definition modulation_matrix.h:391
@ kNumColumns
Definition modulation_matrix.h:395
void resized() override
Handles resizing of the component.
Definition modulation_matrix.cpp:848
void setVisible(bool should_be_visible) override
Sets visibility of this component, and updates modulations if visible.
Definition modulation_matrix.h:454
void setAllValues(vital::control_map &controls) override
Sets all parameter values from a control map.
Definition modulation_matrix.cpp:934
void initOpenGlComponents(OpenGlWrapper &open_gl) override
Initializes OpenGL components.
Definition modulation_matrix.cpp:1102
void importLfo() override
Imports an LFO file to the current line editor.
Definition modulation_matrix.cpp:1178
void nextClicked() override
Called when 'next' is clicked on the preset selector.
Definition modulation_matrix.cpp:1145
static constexpr int kDefaultGridSizeX
Definition modulation_matrix.h:366
void setScrollBarRange()
Sets the range of the scroll bar based on the current rows.
Definition modulation_matrix.cpp:1237
void prevClicked() override
Called when 'previous' is clicked on the preset selector.
Definition modulation_matrix.cpp:1136
void lineEditorScrolled(const MouseEvent &e, const MouseWheelDetails &wheel) override
Handles line editor scrolling for pattern or grid adjustments.
Definition modulation_matrix.cpp:1166
static constexpr int kRowPadding
Definition modulation_matrix.h:364
void paintBackground(Graphics &g) override
Paints the background of the modulation matrix.
Definition modulation_matrix.cpp:693
void mouseDown(const MouseEvent &e) override
Handles mouse down events for sorting column selection.
Definition modulation_matrix.cpp:1007
void loadFile(const File &file) override
Loads a specific file (LFO configuration).
Definition modulation_matrix.cpp:1208
void paintBackgroundShadow(Graphics &g) override
Paints background shadows for the matrix sections.
Definition modulation_matrix.cpp:791
void renderOpenGlComponents(OpenGlWrapper &open_gl, bool animate) override
Renders OpenGL components, including animated elements.
Definition modulation_matrix.cpp:1107
void togglePaintMode(bool enabled, bool temporary_switch) override
Toggles paint mode for the line editors.
Definition modulation_matrix.cpp:1173
static String getMenuSourceDisplayName(const String &original)
Returns a user-friendly display name for a given source string in menu context.
Definition modulation_matrix.cpp:513
virtual ~ModulationMatrix()
Destructor.
Definition modulation_matrix.cpp:664
void setMeterBounds()
Sets the meter bounds for each modulation row.
Definition modulation_matrix.cpp:897
void sliderValueChanged(Slider *changed_slider) override
Handles slider value changes.
Definition modulation_matrix.cpp:903
void setRowPositions()
Positions rows within the viewport.
Definition modulation_matrix.cpp:826
void exportLfo() override
Exports the current LFO to a file.
Definition modulation_matrix.cpp:1187
File getCurrentFile() override
Gets the currently loaded file.
Definition modulation_matrix.h:567
static String getUiSourceDisplayName(const String &original)
Returns a display name suitable for the UI given a source string.
Definition modulation_matrix.cpp:529
void scrollBarMoved(ScrollBar *scroll_bar, double range_start) override
Called when the scroll bar moves.
Definition modulation_matrix.cpp:1232
void paintScrollableBackground()
Paints the portion of the matrix that scrolls, i.e., the rows.
Definition modulation_matrix.cpp:666
void textMouseDown(const MouseEvent &e) override
Handles mouse down events on text components (for loading browser).
Definition modulation_matrix.cpp:1154
void buttonClicked(Button *button) override
Handles button clicks.
Definition modulation_matrix.cpp:920
void destroyOpenGlComponents(OpenGlWrapper &open_gl) override
Destroys OpenGL components.
Definition modulation_matrix.cpp:1131
ModulationMatrix(const vital::output_map &sources, const vital::output_map &destinations)
Constructs a ModulationMatrix.
Definition modulation_matrix.cpp:533
static constexpr int kDefaultGridSizeY
Definition modulation_matrix.h:367
void updateModulations()
Updates the displayed modulations when changes occur.
Definition modulation_matrix.cpp:940
void updateModulationValue(int index)
Updates the modulation value for a given index.
Definition modulation_matrix.cpp:957
void checkNumModulationsShown()
Ensures the correct number of modulation rows is displayed based on connectivity.
Definition modulation_matrix.cpp:962
void parentHierarchyChanged() override
Called when this component's parent hierarchy changes, used to initialize rows and connections.
Definition modulation_matrix.cpp:800
void fileLoaded() override
Called when a file is loaded.
Definition modulation_matrix.cpp:1203
void rowSelected(ModulationMatrixRow *selected_row) override
Called when a modulation row is selected (from ModulationMatrixRow::Listener).
Definition modulation_matrix.cpp:982
Interface for objects that need to respond to row selection changes.
Definition modulation_matrix.h:166
Represents a single row in the modulation matrix, showing source, destination, and associated paramet...
Definition modulation_matrix.h:160
ModulationMatrixRow(int index, PopupItems *source_items, PopupItems *destination_items, const std::vector< String > *sources, const std::vector< String > *destinations)
Constructs a ModulationMatrixRow.
Definition modulation_matrix.cpp:305
std::unique_ptr< SynthSlider > amount_slider_
The slider for modulation amount.
Definition modulation_matrix.h:344
std::unique_ptr< SynthSlider > power_slider_
The slider for the morph/power parameter.
Definition modulation_matrix.h:345
double last_destination_value_
The last known destination value.
Definition modulation_matrix.h:342
void sliderValueChanged(Slider *changed_slider) override
Callback when a slider's value changes.
Definition modulation_matrix.cpp:472
force_inline float morph() const
Gets the morph value of this modulation.
Definition modulation_matrix.h:324
force_inline int stereo() const
Gets whether this row is set to stereo modulation.
Definition modulation_matrix.h:312
int index_
The index of this row.
Definition modulation_matrix.h:335
std::unique_ptr< ModulationSelector > source_
The source selector.
Definition modulation_matrix.h:339
void resized() override
Handles component resizing.
Definition modulation_matrix.cpp:365
bool connected() const
Checks if this row represents a connected modulation.
Definition modulation_matrix.cpp:462
double last_source_value_
The last known source value.
Definition modulation_matrix.h:341
bool matchesSourceAndDestination(const std::string &source, const std::string &destination) const
Checks if this row matches the given source and destination names.
Definition modulation_matrix.cpp:466
force_inline int bipolar() const
Gets whether this row is set to bipolar modulation.
Definition modulation_matrix.h:318
force_inline int index() const
Gets the row index.
Definition modulation_matrix.h:294
Rectangle< int > getMeterBounds()
Gets the bounds where a modulation meter may be drawn.
Definition modulation_matrix.cpp:499
std::vector< Listener * > listeners_
Registered row listeners.
Definition modulation_matrix.h:333
void select()
Selects this row and notifies listeners.
Definition modulation_matrix.h:261
bool selected_
Whether this row is currently selected.
Definition modulation_matrix.h:352
OverlayBackgroundRenderer highlight_
Renders a highlight overlay when selected.
Definition modulation_matrix.h:349
std::unique_ptr< SynthButton > stereo_
The stereo toggle button.
Definition modulation_matrix.h:347
force_inline int source() const
Gets the selected source index.
Definition modulation_matrix.h:300
SynthGuiInterface * parent_
The parent GUI interface.
Definition modulation_matrix.h:337
force_inline float amount() const
Gets the modulation amount value.
Definition modulation_matrix.h:330
void updateDisplayValue()
Updates only the display values (like amount) without changing source/destination selection.
Definition modulation_matrix.cpp:438
std::unique_ptr< OpenGlShapeButton > bipolar_
The bipolar toggle button.
Definition modulation_matrix.h:346
force_inline int destination() const
Gets the selected destination index.
Definition modulation_matrix.h:306
void buttonClicked(Button *button) override
Callback when a button is clicked.
Definition modulation_matrix.cpp:417
vital::ModulationConnection * connection_
The modulation connection for this row.
Definition modulation_matrix.h:336
void updateDisplay()
Updates the display of the row based on the current connection state.
Definition modulation_matrix.cpp:426
double last_amount_value_
The last known amount value.
Definition modulation_matrix.h:343
void paintBackground(Graphics &g) override
Paints the background of this row.
Definition modulation_matrix.cpp:404
bool updating_
Indicates if the row is currently updating to avoid recursive changes.
Definition modulation_matrix.h:351
std::unique_ptr< ModulationSelector > destination_
The destination selector.
Definition modulation_matrix.h:340
std::unique_ptr< SynthButton > bypass_
The bypass button for this modulation row.
Definition modulation_matrix.h:348
Definition modulation_matrix.cpp:184
void paintBackground(Graphics &g) override
Paints a standard background for the component.
Definition modulation_matrix.cpp:246
void setMeterActive(int i, bool active)
Definition modulation_matrix.cpp:252
void render(OpenGlWrapper &open_gl, bool animate) override
Renders the bars using the current OpenGL context.
Definition modulation_matrix.cpp:234
void setScrollOffset(int offset)
Definition modulation_matrix.cpp:256
void parentHierarchyChanged() override
Called when the component's parent hierarchy changes.
Definition modulation_matrix.cpp:197
void loadAmountOutputs()
Definition modulation_matrix.cpp:189
ModulationMeterReadouts()
Definition modulation_matrix.cpp:186
void setMeterBounds(int i, Rectangle< int > bounds)
Definition modulation_matrix.cpp:248
void updatePositions(int index)
Definition modulation_matrix.cpp:207
void mouseDown(const juce::MouseEvent &e) override
Handles mouse down events, showing the popup selection menu.
Definition modulation_matrix.cpp:292
String getTextFromValue(double value) override
Converts a numeric value to a display text representing the currently selected modulation.
Definition modulation_matrix.cpp:268
void setValueFromName(const String &name, NotificationType notification_type)
Sets the current value based on a given name.
Definition modulation_matrix.cpp:274
void addListener(Listener *listener)
Adds a listener to be notified of scrolling changes.
Definition modulation_matrix.h:139
static bool setViewPort(Component *component, Rectangle< int > bounds, OpenGlWrapper &open_gl)
Sets the OpenGL viewport to match a specified rectangle within a component.
Definition open_gl_component.cpp:42
void setOwnImage(Image &image)
Sets the image from an owned copy.
Definition open_gl_image.h:69
void init(OpenGlWrapper &open_gl)
Initializes the OpenGL buffers and shader attributes needed for rendering the image.
Definition open_gl_image.cpp:33
void setBottomLeft(float x, float y)
Sets the bottom-left corner position of the image quad.
Definition open_gl_image.h:116
int getImageWidth()
Gets the width of the currently set image.
Definition open_gl_image.h:132
void drawImage(OpenGlWrapper &open_gl)
Draws the image to the current OpenGL context.
Definition open_gl_image.cpp:59
int getImageHeight()
Gets the height of the currently set image.
Definition open_gl_image.h:138
void setBottomRight(float x, float y)
Sets the bottom-right corner position of the image quad.
Definition open_gl_image.h:121
void setTopLeft(float x, float y)
Sets the top-left corner position of the image quad.
Definition open_gl_image.h:111
void destroy(OpenGlWrapper &open_gl)
Releases any OpenGL resources allocated by this object.
Definition open_gl_image.cpp:128
void setTopRight(float x, float y)
Sets the top-right corner position of the image quad.
Definition open_gl_image.h:126
void setColor(Colour color)
Sets the color tint applied to the image.
Definition open_gl_image.h:92
void redoImage(bool skip_image=false)
Definition synth_slider.cpp:64
SynthSection * parent_
The parent SynthSection.
Definition synth_slider.h:289
void setText(String text)
Definition synth_button.h:365
void setColor(const Colour &color)
Sets the overlay color.
Definition overlay.h:109
static Path halfSinCurve()
Definition paths.h:751
static Path bipolar()
Definition paths.h:668
static Path paintBrush()
Definition paths.h:804
@ kTitle
Definition open_gl_image_component.h:308
@ kWidgetMargin
Definition skin.h:103
@ kBodyRounding
Definition skin.h:71
@ kTextComponentOffset
Definition skin.h:85
@ kWidgetRoundedCorner
Definition skin.h:104
@ kModulationMeterRight
Definition skin.h:177
@ kModulationMeterLeft
Definition skin.h:176
@ kBackground
Definition skin.h:128
@ kBodyText
Definition skin.h:133
@ kLightenScreen
Definition skin.h:141
@ kPopupSelectorBackground
Definition skin.h:143
@ kTextComponentBackground
Definition skin.h:147
@ kHeadingText
Definition skin.h:131
@ kTextComponentText
Definition skin.h:148
@ kBody
Definition skin.h:129
@ kModulationMatrix
Definition skin.h:56
vital::ModulationConnectionBank & getModulationBank()
Retrieves the ModulationConnectionBank managing all modulation connections.
Definition synth_base.cpp:730
const vital::StatusOutput * getStatusOutput(const std::string &name)
Retrieves a status output by name.
Definition synth_base.cpp:262
A specialized OpenGlToggleButton with additional functionality for the Vital synth.
Definition synth_button.h:450
An interface class linking the Vital synthesizer backend (SynthBase) with a GUI.
Definition synth_gui_interface.h:56
SynthBase * getSynth()
Returns the SynthBase instance this interface is managing.
Definition synth_gui_interface.h:85
void disconnectModulation(std::string source, std::string destination)
Disconnects a modulation from the GUI layer.
Definition synth_gui_interface.cpp:146
void connectModulation(std::string source, std::string destination)
Connects a modulation source to a destination parameter through the GUI.
Definition synth_gui_interface.cpp:91
void notifyModulationValueChanged(int index)
Notifies the GUI that a specific modulation's value changed.
Definition synth_gui_interface.cpp:87
Base class for all synthesizer sections, providing UI layout, painting, and interaction logic.
Definition synth_section.h:193
virtual void buttonClicked(Button *clicked_button) override
Called when a button is clicked. Updates the synth parameter accordingly.
Definition synth_section.cpp:398
virtual void renderOpenGlComponents(OpenGlWrapper &open_gl, bool animate)
Renders all OpenGL components in this section and sub-sections.
Definition synth_section.cpp:357
float getPadding()
Definition synth_section.cpp:660
float getSliderWidth()
Definition synth_section.cpp:637
void addSlider(SynthSlider *slider, bool show=true, bool listen=true)
Definition synth_section.cpp:445
void createOffOverlay()
Definition synth_section.cpp:514
virtual void animate(bool animate)
Triggers animation state change in sub-sections if needed.
Definition synth_section.cpp:822
void addSubSection(SynthSection *section, bool show=true)
Adds a subsection (another SynthSection) as a child.
Definition synth_section.cpp:457
void paintOpenGlChildrenBackgrounds(Graphics &g)
Paints the backgrounds for all OpenGL child components.
Definition synth_section.cpp:267
virtual void resized() override
Called when the component is resized. Arranges layout of child components.
Definition synth_section.cpp:35
virtual void paintBody(Graphics &g, Rectangle< int > bounds)
Paints the body background within given bounds.
Definition synth_section.cpp:165
float getWidgetRounding()
Definition synth_section.cpp:680
void updatePopupBrowser(SynthSection *owner)
Updates the currently visible popup browser if any.
Definition synth_section.cpp:113
void paintKnobShadows(Graphics &g)
Paints knob shadows for all sliders.
Definition synth_section.cpp:253
virtual int getPixelMultiple() const
Definition synth_section.cpp:729
void setLabelFont(Graphics &g)
Sets the Graphics context font and color for labels.
Definition synth_section.cpp:740
virtual void destroyOpenGlComponents(OpenGlWrapper &open_gl)
Destroys all OpenGL components in this section and sub-sections.
Definition synth_section.cpp:383
void showPopupSelector(Component *source, Point< int > position, const PopupItems &options, std::function< void(int)> callback, std::function< void()> cancel={ })
Shows a popup selector with options.
Definition synth_section.cpp:119
float size_ratio_
Definition synth_section.h:821
void addButton(OpenGlToggleButton *button, bool show=true)
Definition synth_section.cpp:428
float findValue(Skin::ValueId value_id) const
Finds a value in the skin overrides or from the parent if not found locally.
Definition synth_section.cpp:18
virtual void paintBorder(Graphics &g, Rectangle< int > bounds)
Paints the border around given bounds.
Definition synth_section.cpp:170
bool isActive() const
Checks if the section is currently active.
Definition synth_section.h:683
void showDualPopupSelector(Component *source, Point< int > position, int width, const PopupItems &options, std::function< void(int)> callback)
Shows a dual popup selector for hierarchical selection.
Definition synth_section.cpp:126
void addOpenGlComponent(OpenGlComponent *open_gl_component, bool to_beginning=false)
Definition synth_section.cpp:489
float getSizeRatio() const
Definition synth_section.h:765
virtual void sliderValueChanged(Slider *moved_slider) override
Called when a slider value changes. Updates the synth parameter accordingly.
Definition synth_section.cpp:391
virtual void setAllValues(vital::control_map &controls)
Sets values for all known parameters from a control map.
Definition synth_section.cpp:827
void setPresetSelector(PresetSelector *preset_selector, bool half=false)
Definition synth_section.h:779
float getTitleWidth()
Definition synth_section.cpp:629
virtual void setScrollWheelEnabled(bool enabled)
Enables or disables scroll wheel support for this section and sub-sections.
Definition synth_section.cpp:481
void setSkinOverride(Skin::SectionOverride skin_override)
Definition synth_section.h:303
virtual void initOpenGlComponents(OpenGlWrapper &open_gl)
Initializes all OpenGL components in this section and sub-sections.
Definition synth_section.cpp:349
virtual void paintTabShadow(Graphics &g)
Paints a tab-like shadow effect around the component.
Definition synth_section.cpp:188
void showPopupBrowser(SynthSection *owner, Rectangle< int > bounds, std::vector< File > directories, String extensions, std::string passthrough_name, std::string additional_folders_name)
Shows a file browser popup (e.g., for loading samples or wavetables).
Definition synth_section.cpp:105
float getWidgetMargin()
Definition synth_section.cpp:676
static TextLookAndFeel * instance()
Singleton instance access.
Definition text_look_and_feel.h:106
A container managing a fixed number of ModulationConnections.
Definition synth_types.h:87
ModulationConnection * atIndex(int index)
Retrieves a ModulationConnection by index.
Definition synth_types.h:114
static const ValueDetails & getDetails(const std::string &name)
Definition synth_parameters.h:200
A helper class to track the "status" of a particular Output as a poly_float value.
Definition synth_module.h:35
static constexpr float kClearValue
Special "clear" value indicating no status.
Definition synth_module.h:37
force_inline poly_float value() const
Returns the current status value.
Definition synth_module.h:49
nlohmann::json json
Definition line_generator.h:7
const std::string kPaintPatternNames[]
Names for painting patterns in wavetable or LFO editing.
Definition synth_strings.h:327
force_inline poly_float clamp(poly_float value, mono_float min, mono_float max)
Clamps each lane of a vector to [min, max].
Definition poly_utils.h:306
force_inline int nextPowerOfTwo(mono_float value)
Finds the next power of two greater than or equal to a float value.
Definition utils.h:370
force_inline poly_float interpolate(poly_float from, poly_float to, mono_float t)
Performs a linear interpolation between two poly_floats using a scalar t in [0..1].
Definition poly_utils.h:182
Contains classes and functions used within the Vital synthesizer framework.
std::map< std::string, Output * > output_map
Maps parameter names to Output pointers, representing output signals from various modules.
Definition synth_types.h:229
std::map< std::string, Value * > control_map
Maps parameter names to Value pointers representing synth control parameters.
Definition synth_types.h:214
constexpr int kMaxModulationConnections
Maximum number of modulation connections allowed.
Definition synth_constants.h:49
const std::string kLfoExtension
File extension for Vital LFO shape files.
Definition synth_constants.h:100
Definition modulation_matrix.cpp:160
bool operator()(const String &first, const String &second) const
Definition modulation_matrix.cpp:161
A helper struct containing references to OpenGL context, shaders, and display scale.
Definition shaders.h:174
A hierarchical structure of popup menu items for a selector component.
Definition synth_section.h:29
void addItem(int sub_id, const std::string &sub_name, bool sub_selected=false)
Adds a new item as a submenu entry.
Definition synth_section.h:51
A structure representing a single modulation connection between a modulation source and a destination...
Definition synth_types.h:30
std::unique_ptr< ModulationConnectionProcessor > modulation_processor
Processor applying scaling/mapping.
Definition synth_types.h:75
std::string destination_name
The name of the destination parameter.
Definition synth_types.h:74
std::string source_name
The name of the modulation source.
Definition synth_types.h:73
Holds metadata about a single parameter (control) in the Vital synthesizer.
Definition synth_parameters.h:23
mono_float max
Maximum parameter value.
Definition synth_parameters.h:41
@ kLinear
Parameter scales linearly between min and max.
Definition synth_parameters.h:30
@ kIndexed
Parameter steps through discrete indexed values.
Definition synth_parameters.h:29
mono_float min
Minimum parameter value.
Definition synth_parameters.h:40
std::string display_name
Human-readable name for display in UI.
Definition synth_parameters.h:48
std::string local_description
Local description or additional metadata.
Definition synth_parameters.h:50
ValueScale value_scale
The scaling mode of the parameter value.
Definition synth_parameters.h:45
Defines the SynthModule class which extends ProcessorRouter to form a building block of the Vital syn...
Declares the SynthSlider and related classes, providing various slider styles and functionality in th...
Declares the TextSelector class and PaintPatternSelector class for selecting text-based options and d...