#include <config.h>
#include "MFXDecalsTable.h"
#include <netedit/GNEViewNet.h>
#include <netedit/GNEViewParent.h>
#include <netedit/GNEApplicationWindow.h>
#include <utils/gui/div/GUIDesigns.h>
#include <utils/gui/images/GUIIconSubSys.h>
#include <utils/gui/windows/GUIAppEnum.h>
#include <utils/gui/windows/GUIDialog_ViewSettings.h>
#define EXTRAMARGIN 4
#define MAXROWS 100
FXDEFMAP(MFXDecalsTable) MFXDecalsTableMap[] = {
FXMAPFUNC(MID_MBTTIP_FOCUS, 0, MFXDecalsTable::onFocusRow),
FXMAPFUNC(SEL_FOCUSIN, MID_DECALSTABLE_TEXTFIELD, MFXDecalsTable::onFocusRow),
FXMAPFUNC(SEL_KEYPRESS, MID_DECALSTABLE_TEXTFIELD, MFXDecalsTable::onCmdKeyPress),
FXMAPFUNC(SEL_COMMAND, MID_DECALSTABLE_TEXTFIELD, MFXDecalsTable::onCmdEditRowString),
FXMAPFUNC(SEL_COMMAND, MID_DECALSTABLE_SPINNER, MFXDecalsTable::onCmdEditRowSpinner),
FXMAPFUNC(SEL_COMMAND, MID_DECALSTABLE_CHECKBOX, MFXDecalsTable::onCmdEditRowCheckBox),
FXMAPFUNC(SEL_COMMAND, MID_DECALSTABLE_OPEN, MFXDecalsTable::onCmdOpenDecal),
FXMAPFUNC(SEL_COMMAND, MID_DECALSTABLE_ADD, MFXDecalsTable::onCmdAddRow),
FXMAPFUNC(SEL_UPDATE, MID_DECALSTABLE_ADD, MFXDecalsTable::onUpdAddRow),
FXMAPFUNC(SEL_COMMAND, MID_DECALSTABLE_REMOVE, MFXDecalsTable::onCmdRemoveRow),
};
FXIMPLEMENT(MFXDecalsTable, FXVerticalFrame, MFXDecalsTableMap, ARRAYNUMBER(MFXDecalsTableMap))
MFXDecalsTable::MFXDecalsTable(GUIDialog_ViewSettings* dialogViewSettingsParent, FXComposite* parent) :
FXVerticalFrame(parent, GUIDesignAuxiliarFrameFixHeight),
myIndexFont(new FXFont(getApp(), "Segoe UI", 9)),
myIndexSelectedFont(new FXFont(getApp(), "Segoe UI", 9, FXFont::Bold)),
myDialogViewSettings(dialogViewSettingsParent) {
myColumnsFrame = new FXHorizontalFrame(this, GUIDesignAuxiliarFrame);
myAddButton = GUIDesigns::buildFXButton(this, "", TL("Add decal"), TL("Add decal."),
GUIIconSubSys::getIcon(GUIIcon::ADD), this, MID_DECALSTABLE_ADD, GUIDesignButtonIcon);
}
MFXDecalsTable::~MFXDecalsTable() {
delete myIndexFont;
delete myIndexSelectedFont;
}
void
MFXDecalsTable::clearTable() {
for (const auto& row : myRows) {
delete row;
}
for (const auto& column : myColumns) {
delete column;
}
myRows.clear();
myColumns.clear();
}
void
MFXDecalsTable::fillTable() {
clearTable();
const auto decals = myDialogViewSettings->getSUMOAbstractView()->getDecals();
std::string columnsType = "ibfppppppcd";
for (int i = 0; i < (FXint)columnsType.size(); i++) {
myColumns.push_back(new Column(this, i, columnsType.at(i)));
}
const int numDecals = decals.size() < MAXROWS ? (int)decals.size() : MAXROWS;
for (int i = 0; i < numDecals; i++) {
const auto& decal = decals.at(i);
auto row = new Row(this);
row->getCells().at(2)->getTextField()->setText(decal.filename.c_str());
row->getCells().at(3)->getSpinner()->setValue(decal.centerX);
row->getCells().at(4)->getSpinner()->setValue(decal.centerY);
row->getCells().at(5)->getSpinner()->setValue(decal.width);
row->getCells().at(6)->getSpinner()->setValue(decal.height);
row->getCells().at(7)->getSpinner()->setValue(decal.rot);
row->getCells().at(8)->getSpinner()->setValue(decal.layer);
if (decal.screenRelative) {
row->getCells().at(9)->getCheckButton()->setCheck(true);
row->getCells().at(9)->getCheckButton()->setText("true");
} else {
row->getCells().at(9)->getCheckButton()->setCheck(false);
row->getCells().at(9)->getCheckButton()->setText("false");
}
myRows.push_back(row);
}
myColumns.at(2)->setColumnLabel("filename", "");
myColumns.at(3)->setColumnLabel("centerX", "");
myColumns.at(4)->setColumnLabel("centerY", "");
myColumns.at(5)->setColumnLabel("width", "");
myColumns.at(6)->setColumnLabel("height", "");
myColumns.at(7)->setColumnLabel("rotation", "");
myColumns.at(8)->setColumnLabel("layer", "");
myColumns.at(9)->setColumnLabel("sRel", "screen relative");
setHeight((numDecals + 2) * GUIDesignHeight);
create();
}
void
MFXDecalsTable::setItemText(FXint row, FXint column, const std::string& text) {
if ((row >= 0) && (row < (FXint)myRows.size()) &&
(column >= 0) && (column < (FXint)myColumns.size())) {
myRows.at(row)->setText(column, text);
} else {
throw ProcessError(TL("Invalid row or column"));
}
}
std::string
MFXDecalsTable::getItemText(const int row, const int column) const {
if ((row >= 0) && (row < (FXint)myRows.size()) &&
(column >= 0) && (column < (FXint)myColumns.size())) {
return myRows.at(row)->getText(column);
}
throw ProcessError(TL("Invalid row or column"));
}
int
MFXDecalsTable::getNumRows() const {
return (int)myRows.size();
}
int
MFXDecalsTable::getCurrentSelectedRow() const {
return myCurrentSelectedRow;
}
void
MFXDecalsTable::selectRow(const int row) {
if ((row >= 0) && (row < (FXint)myRows.size())) {
myCurrentSelectedRow = row;
updateIndexLabel();
} else {
throw ProcessError(TL("Invalid row"));
}
}
void
MFXDecalsTable::setColumnLabel(const int column, const std::string& text, const std::string& tooltip) {
if ((column >= 0) && (column < (int)myColumns.size())) {
myColumns.at(column)->setColumnLabel(text, tooltip);
} else {
throw ProcessError(TL("Invalid column"));
}
}
long
MFXDecalsTable::onFocusRow(FXObject* sender, FXSelector, void*) {
int selectedRow = -1;
for (int rowIndex = 0; rowIndex < (int)myRows.size(); rowIndex++) {
for (const auto& cell : myRows.at(rowIndex)->getCells()) {
if ((cell->getTextField() == sender) || (cell->getButton() == sender)) {
selectedRow = rowIndex;
}
}
}
updateIndexLabel();
if (myCurrentSelectedRow != selectedRow) {
myCurrentSelectedRow = selectedRow;
updateIndexLabel();
}
return 0;
}
long
MFXDecalsTable::onCmdKeyPress(FXObject* sender, FXSelector sel, void* ptr) {
FXEvent* eventInfo = (FXEvent*)ptr;
if (eventInfo->code == 65362) {
if (myCurrentSelectedRow > 0) {
myCurrentSelectedRow -= 1;
} else {
myCurrentSelectedRow = ((int)myRows.size() - 1);
}
updateIndexLabel();
moveFocus();
return 1;
} else if (eventInfo->code == 65364) {
if (myCurrentSelectedRow < ((int)myRows.size() - 1)) {
myCurrentSelectedRow += 1;
} else {
myCurrentSelectedRow = 0;
}
updateIndexLabel();
moveFocus();
return 1;
} else {
return sender->handle(sender, sel, ptr);
}
}
long
MFXDecalsTable::onCmdEditRowString(FXObject* sender, FXSelector, void*) {
auto& decals = myDialogViewSettings->getSUMOAbstractView()->getDecals();
const std::string value = dynamic_cast<FXTextField*>(sender)->getText().text();
for (int rowIndex = 0; rowIndex < (int)myRows.size(); rowIndex++) {
if (myRows.at(rowIndex)->getCells().at(2)->getTextField() == sender) {
decals.at(rowIndex).filename = value;
}
}
myDialogViewSettings->getSUMOAbstractView()->update();
return 1;
}
long
MFXDecalsTable::onCmdEditRowSpinner(FXObject* sender, FXSelector, void*) {
auto& decals = myDialogViewSettings->getSUMOAbstractView()->getDecals();
const auto value = dynamic_cast<FXRealSpinner*>(sender)->getValue();
for (int rowIndex = 0; rowIndex < (int)myRows.size(); rowIndex++) {
if (myRows.at(rowIndex)->getCells().at(3)->getSpinner() == sender) {
decals.at(rowIndex).centerX = value;
} else if (myRows.at(rowIndex)->getCells().at(4)->getSpinner() == sender) {
decals.at(rowIndex).centerY = value;
} else if (myRows.at(rowIndex)->getCells().at(5)->getSpinner() == sender) {
decals.at(rowIndex).width = value;
} else if (myRows.at(rowIndex)->getCells().at(6)->getSpinner() == sender) {
decals.at(rowIndex).height = value;
} else if (myRows.at(rowIndex)->getCells().at(7)->getSpinner() == sender) {
decals.at(rowIndex).rot = value;
} else if (myRows.at(rowIndex)->getCells().at(8)->getSpinner() == sender) {
decals.at(rowIndex).layer = value;
}
}
myDialogViewSettings->getSUMOAbstractView()->update();
return 1;
}
long
MFXDecalsTable::onCmdEditRowCheckBox(FXObject* sender, FXSelector, void*) {
auto& decals = myDialogViewSettings->getSUMOAbstractView()->getDecals();
auto checkButton = dynamic_cast<FXCheckButton*>(sender);
checkButton->setText((checkButton->getCheck() == TRUE) ? "true" : "false");
for (int rowIndex = 0; rowIndex < (int)myRows.size(); rowIndex++) {
if (myRows.at(rowIndex)->getCells().at(9)->getCheckButton() == sender) {
decals.at(rowIndex).screenRelative = (checkButton->getCheck() == TRUE) ? true : false;
}
}
myDialogViewSettings->getSUMOAbstractView()->update();
return 1;
}
long
MFXDecalsTable::onCmdOpenDecal(FXObject* sender, FXSelector, void*) {
FXFileDialog opendialog(this, TL("Open decal"));
opendialog.setSelectMode(SELECTFILE_EXISTING);
opendialog.setIcon(GUIIconSubSys::getIcon(GUIIcon::OPEN));
opendialog.setPatternList(SUMOXMLDefinitions::ImageFileExtensions.getMultilineString().c_str());
if (gCurrentFolder.length() != 0) {
opendialog.setDirectory(gCurrentFolder);
}
opendialog.execute();
if (!opendialog.getFilename().empty()) {
auto& decals = myDialogViewSettings->getSUMOAbstractView()->getDecals();
for (int rowIndex = 0; rowIndex < (int)myRows.size(); rowIndex++) {
if (myRows.at(rowIndex)->getCells().at(1)->getButton() == sender) {
myRows.at(rowIndex)->getCells().at(2)->getTextField()->setText(opendialog.getFilename());
decals.at(rowIndex).filename = opendialog.getFilename().text();
myDialogViewSettings->getSUMOAbstractView()->update();
return 1;
}
}
}
return 1;
}
long
MFXDecalsTable::onCmdAddRow(FXObject*, FXSelector, void*) {
myDialogViewSettings->getSUMOAbstractView()->getDecals().push_back(GUISUMOAbstractView::Decal());
fillTable();
return 1;
}
long
MFXDecalsTable::onUpdAddRow(FXObject* sender, FXSelector, void* ptr) {
if (myDialogViewSettings->getSUMOAbstractView()->getDecals().size() < MAXROWS) {
return sender->handle(this, FXSEL(SEL_COMMAND, ID_ENABLE), ptr);
} else {
return sender->handle(this, FXSEL(SEL_COMMAND, ID_DISABLE), ptr);
}
}
long
MFXDecalsTable::onCmdRemoveRow(FXObject* sender, FXSelector, void*) {
auto& decals = myDialogViewSettings->getSUMOAbstractView()->getDecals();
for (int rowIndex = 0; rowIndex < (int)myRows.size(); rowIndex++) {
if (myRows.at(rowIndex)->getCells().back()->getButton() == sender) {
decals.erase(decals.begin() + rowIndex);
myDialogViewSettings->getSUMOAbstractView()->update();
fillTable();
return 1;
}
}
return 1;
}
void
MFXDecalsTable::updateIndexLabel() {
for (int rowIndex = 0; rowIndex < (int)myRows.size(); rowIndex++) {
for (const auto& cell : myRows.at(rowIndex)->getCells()) {
if (cell->getIndexLabel()) {
if (myCurrentSelectedRow == rowIndex) {
cell->showIndexLabelBold();
} else {
cell->showIndexLabelNormal();
}
}
}
}
}
bool
MFXDecalsTable::moveFocus() {
for (int rowIndex = 0; rowIndex < (int)myRows.size(); rowIndex++) {
for (int cellIndex = 0; cellIndex < (int)myRows.at(rowIndex)->getCells().size(); cellIndex++) {
if (myRows.at(rowIndex)->getCells().at(cellIndex)->hasFocus()) {
myRows.at(myCurrentSelectedRow)->getCells().at(cellIndex)->setFocus();
return true;
}
}
}
return false;
}
MFXDecalsTable::Cell::Cell(MFXDecalsTable* decalsTable, FXTextField* textField, int col, int row) :
myDecalsTable(decalsTable),
myTextField(textField),
myCol(col),
myRow(row) {
}
MFXDecalsTable::Cell::Cell(MFXDecalsTable* decalsTable, FXLabel* indexLabel, FXLabel* indexLabelBold, int col, int row) :
myDecalsTable(decalsTable),
myIndexLabel(indexLabel),
myIndexLabelBold(indexLabelBold),
myCol(col),
myRow(row) {
indexLabelBold->hide();
indexLabelBold->setBackColor(FXRGBA(210, 233, 255, 255));
}
MFXDecalsTable::Cell::Cell(MFXDecalsTable* decalsTable, FXButton* button, int col, int row) :
myDecalsTable(decalsTable),
myButton(button),
myCol(col),
myRow(row) {
}
MFXDecalsTable::Cell::Cell(MFXDecalsTable* decalsTable, FXCheckButton* checkButton, int col, int row) :
myDecalsTable(decalsTable),
myCheckButton(checkButton),
myCol(col),
myRow(row) {
}
MFXDecalsTable::Cell::Cell(MFXDecalsTable* decalsTable, FXRealSpinner* spinner, int col, int row) :
myDecalsTable(decalsTable),
mySpinner(spinner),
myCol(col),
myRow(row) {
}
MFXDecalsTable::Cell::~Cell() {
if (myTextField) {
delete myTextField;
}
if (myIndexLabel) {
delete myIndexLabel;
}
if (myIndexLabelBold) {
delete myIndexLabelBold;
}
if (myButton) {
delete myButton;
}
if (myCheckButton) {
delete myCheckButton;
}
}
bool
MFXDecalsTable::Cell::hasFocus() const {
if (myTextField && myTextField->hasFocus()) {
return true;
} else if (myButton && myButton->hasFocus()) {
return true;
} else if (myCheckButton && myCheckButton->hasFocus()) {
return true;
} else {
return false;
}
}
void
MFXDecalsTable::Cell::setFocus() {
if (myTextField) {
myTextField->setFocus();
} else if (myButton) {
myButton->setFocus();
} else if (myCheckButton) {
myCheckButton->setFocus();
}
}
FXTextField*
MFXDecalsTable::Cell::getTextField() const {
return myTextField;
}
FXLabel*
MFXDecalsTable::Cell::getIndexLabel() const {
return myIndexLabel;
}
FXButton*
MFXDecalsTable::Cell::getButton() {
return myButton;
}
FXCheckButton*
MFXDecalsTable::Cell::getCheckButton() {
return myCheckButton;
}
FXRealSpinner*
MFXDecalsTable::Cell::getSpinner() {
return mySpinner;
}
void
MFXDecalsTable::Cell::showIndexLabelNormal() {
myIndexLabel->show();
myIndexLabelBold->hide();
myIndexLabel->recalc();
myIndexLabelBold->recalc();
}
void
MFXDecalsTable::Cell::showIndexLabelBold() {
myIndexLabel->hide();
myIndexLabelBold->show();
myIndexLabel->recalc();
myIndexLabelBold->recalc();
}
int
MFXDecalsTable::Cell::getCol() const {
return myCol;
}
int
MFXDecalsTable::Cell::getRow() const {
return myRow;
}
char
MFXDecalsTable::Cell::getType() const {
return myDecalsTable->myColumns.at(myCol)->getType();
}
MFXDecalsTable::Cell::Cell() :
myCol(-1),
myRow(-1) {
}
MFXDecalsTable::Column::Column(MFXDecalsTable* table, const int index, const char type) :
myTable(table),
myIndex(index),
myType(type) {
auto staticTooltip = table->myDialogViewSettings->getSUMOAbstractView()->getGUIGlChildWindow()->getGUIMainWindowParent()->getStaticTooltipMenu();
if (myType == 'f') {
myVerticalFrame = new FXVerticalFrame(table->myColumnsFrame, GUIDesignAuxiliarFrame);
} else {
myVerticalFrame = new FXVerticalFrame(table->myColumnsFrame, GUIDesignAuxiliarFrameFixedWidth(100));
}
switch (myType) {
case ('f'): {
myTopLabel = new MFXLabelTooltip(myVerticalFrame, staticTooltip, "", nullptr, GUIDesignLabelThick(JUSTIFY_NORMAL));
break;
}
case ('p'):
case ('s'): {
myTopLabel = new MFXLabelTooltip(myVerticalFrame, staticTooltip, "", nullptr, GUIDesignLabelThickedFixed(0));
break;
}
case ('c'): {
myTopLabel = new MFXLabelTooltip(myVerticalFrame, staticTooltip, "", nullptr, GUIDesignLabelThickedFixed(30));
break;
}
case ('i'): {
myTopLabel = new MFXLabelTooltip(myVerticalFrame, staticTooltip, "", nullptr, GUIDesignLabelFixed(30));
break;
}
default: {
myTopLabel = new MFXLabelTooltip(myVerticalFrame, staticTooltip, "", nullptr, GUIDesignLabelFixed(0));
break;
}
}
if (myType == 'f') {
myVerticalCellFrame = new FXVerticalFrame(myVerticalFrame, GUIDesignAuxiliarFrame);
} else {
myVerticalCellFrame = new FXVerticalFrame(myVerticalFrame, GUIDesignAuxiliarFrameFixedWidth(100));
}
myVerticalFrame->create();
myTopLabel->create();
myVerticalCellFrame->create();
adjustColumnWidth();
}
MFXDecalsTable::Column::~Column() {
delete myVerticalFrame;
}
FXVerticalFrame*
MFXDecalsTable::Column::getVerticalCellFrame() const {
return myVerticalCellFrame;
}
char
MFXDecalsTable::Column::getType() const {
return myType;
}
FXString
MFXDecalsTable::Column::getColumnLabel() const {
return myTopLabel->getText();
}
void
MFXDecalsTable::Column::setColumnLabel(const std::string& text, const std::string& tooltip) {
myTopLabel->setText(text.c_str());
myTopLabel->setTipText(tooltip.c_str());
adjustColumnWidth();
}
void
MFXDecalsTable::Column::adjustColumnWidth() {
if (myType != 'f') {
int columnWidth = GUIDesignHeight;
if ((myType == 's') || (myType == 'p') || (myType == 'c')) {
columnWidth = myTopLabel->getFont()->getTextWidth(myTopLabel->getText().text(), myTopLabel->getText().length() + EXTRAMARGIN);
}
for (const auto& row : myTable->myRows) {
if (row->getCells().at(myIndex)->getTextField()) {
row->getCells().at(myIndex)->getTextField()->setWidth(columnWidth);
} else if (row->getCells().at(myIndex)->getButton()) {
row->getCells().at(myIndex)->getButton()->setWidth(columnWidth);
} else if (row->getCells().at(myIndex)->getSpinner()) {
row->getCells().at(myIndex)->getSpinner()->setWidth(columnWidth);
}
}
myVerticalFrame->setWidth(columnWidth);
myTopLabel->setWidth(columnWidth);
myVerticalCellFrame->setWidth(columnWidth);
}
}
MFXDecalsTable::Column::Column() :
myIndex(0),
myType('-') {}
MFXDecalsTable::Row::Row(MFXDecalsTable* table) :
myTable(table) {
for (int columnIndex = 0; columnIndex < (FXint)table->myColumns.size(); columnIndex++) {
const int numCells = (int)myCells.size();
switch (table->myColumns.at(columnIndex)->getType()) {
case ('f'): {
auto textField = new FXTextField(table->myColumns.at(columnIndex)->getVerticalCellFrame(),
GUIDesignTextFieldNCol, table, MID_DECALSTABLE_TEXTFIELD, GUIDesignTextField);
myCells.push_back(new Cell(table, textField, columnIndex, numCells));
break;
}
case ('s'): {
auto textField = new FXTextField(table->myColumns.at(columnIndex)->getVerticalCellFrame(),
GUIDesignTextFieldNCol, table, MID_DECALSTABLE_TEXTFIELD, GUIDesignTextFieldFixedRestricted(0, TEXTFIELD_REAL));
myCells.push_back(new Cell(table, textField, columnIndex, numCells));
break;
}
case ('p'): {
auto spinner = new FXRealSpinner(table->myColumns.at(columnIndex)->getVerticalCellFrame(), GUIDesignTextFieldNCol,
table, MID_DECALSTABLE_SPINNER, GUIDesignSpinDialDecalsTable);
myCells.push_back(new Cell(table, spinner, columnIndex, numCells));
break;
}
case ('i'): {
auto indexLabel = new FXLabel(table->myColumns.at(columnIndex)->getVerticalCellFrame(),
toString(myTable->myRows.size()).c_str(), nullptr, GUIDesignLabelThickedFixed(30));
auto indexLabelBold = new FXLabel(table->myColumns.at(columnIndex)->getVerticalCellFrame(),
toString(myTable->myRows.size()).c_str(), nullptr, GUIDesignLabelThickedFixed(30));
indexLabel->setFont(myTable->myIndexFont);
indexLabelBold->setFont(myTable->myIndexSelectedFont);
myCells.push_back(new Cell(table, indexLabel, indexLabelBold, columnIndex, numCells));
break;
}
case ('c'): {
auto checkableButton = new FXCheckButton(table->myColumns.at(columnIndex)->getVerticalCellFrame(),
"false", table, MID_DECALSTABLE_CHECKBOX, GUIDesignMFXCheckableButton);
myCells.push_back(new Cell(table, checkableButton, columnIndex, numCells));
break;
}
case ('b'): {
auto button = GUIDesigns::buildFXButton(table->myColumns.at(columnIndex)->getVerticalCellFrame(),
"", TL("Open decal"), TL("Open decal."),
GUIIconSubSys::getIcon(GUIIcon::OPEN), table, MID_DECALSTABLE_OPEN, GUIDesignButtonIcon);
myCells.push_back(new Cell(table, button, columnIndex, numCells));
break;
}
case ('d'): {
auto button = GUIDesigns::buildFXButton(table->myColumns.at(columnIndex)->getVerticalCellFrame(),
"", TL("Remove decal"), TL("Remove decal."),
GUIIconSubSys::getIcon(GUIIcon::REMOVE), table, MID_DECALSTABLE_REMOVE, GUIDesignButtonIcon);
myCells.push_back(new Cell(table, button, columnIndex, numCells));
break;
}
default:
throw ProcessError("Invalid Cell type");
}
}
}
MFXDecalsTable::Row::~Row() {
for (const auto& cell : myCells) {
delete cell;
}
}
std::string
MFXDecalsTable::Row::getText(int index) const {
if (myCells.at(index)->getTextField()) {
return myCells.at(index)->getTextField()->getText().text();
} else {
throw ProcessError("Cell doesn't have a textField");
}
}
void
MFXDecalsTable::Row::setText(int index, const std::string& text) const {
myCells.at(index)->getTextField()->setText(text.c_str());
}
const std::vector<MFXDecalsTable::Cell*>&
MFXDecalsTable::Row::getCells() const {
return myCells;
}
MFXDecalsTable::Row::Row() {}