Path: blob/main/src/netedit/frames/demand/GNEDistributionFrame.cpp
193780 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file GNEDistributionFrame.cpp14/// @author Pablo Alvarez Lopez15/// @date Jun 202316///17// The Widget for edit distribution elements18/****************************************************************************/1920#include <netedit/changes/GNEChange_DemandElement.h>21#include <netedit/dialogs/elements/GNEDistributionRefDialog.h>22#include <netedit/elements/demand/GNERouteDistribution.h>23#include <netedit/elements/demand/GNEVTypeDistribution.h>24#include <netedit/frames/GNEAttributesEditor.h>25#include <netedit/GNEApplicationWindow.h>26#include <netedit/GNENet.h>27#include <netedit/GNETagProperties.h>28#include <netedit/GNEUndoList.h>29#include <netedit/GNEViewParent.h>30#include <utils/foxtools/MFXTextFieldIcon.h>31#include <utils/gui/div/GUIDesigns.h>3233#include "GNEDistributionFrame.h"3435#define TEMPORAL_FILENAME std::string()3637// ===========================================================================38// FOX callback mapping39// ===========================================================================4041FXDEFMAP(GNEDistributionFrame::DistributionEditor) DistributionEditorMap[] = {42FXMAPFUNC(SEL_COMMAND, MID_GNE_CREATE, GNEDistributionFrame::DistributionEditor::onCmdCreateDistribution),43FXMAPFUNC(SEL_COMMAND, MID_GNE_DELETE, GNEDistributionFrame::DistributionEditor::onCmdDeleteDistribution),44FXMAPFUNC(SEL_UPDATE, MID_GNE_DELETE, GNEDistributionFrame::DistributionEditor::onUpdDeleteDistribution),45};4647FXDEFMAP(GNEDistributionFrame::DistributionSelector) DistributionSelectorMap[] = {48FXMAPFUNC(SEL_COMMAND, MID_GNE_SET_TYPE, GNEDistributionFrame::DistributionSelector::onCmdSelectDistribution),49FXMAPFUNC(SEL_UPDATE, MID_GNE_SET_TYPE, GNEDistributionFrame::DistributionSelector::onCmdUpdateDistribution)50};515253FXDEFMAP(GNEDistributionFrame::DistributionRow) DistributionRowMap[] = {54FXMAPFUNC(SEL_COMMAND, MID_GNE_SET_ATTRIBUTE, GNEDistributionFrame::DistributionRow::onCmdSetProbability)55};5657FXDEFMAP(GNEDistributionFrame::DistributionValuesEditor) DistributionValuesEditorMap[] = {58FXMAPFUNC(SEL_COMMAND, MID_GNE_BUTTON_ADD, GNEDistributionFrame::DistributionValuesEditor::onCmdAddRow),59FXMAPFUNC(SEL_COMMAND, MID_GNE_BUTTON_REMOVE, GNEDistributionFrame::DistributionValuesEditor::onCmdRemoveRow)60};6162// Object implementation63FXIMPLEMENT(GNEDistributionFrame::DistributionEditor, GNEGroupBoxModule, DistributionEditorMap, ARRAYNUMBER(DistributionEditorMap))64FXIMPLEMENT(GNEDistributionFrame::DistributionSelector, GNEGroupBoxModule, DistributionSelectorMap, ARRAYNUMBER(DistributionSelectorMap))65FXIMPLEMENT(GNEDistributionFrame::DistributionRow, FXHorizontalFrame, DistributionRowMap, ARRAYNUMBER(DistributionRowMap))66FXIMPLEMENT(GNEDistributionFrame::DistributionValuesEditor, GNEGroupBoxModule, DistributionValuesEditorMap, ARRAYNUMBER(DistributionValuesEditorMap))676869// ===========================================================================70// method definitions71// ===========================================================================7273// ---------------------------------------------------------------------------74// GNEDistributionFrame::DistributionEditor - methods75// ---------------------------------------------------------------------------7677GNEDistributionFrame::DistributionEditor::DistributionEditor(GNEFrame* frameParent, SumoXMLTag distributionTag, GUIIcon icon) :78GNEGroupBoxModule(frameParent, TL("Distribution Editor")),79myFrameParent(frameParent),80myDistributionTag(distributionTag) {81// get staticTooltip menu82auto staticTooltipMenu = myFrameParent->getViewNet()->getViewParent()->getGNEAppWindows()->getStaticTooltipMenu();83// Create new distribution84myCreateDistributionButton = new MFXButtonTooltip(getCollapsableFrame(), staticTooltipMenu, TL("New"),85GUIIconSubSys::getIcon(icon), this, MID_GNE_CREATE, GUIDesignButton);86myCreateDistributionButton->setTipText(TLF("Create new %", toString(myDistributionTag)).c_str()),87// Delete distribution88myDeleteDistributionButton = new MFXButtonTooltip(getCollapsableFrame(), staticTooltipMenu, TL("Delete"),89GUIIconSubSys::getIcon(GUIIcon::MODEDELETE), this, MID_GNE_DELETE, GUIDesignButton);90myDeleteDistributionButton->setTipText(TLF("Delete current edited %", toString(myDistributionTag)).c_str()),91// show editor92show();93}949596GNEDistributionFrame::DistributionEditor::~DistributionEditor() {}979899SumoXMLTag100GNEDistributionFrame::DistributionEditor::getDistributionTag() const {101return myDistributionTag;102}103104105long106GNEDistributionFrame::DistributionEditor::onCmdCreateDistribution(FXObject*, FXSelector, void*) {107auto undoList = myFrameParent->getViewNet()->getUndoList();108// obtain a new valid ID109const auto distributionID = myFrameParent->getViewNet()->getNet()->getAttributeCarriers()->generateDemandElementID(myDistributionTag);110// get bucket111auto bucket = myFrameParent->getViewNet()->getNet()->getACTemplates()->getTemplateAC(myDistributionTag)->getFileBucket();112// create new distribution113GNEDemandElement* distribution = nullptr;114if (myDistributionTag == SUMO_TAG_VTYPE_DISTRIBUTION) {115distribution = new GNEVTypeDistribution(distributionID, myFrameParent->getViewNet()->getNet(), bucket, -1);116} else if (myDistributionTag == SUMO_TAG_ROUTE_DISTRIBUTION) {117distribution = new GNERouteDistribution(distributionID, myFrameParent->getViewNet()->getNet(), bucket);118} else {119throw ProcessError("Invalid distribution");120}121// add it using undoList (to allow undo-redo)122undoList->begin(distribution->getTagProperty()->getGUIIcon(), "create distribution");123undoList->add(new GNEChange_DemandElement(distribution, true), true);124undoList->end();125// refresh selector using created distribution126myDistributionSelector->setDistribution(distribution);127return 1;128}129130131long132GNEDistributionFrame::DistributionEditor::onCmdDeleteDistribution(FXObject*, FXSelector, void*) {133auto undoList = myFrameParent->getViewNet()->getUndoList();134auto currentDistribution = myDistributionSelector->getCurrentDistribution();135if (currentDistribution) {136// begin undo list operation137undoList->begin(currentDistribution->getTagProperty()->getGUIIcon(), "delete " + currentDistribution->getTagProperty()->getTagStr() + " distribution");138// remove distribution139myFrameParent->getViewNet()->getNet()->deleteDemandElement(myDistributionSelector->getCurrentDistribution(), undoList);140// end undo list operation141undoList->end();142// refresh selector143myDistributionSelector->refreshDistributionSelector();144}145return 1;146}147148149long150GNEDistributionFrame::DistributionEditor::onUpdDeleteDistribution(FXObject* sender, FXSelector, void*) {151// check if we have a selected distribution152if (myDistributionSelector->getCurrentDistribution()) {153return sender->handle(this, FXSEL(SEL_COMMAND, ID_ENABLE), nullptr);154} else {155return sender->handle(this, FXSEL(SEL_COMMAND, ID_DISABLE), nullptr);156}157}158159// ---------------------------------------------------------------------------160// GNETypeFrame::DistributionSelector - methods161// ---------------------------------------------------------------------------162163GNEDistributionFrame::DistributionSelector::DistributionSelector(GNEFrame* frameParent) :164GNEGroupBoxModule(frameParent, TL("Distribution selector")),165myFrameParent(frameParent) {166// Create MFXComboBoxIcon167myDistributionsComboBox = new MFXComboBoxIcon(getCollapsableFrame(), frameParent->getViewNet()->getViewParent()->getGNEAppWindows()->getStaticTooltipMenu(),168true, GUIDesignComboBoxVisibleItems, this, MID_GNE_SET_TYPE, GUIDesignComboBox);169// DistributionSelector is always shown170show();171}172173174GNEDistributionFrame::DistributionSelector::~DistributionSelector() {}175176177void178GNEDistributionFrame::DistributionSelector::setDistribution(GNEDemandElement* distribution) {179myCurrentDistribution = distribution;180refreshDistributionSelector();181}182183184GNEDemandElement*185GNEDistributionFrame::DistributionSelector::getCurrentDistribution() const {186return myCurrentDistribution;187188}189190191void192GNEDistributionFrame::DistributionSelector::refreshDistributionIDs() {193// fill distributions194fillDistributionComboBox();195// set current item196for (int i = 0; i < (int)myDistributionsComboBox->getNumItems(); i++) {197if (myDistributionsComboBox->getItemText(i) == myCurrentDistribution->getID()) {198myDistributionsComboBox->setCurrentItem(i);199}200}201}202203204void205GNEDistributionFrame::DistributionSelector::refreshDistributionSelector() {206// fill distributions207const auto distributions = fillDistributionComboBox();208// update current distribution (used if myCurrentDistribution was deleted during undo-redo)209myCurrentDistribution = myFrameParent->getViewNet()->getNet()->getAttributeCarriers()->retrieveDemandElement(myCurrentDistribution, false);210// update comboBox211if (myCurrentDistribution) {212for (int i = 0; i < (int)myDistributionsComboBox->getNumItems(); i++) {213if (myDistributionsComboBox->getItemText(i) == myCurrentDistribution->getID()) {214myDistributionsComboBox->setCurrentItem(i);215}216}217} else if (distributions.size() > 0) {218// set first distribution219myCurrentDistribution = distributions.begin()->second;220}221// continue depending of myCurrentDistribution222if (myCurrentDistribution) {223// show modules224myAttributesEditor->showAttributesEditor(myCurrentDistribution, true);225myDistributionValuesEditor->showDistributionValuesEditor();226} else {227// hide modules228myAttributesEditor->hideAttributesEditor();229myDistributionValuesEditor->hideDistributionValuesEditor();230}231}232233234long235GNEDistributionFrame::DistributionSelector::onCmdSelectDistribution(FXObject*, FXSelector, void*) {236const auto viewNet = myFrameParent->getViewNet();237const auto& distributions = viewNet->getNet()->getAttributeCarriers()->getDemandElements().at(myDistributionEditor->getDistributionTag());238// Check if value of myTypeMatchBox correspond of an allowed additional tags239for (const auto& distribution : distributions) {240if (distribution.second->getID() == myDistributionsComboBox->getText().text()) {241// set pointer242myCurrentDistribution = distribution.second;243// set color of myTypeMatchBox to black (valid)244myDistributionsComboBox->setTextColor(GUIDesignTextColorBlack);245// show modules246myAttributesEditor->showAttributesEditor(distribution.second, true);247myDistributionValuesEditor->showDistributionValuesEditor();248// update viewNet249viewNet->updateViewNet();250return 1;251}252}253// not found, then reset myCurrentDistribution254myCurrentDistribution = nullptr;255// hide modules256myAttributesEditor->hideAttributesEditor();257myDistributionValuesEditor->hideDistributionValuesEditor();258// set color of myTypeMatchBox to red (invalid)259myDistributionsComboBox->setTextColor(GUIDesignTextColorRed);260// update viewNet261viewNet->updateViewNet();262return 1;263}264265266long267GNEDistributionFrame::DistributionSelector::onCmdUpdateDistribution(FXObject* sender, FXSelector, void*) {268const auto& demandElements = myFrameParent->getViewNet()->getNet()->getAttributeCarriers()->getDemandElements();269if (demandElements.at(myDistributionEditor->getDistributionTag()).size() > 0) {270return sender->handle(this, FXSEL(SEL_COMMAND, ID_ENABLE), nullptr);271} else {272return sender->handle(this, FXSEL(SEL_COMMAND, ID_DISABLE), nullptr);273}274}275276277std::map<std::string, GNEDemandElement*>278GNEDistributionFrame::DistributionSelector::fillDistributionComboBox() {279// get ACs280const auto& ACs = myFrameParent->getViewNet()->getNet()->getAttributeCarriers();281// clear items282myDistributionsComboBox->clearItems();283// fill with distributions sorted by ID284std::map<std::string, GNEDemandElement*> distributions;285for (const auto& distribution : ACs->getDemandElements().at(myDistributionEditor->getDistributionTag())) {286distributions[distribution.second->getID()] = distribution.second;287}288for (const auto& distribution : distributions) {289myDistributionsComboBox->appendIconItem(distribution.first.c_str(), distribution.second->getACIcon());290}291// return distributions sorted by ID292return distributions;293}294295// ---------------------------------------------------------------------------296// GNEDistributionFrame::DistributionRow - methods297// ---------------------------------------------------------------------------298299GNEDistributionFrame::DistributionRow::DistributionRow(DistributionValuesEditor* attributeEditorParent, GNEDemandElement* distributionReference) :300FXHorizontalFrame(attributeEditorParent->getCollapsableFrame(), GUIDesignAuxiliarHorizontalFrame),301myDistributionValuesEditorParent(attributeEditorParent),302myDistributionReference(distributionReference) {303// get staticTooltip menu304auto staticTooltipMenu = attributeEditorParent->getFrameParent()->getViewNet()->getViewParent()->getGNEAppWindows()->getStaticTooltipMenu();305// create label306myIconLabel = new FXLabel(this, "", myDistributionReference->getACIcon(), GUIDesignLabelIconThick);307// Create and disable MFXTextFieldIcon for string attributes308myIDTextField = new MFXTextFieldIcon(this, staticTooltipMenu, GUIIcon::EMPTY, this, MID_GNE_SET_ATTRIBUTE, GUIDesignTextFieldFixed(100 - GUIDesignHeight));309myIDTextField->disable();310// Create MFXTextFieldIcon for string attributes311myProbabilityTextField = new MFXTextFieldIcon(this, staticTooltipMenu, GUIIcon::EMPTY, this, MID_GNE_SET_ATTRIBUTE,312GUIDesignTextFieldRestricted(TEXTFIELD_REAL));313// create delete buton314myDeleteRowButton = new MFXButtonTooltip(this, staticTooltipMenu, "", GUIIconSubSys::getIcon(GUIIcon::REMOVE),315myDistributionValuesEditorParent, MID_GNE_BUTTON_REMOVE, GUIDesignButtonIcon);316myDeleteRowButton->setTipText(TL("Delete distribution value"));317// only create if parent was created318if (getParent()->id() && attributeEditorParent->myDistributionSelector->getCurrentDistribution()) {319// create DistributionRow320FXHorizontalFrame::create();321// set values322myIDTextField->setText(myDistributionReference->getAttribute(SUMO_ATTR_REFID).c_str());323myProbabilityTextField->setText(myDistributionReference->getAttribute(SUMO_ATTR_PROB).c_str());324// set color depending if attribute is computed325if (myDistributionReference->isAttributeComputed(SUMO_ATTR_PROB)) {326myProbabilityTextField->setTextColor(MFXUtils::getFXColor(RGBColor::BLUE));327} else {328myProbabilityTextField->setTextColor(MFXUtils::getFXColor(RGBColor::BLACK));329}330// Show DistributionRow331show();332}333}334335336void337GNEDistributionFrame::DistributionRow::destroy() {338// only destroy if parent was created339if (getParent()->id()) {340FXHorizontalFrame::destroy();341}342}343344345GNEDemandElement*346GNEDistributionFrame::DistributionRow::getDistributionReference() const {347return myDistributionReference;348}349350351MFXButtonTooltip*352GNEDistributionFrame::DistributionRow::getDeleteRowButton() const {353return myDeleteRowButton;354}355356357long358GNEDistributionFrame::DistributionRow::onCmdSetProbability(FXObject*, FXSelector, void*) {359// set default value if value is empty360if (myProbabilityTextField->getText().empty()) {361myProbabilityTextField->setText(myDistributionReference->getTagProperty()->getAttributeProperties(SUMO_ATTR_PROB)->getDefaultStringValue().c_str());362}363// if is valid, update value in AC364if (myDistributionReference->isValid(SUMO_ATTR_PROB, myProbabilityTextField->getText().text())) {365myDistributionReference->setAttribute(SUMO_ATTR_PROB, myProbabilityTextField->getText().text(), myDistributionReference->getNet()->getUndoList());366myDistributionValuesEditorParent->updateSumLabel();367// update probablity text field (needed for show the default value)368myProbabilityTextField->setText(myDistributionReference->getAttribute(SUMO_ATTR_PROB).c_str(), FALSE);369// set color depending if attribute is computed370if (myDistributionReference->isAttributeComputed(SUMO_ATTR_PROB)) {371myProbabilityTextField->setTextColor(MFXUtils::getFXColor(RGBColor::BLUE));372} else {373myProbabilityTextField->setTextColor(MFXUtils::getFXColor(RGBColor::BLACK));374}375myProbabilityTextField->killFocus();376} else {377myProbabilityTextField->setTextColor(MFXUtils::getFXColor(RGBColor::RED));378}379return 1;380}381382// ---------------------------------------------------------------------------383// GNEDistributionFrame::DistributionValuesEditor - methods384// ---------------------------------------------------------------------------385386GNEDistributionFrame::DistributionValuesEditor::DistributionValuesEditor(GNEFrame* frameParent, DistributionEditor* distributionEditor,387DistributionSelector* distributionSelector, GNEAttributesEditor* attributesEditor) :388GNEGroupBoxModule(frameParent, TL("Distribution values")),389myFrameParent(frameParent),390myDistributionEditor(distributionEditor),391myDistributionSelector(distributionSelector),392myAttributesEditor(attributesEditor) {393// set relations394myDistributionEditor->myDistributionSelector = myDistributionSelector;395myDistributionSelector->myDistributionEditor = myDistributionEditor;396myDistributionSelector->myAttributesEditor = myAttributesEditor;397myDistributionSelector->myDistributionValuesEditor = this;398// get staticTooltip menu399auto staticTooltipMenu = frameParent->getViewNet()->getViewParent()->getGNEAppWindows()->getStaticTooltipMenu();400// Create bot frame elements401myBotFrame = new FXHorizontalFrame(getCollapsableFrame(), GUIDesignAuxiliarHorizontalFrame);402myAddButton = new MFXButtonTooltip(myBotFrame, staticTooltipMenu, "", GUIIconSubSys::getIcon(GUIIcon::ADD), this, MID_GNE_BUTTON_ADD, GUIDesignButtonIcon);403myAddButton->setTipText(TL("Add new distribution value"));404new FXHorizontalFrame(myBotFrame, GUIDesignAuxiliarHorizontalFrame);405new FXLabel(myBotFrame, "", GUIIconSubSys::getIcon(GUIIcon::SUM), GUIDesignLabelIconThick);406mySumLabel = new FXLabel(myBotFrame, "", nullptr, GUIDesignLabelThickedFixed(50));407new FXLabel(myBotFrame, "", GUIIconSubSys::getIcon(GUIIcon::EMPTY), GUIDesignLabelFixed(GUIDesignHeight));408}409410411void412GNEDistributionFrame::DistributionValuesEditor::showDistributionValuesEditor() {413// remake rows414refreshRows();415// show DistributionValuesEditor416show();417}418419420void421GNEDistributionFrame::DistributionValuesEditor::hideDistributionValuesEditor() {422// hide also DistributionValuesEditor423hide();424}425426427void428GNEDistributionFrame::DistributionValuesEditor::refreshRows() {429// first remove all rows430for (auto& row : myDistributionRows) {431// destroy and delete all rows432if (row != nullptr) {433row->destroy();434delete row;435row = nullptr;436}437}438myDistributionRows.clear();439// continue if we have a distribution to edit440if (myDistributionSelector->getCurrentDistribution()) {441// Iterate over distribution key-values442for (const auto& distributionRef : myDistributionSelector->getCurrentDistribution()->getChildDemandElements()) {443if (distributionRef->getTagProperty()->isDistributionReference()) {444if (distributionRef->getTagProperty()->isDistributionReference() && (distributionRef->getParentDemandElements().front() == myDistributionSelector->getCurrentDistribution())) {445// create distribution row446auto distributionRow = new DistributionRow(this, distributionRef);447// add into distribution rows448myDistributionRows.push_back(distributionRow);449}450} else {451// update geometry of vehicle452distributionRef->updateGeometry();453}454}455}456// check if enable or disable add button457if (myDistributionRows.size() > 0) {458myAddButton->enable();459} else {460myAddButton->disable();461}462// update sum label463updateSumLabel();464// reparent bot frame button (to place it at bottom)465myBotFrame->reparent(getCollapsableFrame());466}467468469GNEFrame*470GNEDistributionFrame::DistributionValuesEditor::getFrameParent() const {471return myFrameParent;472}473474475long476GNEDistributionFrame::DistributionValuesEditor::onCmdAddRow(FXObject*, FXSelector, void*) {477// open distribution dialog478GNEDistributionRefDialog distributionDialog(myDistributionSelector->getCurrentDistribution());479// only refresh if we added a new row480if (distributionDialog.getResult() == GNEDialog::Result::ACCEPT) {481refreshRows();482}483return 1;484}485486487long488GNEDistributionFrame::DistributionValuesEditor::onCmdRemoveRow(FXObject* obj, FXSelector, void*) {489for (const auto& row : myDistributionRows) {490if (row->getDeleteRowButton() == obj) {491myFrameParent->getViewNet()->getNet()->deleteDemandElement(row->getDistributionReference(), myFrameParent->getViewNet()->getUndoList());492return 1;493}494}495return 1;496}497498499void500GNEDistributionFrame::DistributionValuesEditor::updateSumLabel() {501// update probability502double sumProbability = 0;503if (myDistributionSelector->getCurrentDistribution()) {504for (const auto& distributionRef : myDistributionSelector->getCurrentDistribution()->getChildDemandElements()) {505if (distributionRef->getTagProperty()->isDistributionReference()) {506sumProbability += distributionRef->getAttributeDouble(SUMO_ATTR_PROB);507}508}509// vType distributions510if (myDistributionSelector->getCurrentDistribution()->getTagProperty()->getTag() == SUMO_TAG_VTYPE_DISTRIBUTION) {511const auto& vTypes = myFrameParent->getViewNet()->getNet()->getAttributeCarriers()->getDemandElements().at(SUMO_TAG_VTYPE);512if (vTypes.size() == myDistributionRows.size()) {513myAddButton->disable();514} else {515myAddButton->enable();516}517}518// route distribution519if (myDistributionSelector->getCurrentDistribution()->getTagProperty()->getTag() == SUMO_TAG_ROUTE_DISTRIBUTION) {520const auto& routes = myFrameParent->getViewNet()->getNet()->getAttributeCarriers()->getDemandElements().at(SUMO_TAG_ROUTE);521if (routes.size() == myDistributionRows.size()) {522myAddButton->disable();523} else {524myAddButton->enable();525}526}527} else {528myAddButton->disable();529}530mySumLabel->setText(toString(sumProbability).c_str());531}532533/****************************************************************************/534535536