CloneGroupDialog.cpp
Go to the documentation of this file.
1 /*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @package ArmarX::
17 * @author Valerij Wittenbeck (valerij.wittenbeck at student dot kit dot edu
18 * @date 2015
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22 
23 #include "CloneGroupDialog.h"
24 #include <ArmarXGui/gui-plugins/StatechartEditorPlugin/view/dialogs/ui_CloneGroupDialog.h>
26 #include <QFileDialog>
27 #include <QTimer>
28 
29 #include <filesystem>
30 
32 
33 #include <QtGui>
34 #include <QPushButton>
35 
36 namespace armarx
37 {
38 
40  QDialog(parent),
41  ui(new Ui::CloneGroupDialog),
42  packageTool(packageTool),
43  group(group),
44  groupCloner(groupCloner),
45  validGroupNameRegExp("([a-zA-Z][a-zA-Z0-9]*)"),
46  colorGreen(QColor::fromRgb(120, 255, 120)),
47  colorRed(QColor::fromRgb(255, 120, 120)),
48  colorYellow(QColor::fromRgb(255, 200, 0)),
49  alreadyChecking(false)
50  {
51  ui->setupUi(this);
52 
53  connect(ui->btnSelectPackageFolder, SIGNAL(clicked()), this, SLOT(selectPackagePath()));
54  connect(ui->editPackagePath, SIGNAL(textChanged(QString)), this, SLOT(requestCheckPackagePath(QString)));
55  connect(ui->editGroupPrefix, SIGNAL(textChanged(QString)), this, SLOT(requestPrefixUpdate(QString)));
56  connect(ui->groupsWidget, SIGNAL(itemChanged(QTableWidgetItem*)), this, SLOT(verifyName(QTableWidgetItem*)));
57  connect(ui->checkBoxCloneRobotSkillTemplates, SIGNAL(toggled(bool)), this, SLOT(cloneRobotSkillTemplatesToggled(bool)));
58 
59  ui->editGroupPrefix->setEnabled(false);
60  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
61 
62  ui->editGroupPrefix->setValidator(new QRegExpValidator(validGroupNameRegExp, this));
63 
64  timer = new QTimer(this);
65  connect(timer, SIGNAL(timeout()), this, SLOT(checkPackagePath()));
66  timer->setSingleShot(true);
67  }
68 
70  {
71  delete ui;
72  }
73 
75  {
76  return packageTool;
77  }
78 
80  {
81  std::filesystem::path path = getPackagePath().toUtf8().data();
82  return QString::fromUtf8(path.filename().c_str());
83  }
84 
86  {
87  std::filesystem::path path = ui->editPackagePath->text().toUtf8().data();
88  std::filesystem::path cleanPath = path;
89 
90  try
91  {
92  cleanPath = std::filesystem::canonical(path);
93  }
94  catch (...)
95  {
96  cleanPath = ArmarXDataPath::cleanPath(path.string());
97 
98  if (*cleanPath.string().rbegin() == '/' || *cleanPath.string().rbegin() == '\\')
99  {
100  cleanPath = cleanPath.remove_filename();
101  }
102  }
103 
104  return QString::fromUtf8(cleanPath.c_str());
105  }
106 
108  {
109  return group;
110  }
111 
113  {
114  return groupCloner;
115  }
116 
118  {
119  return mapping;
120  }
121 
122  QVector<QPair<StatechartGroupPtr, QString>> CloneGroupDialog::getGroupsToClone() const
123  {
124  QVector<QPair<StatechartGroupPtr, QString>> result;
125 
126  if (ui->groupsWidget->rowCount() == 0 || ui->groupsWidget->columnCount() < 2)
127  {
128  return result;
129  }
130 
131  for (const auto& g : groupsToClone)
132  {
133  for (int i = 0; i < ui->groupsWidget->rowCount(); ++i)
134  {
135  const auto oldName = ui->groupsWidget->item(i, 0)->data(Qt::UserRole).toString();
136 
137  if (oldName == g->getName())
138  {
139  const auto newName = ui->groupsWidget->item(i, 1)->text();
140  ARMARX_CHECK_EXPRESSION(oldName != newName) << "oldName == newName of group " << g->getName();
141  result.push_back({g, newName});
142  }
143  }
144  }
145 
146  ARMARX_CHECK_EXPRESSION(groupsToClone.size() == result.size());
147  return result;
148  }
149 
151  {
152  return groupPrefix;
153  }
154 
155  void CloneGroupDialog::verifyName(QTableWidgetItem* item)
156  {
157 
158  const int c = item->column();
159 
160  if (c == 0 || ui->groupsWidget->columnCount() < 2)
161  {
162  return;
163  }
164 
165  //setting the background color triggers the "itemchanged" signal, which is what called this method in the first place
166  //this elegant solution ensures that no recursive calling is taking place
167  if (alreadyChecking)
168  {
169  return;
170  }
171 
172  alreadyChecking = true;
173 
174  bool hasNameCollisions = false;
175 
176  for (int i = 0; i < ui->groupsWidget->rowCount(); ++i)
177  {
178  const auto& itemTest = ui->groupsWidget->item(i, 1);
179 
180  if (!itemTest || (!itemTest->flags().testFlag(Qt::ItemIsEditable)))
181  {
182  continue;
183  }
184 
185  bool collision = false;
186 
187  for (int j = 0; j < ui->groupsWidget->rowCount() && !collision; ++j)
188  {
189  const auto& itemL = ui->groupsWidget->item(j, 0);
190  const auto& itemR = ui->groupsWidget->item(j, 1);
191 
192  collision |= itemL && itemTest->text() == itemL->text();
193  collision |= itemR && (i != j) && itemTest->text() == itemR->text();
194  }
195 
196  itemTest->setBackgroundColor(collision ? colorRed : colorGreen);
197  hasNameCollisions |= collision;
198  }
199 
200  bool validName = validGroupNameRegExp.exactMatch(item->text());
201  item->setBackgroundColor(!validName ? colorRed : item->backgroundColor());
202 
203  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(validName && !hasNameCollisions);
204  alreadyChecking = false;
205  }
206 
208  {
209  timer->start(250);
210  }
211 
213  {
214  groupPrefix = prefix;
215  buildGroupList();
216  }
217 
219  {
220  buildGroupList();
221  }
222 
223  void CloneGroupDialog::buildGroupList()
224  {
225  if (groupPrefix.size() == 0)
226  {
227  ui->groupsWidget->clear();
228  ui->groupsWidget->setColumnCount(0);
229  ui->groupsWidget->setRowCount(0);
230  }
231 
232  updateGroupDependencies();
233 
234  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!deps.empty() && !groupsToClone.empty() && groupPrefix.size() != 0);
235 
236  if (deps.empty() || groupPrefix.size() == 0)
237  {
238  return;
239  }
240 
241  ui->groupsWidget->clear();
242  ui->groupsWidget->setRowCount(deps.size());
243  ui->groupsWidget->setColumnCount(2);
244  ui->groupsWidget->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
245  ui->groupsWidget->setHorizontalHeaderLabels({"Original StatechartGroup", "Cloned StatechartGroup"});
246 
247  for (int i = 0; i < deps.size(); ++i)
248  {
249  const auto& g = deps[i];
250 
251  QTableWidgetItem* item = new QTableWidgetItem(g->getName() + " [" + g->getPackageName() + "]");
252  item->setData(Qt::UserRole, g->getName());
253  item->setFlags(item->flags() ^ Qt::ItemIsEditable);
254  QTableWidgetItem* renamed = new QTableWidgetItem(groupPrefix + g->getName());
255 
256  bool alreadyCloned = std::find_if(groupsToClone.constBegin(), groupsToClone.constEnd(), [&](const StatechartGroupPtr & dep)
257  {
258  return g->getGroupPath() == dep->getGroupPath();
259  }) == groupsToClone.constEnd();
260 
261  if (!alreadyCloned)
262  {
263  item->setBackgroundColor(colorGreen);
264  renamed->setBackgroundColor(colorGreen);
265  }
266  else
267  {
268  item->setBackgroundColor(colorYellow);
269  renamed->setBackgroundColor(colorYellow);
270  renamed->setFlags(renamed->flags() ^ Qt::ItemIsEditable);
271 
272  if (auto newGroupName = mapping.queryMappedGroupName(g->getName()))
273  {
274  renamed->setText(*newGroupName);
275  }
276  else
277  {
278  renamed->setText("<Error: Something went terribly wrong>");
279  renamed->setBackgroundColor(colorRed);
280  }
281  }
282 
283  ui->groupsWidget->setItem(i, 0, item);
284  ui->groupsWidget->setItem(i, 1, renamed);
285  }
286 
287  if (groupsToClone.empty())
288  {
289  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
290  }
291  }
292 
294  {
295  QFileDialog selectFolder(this, "Select Target Package Root Folder");
296  QList<QUrl> urls;
297  urls << QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::HomeLocation))
298  << QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::DesktopLocation));
299 
301  {
302  urls << QUrl::fromLocalFile(QString::fromStdString(ArmarXDataPath::getHomePath()));
303  }
304 
305  selectFolder.setSidebarUrls(urls);
306  // selectFolder.setOption(QFileDialog::ShowDirsOnly, true);
307  selectFolder.setOption(QFileDialog::ReadOnly, true);
308  selectFolder.setOption(QFileDialog::HideNameFilterDetails, false);
309  selectFolder.setFileMode(QFileDialog::Directory);
310 
311  if (selectFolder.exec() == QDialog::Accepted)
312  {
313  ui->editPackagePath->setText(*selectFolder.selectedFiles().begin());
314  }
315  }
316 
317  void CloneGroupDialog::updateGroupDependencies()
318  {
319  deps = groupCloner->getGroupDependencies(group, true);
320  if (!ui->checkBoxCloneRobotSkillTemplates->isChecked())
321  {
322  auto it = std::remove_if(deps.begin(), deps.end(), [&](const StatechartGroupPtr & g)
323  {
324  return g->getPackageName() == "RobotSkillTemplates";
325  });
326  deps.erase(it, deps.end());
327  }
328 
329  QVector<StatechartGroupPtr> existingDeps = groupCloner->getMappedGroups(mapping);
330 
331  groupsToClone.clear();
332 
333  for (const auto& g : deps)
334  {
335  bool alreadyCloned = std::find_if(existingDeps.constBegin(), existingDeps.constEnd(), [&](const StatechartGroupPtr & dep)
336  {
337  return g->getGroupPath() == dep->getGroupPath();
338  }) != existingDeps.constEnd();
339 
340  if (!alreadyCloned)
341  {
342  groupsToClone.push_back(g);
343  }
344  }
345  }
346 
348  {
349  if (packageTool->checkPackagePath(ui->editPackagePath->text().toStdString()))
350  {
351  ui->labelPackageError->setText("Package path is valid.");
352  QPalette p(ui->labelPackageError->palette());
353  p.setColor(ui->labelPackageError->backgroundRole(), colorGreen);
354  // p.setBrush(ui->labelPackageError->backgroundRole(), p.light());
355  ui->labelPackageError->setPalette(p);
356  // ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
357 
358  mapping = StatechartGroupMapping();
359 
360  // if (auto mappingOpt = StatechartGroupMapping::ReadStatechartGroupMappingFile(ui->editPackagePath->text()))
361  // {
362  // mapping = mappingOpt.get();
363  // }
364 
365  updateGroupDependencies();
366 
367  ui->editGroupPrefix->setEnabled(true);
368 
369  QString prefixSuggestion;
370 
371  if (!mapping.groupMappings.empty())
372  {
373  const auto gm = *mapping.groupMappings.begin();
374  prefixSuggestion = QString(gm.newGroupName).replace(gm.groupName, "");
375  }
376 
377  ui->editGroupPrefix->setText(prefixSuggestion);
378  }
379  else
380  {
381  ui->labelPackageError->setText("Package path is not valid!");
382  QPalette p(ui->labelPackageError->palette());
383  p.setColor(ui->labelPackageError->backgroundRole(), colorRed);
384  ui->labelPackageError->setPalette(p);
385  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
386  }
387  }
388 
389 } // namespace armarx
armarx::CloneGroupDialog::cloneRobotSkillTemplatesToggled
void cloneRobotSkillTemplatesToggled(bool state)
Definition: CloneGroupDialog.cpp:218
armarx::CloneGroupDialog::getPackageTool
ArmarXPackageToolInterfacePtr getPackageTool() const
Definition: CloneGroupDialog.cpp:74
armarx::CloneGroupDialog::verifyName
void verifyName(QTableWidgetItem *item)
Definition: CloneGroupDialog.cpp:155
armarx::CloneGroupDialog::getPackageName
QString getPackageName() const
Definition: CloneGroupDialog.cpp:79
armarx::ArmarXPackageToolInterfacePtr
std::shared_ptr< ArmarXPackageToolInterface > ArmarXPackageToolInterfacePtr
Definition: ArmarXPackageToolInterface.h:55
armarx::CloneGroupDialog::selectPackagePath
void selectPackagePath()
Definition: CloneGroupDialog.cpp:293
armarx::CloneGroupDialog::checkPackagePath
void checkPackagePath()
Definition: CloneGroupDialog.cpp:347
armarx::CloneGroupDialog::getStatechartGroupMapping
StatechartGroupMapping getStatechartGroupMapping() const
Definition: CloneGroupDialog.cpp:117
armarx::ArmarXDataPath::cleanPath
static std::string cleanPath(const std::string &filepathStr)
Definition: ArmarXDataPath.cpp:331
armarx::ArmarXDataPath::getHomePath
static std::string getHomePath()
Definition: ArmarXDataPath.cpp:583
c
constexpr T c
Definition: UnscentedKalmanFilterTest.cpp:43
armarx::CloneGroupDialog
Definition: CloneGroupDialog.h:41
armarx::CloneGroupDialog::CloneGroupDialog
CloneGroupDialog(ArmarXPackageToolInterfacePtr packageTool, StatechartGroupPtr group, GroupClonerPtr groupCloner, QWidget *parent=0)
Definition: CloneGroupDialog.cpp:39
armarx::GroupClonerPtr
std::shared_ptr< GroupCloner > GroupClonerPtr
Definition: GroupCloner.h:46
cxxopts::empty
bool empty(const std::string &s)
Definition: cxxopts.hpp:255
armarx::StatechartGroupMapping
Definition: StatechartGroupMapping.h:34
armarx::CloneGroupDialog::getGroupsToClone
QVector< QPair< StatechartGroupPtr, QString > > getGroupsToClone() const
Definition: CloneGroupDialog.cpp:122
Ui
ArmarX Headers.
Definition: ArmarXMainWindow.h:58
armarx::CloneGroupDialog::getGroupCloner
GroupClonerPtr getGroupCloner() const
Definition: CloneGroupDialog.cpp:112
armarx::StatechartGroupMapping::groupMappings
std::set< GroupMapping > groupMappings
Definition: StatechartGroupMapping.h:60
armarx::CloneGroupDialog::getPackagePath
QString getPackagePath() const
Definition: CloneGroupDialog.cpp:85
armarx::StatechartGroupPtr
std::shared_ptr< StatechartGroup > StatechartGroupPtr
Definition: StatechartGroupDefs.h:34
armarx::CloneGroupDialog::getGroup
StatechartGroupPtr getGroup() const
Definition: CloneGroupDialog.cpp:107
ARMARX_CHECK_EXPRESSION
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
Definition: ExpressionException.h:73
armarx::CloneGroupDialog::requestCheckPackagePath
void requestCheckPackagePath(QString path)
Definition: CloneGroupDialog.cpp:207
armarx::CloneGroupDialog::getGroupPrefix
QString getGroupPrefix() const
Definition: CloneGroupDialog.cpp:150
Logging.h
CloneGroupDialog.h
armarx::StatechartGroupMapping::queryMappedGroupName
std::optional< QString > queryMappedGroupName(const QString &sourceGroupName) const
Definition: StatechartGroupMapping.cpp:88
armarx::CloneGroupDialog::requestPrefixUpdate
void requestPrefixUpdate(QString prefix)
Definition: CloneGroupDialog.cpp:212
ArmarXDataPath.h
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:28
armarx::CloneGroupDialog::~CloneGroupDialog
~CloneGroupDialog() override
Definition: CloneGroupDialog.cpp:69