2018-02-27 08:25:20 +08:00
|
|
|
//
|
|
|
|
// Model.cpp
|
|
|
|
// Lazarus
|
|
|
|
//
|
|
|
|
// Created by Alex Birch on 01/09/2017.
|
|
|
|
//
|
|
|
|
//
|
|
|
|
|
|
|
|
#include "TableComponent.h"
|
2019-07-14 05:37:26 +08:00
|
|
|
#include "Util.h"
|
2018-02-27 08:25:20 +08:00
|
|
|
|
|
|
|
using namespace std;
|
2019-07-14 05:37:26 +08:00
|
|
|
using namespace Util;
|
2018-02-27 08:25:20 +08:00
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
/**
|
|
|
|
This class shows how to implement a TableListBoxModel to show in a TableListBox.
|
|
|
|
*/
|
|
|
|
TableComponent::TableComponent(
|
2019-07-15 00:22:36 +08:00
|
|
|
AudioProcessorValueTreeState& valueTreeState
|
2019-07-13 07:16:35 +08:00
|
|
|
// const vector<string> &columns,
|
2019-07-14 05:37:26 +08:00
|
|
|
// const vector<TableRow> &rows,
|
2019-07-14 21:19:27 +08:00
|
|
|
// const function<void (int)> &onRowSelected,
|
2019-07-13 07:16:35 +08:00
|
|
|
// const function<int (const vector<string>&)> &rowToIDMapper,
|
2019-07-14 21:19:27 +08:00
|
|
|
// int initiallySelectedRow
|
2018-02-27 08:25:20 +08:00
|
|
|
)
|
2019-07-14 05:37:26 +08:00
|
|
|
: valueTreeState{valueTreeState}
|
|
|
|
, font{14.0f}
|
|
|
|
//, columns{columns}
|
|
|
|
//, rows{rows}
|
2019-07-14 21:19:27 +08:00
|
|
|
// , onRowSelected{onRowSelected}
|
|
|
|
// rowToIDMapper(rowToIDMapper)
|
2018-02-27 08:25:20 +08:00
|
|
|
{
|
|
|
|
// Create our table component and add it to this component..
|
|
|
|
addAndMakeVisible (table);
|
|
|
|
table.setModel (this);
|
|
|
|
|
|
|
|
// give it a border
|
|
|
|
table.setColour (ListBox::outlineColourId, Colours::grey);
|
|
|
|
table.setOutlineThickness (1);
|
|
|
|
|
|
|
|
int columnIx = 1;
|
|
|
|
|
|
|
|
// Add some columns to the table header, based on the column list in our database..
|
2019-07-13 07:16:35 +08:00
|
|
|
// for (auto &column : columns) // access by reference to avoid copying
|
|
|
|
// {
|
|
|
|
// const int colWidth{ columnIx == 1 ? 30 : 200 };
|
|
|
|
// table.getHeader().addColumn (
|
|
|
|
// String(column),
|
|
|
|
// columnIx++,
|
|
|
|
// colWidth, // column width
|
|
|
|
// 30, // min width
|
|
|
|
// 400, // max width
|
|
|
|
// TableHeaderComponent::defaultFlags
|
|
|
|
// );
|
|
|
|
// }
|
|
|
|
table.getHeader().addColumn (
|
|
|
|
String("#"),
|
|
|
|
columnIx++,
|
|
|
|
30, // column width
|
|
|
|
30, // min width
|
|
|
|
400, // max width
|
|
|
|
TableHeaderComponent::defaultFlags
|
|
|
|
);
|
|
|
|
table.getHeader().addColumn (
|
|
|
|
String("Name"),
|
|
|
|
columnIx++,
|
|
|
|
200, // column width
|
|
|
|
30, // min width
|
|
|
|
400, // max width
|
|
|
|
TableHeaderComponent::defaultFlags
|
|
|
|
);
|
2018-02-27 08:25:20 +08:00
|
|
|
|
|
|
|
table.setWantsKeyboardFocus(false);
|
|
|
|
|
2019-07-14 21:19:27 +08:00
|
|
|
// table.selectRow();
|
2019-07-15 00:22:36 +08:00
|
|
|
ValueTree presets{valueTreeState.state.getChildWithName("presets")};
|
|
|
|
loadModelFrom(presets);
|
2019-07-14 21:19:27 +08:00
|
|
|
// selectCurrentPreset();
|
2018-02-27 08:25:20 +08:00
|
|
|
|
|
|
|
// we could now change some initial settings..
|
2019-07-13 07:16:35 +08:00
|
|
|
table.getHeader().setSortColumnId(1, false); // sort ascending by ID column
|
2018-02-27 08:25:20 +08:00
|
|
|
// table.getHeader().setColumnVisible (7, false); // hide the "length" column until the user shows it
|
|
|
|
|
|
|
|
// un-comment this line to have a go of stretch-to-fit mode
|
|
|
|
// table.getHeader().setStretchToFitActive (true);
|
|
|
|
|
|
|
|
// table.setMultipleSelectionEnabled (false);
|
2019-07-13 07:16:35 +08:00
|
|
|
valueTreeState.state.addListener(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
TableComponent::~TableComponent() {
|
|
|
|
valueTreeState.state.removeListener(this);
|
|
|
|
}
|
|
|
|
|
2019-07-14 21:19:27 +08:00
|
|
|
void TableComponent::loadModelFrom(ValueTree& presets) {
|
|
|
|
rows.clear();
|
|
|
|
int numChildren{presets.getNumChildren()};
|
|
|
|
for(int i{0}; i<numChildren; i++) {
|
|
|
|
ValueTree child{presets.getChild(i)};
|
|
|
|
int num{child.getProperty("num")};
|
|
|
|
String name{child.getProperty("name")};
|
|
|
|
rows.emplace_back(num, name);
|
|
|
|
}
|
|
|
|
table.deselectAllRows();
|
|
|
|
table.updateContent();
|
|
|
|
table.getHeader().setSortColumnId(0, true);
|
|
|
|
selectCurrentPreset();
|
|
|
|
table.repaint();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TableComponent::parameterChanged(const String& parameterID, float newValue) {
|
|
|
|
// valueTreeState.getParameter
|
|
|
|
if (parameterID == "preset") {
|
|
|
|
selectCurrentPreset();
|
|
|
|
// RangedAudioParameter *param {valueTreeState.getParameter("preset")};
|
|
|
|
// jassert(dynamic_cast<AudioParameterInt*> (param) != nullptr);
|
|
|
|
// AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)};
|
|
|
|
// int value{castParam->get()};
|
|
|
|
}
|
|
|
|
}
|
2019-07-13 07:16:35 +08:00
|
|
|
|
2019-07-15 00:45:08 +08:00
|
|
|
// void TableComponent::valueTreePropertyChanged(
|
|
|
|
// ValueTree& treeWhosePropertyHasChanged,
|
|
|
|
// const Identifier& property) {
|
|
|
|
// if (treeWhosePropertyHasChanged.getType() == StringRef("presets")) {
|
|
|
|
// loadModelFrom(treeWhosePropertyHasChanged);
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
void TableComponent::valueTreeParentChanged(ValueTree& treeWhoseParentHasChanged) {
|
|
|
|
if (treeWhoseParentHasChanged.getType() == StringRef("presets")) {
|
|
|
|
loadModelFrom(treeWhoseParentHasChanged);
|
2019-07-13 07:16:35 +08:00
|
|
|
}
|
2018-02-27 08:25:20 +08:00
|
|
|
}
|
|
|
|
|
2019-07-14 05:37:26 +08:00
|
|
|
// void TableComponent::setRows(const vector<vector<string>>& rows, int initiallySelectedRow) {
|
|
|
|
// this->rows = rows;
|
|
|
|
// table.deselectAllRows();
|
|
|
|
// table.updateContent();
|
|
|
|
// table.getHeader().setSortColumnId(0, true);
|
|
|
|
// table.selectRow(initiallySelectedRow);
|
|
|
|
// table.repaint();
|
|
|
|
// }
|
2018-02-27 08:25:20 +08:00
|
|
|
|
|
|
|
// This is overloaded from TableListBoxModel, and must return the total number of rows in our table
|
|
|
|
int TableComponent::getNumRows()
|
|
|
|
{
|
|
|
|
return static_cast<int>(rows.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is overloaded from TableListBoxModel, and should fill in the background of the whole row
|
|
|
|
void TableComponent::paintRowBackground (
|
|
|
|
Graphics& g,
|
|
|
|
int rowNumber,
|
|
|
|
int /*width*/,
|
|
|
|
int /*height*/,
|
|
|
|
bool rowIsSelected
|
|
|
|
) {
|
|
|
|
const Colour alternateColour (getLookAndFeel().findColour (ListBox::backgroundColourId)
|
|
|
|
.interpolatedWith (getLookAndFeel().findColour (ListBox::textColourId), 0.03f));
|
|
|
|
if (rowIsSelected)
|
|
|
|
g.fillAll (Colours::lightblue);
|
|
|
|
else if (rowNumber % 2)
|
|
|
|
g.fillAll (alternateColour);
|
|
|
|
}
|
|
|
|
|
2019-07-14 21:19:27 +08:00
|
|
|
String TableRow::getStringContents(int columnId) {
|
|
|
|
if (columnId <= 1) {
|
2019-07-15 00:22:36 +08:00
|
|
|
return String(preset);
|
2019-07-14 21:19:27 +08:00
|
|
|
}
|
2019-07-15 00:22:36 +08:00
|
|
|
return name;
|
2019-07-14 21:19:27 +08:00
|
|
|
}
|
|
|
|
|
2018-02-27 08:25:20 +08:00
|
|
|
// This is overloaded from TableListBoxModel, and must paint any cells that aren't using custom
|
|
|
|
// components.
|
|
|
|
void TableComponent::paintCell (
|
|
|
|
Graphics& g,
|
|
|
|
int rowNumber,
|
|
|
|
int columnId,
|
|
|
|
int width,
|
|
|
|
int height,
|
|
|
|
bool /*rowIsSelected*/
|
|
|
|
) {
|
|
|
|
g.setColour (getLookAndFeel().findColour (ListBox::textColourId));
|
|
|
|
g.setFont (font);
|
|
|
|
|
2019-07-14 05:37:26 +08:00
|
|
|
TableRow& row{rows[rowNumber]};
|
2019-07-14 21:19:27 +08:00
|
|
|
String text{row.getStringContents(columnId)};
|
2019-07-14 05:37:26 +08:00
|
|
|
g.drawText (text, 2, 0, width - 4, height, Justification::centredLeft, true);
|
2018-02-27 08:25:20 +08:00
|
|
|
|
|
|
|
g.setColour (getLookAndFeel().findColour (ListBox::backgroundColourId));
|
|
|
|
g.fillRect (width - 1, 0, 1, height);
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is overloaded from TableListBoxModel, and tells us that the user has clicked a table header
|
|
|
|
// to change the sort order.
|
|
|
|
void TableComponent::sortOrderChanged (
|
|
|
|
int newSortColumnId,
|
|
|
|
bool isForwards
|
|
|
|
) {
|
|
|
|
if (newSortColumnId != 0) {
|
2019-07-14 21:19:27 +08:00
|
|
|
// int selectedRowIx = table.getSelectedRow();
|
2019-07-14 05:37:26 +08:00
|
|
|
// TableRow* selectedRow;
|
|
|
|
// if (selectedRowIx >= 0) {
|
|
|
|
// selectedRow = &rows[selectedRowIx];
|
|
|
|
// }
|
2018-02-27 08:25:20 +08:00
|
|
|
|
|
|
|
TableComponent::DataSorter sorter (newSortColumnId, isForwards);
|
|
|
|
sort(rows.begin(), rows.end(), sorter);
|
|
|
|
|
|
|
|
table.updateContent();
|
2019-07-14 21:19:27 +08:00
|
|
|
selectCurrentPreset();
|
|
|
|
|
|
|
|
// if (selectedRowIx >= 0) {
|
|
|
|
// for (auto it = rows.begin(); it != rows.end(); ++it) {
|
|
|
|
// if(it->preset == value) {
|
|
|
|
// int index {static_cast<int>(std::distance(rows.begin(), it))};
|
|
|
|
// table.selectRow(index);
|
|
|
|
// break;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TableComponent::selectCurrentPreset() {
|
|
|
|
table.deselectAllRows();
|
|
|
|
RangedAudioParameter *param {valueTreeState.getParameter("preset")};
|
|
|
|
jassert(dynamic_cast<AudioParameterInt*> (param) != nullptr);
|
|
|
|
AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)};
|
|
|
|
int value{castParam->get()};
|
2018-02-27 08:25:20 +08:00
|
|
|
|
2019-07-14 21:19:27 +08:00
|
|
|
for (auto it = rows.begin(); it != rows.end(); ++it) {
|
|
|
|
if(it->preset == value) {
|
|
|
|
int index {static_cast<int>(std::distance(rows.begin(), it))};
|
|
|
|
table.selectRow(index);
|
|
|
|
break;
|
2018-02-27 08:25:20 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is overloaded from TableListBoxModel, and should choose the best width for the specified
|
|
|
|
// column.
|
|
|
|
int TableComponent::getColumnAutoSizeWidth (int columnId) {
|
2019-06-30 18:39:39 +08:00
|
|
|
if (columnId == 1)
|
2019-07-13 07:16:35 +08:00
|
|
|
return 30;
|
2018-02-27 08:25:20 +08:00
|
|
|
|
2019-06-30 18:39:39 +08:00
|
|
|
|
2018-02-27 08:25:20 +08:00
|
|
|
int widest = 32;
|
|
|
|
|
|
|
|
// find the widest bit of text in this column..
|
2019-07-14 05:37:26 +08:00
|
|
|
for (int i{getNumRows()}; --i >= 0;) {
|
|
|
|
TableRow& row{rows[i]};
|
2019-07-14 21:19:27 +08:00
|
|
|
String text{row.getStringContents(columnId)};
|
2019-07-14 05:37:26 +08:00
|
|
|
widest = jmax (widest, font.getStringWidth (text));
|
2018-02-27 08:25:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return widest + 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
void TableComponent::resized() {
|
|
|
|
// position our table with a gap around its edge
|
|
|
|
table.setBoundsInset (BorderSize<int> (7));
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
// A comparator used to sort our data when the user clicks a column header
|
|
|
|
|
|
|
|
TableComponent::DataSorter::DataSorter (
|
|
|
|
int columnByWhichToSort,
|
|
|
|
bool forwards
|
|
|
|
)
|
2019-07-14 05:37:26 +08:00
|
|
|
: columnByWhichToSort (columnByWhichToSort)
|
|
|
|
, direction (forwards ? 1 : -1)
|
2018-02-27 08:25:20 +08:00
|
|
|
{}
|
|
|
|
|
|
|
|
bool TableComponent::DataSorter::operator ()(
|
2019-07-14 05:37:26 +08:00
|
|
|
TableRow first,
|
|
|
|
TableRow second
|
2018-02-27 08:25:20 +08:00
|
|
|
) {
|
2019-07-14 05:37:26 +08:00
|
|
|
int result;
|
|
|
|
if (columnByWhichToSort <= 1) {
|
|
|
|
result = compare(first.preset, second.preset);
|
|
|
|
} else {
|
|
|
|
result = first.name
|
|
|
|
.compareNatural (second.name);
|
|
|
|
if (result == 0) {
|
|
|
|
result = compare(first.preset, second.preset);
|
|
|
|
}
|
|
|
|
}
|
2018-02-27 08:25:20 +08:00
|
|
|
|
|
|
|
result *= direction;
|
|
|
|
|
|
|
|
return result > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TableComponent::selectedRowsChanged (int row) {
|
|
|
|
if (row < 0) {
|
|
|
|
return;
|
|
|
|
}
|
2019-07-13 07:16:35 +08:00
|
|
|
// onRowSelected(rowToIDMapper(rows[row]));
|
|
|
|
// onRowSelected(stoi(rows[row][0]));
|
2019-07-14 05:37:26 +08:00
|
|
|
int newPreset{rows[row].preset};
|
2019-07-13 07:16:35 +08:00
|
|
|
RangedAudioParameter *param {valueTreeState.getParameter("preset")};
|
|
|
|
jassert(dynamic_cast<AudioParameterInt*> (param) != nullptr);
|
|
|
|
AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)};
|
|
|
|
*castParam = newPreset;
|
2018-02-27 08:25:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TableComponent::keyPressed(const KeyPress &key) {
|
|
|
|
return table.keyPressed(key);
|
2019-06-30 18:39:39 +08:00
|
|
|
}
|
2019-07-14 05:37:26 +08:00
|
|
|
|
|
|
|
TableRow::TableRow(
|
|
|
|
int preset,
|
|
|
|
String name
|
|
|
|
)
|
|
|
|
: preset{preset}
|
2019-07-14 21:19:27 +08:00
|
|
|
, name{name}
|
2019-07-14 05:37:26 +08:00
|
|
|
{}
|