StateTreeController.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5 *
6 * ArmarX is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * ArmarX is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * @package
19 * @author
20 * @date
21 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22 * GNU General Public License
23 */
24#include "StateTreeController.h"
25
26#include <QBoxLayout>
27#include <QCheckBox>
28#include <QDialogButtonBox>
29#include <QFileDialog>
30#include <QIcon>
31#include <QInputDialog>
32#include <QLabel>
33#include <QMenu>
34#include <QMessageBox>
35#include <QProgressDialog>
36#include <QtGui>
37
38// Qt headers must come before <filesystem>
39// https://stackoverflow.com/questions/69407237/qt-moc-errorusr-include-c-10-bits-fs-fwd-39-parse-error-at-std
40#include <filesystem>
41
46
54
56#include "io/GroupXmlReader.h"
57#include "io/GroupXmlWriter.h"
58#include "io/XmlWriter.h"
59
60using namespace armarx;
61using namespace statechartio;
62using namespace statechartmodel;
63
65 VariantInfoPtr variantInfo,
66 QList<QVariant> headerInfo,
67 QTreeView* treeView,
68 QLineEdit* filterLineEdit,
69 const ArmarXPackageToolInterfacePtr& packageTool,
70 const StatechartProfilesPtr& statechartProfiles,
71 StatechartProfilePtr currentProfile,
72 QObject* parent) :
73 QAbstractItemModel(parent)
74{
75 qRegisterMetaType<StateTreeNodePtr>("StateTreeNodePtr");
76 CMakePackageFinder finder("ArmarXCore");
77 this->appForDrag.reset(new ScenarioManager::Data_Structure::Application(
78 "XMLRemoteStateOffererRun", finder.getBinaryDir(), "ArmarXCore"));
79 this->variantInfo = variantInfo;
80 this->profiles = statechartProfiles;
81 this->currentProfile = currentProfile;
82 this->packageTool = packageTool;
83 this->headerInfo = headerInfo;
84 this->treeView = treeView;
85 this->stateTreeModel.reset(new StateTreeModel(ic, variantInfo));
86 this->rootNode = this->stateTreeModel->getRootNode();
87 this->groupCloner = std::make_shared<GroupCloner>(stateTreeModel, packageTool);
88 this->groupRenamer = std::make_shared<GroupRenamer>(groupCloner);
89 this->communicator = ic;
90
91
92 proxyModel = new InfixFilterModel(this);
93 proxyModel->setSourceModel(this);
94 proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
95 treeView->setModel(proxyModel);
96
97
98 contextMenuGroup = new QMenu(treeView);
99 contextMenuFolder = new QMenu(treeView);
100 contextMenuState = new QMenu(treeView);
101 treeView->setContextMenuPolicy(Qt::CustomContextMenu);
102
103 actionDeleteNode = new QAction(QIcon(":/icons/delete.ico"), "Delete", treeView);
104 actionMakeStatePublic = new QAction("Public State", treeView);
105 actionMakeStatePublic->setCheckable(true);
106 actionNewFolder = new QAction(QIcon(":/icons/folder-new.svg"), "Add new Folder", treeView);
107 actionNewStateDefinition =
108 new QAction(QIcon(":/icons/images/add-state.png"), "Add new State Definition", treeView);
109 actionNewDynamicRemoteStateDefinition =
110 new QAction("Add new Dynamic Remote State Definition", treeView);
111 actionFindStateUsages = new QAction(QIcon(":/icons/search.svg"), "Find State Usages", treeView);
112 actionRenameState = new QAction(QIcon(":/icons/rename.svg"), "Rename State", treeView);
113 actionOpenCMakeProject =
114 new QAction(QIcon(":/icons/qt-small.png"), "Open CMake Project", treeView);
115 actionShowFullPath = new QAction(QIcon(":/icons/search.svg"), "Show Full Path", treeView);
116 actionOpenStateCPP = new QAction(QIcon(":/icons/cpp.svg"), "Open C++ Code", treeView);
117 actionExecuteGroup =
118 new QAction(QIcon(":/icons/run.svg"), "Execute Statechart Group", treeView);
119 //actionGenerateStateBaseClass = new QAction(QIcon(":/icons/document-properties.svg"), "Generate Base Class", treeView);
120 //actionGenerateRsoBaseClass = new QAction(QIcon(":/icons/document-properties.svg"), "Generate RSO Base Class", treeView);
121 actionGenerateStateCPP =
122 new QAction(QIcon(":/icons/document-properties.svg"), "Generate State C++ Files", treeView);
123 actionRenameGroup = new QAction(QIcon(":/icons/rename.svg"), "Rename Group", treeView);
124 actionCloneGroup = new QAction(QIcon(":/icons/clone.svg"), "Clone Group", treeView);
125 actionGroupProperties =
126 new QAction(QIcon(":/icons/document-properties.svg"), "Group Properties", treeView);
127
128 QList<QAction*> groupActions, folderActions, stateActions;
129
130 groupActions.append(actionNewStateDefinition);
131 groupActions.append(actionNewDynamicRemoteStateDefinition);
132 groupActions.append(actionNewFolder);
133 groupActions.append(actionDeleteNode);
134 groupActions.append(actionOpenCMakeProject);
135 groupActions.append(actionShowFullPath);
136 groupActions.append(actionExecuteGroup);
137 //groupActions.append(actionGenerateRsoBaseClass);
138 groupActions.append(actionRenameGroup);
139 groupActions.append(actionCloneGroup);
140 groupActions.append(actionGroupProperties);
141
142 folderActions.append(actionNewStateDefinition);
143 folderActions.append(actionNewDynamicRemoteStateDefinition);
144 folderActions.append(actionNewFolder);
145 folderActions.append(actionDeleteNode);
146
147 stateActions.append(actionMakeStatePublic);
148 stateActions.append(actionFindStateUsages);
149 stateActions.append(actionRenameState);
150 stateActions.append(actionDeleteNode);
151 stateActions.append(actionOpenStateCPP);
152 //stateActions.append(actionGenerateStateBaseClass);
153 stateActions.append(actionGenerateStateCPP);
154
155 for (QAction* a : groupActions)
156 {
157 a->setIconVisibleInMenu(true);
158 }
159
160 for (QAction* a : folderActions)
161 {
162 a->setIconVisibleInMenu(true);
163 }
164
165 for (QAction* a : stateActions)
166 {
167 a->setIconVisibleInMenu(true);
168 }
169
170 contextMenuGroup->addActions(groupActions);
171 contextMenuFolder->addActions(folderActions);
172 contextMenuState->addActions(stateActions);
173
174
175 connect(stateTreeModel.get(), SIGNAL(stateAddedOrRemoved()), SLOT(stateAddedOrRemoved()));
176 connect(treeView, SIGNAL(customContextMenuRequested(QPoint)), SLOT(onContextMenu(QPoint)));
177
178 connect(actionNewStateDefinition, SIGNAL(triggered()), SLOT(onNewStateDefinition()));
179 connect(actionNewDynamicRemoteStateDefinition,
180 SIGNAL(triggered()),
182 connect(actionDeleteNode, SIGNAL(triggered()), SLOT(onDeleteNode()));
183 connect(actionMakeStatePublic, SIGNAL(triggered(bool)), SLOT(onMakeStatePublic(bool)));
184 connect(actionNewFolder, SIGNAL(triggered()), SLOT(onNewFolder()));
185 connect(actionFindStateUsages, SIGNAL(triggered()), SLOT(onFindStateUsages()));
186 connect(actionRenameState, SIGNAL(triggered()), SLOT(onRenameState()));
187 connect(actionOpenCMakeProject, SIGNAL(triggered()), this, SLOT(onOpenCMakeProject()));
188 connect(actionShowFullPath, SIGNAL(triggered()), this, SLOT(onShowFullPath()));
189 connect(actionOpenStateCPP, SIGNAL(triggered()), this, SLOT(onOpenStateCPP()));
190 connect(actionExecuteGroup, SIGNAL(triggered()), this, SLOT(onExecuteGroup()));
191 //connect(actionGenerateStateBaseClass, SIGNAL(triggered()), this, SLOT(onGenerateStateBaseClass()));
192 connect(actionGenerateStateCPP, SIGNAL(triggered()), this, SLOT(onGenerateStateCppFiles()));
193 //connect(actionGenerateRsoBaseClass, SIGNAL(triggered()), this, SLOT(onGenerateRsoBaseClass()));
194 connect(actionRenameGroup, SIGNAL(triggered()), this, SLOT(onRenameGroup()));
195 connect(actionCloneGroup, SIGNAL(triggered()), this, SLOT(onCloneGroup()));
196 connect(actionGroupProperties, SIGNAL(triggered()), this, SLOT(onGroupProperties()));
197
198 connect(filterLineEdit,
199 SIGNAL(textChanged(QString)),
200 proxyModel,
201 SLOT(setFilterFixedString(QString)));
202 connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(expandFilterResults(QString)));
203}
204
206{
207 //delete rootNode;
208 ARMARX_INFO << "Killing " << processes.size() << " statechart processes";
209 for (auto& process : processes)
210 {
211 ARMARX_INFO << "stopping " << process->pid();
212 process->terminate();
213 }
214 for (auto& process : processes)
215 {
216 ARMARX_INFO << "Waiting for process " << process->pid();
217 process->waitForFinished(2000);
218 }
219 for (auto& process : processes)
220 {
221 if (process->pid() > 0)
222 {
223 ARMARX_INFO << "killing " << process->pid();
224 process->kill();
225 }
226 }
227}
228
229QVariant
230StateTreeController::data(const QModelIndex& index, int role) const
231{
232 if (!index.isValid())
233 {
234 return QVariant();
235 }
236
237 StateTreeNodePtr node = getNode(index); //static_cast<StateTreeNode*>(index.internalPointer());
238
239 switch (role)
240 {
241 case Qt::DisplayRole:
242 if (node->getNodeType() == StateTreeNode::State)
243 {
244 return node->getDisplay() + (node->getState() ? "" : " [no .xml]") +
245 (node->getCppExists() ? "" : " [no .cpp]") +
246 (node->isPublic() ? " (public)" : "") +
247 (node->getGroup()->getWriteAccess() == StatechartGroup::eReadOnly
248 ? " (read-only)"
249 : "");
250 }
251 else
252 {
253 return node->getDisplay();
254 }
255
256 case Qt::DecorationRole:
257 if (node->getNodeType() == StateTreeNode::Folder ||
258 node->getNodeType() == StateTreeNode::Group)
259 {
260 if (node->getGroup()->getWriteAccess() == StatechartGroup::eReadOnly)
261 {
262 return getIcon(":/icons/folder-locked.svg");
263 }
264 if (treeView->isExpanded(index))
265 {
266 return getIcon(":/icons/folder-open.svg");
267 }
268 else
269 {
270 return getIcon(":/icons/folder.svg");
271 }
272 }
273 else if (node->getNodeType() == StateTreeNode::State)
274 {
275 if (node->getState())
276 {
277 if (node->getState()->getSubstates().count() > 0)
278 {
279 return getIcon(node->isPublic() ? ":/statechart-editor/states-public.svg"
280 : ":/statechart-editor/states.svg");
281 }
282 else
283 {
284 return getIcon(node->isPublic() ? ":/statechart-editor/state-public.svg"
285 : ":/statechart-editor/state.svg");
286 }
287 }
288 else
289 {
290 return getIcon(":/statechart-editor/state-broken.svg");
291 }
292 }
293
294 return QVariant();
295
296 case Qt::ToolTipRole:
297
298 return QString::fromStdString(node->getAbsoluteBoostPath().string());
299
300 default:
301 return QVariant();
302 }
303}
304
305Qt::ItemFlags
306StateTreeController::flags(const QModelIndex& index) const
307{
308 if (!index.isValid())
309 {
310 return 0;
311 }
312
313 Qt::ItemFlags flags;
315 flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
316
317 if (node)
318 {
319 if (node->getState() || node->isGroup())
320 {
321 flags |= Qt::ItemIsDragEnabled;
322 }
323 }
324
325 return flags;
326}
327
328QVariant
329StateTreeController::headerData(int section, Qt::Orientation orientation, int role) const
330{
331 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
332 {
333 return headerInfo.value(section);
334 }
335
336 return QVariant();
337}
338
339QModelIndex
340StateTreeController::index(int row, int column, const QModelIndex& parentIndex) const
341{
342 if (!hasIndex(row, column, parentIndex))
343 {
344 return QModelIndex();
345 }
346
347 StateTreeNode* parentNode;
348
349 if (!parentIndex.isValid())
350 {
351 parentNode = this->rootNode.get();
352 }
353 else
354 {
355 parentNode = static_cast<StateTreeNode*>(parentIndex.internalPointer());
356 }
357
358 StateTreeNode* childNode = parentNode->child(row).get();
359
360 if (childNode)
361 {
362 return createIndex(row, column, childNode);
363 }
364 else
365 {
366 return QModelIndex();
367 }
368}
369
370QModelIndex
371StateTreeController::parent(const QModelIndex& index) const
372{
373 if (!index.isValid())
374 {
375 return QModelIndex();
376 }
377
378 StateTreeNode* childNode = static_cast<StateTreeNode*>(index.internalPointer());
379 StateTreeNodePtr parentNode = childNode->getParent();
380
381 if (parentNode == rootNode)
382 {
383 return QModelIndex();
384 }
385
386 return createIndex(parentNode->row(), 0, parentNode.get());
387}
388
389int
390StateTreeController::rowCount(const QModelIndex& parent) const
391{
392 //StatechartGroupTreeNode *parentNode;
393 if (parent.column() > 0)
394 {
395 return 0;
396 }
397
398 if (!parent.isValid())
399 {
400 return rootNode->childCount();
401 }
402 else
403 {
404 ARMARX_CHECK_EXPRESSION(parent.internalPointer())
405 << "The internal pointer of a tree node is zero";
406 return static_cast<StateTreeNode*>(parent.internalPointer())->childCount();
407 }
408
409 //return parentNode->childCount();
410}
411
412int
414{
415 if (parent.isValid())
416 {
417 return static_cast<StateTreeNode*>(parent.internalPointer())->columnCount();
418 }
419 else
420 {
421 return rootNode->columnCount();
422 }
423}
424
426StateTreeController::loadAndAddGroup(QString groupDefinitionFile)
427{
428 bool load = !groupDefinitionFile.isEmpty();
429 if (!load)
430 {
431 QFileDialog selectGroupFile(treeView, "Open Statechart Group");
432 // selectFolder.setOption(QFileDialog::ShowDirsOnly, true);
433 selectGroupFile.setOption(QFileDialog::ReadOnly, true);
434 selectGroupFile.setOption(QFileDialog::HideNameFilterDetails, false);
435 selectGroupFile.setFileMode(QFileDialog::ExistingFile);
436 QList<QUrl> urls;
437 urls << QUrl::fromLocalFile(
438 QDesktopServices::storageLocation(QDesktopServices::HomeLocation))
439 << QUrl::fromLocalFile(
440 QDesktopServices::storageLocation(QDesktopServices::DesktopLocation));
441
442 if (!ArmarXDataPath::getHomePath().empty())
443 {
444 urls << QUrl::fromLocalFile(QString::fromStdString(ArmarXDataPath::getHomePath()));
445 }
446
447 selectGroupFile.setSidebarUrls(urls);
448 QStringList filters;
449 filters << "Statechart Group (*.scgxml)";
450 filters << "All Files(*.*)";
451 selectGroupFile.setNameFilters(filters);
452 load = selectGroupFile.exec() == QDialog::Accepted;
453 if (load && groupDefinitionFile.isEmpty())
454 {
455 groupDefinitionFile = selectGroupFile.selectedFiles().first();
456 }
457 }
458
459 StatechartGroupPtr group;
460 if (load)
461 {
462 try
463 {
464 group = loadGroup(groupDefinitionFile);
465
466 if (!group)
467 {
468 return group;
469 }
470
471 try
472 {
473 stateTreeModel->loadGroupStates(group);
474 }
475 catch (std::exception& e)
476 {
477 QString reason(QString::fromStdString(e.what()));
478 reason.truncate(200);
479 ARMARX_WARNING_S << "Exception caught: " << e.what();
480 QMessageBox::warning(0,
481 "Error while loading States",
482 QString("An error while loading states - Reason:\n" + reason));
483 }
484 }
485 catch (std::exception& e)
486 {
487 QString reason = QString::fromStdString(e.what());
488 reason.truncate(200);
489 QMessageBox::warning(0,
490 "Error while loading Statechart Group",
491 QString("An error while loading the statechart group file '") +
492 groupDefinitionFile + "' - Reason:\n" + reason);
493 }
494 }
495
496 return group;
497}
498
500StateTreeController::loadGroup(QString groupDefinitionFile)
501{
502 //StatechartGroupTreeNodePtr root = treeController->getRoot();
503 //beginInsertRows(getIndex(rootNode), rowCount(), rowCount());
504
505 this->layoutAboutToBeChanged();
506 StatechartGroupPtr group = stateTreeModel->loadGroup(groupDefinitionFile, rootNode);
507
508 if (!group)
509 {
510 return group;
511 }
512
513 rootNode->appendChild(group->getRootNode());
514 rootNode->sortChildren();
515 //endInsertRows();
516 this->layoutChanged();
517
518 StateTreeNodePtr groupNode = group->getRootNode();
519 treeView->expand(getIndex(groupNode));
520 treeView->setCurrentIndex(getIndex(groupNode));
521
522 return group;
523}
524
526StateTreeController::addNewGroup(const QString& groupName,
527 const QString& groupPath,
528 const QString& description,
529 const QString& packageName,
530 const QList<QString> proxies,
531 bool generateContext,
532 const QMap<QString, QString>& statechartGroupConfigurations)
533{
534 beginInsertRows(getIndex(rootNode), rowCount(), rowCount());
535 std::filesystem::path defPath = groupPath.toUtf8().data();
536 defPath /= (groupName + ".scgxml").toUtf8().data();
537 StatechartGroupPtr group(new StatechartGroup(QString::fromUtf8(defPath.c_str()),
538 groupPath,
539 groupName,
540 description,
541 packageName,
542 proxies,
543 generateContext,
544 statechartGroupConfigurations,
546 StateTreeNodePtr groupRootNode(
547 new StateTreeNode(group->getName(), "", StateTreeNode::Group, rootNode, group, false));
548 group->setRootNode(groupRootNode);
549
550 stateTreeModel->addGroup(group);
551 rootNode->appendChild(group->getRootNode());
552 endInsertRows();
553 rootNode->sortChildren();
554
555 // emit dataChanged(getIndex(group->getRootNode()),getIndex(group->getRootNode()));
556
557 QMetaObject::invokeMethod(
558 this, "selectNode", Qt::QueuedConnection, Q_ARG(StateTreeNodePtr, group->getRootNode()));
559
560 return group;
561}
562
563void
565{
566 stateTreeModel->loadAllStates();
567}
568
571{
572 return stateTreeModel->getNodeByState(state);
573}
574
577{
578 QModelIndex selected = treeView->currentIndex();
579
580 if (!selected.isValid())
581 {
582 return StateTreeNodePtr();
583 }
584
585 return getNode(selected);
586}
587
590{
591 return node ? node->getClosestParentFolderOrGroupNode() : StateTreeNodePtr();
592}
593
594bool
596{
597 return node ? node->getNodeType() == StateTreeNode::State : false;
598}
599
600bool
602{
603 return node ? node->getNodeType() == StateTreeNode::Folder : false;
604}
605
606bool
608{
609 return node ? node->getNodeType() == StateTreeNode::Group : false;
610}
611
612bool
614{
615 return node ? node->getNodeType() == StateTreeNode::Folder ||
616 node->getNodeType() == StateTreeNode::Group
617 : false;
618}
619
620bool
625
626bool
631
632bool
637
638bool
643
646{
648
649 if (!nodeIsFolderOrGroup(node))
650 {
651 return StateTreeNodePtr();
652 }
653
654 return createNewState(name, node);
655}
656
657void
659{
661
662 if (!nodeIsState(node))
663 {
664 return;
665 }
666
667 QList<StateInstancePtr> instances = stateTreeModel->findStateUsages(node);
668
669 if (instances.count() > 0)
670 {
671 QString message = QString("Cannot delete state, it is still being used by %1 state(s):\n%2")
672 .arg(instances.count())
673 .arg(buildStateUsageString(instances));
674 QMessageBox::warning(NULL, "Cannot delete State", message);
675 return;
676 }
677
678 if (node->getState())
679 {
680 QString question = QString("You are about to delete the State Definition '%1'.\nDo you "
681 "also want to permanently remove the underlying file %2?")
682 .arg(node->getDisplay(), node->getBasename());
683 QMessageBox::StandardButton reply =
684 QMessageBox::question(NULL,
685 "Delete State Definition File?",
686 question,
687 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
688
689 if (reply == QMessageBox::Cancel)
690 {
691 return;
692 }
693 else if (reply == QMessageBox::Yes)
694 {
695 std::filesystem::remove(node->getAbsoluteBoostPath());
696 if (node->getCppExists())
697 {
698 std::filesystem::remove(node->getBoostCppFilePath());
699 std::filesystem::remove(node->getBoostHFilePath());
700 std::filesystem::remove(node->getBoostGeneratedHFilePath());
701 }
702 }
703 }
704 else
705 {
706 QString question = QString("Remove State Definition '%1'?").arg(node->getDisplay());
707 QMessageBox::StandardButton reply = QMessageBox::question(
708 NULL, "Delete State Definition?", question, QMessageBox::Ok | QMessageBox::Cancel);
709
710 if (reply == QMessageBox::Cancel)
711 {
712 return;
713 }
714 }
715
716 removeState(node);
718 node->getGroup(),
719 QString::fromUtf8((node->getGroup()->getBoostDefinitionFilePath()).c_str()));
720}
721
725 bool createDynamicRemoteState)
726{
727 this->layoutAboutToBeChanged();
728
730
731 if (!createDynamicRemoteState)
732 {
733 state.reset(new statechartmodel::State());
734 state->addEndSubstate("Success", "Success");
735 state->addEndSubstate("Failure", "Failure");
737 evt->name = "Failure";
738 evt->description =
739 "Event for statechart-internal failures or optionally user-code failures";
740 state->setOutgoingEvents(EventList{evt});
741 }
742 else
743 {
745 }
746
747
748 state->setStateName(name);
749 state->setEditable(parent->getGroup()->getWriteAccess() == StatechartGroup::eWritable);
751 name, name + ".xml", StateTreeNode::State, parent, parent->getGroup(), false));
752 stateTreeModel->setNodeState(node, state);
753 parent->appendChild(node);
754 parent->sortChildren();
755 stateTreeModel->notifyNewStateTreeNode(node);
756 this->layoutChanged();
757 treeView->sortByColumn(0, Qt::AscendingOrder);
758
759 QMetaObject::invokeMethod(
760 this, "selectNode", Qt::QueuedConnection, Q_ARG(StateTreeNodePtr, node));
761
762 return node;
763}
764
767{
768 this->layoutAboutToBeChanged();
769 StateTreeNodePtr node(
770 new StateTreeNode(name, name, StateTreeNode::Folder, parent, parent->getGroup(), false));
771 parent->appendChild(node);
772 parent->sortChildren();
773
774 this->layoutChanged();
775
776 treeView->expand(getIndex(parent));
777 treeView->setCurrentIndex(getIndex(node));
778
779 return node;
780}
781
784{
785 if (!index.isValid())
786 {
787 return rootNode;
788 }
789 else
790 {
791 if (index.model() == proxyModel)
792 {
793 index = proxyModel->mapToSource(index);
794 }
795 return static_cast<StateTreeNode*>(index.internalPointer())->shared_from_this();
796 }
797}
798
799void
801{
802 StateTreeNodePtr node = stateTreeModel->getNodeByState(state);
803
804 if (node)
805 {
806 treeView->setCurrentIndex(proxyModel->mapFromSource(getIndex(node)));
807 }
808}
809
810void
812{
813 QModelIndex index = treeView->indexAt(point);
814 // index = this->index(index.row(), index.column(), index.parent());
815 // if(!index.isValid())
816 // {
817 // return;
818 // }
819 // StateTreeNodePtr node = static_cast<StateTreeNode*>(index.internalPointer())->shared_from_this();
821
822 // reset to initial state
823 actionMakeStatePublic->setEnabled(true);
824 actionRenameState->setEnabled(true);
825 actionRenameGroup->setEnabled(true);
826 actionGenerateStateCPP->setEnabled(true);
827 actionNewFolder->setEnabled(true);
828 actionNewStateDefinition->setEnabled(true);
829 actionNewDynamicRemoteStateDefinition->setEnabled(true);
830 actionOpenCMakeProject->setEnabled(true);
831 actionOpenStateCPP->setEnabled(true);
832 actionGroupProperties->setEnabled(true);
833 actionDeleteNode->setEnabled(true);
834
835 auto applyReadOnlyStatusToMenu = [&, this]()
836 {
837 bool writable =
838 node->getGroup() && node->getGroup()->getWriteAccess() == StatechartGroup::eWritable;
839
840 if (!writable)
841 {
842 actionMakeStatePublic->setEnabled(writable);
843 actionRenameState->setEnabled(writable);
844 actionRenameGroup->setEnabled(writable);
845 actionGenerateStateCPP->setEnabled(writable);
846 actionNewFolder->setEnabled(writable);
847 actionNewStateDefinition->setEnabled(writable);
848 actionNewDynamicRemoteStateDefinition->setEnabled(writable);
849 actionOpenCMakeProject->setEnabled(writable);
850 actionOpenStateCPP->setEnabled(writable);
851 actionGroupProperties->setEnabled(writable);
852 actionDeleteNode->setEnabled(writable);
853 }
854 };
855
856
857 if (node->isState())
858 {
859 actionMakeStatePublic->setEnabled(node->getState() != NULL);
860 actionMakeStatePublic->setChecked(node->isPublic());
861 actionFindStateUsages->setEnabled(node->getState() != NULL);
862 actionRenameState->setEnabled(node->isState());
863 actionGenerateStateCPP->setEnabled(!node->getCppExists() &&
864 node->getState()->getType() == eNormalState);
865 actionOpenStateCPP->setEnabled(node->getCppExists());
866 applyReadOnlyStatusToMenu();
867 contextMenuState->exec(treeView->mapToGlobal(point));
868 }
869 else if (node->isGroup())
870 {
871 // @@@ Buggy implementation: if process is stopped while context menu is open a new process is started. Better: use 2 different context menu entries and 2 differend handler functions.
872 QMap<QString, QProcess*>::const_iterator it;
873
874 if (node->getGroup() // @@@ check not needed, every node has a group
875 && (it = processes.find(node->getGroup()->getGroupPath())) != processes.end() &&
876 it.value()->state() != QProcess::NotRunning)
877 {
878 actionExecuteGroup->setText("Stop and Execute Statechart Group");
879 }
880 else
881 {
882 actionExecuteGroup->setText("Execute Statechart Group");
883 }
884 applyReadOnlyStatusToMenu();
885 contextMenuGroup->exec(treeView->mapToGlobal(point));
886 }
887 else if (node->isFolder() && node != rootNode)
888 {
889 applyReadOnlyStatusToMenu();
890 contextMenuFolder->exec(treeView->mapToGlobal(point));
891 }
892}
893
894struct StateCreationDialog : public QDialog
895{
896 QCheckBox* checkbox;
897 QLabel* label;
898 QLineEdit* editField;
899 QDialogButtonBox* buttonBox;
900
901 StateCreationDialog(QWidget* parent = 0, Qt::WindowFlags flags = 0) : QDialog(parent, flags)
902 {
903 label = new QLabel("Enter the state name", this);
904 editField = new QLineEdit(this);
905 QRegExp rx("^([a-zA-Z_]{1})([a-zA-Z_0-9]+)$");
906 QValidator* validator = new QRegExpValidator(rx, this);
907 editField->setValidator(validator);
908 setWindowTitle("New State Definition");
909 resize(250, 120);
910 checkbox = new QCheckBox("Create C++ Files", this);
911 QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom, this);
912 buttonBox = new QDialogButtonBox(this);
913 buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
914 buttonBox->setOrientation(Qt::Horizontal);
915 buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
916 layout->addWidget(label);
917 layout->addWidget(editField);
918 layout->addWidget(checkbox);
919 layout->addWidget(buttonBox);
920 QObject::connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
921 QObject::connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
922 }
923};
924
925bool
926StateTreeController::retrieveNewStateName(QString& newStateName,
927 StateTreeNodePtr& node,
928 bool& createCPPFiles,
929 bool askForCPPFiles)
930{
931 node = getSelectedNode();
932
933 if (!node)
934 {
935 QMessageBox::warning(
936 NULL, "Nothing selected", "Error: No state, group or folder selected.");
937 return false;
938 }
939
940 node = node->getClosestParentFolderOrGroupNode();
941
942 StateCreationDialog dialog;
943 dialog.checkbox->setEnabled(askForCPPFiles);
944
945 if (dialog.exec() == QDialog::Rejected)
946 {
947 return false;
948 }
949
950 newStateName = dialog.editField->text();
951 createCPPFiles = dialog.checkbox->isChecked();
952
953 if (!StateTreeNode::CheckNodeName(newStateName))
954 {
955 QMessageBox::warning(NULL,
956 "Invalid name",
957 QString("Error: Provided name '%1' does not meet requirements: Has to "
958 "start with a letter, followed by letters and digits. Sorry.")
959 .arg(newStateName));
960 return false;
961 }
962
963 if (node->getGroup()->findNodeByStateName(newStateName))
964 {
965 QMessageBox::warning(
966 NULL,
967 "Duplicate State Name",
968 QString("Error: State with name '%1' exists already. Sorry.").arg(newStateName));
969 return false;
970 }
971
972 if (node->getGroup()->getName() == newStateName)
973 {
974 QMessageBox::warning(
975 NULL, "Name Conflict", QString("Error: State cannot have same name as Group. Sorry."));
976 return false;
977 }
978
979 return true;
980}
981
982QIcon
983StateTreeController::getIcon(const QString& path) const
984{
985 if (!icons.contains(path))
986 {
987 icons[path] = QIcon(path);
988 }
989 return icons[path];
990}
991
992void
994{
995 bool createCPPFiles;
996 StateTreeNodePtr node;
997 QString newStateName;
998
999 if (!retrieveNewStateName(newStateName, node, createCPPFiles, true))
1000 {
1001 return;
1002 }
1003
1004
1005 StateTreeNodePtr newState = createNewState(newStateName, node);
1006
1007 ARMARX_CHECK_EXPRESSION(packageTool);
1008
1009 if (createCPPFiles)
1010 {
1011 if (!packageTool->addFullXmlStatechart(
1012 newState->getGroup()->getName().toUtf8().data(),
1013 newState->getBoostPathRelativeToGroup().replace_extension().string(),
1014 newState->getGroup()->getPackagePath().toUtf8().data()))
1015 {
1016 ARMARX_WARNING_S << "Creating source files for state " << newStateName << " failed";
1017 }
1018 }
1019 else
1020 {
1021 if (!packageTool->addXmlOnlyXmlStatechart(
1022 newState->getGroup()->getName().toUtf8().data(),
1023 newState->getBoostPathRelativeToGroup().replace_extension().string(),
1024 newState->getGroup()->getPackagePath().toUtf8().data()))
1025 {
1026 ARMARX_WARNING_S << "Creating source files for state " << newStateName << " failed";
1027 }
1028 }
1029
1030 newState->checkCppExists();
1031 saveAll();
1032}
1033
1034void
1036{
1037 bool createCPPFiles;
1038 StateTreeNodePtr node;
1039 QString newStateName;
1040
1041 if (!retrieveNewStateName(newStateName, node, createCPPFiles, false))
1042 {
1043 return;
1044 }
1045
1046
1047 createNewState(newStateName, node, true);
1048 saveAll();
1049}
1050
1051void
1053{
1055
1056 if (!node)
1057 {
1058 QMessageBox::warning(
1059 NULL, "Nothing selected", "Error: No state, group or folder selected.");
1060 return;
1061 }
1062
1063 if (!nodeIsFolderOrGroup(node))
1064 {
1065 QMessageBox::warning(NULL, "Internal error", "Internal error.");
1066 return;
1067 }
1068
1069 bool ok;
1070 QString name = QInputDialog::getText(
1071 NULL, QString("New Folder"), QString("Enter name"), QLineEdit::Normal, QString(), &ok);
1072
1073 if (!ok)
1074 {
1075 return;
1076 }
1077
1078 StateTreeNodePtr newFolderNode = createNewFolder(name, node);
1079 std::filesystem::create_directory(newFolderNode->getAbsoluteBoostPath());
1080}
1081
1082void
1084{
1085 if (selectedNodeIsState())
1086 {
1088 return;
1089 }
1090
1092 {
1094 return;
1095 }
1096
1097 if (selectedNodeIsGroup())
1098 {
1100 return;
1101 }
1102
1103 QMessageBox msgBox;
1104 msgBox.setText("Error: No state selected.");
1105 msgBox.exec();
1106 return;
1107}
1108
1109void
1111{
1112 getSelectedNode()->setPublic(enable);
1113 treeView->repaint();
1114}
1115
1116void
1118{
1119 QList<StateInstancePtr> instances = stateTreeModel->findStateUsages(getSelectedNode());
1120 QMessageBox msgBox;
1121 QString message("Usage count: ");
1122 message += QString::number(instances.count());
1123 message += "\n" + buildStateUsageString(instances);
1124
1125 msgBox.setText(message);
1126 msgBox.exec();
1127}
1128
1129void
1131{
1132 auto node = getSelectedNode();
1133
1134 if (node && node->isState())
1135 {
1136 statechartmodel::StatePtr state = node->getState();
1137 StatechartGroupPtr group = stateTreeModel->getNodeByState(state)->getGroup();
1138
1139 RenameStateDialog d(stateTreeModel, group, state);
1140
1141 if (d.exec() == QDialog::Accepted)
1142 {
1143 if (d.isSaveAllRequested())
1144 {
1145 ARMARX_VERBOSE_S << "Saving all changes first...";
1146 saveAll();
1147 }
1148
1149 ARMARX_VERBOSE_S << "closing tabs";
1150 emit closeAllTabsRequested();
1151
1152 ARMARX_VERBOSE_S << "removing groups from tree view";
1153 auto groups = stateTreeModel->getGroups();
1154 stateTreeModel->clear();
1155
1156 for (const auto& c : rootNode->getChildren())
1157 {
1158 if (c->isGroup())
1159 {
1161 }
1162 }
1163
1165 d.getInstanceRenameInfos(), node, d.getNewStateName(), group))
1166 {
1167 for (const StatechartGroupPtr& g : groups)
1168 {
1169 ARMARX_VERBOSE_S << "reloading " << g->getName();
1170 auto loadedGroup = loadAndAddGroup(g->getDefinitionFilePath());
1171
1172 if (loadedGroup->getDefinitionFilePath() == group->getDefinitionFilePath())
1173 {
1174 stateTreeModel->generateBaseClasses(loadedGroup);
1175 }
1176 }
1177 }
1178 else
1179 {
1180 ARMARX_WARNING_S << "Renaming state '" << state->getStateName() << "' to '"
1181 << d.getNewStateName() << "' failed";
1182 }
1183 }
1184 }
1185}
1186
1187void
1189{
1191
1192 if (!node)
1193 {
1194 return;
1195 }
1196
1197 ARMARX_INFO_S << node->getBoostPathRelativeToGroup().string()
1198 << " group path: " << node->getGroup()->getGroupPath().toStdString();
1199 EditorFileOpener opener;
1200 opener.openFile("qtcreatorProject",
1201 node->getGroup()->getPackagePath().toStdString() + "/CMakeLists.txt");
1202}
1203
1204void
1206{
1208
1209 if (!node)
1210 {
1211 return;
1212 }
1213
1214 QMessageBox::information(treeView,
1215 "Path for Group " + node->getGroup()->getName(),
1216 node->getGroup()->getGroupPath());
1217}
1218
1219void
1224
1225void
1227{
1228 loadAndAddGroup(groupFile);
1229}
1230
1231void
1233{
1234 if (processes.find(group->getGroupPath()) != processes.end())
1235 {
1236 processes[group->getGroupPath()]->terminate();
1237 processes[group->getGroupPath()]->waitForFinished(1000);
1238 processes[group->getGroupPath()]->kill();
1239 processes.remove(group->getGroupPath());
1240 }
1241}
1242
1243void
1245{
1246 stopGroupExecution(group);
1247
1248 if (processes.find(group->getGroupPath()) == processes.end())
1249 {
1250 processes[group->getGroupPath()] = new QProcess(this);
1251 }
1252
1253
1254 QProcess* p = processes[group->getGroupPath()];
1255 connect(p, SIGNAL(finished(int)), this, SLOT(onStatechartFinished(int)));
1256 CMakePackageFinder core("ArmarXCore");
1257 QStringList args;
1258 if (!startState.isEmpty())
1259 {
1260 args << "--ArmarX.XMLStateComponent.StatesToEnter=" + startState;
1261 }
1262 args << "--ArmarX.XMLStateComponent.XMLStatechartGroupDefinitionFile=" +
1263 group->getDefinitionFilePath();
1264 args << "--ArmarX.XMLStateComponent.XMLStatechartProfile=" +
1265 QString::fromStdString(currentProfile->getName());
1266 args << "--ArmarX.XMLStateComponent.ObjectName=" + group->getName() + "StateComponent";
1267 args << "--ArmarX.ApplicationName=" + group->getName() + "StateComponentApp";
1268 QString path = QString::fromStdString(core.getBinaryDir()) + "/XMLRemoteStateOffererRun";
1269 ARMARX_INFO_S << path << " " << args.join(" ").toStdString();
1270
1271 std::string username;
1272 if (getenv("USER"))
1273 {
1274 username = getenv("USER");
1275 }
1276 std::string tempLogDir = (std::filesystem::temp_directory_path() / ("armarx-" + username) /
1277 group->getName().toStdString())
1278 .string();
1279 if (!std::filesystem::exists(tempLogDir) && !std::filesystem::create_directories(tempLogDir))
1280 {
1281 ARMARX_WARNING_S << "Could not create log dirs for executing group - aborting";
1282 return;
1283 }
1284 p->setStandardOutputFile(QString::fromStdString(tempLogDir) + "/out.log");
1285 p->setStandardErrorFile(QString::fromStdString(tempLogDir) + "/err.log");
1286 p->setProcessChannelMode(QProcess::MergedChannels);
1287 p->start(path, args);
1288 connect(p, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutputReady()));
1289 connect(p, SIGNAL(readyReadStandardError()), this, SLOT(processErrorsReady()));
1290}
1291
1292void
1294{
1295 auto deps = GroupCloner(stateTreeModel, packageTool).getGroupDependencies(group, false);
1296 executeGroup(group, startState);
1297 for (StatechartGroupPtr dep : deps)
1298 {
1299 executeGroup(dep);
1300 }
1301}
1302
1303void
1305{
1306 auto deps = GroupCloner(stateTreeModel, packageTool).getGroupDependencies(group, false);
1307 deps.push_back(group);
1308
1309 for (StatechartGroupPtr dep : deps)
1310 {
1311 if (processes.find(dep->getGroupPath()) != processes.end())
1312 {
1313 processes[dep->getGroupPath()]->terminate();
1314 }
1315 }
1316 auto startTime = IceUtil::Time::now();
1317 for (StatechartGroupPtr dep : deps)
1318 {
1319 if (processes.find(dep->getGroupPath()) != processes.end())
1320 {
1321 auto timeLeftToWait =
1322 std::max<int>(0, 1000 - (IceUtil::Time::now() - startTime).toMilliSeconds());
1323 processes[dep->getGroupPath()]->waitForFinished(timeLeftToWait);
1324 processes[dep->getGroupPath()]->kill();
1325 processes.remove(dep->getGroupPath());
1326 }
1327 }
1328}
1329
1330void
1332{
1334
1335 if (!node)
1336 {
1337 return;
1338 }
1339
1340 QStringList states;
1341 QString noneState("---");
1342 states << noneState;
1343 for (StateTreeNodePtr curnode : node->getGroup()->getAllNodes())
1344 {
1345 if (curnode->isPublic() && curnode->isState())
1346 {
1347 states << curnode->getState()->getStateName();
1348 }
1349 }
1350 bool ok;
1351 QString startState = QInputDialog::getItem(
1352 0, "State", "Select the state to start or none", states, 0, false, &ok);
1353 if (!ok)
1354 {
1355 return;
1356 }
1357 if (startState == noneState)
1358 {
1359 startState = "";
1360 }
1361 StatechartGroupPtr group = node->getGroup();
1362 executeGroupWithDependencies(group, startState);
1363
1364
1365 // p->start(QString::fromStdString(core.getBinaryDir()) + "/startApplication.sh", args);
1366 // sleep(2);
1367 // QString p_stdout = p->readAllStandardOutput();
1368 // QString p_stderr = p->readAllStandardError();
1369 // ARMARX_IMPORTANT_S << "Errors:" << p_stderr;
1370 // ARMARX_IMPORTANT_S << "Output: " << p_stdout;
1371}
1372
1373void
1375{
1376 auto process = qobject_cast<QProcess*>(sender());
1377 if (process)
1378 {
1379 ARMARX_INFO_S << process->pid() << " exited with code " << exitCode;
1380 }
1381 else
1382 {
1383 ARMARX_INFO_S << "Exited with code " << exitCode;
1384 }
1385}
1386
1387/*
1388void StateTreeController::onGenerateStateBaseClass()
1389{
1390 StateTreeNodePtr node = getSelectedNode();
1391
1392 XmlWriter writer;
1393 writer.serialize(node->getState());
1394 std::string xml = writer.getXmlString(false).toUtf8().data();
1395 std::vector<std::string> namespaces;
1396 namespaces.push_back("armarx");
1397 namespaces.push_back(node->getGroup()->getName().toUtf8().data());
1398
1399 std::vector<std::string> proxies;
1400 for(QString p : node->getGroup()->getProxies())
1401 {
1402 proxies.push_back(p.toUtf8().data());
1403 }
1404
1405 std::string cpp = XmlStateBaseClassGenerator::GenerateCpp(namespaces, RapidXmlReader::FromXmlString(xml), proxies, node->getGroup()->contextGenerationEnabled(), node->getGroup()->getName().toUtf8().data(), variantInfo);
1406 //QMessageBox::information(NULL, "test", QString::fromUtf8(cpp.c_str()));
1407
1408 std::string targetPath(node->getBoostCppFilePath().replace_extension().string() + ".generated.h");
1409 bool identical = false;
1410 if(std::filesystem::exists(std::filesystem::path(targetPath)))
1411 {
1412 std::string currentContents = RapidXmlReader::ReadFileContents(targetPath);
1413 identical = cpp == currentContents;
1414 }
1415 if(!identical)
1416 {
1417 QString qtargetPath = QString::fromUtf8(targetPath.data());
1418 //QMessageBox::information(NULL, "test", qtargetPath);
1419 ARMARX_LOG_S << "Generated file " << targetPath;
1420 GroupXmlWriter::WriteFileContents(qtargetPath, QString::fromUtf8(cpp.data()));
1421 }
1422 else
1423 {
1424 ARMARX_LOG_S << "Skipping identical file " << targetPath;
1425 QMessageBox::information(NULL, "test", "current file and generated output are identical.");
1426 }
1427
1428}
1429
1430void StateTreeController::onGenerateRsoBaseClass()
1431{
1432
1433}*/
1434
1435void
1437{
1439
1440 if (!node || !node->isState())
1441 {
1442 QMessageBox::warning(NULL, "No State selected", "Error: No state selected.");
1443 return;
1444 }
1445
1446 if (!packageTool->addCppOnlyXmlStatechart(
1447 node->getGroup()->getName().toUtf8().data(),
1448 node->getBoostPathRelativeToGroup().replace_extension().string(),
1449 node->getGroup()->getPackagePath().toUtf8().data()))
1450 {
1451 ARMARX_WARNING_S << "Creating source files for state " << node->getBasename()
1452 << " failed:\n"
1453 << packageTool->getLastOutput();
1454 }
1455
1456 node->checkCppExists();
1457}
1458
1459void
1461{
1463
1464 if (node && node->isGroup())
1465 {
1466 StatechartGroupPtr group = node->getGroup();
1467
1468 CloneGroupDialog d(packageTool, group, groupCloner);
1469
1470 if (d.exec() == QDialog::Accepted)
1471 {
1472 for (const auto& g : d.getGroupsToClone())
1473 {
1474 if (!g.first->existsCMakeLists())
1475 {
1476 QMessageBox::warning(0,
1477 "Cloning failed",
1478 g.first->getName() + " at " + g.first->getGroupPath() +
1479 " contains no CMakeLists.txt - is it an installed "
1480 "package? Installed packages cannot be cloned.");
1481 return;
1482 }
1483 }
1484
1485 if (auto newMapping = groupCloner->cloneGroupsTo(
1487 {
1489 << "Cloning successful, creating/overwriting mapping file in package: "
1490 << d.getPackagePath();
1491 newMapping.value().writeToFile(d.getPackagePath());
1492
1493 const QString packageName = QFileInfo(d.getPackagePath()).fileName();
1494
1495 for (const QPair<StatechartGroupPtr, QString>& g : d.getGroupsToClone())
1496 {
1497 QDir groupDir(d.getPackagePath());
1498 groupDir.cd("source");
1499 groupDir.cd(packageName);
1500 groupDir.cd("statecharts");
1501 groupDir.cd(g.second);
1502 QString newDefFile = groupDir.path() + QDir::separator() + g.second + ".scgxml";
1503 stateTreeModel->generateBaseClasses(loadAndAddGroup(newDefFile));
1504 }
1505 }
1506 }
1507 }
1508}
1509
1510void
1512{
1514
1515 if (node && node->isGroup())
1516 {
1517 StatechartGroupPtr group = node->getGroup();
1518 RenameGroupDialog d(rootNode, stateTreeModel, group, groupRenamer);
1519
1520 if (d.exec() == QDialog::Accepted)
1521 {
1522 if (d.isSaveAllRequested())
1523 {
1524 ARMARX_VERBOSE_S << "Saving all changes first...";
1525 saveAll();
1526 }
1527
1528 if (auto newGroupDirPath =
1529 groupRenamer->renameGroup(group, d.getNewName(), d.getDependantGroups()))
1530 {
1531 QString newGroupDefinitionPath =
1532 *newGroupDirPath + QDir::separator() + d.getNewName() + ".scgxml";
1533
1534 ARMARX_VERBOSE_S << "closing tabs";
1535 emit closeAllTabsRequested();
1536
1537 ARMARX_VERBOSE_S << "removing groups from tree view";
1538 stateTreeModel->clear();
1539
1540 for (const auto& c : rootNode->getChildren())
1541 {
1542 if (c->isGroup())
1543 {
1545 }
1546 }
1547
1548 for (const StatechartGroupPtr& g : d.getAllGroups())
1549 {
1550 if (g->getGroupPath() == group->getGroupPath())
1551 {
1552 continue;
1553 }
1554
1555 ARMARX_VERBOSE_S << "reloading " << g->getName();
1556 onOpenGroup(g->getDefinitionFilePath());
1557 }
1558
1559 onOpenGroup(newGroupDefinitionPath);
1560 }
1561 }
1562 }
1563}
1564
1565void
1567{
1569
1570 if (node && node->isGroup())
1571 {
1572 StatechartGroupPtr group = node->getGroup();
1574 group->getName(),
1575 packageTool,
1576 variantInfo,
1577 group->getProxies(),
1578 group->contextGenerationEnabled(),
1579 profiles,
1580 group->getConfigurations(),
1581 group->getDescription(),
1582 group);
1583
1584 if (d.exec() == QDialog::Accepted)
1585 {
1586 group->setDescription(d.getGroupDescription());
1587 group->setProxies(d.getProxies());
1588 group->setContextGeneration(d.contextGenerationEnabled());
1589 group->setConfigurations(d.getConfigurations());
1590 }
1591 }
1592}
1593
1594void
1596{
1597 treeView->collapseAll();
1598}
1599
1600void
1602{
1603
1604 treeView->expand(proxyModel->mapFromSource(getIndex(node->getParent())));
1605 treeView->setCurrentIndex(proxyModel->mapFromSource(getIndex(node)));
1606}
1607
1608void
1610{
1611 if (!node->getCppExists())
1612 {
1613 QMessageBox::warning(NULL,
1614 "File missing",
1615 QString("File '") + node->getBoostCppFilePath().c_str() +
1616 "' not found.");
1617 }
1618 else
1619 {
1620 opener.openFileWithDefaultEditor(node->getBoostCppFilePath().c_str());
1621 }
1622}
1623
1624void
1625StateTreeController::processOutputReady()
1626{
1627 QProcess* p = qobject_cast<QProcess*>(sender());
1628
1629 if (p)
1630 {
1631 std::cout << QString(p->readAllStandardOutput());
1632 }
1633 else
1634 {
1635 ARMARX_WARNING_S << "cast failed";
1636 }
1637}
1638
1639void
1640StateTreeController::processErrorsReady()
1641{
1642 QProcess* p = qobject_cast<QProcess*>(sender());
1643
1644 if (p)
1645 {
1646 std::cerr << QString(p->readAllStandardError());
1647 }
1648 else
1649 {
1650 ARMARX_WARNING_S << "cast failed";
1651 }
1652}
1653
1654void
1655StateTreeController::stateAddedOrRemoved()
1656{
1657 std::cout << "StateTreeController::stateAddedOrRemoved()" << std::endl;
1658 treeView->repaint();
1659}
1660
1661void
1662StateTreeController::expandFilterResults(const QString& filterString)
1663{
1664 if (filterString.length() == 0)
1665 {
1666 return;
1667 }
1668
1670}
1671
1672QModelIndex
1673StateTreeController::getIndex(StateTreeNodePtr node)
1674{
1675 if (!node || node == rootNode || !node->getParent())
1676 {
1677 return QModelIndex();
1678 }
1679
1680 return createIndex(node->row(), 0, node.get());
1681}
1682
1683QString
1684StateTreeController::buildStateUsageString(QList<StateInstancePtr> instances)
1685{
1686 QString message;
1687 QListIterator<StateInstancePtr> i(instances);
1688
1689 while (i.hasNext())
1690 {
1691 StateInstancePtr stateInstance = i.next();
1692 message +=
1693 stateTreeModel->getNodeByState(stateInstance->getParent())->getGroup()->getName() +
1694 "/" + stateInstance->getParent()->getStateName() + ": '" +
1695 stateInstance->getInstanceName() + "'\n";
1696 }
1697
1698 return message.trimmed();
1699}
1700
1701void
1703{
1704 removeNodeFromTree(stateNode);
1705 stateTreeModel->notifyDeleteStateTreeNode(stateNode);
1706}
1707
1708void
1710{
1712
1713 if (!nodeIsFolder(node))
1714 {
1715 return;
1716 }
1717
1718 if (node->getChildren().count() > 0)
1719 {
1720 QMessageBox::warning(
1721 NULL, "Folder cannot be deleted.", "Folder cannot be deleted. It is not empty.");
1722 return;
1723 }
1724
1725 try
1726 {
1727 std::filesystem::remove(node->getAbsoluteBoostPath());
1728 removeFolder(node);
1730 node->getGroup(),
1731 QString::fromUtf8((node->getGroup()->getBoostDefinitionFilePath()).c_str()));
1732 }
1733 catch (std::exception&)
1734 {
1735 QMessageBox::warning(
1736 NULL, "Folder cannot be deleted.", "Folder cannot be deleted. It is not empty. ");
1737 }
1738}
1739
1740void
1745
1746void
1748{
1750
1751 if (!nodeIsGroup(node))
1752 {
1753 return;
1754 }
1755
1756 auto statechartsCmakeListPath = QString::fromUtf8(
1757 (node->getAbsoluteBoostPath().remove_filename().parent_path() / "CMakeLists.txt").c_str());
1758 auto filecontents = GuiStatechartGroupXmlReader::ReadFileContents(statechartsCmakeListPath);
1759 filecontents = filecontents.remove("add_subdirectory(" + node->getBasename() + ")");
1760 GroupXmlWriter::WriteFileContents(statechartsCmakeListPath, filecontents);
1761
1762 std::filesystem::remove_all(node->getAbsoluteBoostPath().remove_filename());
1763
1764 removeNodeFromTree(node);
1765}
1766
1767void
1769{
1770 int row = node->row();
1771 // this->layoutAboutToBeChanged();
1772 this->beginRemoveRows(getIndex(node->getParent()), row, row);
1773
1774 node->getParent()->removeChild(node);
1775
1776 this->endRemoveRows();
1777 // this->layoutChanged();
1778}
1779
1780void
1782{
1783 {
1784 QProgressDialog d;
1785 // d.show();
1786 d.setMinimumDuration(1000);
1787 stateTreeModel->saveAll(&d);
1788 }
1789 QProgressDialog d;
1790 // d.show();
1791 stateTreeModel->generateAllBaseClasses(&d);
1792}
1793
1794/*
1795StatechartGroupTreeNodePtr StateTreeController::getRoot()
1796{
1797 return rootNode;
1798}
1799*/
1800
1801
1802QStringList
1804{
1805 QStringList result;
1806 result << "application/x-State";
1807 result << "application/pointer";
1808 return result;
1809}
1810
1811QMimeData*
1812StateTreeController::mimeData(const QModelIndexList& indexes) const
1813{
1814 if (indexes.size() == 0)
1815 {
1816 return new QMimeData();
1817 }
1818
1819 StateTreeNodePtr node = getNode(indexes.first());
1820
1821 if (node->isGroup()) // a group can be dragged into the scenario manager
1822 {
1823 QMimeData* mimeData = new QMimeData();
1824 QByteArray encodedData;
1825
1826 QDataStream stream(&encodedData, QIODevice::WriteOnly);
1827
1828
1829 foreach (const QModelIndex& index, indexes)
1830 {
1831 if (index.isValid())
1832 {
1833
1834 CMakePackageFinder package(node->getGroup()->getPackageName().toStdString());
1836 << node->getGroup()->getPackageName().toStdString();
1838 auto prop = appForDrag->getProperties();
1839 auto relativeGroupFilePath = ArmarXDataPath::relativeTo(
1840 package.getIncludePathList()[0],
1841 node->getGroup()->getBoostDefinitionFilePath().string());
1842 prop->getProperties()->setProperty(
1843 "ArmarX.XMLStateComponent.XMLStatechartGroupDefinitionFile",
1844 relativeGroupFilePath);
1845 prop->getProperties()->setProperty("ArmarX.XMLStateComponent.XMLStatechartProfile",
1846 currentProfile->getName());
1847 prop->getProperties()->setProperty("ArmarX.XMLStateComponent.ObjectName",
1848 node->getGroup()->getName().toStdString() +
1849 "XMLStateComponent");
1850 prop->getProperties()->setProperty(
1851 "ArmarX.ApplicationName", node->getGroup()->getName().toStdString() + "App");
1852 appForDrag->setProperties(prop);
1853
1854
1855 stream << reinterpret_cast<quint64>(appForDrag.get())
1856 << node->getGroup()->getName() /*instanceName*/;
1857 }
1858 }
1859
1860
1861 mimeData->setData("application/pointer", encodedData);
1862 return mimeData;
1863 }
1864
1865 if (!node || !node->getState())
1866 {
1867 return new QMimeData();
1868 }
1869
1870
1871 StateMimeData* data = new StateMimeData(node->getState(), stateTreeModel);
1872 data->setProxyName(node->getGroup()->getName() + STATEOFFERERSUFFIX);
1873 return data;
1874}
1875
1876StateTreeController::StateMimeData::StateMimeData(statechartmodel::StatePtr state,
1877 StateTreeModelPtr stateTreeModel) :
1878 AbstractStateMimeData(state), stateTreeModel(stateTreeModel)
1879{
1880}
1881
1882bool
1883StateTreeController::StateMimeData::isInSameGroup(statechartmodel::StatePtr state) const
1884{
1885 return stateTreeModel->getNodeByState(this->state)->getGroup() ==
1886 stateTreeModel->getNodeByState(state)->getGroup();
1887}
1888
1889bool
1890StateTreeController::StateMimeData::isPublic() const
1891{
1892 return stateTreeModel->getNodeByState(this->state)->isPublic();
1893}
constexpr T c
#define STATEOFFERERSUFFIX
Class containing data about an application Provides methods to get and set the date contained in the ...
Definition Application.h:47
The AbstractStateMimeData class is used to transport state data from the treeview to the stateview an...
statechartmodel::StatePtr state
static std::string relativeTo(const std::string &from, const std::string &to)
Transform an absolute filepath into a relative path of the other absolute filepath.
static std::string getHomePath()
The CMakePackageFinder class provides an interface to the CMake Package finder capabilities.
std::string getBinaryDir() const
std::vector< std::string > getIncludePathList() const
Return the include paths in a vector.
bool packageFound() const
Returns whether or not this package was found with cmake.
StatechartGroupMapping getStatechartGroupMapping() const
QVector< QPair< StatechartGroupPtr, QString > > getGroupsToClone() const
QMap< QString, QString > getConfigurations() const
The EditorFileOpener class.
void openFile(const std::string &editorName, const std::string &filepath, int lineNumber=0)
QVector< StatechartGroupPtr > getGroupDependencies(const StatechartGroupPtr &group, bool includeSelf=false)
static void WriteFileContents(QString path, QString contents)
static void WriteXml(StatechartGroupPtr group, QString path, bool indent=true)
static QString ReadFileContents(QString path)
This proxy model reimplements the filterAcceptsRow function with a new behavior: All elements that fi...
static void ExpandFilterResults(QTreeView *treeView)
Expands the treeview that all items that match the filterstring are expanded and directly visible.
QVector< StatechartGroupPtr > getAllGroups() const
QVector< StatechartGroupPtr > getDependantGroups() const
QVector< StateRenamer::InstanceRenameInfo > getInstanceRenameInfos() const
static bool RenameState(const QVector< InstanceRenameInfo > &instanceRenameInfos, const StateTreeNodePtr &state, const QString &newStateName, const StatechartGroupPtr &group)
QStringList mimeTypes() const override
void stopGroupExecutionWithDependencies(StatechartGroupPtr group)
void onOpenGroup(QString groupFile="")
StateTreeNodePtr getClosestParentFolderOrGroupNode(StateTreeNodePtr node)
StateTreeNodePtr getNodeByState(statechartmodel::StatePtr state)
StatechartGroupPtr addNewGroup(const QString &groupName, const QString &groupPath, const QString &description, const QString &packageName, const QList< QString > proxies, bool generateContext, const QMap< QString, QString > &statechartGroupConfigurations)
void stopGroupExecution(StatechartGroupPtr group)
StateTreeNodePtr createNewState(QString name, StateTreeNodePtr parent, bool createDynamicRemoteState=false)
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void removeFolder(StateTreeNodePtr folderNode)
Qt::ItemFlags flags(const QModelIndex &index) const override
bool nodeIsFolderOrGroup(StateTreeNodePtr node)
void executeGroup(StatechartGroupPtr group, QString startState="")
void removeNodeFromTree(StateTreeNodePtr node)
StateTreeNodePtr tryCreateNewState(QString name)
StateTreeNodePtr createNewFolder(QString name, StateTreeNodePtr parent)
StatechartGroupPtr loadGroup(QString groupDefinitionFile)
void executeGroupWithDependencies(StatechartGroupPtr group, QString startState)
StateTreeController(Ice::CommunicatorPtr ic, VariantInfoPtr variantInfo, QList< QVariant > headerInfo, QTreeView *treeView, QLineEdit *filterLineEdit, const ArmarXPackageToolInterfacePtr &packageTool, const StatechartProfilesPtr &statechartProfiles, StatechartProfilePtr currentProfile, QObject *parent=0)
int columnCount(const QModelIndex &parent=QModelIndex()) const override
void selectNode(StateTreeNodePtr node)
StateTreeNodePtr getNode(QModelIndex index) const
QModelIndex parent(const QModelIndex &index) const override
void onContextMenu(const QPoint &point)
void selectNodeByState(statechartmodel::StatePtr state)
void openStateCPP(StateTreeNodePtr node)
bool nodeIsFolder(StateTreeNodePtr node)
QModelIndex index(int row, int column, const QModelIndex &parentIndex=QModelIndex()) const override
QVariant data(const QModelIndex &index, int role) const override
StatechartGroupPtr loadAndAddGroup(QString groupDefinitionFile)
bool nodeIsState(StateTreeNodePtr node)
QMimeData * mimeData(const QModelIndexList &indexes) const override
bool nodeIsGroup(StateTreeNodePtr node)
void removeState(StateTreeNodePtr stateNode)
StateTreeNodePtr child(int row)
static bool CheckNodeName(QString name)
StateTreeNodePtr getParent() const
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
#define ARMARX_CHECK_GREATER_EQUAL(lhs, rhs)
This macro evaluates whether lhs is greater or equal (>=) rhs and if it turns out to be false it will...
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_INFO_S
Definition Logging.h:202
#define ARMARX_VERBOSE_S
Definition Logging.h:207
#define ARMARX_WARNING_S
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:213
::IceInternal::Handle<::Ice::Communicator > CommunicatorPtr
Definition IceManager.h:49
std::string toUtf8(QString const &qstring)
std::shared_ptr< State > StatePtr
Definition State.h:48
std::shared_ptr< StateInstance > StateInstancePtr
std::shared_ptr< Event > EventPtr
Definition XmlWriter.h:46
QList< EventPtr > EventList
Definition XmlWriter.h:47
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::shared_ptr< StatechartProfiles > StatechartProfilesPtr
std::shared_ptr< StatechartGroup > StatechartGroupPtr
std::shared_ptr< VariantInfo > VariantInfoPtr
Definition VariantInfo.h:39
std::shared_ptr< StateTreeModel > StateTreeModelPtr
std::shared_ptr< class StatechartProfile > StatechartProfilePtr
std::shared_ptr< StateTreeNode > StateTreeNodePtr
std::shared_ptr< ArmarXPackageToolInterface > ArmarXPackageToolInterfacePtr
StateCreationDialog(QWidget *parent=0, Qt::WindowFlags flags=0)
QDialogButtonBox * buttonBox