Line data Source code
1 : // DUNE DAQ modification notice:
2 : // This file has been modified from the original ATLAS dbe source for the DUNE DAQ project.
3 : // Fork baseline commit: dbe-02-12-17 (2022-05-12).
4 : // Renamed since fork: no.
5 :
6 : #include "dbe/MainWindow.hpp"
7 : #include "dbe/BatchChangeWidget.hpp"
8 : #include "dbe/BuildingBlockEditors.hpp"
9 : #include "dbe/Command.hpp"
10 : #include "dbe/CommitDialog.hpp"
11 : #include "dbe/CreateDatabaseWidget.hpp"
12 : #include "dbe/FileInfo.hpp"
13 : #include "dbe/MyApplication.hpp"
14 : #include "dbe/ObjectCreator.hpp"
15 : #include "dbe/ObjectEditor.hpp"
16 : #include "dbe/Preferences.hpp"
17 : #include "dbe/StyleUtility.hpp"
18 : #include "dbe/config_api.hpp"
19 : #include "dbe/config_api_version.hpp"
20 : #include "dbe/messenger.hpp"
21 : #include "dbe/messenger_proxy.hpp"
22 : #include "dbe/subtreeproxy.hpp"
23 : #include "dbe/treenode.hpp"
24 : #include "dbe/version.hpp"
25 :
26 : #include "logging/Logging.hpp"
27 :
28 : #include <QApplication>
29 : #include <QCloseEvent>
30 : #include <QDesktopServices>
31 : #include <QFileDialog>
32 : #include <QItemDelegate>
33 : #include <QMessageBox>
34 : #include <QProgressDialog>
35 : #include <QSettings>
36 : #include <QTime>
37 : #include <QUndoStack>
38 : #include <QUrl>
39 : #include <QWhatsThis>
40 :
41 : #include <future>
42 : #include <thread>
43 :
44 : #include <boost/scope_exit.hpp>
45 :
46 : namespace {
47 : // This allows to select data in the cells but to not modify them
48 : class DummyEditorDelegate : public QItemDelegate
49 : {
50 : public:
51 0 : void setModelData(QWidget* /* editor */,
52 : QAbstractItemModel* /* model */,
53 : const QModelIndex& /* index */) const override
54 : {
55 0 : }
56 : };
57 : }
58 :
59 0 : dbe::MainWindow::MainWindow(QMap<QString, QString> const& cmdargs, QWidget* parent)
60 : : QMainWindow(parent)
61 0 : , m_batch_change_in_progress(false)
62 0 : , this_files(nullptr)
63 0 : , this_filesort(new QSortFilterProxyModel(this))
64 0 : , this_classes(nullptr)
65 0 : , this_treefilter(nullptr)
66 0 : , isArchivedConf(false)
67 : {
68 : // qRegisterMetaType<RDBMap>("RDBMap");
69 :
70 : /// Setting up ui
71 0 : setupUi(this);
72 :
73 : /// Initial Settings
74 0 : init();
75 0 : init_tabs();
76 : // init_rdb_menu();
77 :
78 : /// Setting up application controller
79 0 : attach();
80 :
81 : /// Reading Applications Settings/CommandLine
82 0 : QCoreApplication::setOrganizationName("dunedaq");
83 0 : QCoreApplication::setApplicationName("dbe_main");
84 0 : load_default_settings(); // Start with defaults in case no user setting saved
85 0 : QSettings settings; // Then try user settings
86 0 : apply_settings(settings);
87 0 : argsparse(cmdargs);
88 :
89 0 : if (isArchivedConf == true) {
90 0 : OpenDB->setEnabled(false);
91 : // OpenOracleDB->setEnabled(false);
92 : // ConnectToRdb->setEnabled(false);
93 0 : CreateDatabase->setEnabled(false);
94 0 : Commit->setEnabled(false);
95 :
96 0 : QMessageBox::information(this,
97 : "DBE",
98 0 : QString("The configuration is opened in archival/detached mode.")
99 0 : .append("\nYou can browse or modify objects, but changes cannot be saved or commited."));
100 : }
101 :
102 0 : UndoView->show();
103 0 : }
104 :
105 : void
106 0 : dbe::MainWindow::init_tabs()
107 : {
108 0 : tableholder->addTab(new TableTab(tableholder), "Table View");
109 0 : tableholder->removeTab(0);
110 :
111 0 : QPushButton* addtab_button = new QPushButton("+");
112 0 : tableholder->setCornerWidget(addtab_button, Qt::TopLeftCorner);
113 0 : connect(addtab_button, SIGNAL(clicked()), this, SLOT(slot_add_tab()));
114 :
115 0 : tableholder->setTabsClosable(true);
116 0 : connect(tableholder, SIGNAL(tabCloseRequested(int)), this, SLOT(slot_remove_tab(int)));
117 0 : }
118 :
119 : void
120 0 : dbe::MainWindow::slot_add_tab()
121 : {
122 0 : tableholder->addTab(new TableTab(tableholder), "Table View");
123 0 : tableholder->setCurrentIndex(tableholder->count() - 1);
124 0 : tableholder->show();
125 0 : }
126 :
127 : void
128 0 : dbe::MainWindow::slot_remove_tab(int i)
129 : {
130 0 : if (i == -1 || ((tableholder->count() == 1) && i == 0)) {
131 0 : return;
132 : }
133 :
134 0 : QWidget* Widget = tableholder->widget(i);
135 :
136 0 : tableholder->removeTab(i);
137 :
138 0 : delete Widget;
139 :
140 0 : Widget = nullptr;
141 : }
142 :
143 : void
144 0 : dbe::MainWindow::init()
145 : {
146 : /// Window Settings
147 0 : setWindowTitle("DUNE DAQ Configuration Database Editor (DBE)");
148 : /// Table Settings
149 0 : UndoView->setStack(confaccessor::get_commands().get());
150 0 : SearchLineTable->hide();
151 0 : SearchLineTable->setClearButtonEnabled(true);
152 0 : SearchTreeLine->setClearButtonEnabled(true);
153 0 : CaseSensitiveCheckBoxTable->hide();
154 0 : tableholder->removeTab(1);
155 :
156 : /// Menus Settings
157 0 : HelpMenu->setEnabled(false); // Until help is updated to be useful!!!
158 :
159 : /// Commands Settings
160 0 : Commit->setEnabled(false);
161 0 : UndoAction->setEnabled(true);
162 0 : RedoAction->setEnabled(true);
163 :
164 : /// Search Box Settings
165 0 : SearchBox->setFocusPolicy(Qt::ClickFocus);
166 :
167 : /// What is this
168 0 : TreeView->setWhatsThis("This view shows the classes and objects of the database");
169 0 : FileView->setWhatsThis("This view shows the file structure of the database");
170 0 : UndoView->setWhatsThis("This view shows the commands in the Undo Command stack");
171 :
172 0 : CommittedTable->setHorizontalHeaderLabels(QStringList() << "File" << "Comment" << "Date");
173 0 : CommittedTable->setAlternatingRowColors(true);
174 0 : CommittedTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
175 0 : CommittedTable->horizontalHeader()->setDefaultSectionSize(250);
176 0 : CommittedTable->setWordWrap(true);
177 0 : CommittedTable->setTextElideMode(Qt::ElideRight);
178 0 : CommittedTable->setItemDelegate(new DummyEditorDelegate());
179 :
180 : // Make Files the current tab
181 0 : InfoWidget->setCurrentIndex(0);
182 0 : }
183 :
184 : void
185 0 : dbe::MainWindow::attach()
186 : {
187 0 : connect(OpenDB, SIGNAL(triggered()), this, SLOT(slot_open_database_from_file()));
188 0 : connect(Commit, SIGNAL(triggered()), this, SLOT(slot_commit_database()));
189 0 : connect(Exit, SIGNAL(triggered()), this, SLOT(close()));
190 0 : connect(actionPreferences, SIGNAL(triggered()), this, SLOT(slot_launch_preferences()));
191 0 : connect(UndoAction, SIGNAL(triggered()), UndoView->stack(), SLOT(undo()));
192 0 : connect(RedoAction, SIGNAL(triggered()), UndoView->stack(), SLOT(redo()));
193 0 : connect(UndoAll, SIGNAL(triggered()), this, SLOT(slot_undo_allchanges()));
194 0 : connect(BatchChange, SIGNAL(triggered()), this, SLOT(slot_launch_batchchange()));
195 0 : connect(BatchChangeTable, SIGNAL(triggered()), this, SLOT(slot_launch_batchchange_on_table()));
196 :
197 0 : connect(DisplayClassView, SIGNAL(triggered(bool)), TreeDockWidget, SLOT(setVisible(bool)));
198 0 : connect(DisplayTableView, SIGNAL(triggered(bool)), TableGroupBox, SLOT(setVisible(bool)));
199 0 : connect(DisplayMessages, SIGNAL(triggered(bool)), InfoDockWidget, SLOT(setVisible(bool)));
200 0 : connect(DisplayToolbar, SIGNAL(triggered(bool)), MainToolBar, SLOT(setVisible(bool)));
201 :
202 0 : connect(TreeDockWidget, SIGNAL(visibilityChanged(bool)), DisplayTableView, SLOT(setChecked(bool)));
203 0 : connect(InfoDockWidget, SIGNAL(visibilityChanged(bool)), DisplayMessages, SLOT(setChecked(bool)));
204 0 : connect(MainToolBar, SIGNAL(visibilityChanged(bool)), DisplayToolbar, SLOT(setChecked(bool)));
205 :
206 0 : connect(LoadDefaultSettings, SIGNAL(triggered()), this, SLOT(reload_default_settings()));
207 0 : connect(CreateDatabase, SIGNAL(triggered()), this, SLOT(slot_create_newdb()));
208 : // connect ( OpenOracleDB, SIGNAL ( triggered() ), this, SLOT ( slot_oracle_prepare() ) );
209 :
210 0 : connect(WhatThisAction, SIGNAL(triggered()), this, SLOT(slot_whatisthis()));
211 0 : connect(UserGuide, SIGNAL(triggered()), this, SLOT(slot_show_userguide()));
212 0 : connect(UserChanges, SIGNAL(triggered()), this, SLOT(slot_show_userchanges()));
213 :
214 0 : connect(TreeView, SIGNAL(activated(QModelIndex)), this, SLOT(slot_edit_object_from_class_view(QModelIndex)));
215 :
216 0 : connect(&confaccessor::ref(),
217 : SIGNAL(db_committed(const std::list<std::string>&, const std::string&)),
218 : this,
219 : SLOT(slot_update_committed_files(const std::list<std::string>&, const std::string&)));
220 :
221 0 : connect(confaccessor::gethandler().get(),
222 : SIGNAL(FetchMoreData(const treenode*)),
223 : this,
224 : SLOT(slot_fetch_data(const treenode*)));
225 :
226 0 : connect(&confaccessor::ref(), SIGNAL(object_created(QString, dref)), this, SLOT(slot_toggle_commit_button()));
227 0 : connect(&confaccessor::ref(), SIGNAL(object_renamed(QString, dref)), this, SLOT(slot_toggle_commit_button()));
228 0 : connect(&confaccessor::ref(), SIGNAL(object_changed(QString, dref)), this, SLOT(slot_toggle_commit_button()));
229 0 : connect(&confaccessor::ref(), SIGNAL(object_deleted(QString, dref)), this, SLOT(slot_toggle_commit_button()));
230 0 : connect(&confaccessor::ref(),
231 : SIGNAL(db_committed(const std::list<std::string>&, const std::string&)),
232 : this,
233 : SLOT(slot_toggle_commit_button()));
234 0 : connect(&confaccessor::ref(), SIGNAL(IncludeFileDone()), this, SLOT(slot_toggle_commit_button()));
235 0 : connect(&confaccessor::ref(), SIGNAL(RemoveFileDone()), this, SLOT(slot_toggle_commit_button()));
236 0 : connect(&confaccessor::ref(), SIGNAL(ExternalChangesDetected()), this, SLOT(slot_toggle_commit_button()));
237 0 : connect(&confaccessor::ref(), SIGNAL(ExternalChangesAccepted()), this, SLOT(slot_toggle_commit_button()));
238 0 : connect(this,
239 : SIGNAL(signal_batch_change_stopped(const QList<QPair<QString, QString>>&)),
240 : this,
241 : SLOT(slot_toggle_commit_button()));
242 :
243 0 : connect(&confaccessor::ref(), SIGNAL(IncludeFileDone()), this, SLOT(slot_model_rebuild()));
244 0 : connect(&confaccessor::ref(), SIGNAL(RemoveFileDone()), this, SLOT(slot_model_rebuild()));
245 0 : connect(&confaccessor::ref(), SIGNAL(ExternalChangesAccepted()), this, SLOT(slot_process_externalchanges()));
246 :
247 0 : connect(SearchBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slot_filter_query()));
248 0 : connect(SearchTreeLine, SIGNAL(textChanged(const QString&)), this, SLOT(slot_filter_textchange(const QString&)));
249 0 : connect(SearchTreeLine, SIGNAL(textEdited(const QString&)), this, SLOT(slot_filter_query()));
250 0 : connect(SearchTreeLine, SIGNAL(returnPressed()), this, SLOT(slot_filter_query()));
251 0 : connect(
252 0 : SearchLineTable, SIGNAL(textChanged(const QString&)), this, SLOT(slot_filter_table_textchange(const QString&)));
253 0 : connect(CaseSensitiveCheckBoxTree, SIGNAL(clicked(bool)), this, SLOT(slot_toggle_casesensitive_for_treeview(bool)));
254 : // connect ( ConnectToRdb, SIGNAL ( triggered ( QAction * ) ), this,
255 : // SLOT ( slot_rdb_selected ( QAction * ) ) );
256 :
257 0 : connect(information_about_dbe, SIGNAL(triggered()), this, SLOT(slot_show_information_about_dbe()));
258 :
259 : // Connect to signals from the messenger system
260 :
261 0 : connect(&dbe::interface::messenger_proxy::ref(),
262 : SIGNAL(signal_debug(QString const, QString const)),
263 : this,
264 : SLOT(slot_information_message(QString, QString)),
265 : Qt::QueuedConnection);
266 :
267 0 : connect(&dbe::interface::messenger_proxy::ref(),
268 : SIGNAL(signal_info(QString const, QString const)),
269 : this,
270 : SLOT(slot_information_message(QString, QString)),
271 : Qt::QueuedConnection);
272 :
273 0 : connect(&dbe::interface::messenger_proxy::ref(),
274 : SIGNAL(signal_note(QString const, QString const)),
275 : this,
276 : SLOT(slot_information_message(QString, QString)),
277 : Qt::QueuedConnection);
278 :
279 0 : connect(&dbe::interface::messenger_proxy::ref(),
280 : SIGNAL(signal_warn(QString const, QString const)),
281 : this,
282 : SLOT(slot_warning_message(QString, QString)),
283 : Qt::QueuedConnection);
284 :
285 0 : connect(&dbe::interface::messenger_proxy::ref(),
286 : SIGNAL(signal_error(QString const, QString const)),
287 : this,
288 : SLOT(slot_error_message(QString, QString)),
289 : Qt::QueuedConnection);
290 :
291 0 : connect(&dbe::interface::messenger_proxy::ref(),
292 : SIGNAL(signal_fail(QString const, QString const)),
293 : this,
294 : SLOT(slot_error_message(QString, QString)),
295 : Qt::QueuedConnection);
296 :
297 : // connect ( this, SIGNAL ( signal_rdb_found(const QString&, const RDBMap& ) ),
298 : // this, SLOT ( slot_rdb_found(const QString&, const RDBMap&) ), Qt::AutoConnection );
299 0 : }
300 :
301 : void
302 0 : dbe::MainWindow::build_class_tree_model()
303 : {
304 0 : QStringList Headers{ "Class Name", "# Objects" };
305 :
306 0 : if (this_classes != nullptr) {
307 0 : delete this_classes;
308 0 : delete this_treefilter;
309 : }
310 : /// Creating new Main Model
311 0 : this_classes = new dbe::models::tree(Headers);
312 : /// Resetting attached models
313 0 : this_treefilter = new models::treeselection();
314 0 : this_treefilter->setFilterRegExp("");
315 :
316 0 : connect(this_classes, SIGNAL(ObjectFile(QString)), this, SLOT(slot_loaded_db_file(QString)));
317 :
318 0 : this_treefilter->setDynamicSortFilter(true);
319 0 : this_treefilter->setSourceModel(this_classes);
320 0 : slot_toggle_casesensitive_for_treeview(true);
321 0 : TreeView->setModel(this_treefilter);
322 0 : TreeView->setSortingEnabled(true);
323 0 : TreeView->resizeColumnToContents(0);
324 0 : TreeView->resizeColumnToContents(1);
325 :
326 0 : connect(HideCheckBox, SIGNAL(toggled(bool)), this_treefilter, SLOT(ToggleEmptyClasses(bool)));
327 :
328 0 : connect(ShowDerivedObjects, SIGNAL(toggled(bool)), this_classes, SLOT(ToggleAbstractClassesSelectable(bool)));
329 :
330 0 : update_total_objects();
331 0 : }
332 :
333 : void
334 0 : dbe::MainWindow::build_table_model()
335 : {
336 : /// Displaying table widgets
337 0 : SearchLineTable->clear();
338 0 : SearchLineTable->show();
339 0 : SearchLineTable->setProperty("placeholderText", QVariant(QString("Table Filter")));
340 0 : CaseSensitiveCheckBoxTable->show();
341 0 : }
342 :
343 : void
344 0 : dbe::MainWindow::edit_object_at(const QModelIndex& Index)
345 : {
346 0 : treenode* tree_node = this_classes->getnode(Index);
347 :
348 : /*
349 : * If an object node is linked to this index then launch the object editor
350 : * else build a table for the class , showing all objects
351 : */
352 :
353 0 : if (dynamic_cast<ObjectNode*>(tree_node)) {
354 0 : ObjectNode* NodeObject = dynamic_cast<ObjectNode*>(tree_node);
355 0 : tref ObjectToBeEdited = NodeObject->GetObject();
356 0 : slot_launch_object_editor(ObjectToBeEdited);
357 0 : } else {
358 : // Class node
359 0 : QString const cname = tree_node->GetData(0).toString();
360 0 : dunedaq::conffwk::class_t cinfo = dbe::config::api::info::onclass::definition(cname.toStdString(), false);
361 :
362 0 : if (not cinfo.p_abstract or ShowDerivedObjects->isChecked()) {
363 0 : if (TableTab* CurrentTab = dynamic_cast<TableTab*>(tableholder->currentWidget())) {
364 0 : CurrentTab->CreateModels();
365 0 : dbe::models::table* CurrentTabModel = CurrentTab->GetTableModel();
366 0 : CustomDelegate* CurrentDelegate = CurrentTab->GetTableDelegate();
367 0 : CustomTableView* CurrentView = CurrentTab->GetTableView();
368 :
369 0 : connect(
370 : CurrentView, SIGNAL(OpenEditor(tref)), this, SLOT(slot_launch_object_editor(tref)), Qt::UniqueConnection);
371 0 : connect(CurrentDelegate,
372 : SIGNAL(CreateObjectEditorSignal(tref)),
373 : this,
374 : SLOT(slot_launch_object_editor(tref)),
375 : Qt::UniqueConnection);
376 :
377 0 : if (dynamic_cast<ClassNode*>(tree_node)) {
378 0 : BOOST_SCOPE_EXIT(CurrentTabModel)
379 : {
380 0 : emit CurrentTabModel->layoutChanged();
381 0 : }
382 0 : BOOST_SCOPE_EXIT_END
383 :
384 0 : emit CurrentTabModel->layoutAboutToBeChanged();
385 :
386 0 : CurrentTabModel->BuildTableFromClass(cname, ShowDerivedObjects->isChecked());
387 0 : build_table_model();
388 0 : tableholder->setTabText(tableholder->currentIndex(), cname);
389 0 : CurrentTab->ResetTableView();
390 0 : }
391 :
392 0 : CurrentTab->ResizeHeaders();
393 : }
394 : }
395 0 : }
396 0 : }
397 :
398 : void
399 0 : dbe::MainWindow::build_file_model()
400 : {
401 : /// Changed -> Now accepting rdbconfig this means ||
402 : /// !ConfigWrapper::GetInstance().GetDatabaseImplementation().contains("rdbconfig") was removed
403 :
404 0 : if (!confaccessor::db_implementation_name().contains("roksconflibs")) {
405 0 : if (this_files != nullptr) {
406 0 : delete this_files;
407 : }
408 0 : this_files = new FileModel();
409 :
410 0 : this_filesort.setSourceModel(this_files);
411 0 : FileView->setModel(&this_filesort);
412 :
413 0 : FileView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
414 0 : FileView->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
415 0 : FileView->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
416 0 : FileView->horizontalHeader()->setSectionResizeMode(3, QHeaderView::ResizeToContents);
417 :
418 0 : emit signal_new_file_model();
419 : }
420 0 : }
421 :
422 : void
423 0 : dbe::MainWindow::slot_fetch_data(const treenode* ClassNode)
424 : {
425 0 : if (this_classes->canFetchMore(this_classes->index(ClassNode->GetRow(), 0, QModelIndex()))) {
426 0 : this_classes->fetchMore(this_classes->index(ClassNode->GetRow(), 0, QModelIndex()));
427 : }
428 0 : }
429 :
430 : bool
431 0 : dbe::MainWindow::slot_commit_database(bool Exit)
432 : {
433 0 : CommitDialog* SaveDialog = new CommitDialog();
434 0 : int DialogResult = SaveDialog->exec();
435 :
436 0 : if (DialogResult) {
437 0 : FileInfo::parse_all_objects();
438 0 : for (auto file : dbe::confaccessor::uncommitted_files()) {
439 0 : auto message = FileInfo::check_file_includes(QString::fromStdString(file));
440 0 : if (!message.isEmpty()) {
441 0 : QMessageBox::warning(0, "Save database", message);
442 0 : FileInfo::show_file_info(QString::fromStdString(file));
443 0 : return false;
444 : }
445 0 : }
446 :
447 0 : QString CommitMessage = SaveDialog->GetCommitMessage();
448 :
449 0 : try {
450 0 : std::list<std::string> const& modified = confaccessor::save(CommitMessage);
451 0 : confaccessor::clear_commands();
452 :
453 0 : build_file_model();
454 :
455 0 : if (not modified.empty()) {
456 0 : std::string msg;
457 :
458 0 : for (std::string const& f : modified) {
459 0 : msg += "\n" + f;
460 : }
461 :
462 0 : INFO("List of modified files committed to the database ", "Program execution success", msg);
463 0 : } else {
464 0 : WARN("Changes where committed successfully but list of modified files could not be retrieved",
465 0 : "Unexpected program execution");
466 : }
467 :
468 0 : } catch (dunedaq::conffwk::Exception const& e) {
469 0 : WARN("The changes could not be committed", dbe::config::errors::parse(e).c_str())
470 0 : ers::error(e);
471 0 : return false;
472 0 : }
473 : // Gaahhh confaccessor catches dunedaq::conffwk::Exception and
474 : // rethrows it as daq::dbe::CouldNotCommitChanges!!
475 0 : catch (daq::dbe::CouldNotCommitChanges const& exc) {
476 0 : std::string reason{ exc.what() };
477 0 : auto cause = exc.cause();
478 0 : while (cause != nullptr) {
479 0 : reason = cause->what();
480 0 : cause = cause->cause();
481 : }
482 0 : WARN("The changes could not be committed",
483 : // dbe::config::errors::parse(exc).c_str(),
484 : reason,
485 : "\n\nTry fixing includes from File Info window")
486 0 : ers::error(exc);
487 0 : return false;
488 0 : }
489 0 : } else {
490 0 : if (Exit) {
491 0 : slot_abort_changes();
492 : }
493 : }
494 : return true;
495 : }
496 :
497 : void
498 0 : dbe::MainWindow::slot_abort_changes()
499 : {
500 0 : try {
501 0 : if (confaccessor::is_database_loaded()) {
502 0 : confaccessor::abort();
503 0 : confaccessor::clear_commands();
504 : }
505 0 : } catch (dunedaq::conffwk::Exception const& e) {
506 0 : ERROR("Database changes aborted", dbe::config::errors::parse(e).c_str());
507 0 : ers::error(e);
508 0 : }
509 0 : }
510 :
511 : void
512 0 : dbe::MainWindow::slot_abort_external_changes()
513 : {
514 0 : try {
515 0 : if (confaccessor::is_database_loaded()) {
516 0 : confaccessor::abort();
517 : }
518 0 : } catch (dunedaq::conffwk::Exception const& e) {
519 0 : ERROR("External changes aborted", dbe::config::errors::parse(e).c_str());
520 0 : ers::error(e);
521 0 : }
522 0 : }
523 :
524 : void
525 0 : dbe::MainWindow::slot_launch_object_editor(tref Object)
526 : {
527 0 : bool WidgetFound = false;
528 0 : QString ObjectEditorName = QString("%1@%2").arg(Object.UID().c_str()).arg(Object.class_name().c_str());
529 :
530 0 : for (QWidget* Editor : QApplication::allWidgets()) {
531 0 : ObjectEditor* Widget = dynamic_cast<ObjectEditor*>(Editor);
532 :
533 0 : if (Widget != nullptr) {
534 0 : if ((Widget->objectName()).compare(ObjectEditorName) == 0) {
535 0 : Widget->raise();
536 0 : Widget->setVisible(true);
537 : WidgetFound = true;
538 : }
539 : }
540 0 : }
541 :
542 0 : if (!WidgetFound) {
543 0 : (new ObjectEditor(Object))->show();
544 : }
545 0 : }
546 :
547 : void
548 0 : dbe::MainWindow::slot_launch_batchchange()
549 : {
550 0 : if (confaccessor::is_database_loaded()) {
551 0 : BatchChangeWidget* Batch = new BatchChangeWidget(nullptr);
552 0 : Batch->setWindowModality(Qt::WindowModal);
553 0 : Batch->show();
554 : } else {
555 0 : ERROR("Database must have been loaded", "No database loaded");
556 : }
557 0 : }
558 :
559 : void
560 0 : dbe::MainWindow::slot_launch_batchchange_on_table()
561 : {
562 0 : if (!confaccessor::is_database_loaded()) {
563 0 : ERROR("Database must have been loaded", "No database loaded");
564 0 : return;
565 : }
566 :
567 0 : dbe::models::table* CurrentTableModel = nullptr;
568 0 : TableTab* CurrentTab = dynamic_cast<TableTab*>(tableholder->currentWidget());
569 0 : if (CurrentTab) {
570 0 : CurrentTableModel = CurrentTab->GetTableModel();
571 : }
572 :
573 0 : std::vector<dref> TableObject;
574 :
575 0 : if (!CurrentTab || !CurrentTableModel) {
576 0 : ERROR("Table cannot be processed", "Table is empty");
577 0 : return;
578 : }
579 :
580 0 : if ((*CurrentTableModel->GetTableObjects()).isEmpty()) {
581 0 : ERROR("Table cannot be processed", "Table is empty");
582 0 : return;
583 : }
584 :
585 0 : QString Filter = SearchLineTable->text();
586 :
587 0 : for (dref Object : *CurrentTableModel->GetTableObjects()) {
588 0 : if (Filter.isEmpty()) {
589 0 : TableObject.push_back(Object);
590 : } else {
591 0 : QString ObjectString = QString::fromStdString(Object.UID());
592 :
593 0 : if (ObjectString.contains(Filter, Qt::CaseInsensitive)) {
594 0 : TableObject.push_back(Object);
595 : }
596 0 : }
597 0 : }
598 :
599 0 : BatchChangeWidget* Batch = new BatchChangeWidget(true, CurrentTableModel->get_class_name(), TableObject, nullptr);
600 0 : Batch->setWindowModality(Qt::WindowModal);
601 0 : Batch->show();
602 0 : }
603 :
604 : void
605 0 : dbe::MainWindow::reload_default_settings()
606 : {
607 0 : QSettings settings;
608 0 : settings.clear();
609 0 : load_default_settings();
610 0 : }
611 : void
612 0 : dbe::MainWindow::load_default_settings()
613 : {
614 0 : QSettings defaults(":theme/DBE_Default_User_Settings.conf", QSettings::NativeFormat);
615 0 : apply_settings(defaults);
616 0 : }
617 :
618 : QString
619 0 : dbe::MainWindow::find_db_repository_dir()
620 : {
621 0 : if (confaccessor::dbfullname().isEmpty()) {
622 0 : return "";
623 : }
624 :
625 0 : const QStringList& incs = dbe::config::api::get::file::inclusions({ confaccessor::dbfullname() });
626 0 : for (QString f : allFiles) {
627 0 : for (const QString& j : incs) {
628 0 : if (f.endsWith(j)) {
629 0 : return f.remove(j);
630 : }
631 : }
632 0 : }
633 :
634 0 : return "";
635 0 : }
636 :
637 : void
638 0 : dbe::MainWindow::slot_create_newdb()
639 : {
640 :
641 0 : CreateDatabaseWidget* CreateDatabaseW = new CreateDatabaseWidget(nullptr, false, find_db_repository_dir());
642 0 : CreateDatabaseW->show();
643 0 : connect(CreateDatabaseW,
644 : SIGNAL(CanLoadDatabase(const QString&)),
645 : this,
646 : SLOT(slot_load_db_from_create_widget(const QString&)));
647 0 : }
648 :
649 : void
650 0 : dbe::MainWindow::slot_load_db_from_create_widget(const QString& DatabaseName)
651 : {
652 0 : if (!DatabaseName.isEmpty()) {
653 0 : QFileInfo DatabaseFile = QFileInfo(DatabaseName);
654 :
655 0 : if (DatabaseFile.exists()) {
656 0 : QString Path = QString(DatabaseFile.absoluteFilePath());
657 :
658 0 : if (dbreload()) {
659 0 : confaccessor::setdbinfo(Path);
660 :
661 0 : if (dbload()) {
662 0 : setinternals();
663 0 : build_class_tree_model();
664 : // // build_partition_tree_model();
665 : // build_resource_tree_model();
666 0 : build_file_model();
667 : }
668 : }
669 0 : } else {
670 0 : WARN("File not found during database load",
671 : "File does not exist",
672 : "\n\n Filename:",
673 0 : DatabaseFile.fileName().toStdString());
674 : }
675 0 : } else {
676 0 : ERROR("Database load error", "File was not selected");
677 : }
678 0 : }
679 :
680 : bool
681 0 : dbe::MainWindow::dbreload()
682 : {
683 0 : if (confaccessor::is_database_loaded()) {
684 0 : QMessageBox MessageBox;
685 0 : MessageBox.setText("Do you really wish to abandon the current database and load a new one ?");
686 0 : MessageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
687 0 : MessageBox.setDefaultButton(QMessageBox::Cancel);
688 0 : int UserOption = MessageBox.exec();
689 :
690 0 : switch (UserOption) {
691 :
692 : case QMessageBox::Yes:
693 : return true;
694 :
695 0 : case QMessageBox::Cancel:
696 0 : return false;
697 :
698 0 : default:
699 0 : return false;
700 : }
701 0 : } else {
702 : return true;
703 : }
704 : }
705 :
706 : bool
707 0 : dbe::MainWindow::dbload()
708 : {
709 : // For issues related to loading the configuration in a separate thread, see ATLASDBE-229
710 :
711 0 : const bool alreadyLoaded = confaccessor::is_database_loaded();
712 :
713 : // The QueuedConnection is mandatory to let the loop receive the signal even if
714 : // it is emitted before "exec" is called
715 0 : QEventLoop loop;
716 0 : connect(this, SIGNAL(signal_db_loaded()), &loop, SLOT(quit()), Qt::QueuedConnection);
717 :
718 : // Make life of the progress dialog longer
719 : // Show only the first time, when the configuration is not loaded
720 : // In other cases, just show a busy cursor
721 0 : std::unique_ptr<QProgressDialog> progress_bar;
722 0 : if (!alreadyLoaded) {
723 0 : progress_bar.reset(new QProgressDialog("Loading Configuration...", QString(), 0, 0, this));
724 0 : progress_bar->setWindowModality(Qt::WindowModal);
725 0 : progress_bar->show();
726 : }
727 :
728 0 : BOOST_SCOPE_EXIT(void)
729 : {
730 0 : QApplication::restoreOverrideCursor();
731 0 : }
732 0 : BOOST_SCOPE_EXIT_END
733 :
734 0 : QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
735 :
736 : // Close widgets
737 0 : for (QWidget* widget : QApplication::allWidgets()) {
738 0 : if (dynamic_cast<ObjectEditor*>(widget)) {
739 0 : widget->close();
740 0 : } else if (dynamic_cast<widgets::editors::base*>(widget)) {
741 0 : widget->close();
742 : }
743 0 : }
744 :
745 : // Asynchronous execution only the first time the configuration is loaded
746 0 : std::future<bool> waiter = std::async(alreadyLoaded ? std::launch::deferred : std::launch::async, [this] {
747 0 : const bool result = confaccessor::load(!isArchivedConf);
748 0 : emit signal_db_loaded(); // "loop.exec()" will return now
749 0 : return result;
750 0 : });
751 :
752 : // Do not call "exec" if the previous call is not asynchronous
753 0 : if (!alreadyLoaded) {
754 0 : loop.exec(QEventLoop::ExcludeUserInputEvents);
755 : }
756 :
757 : // If "deferred", the async call is executed now and here
758 0 : return waiter.get();
759 0 : }
760 :
761 : void
762 0 : dbe::MainWindow::setinternals()
763 : {
764 0 : confaccessor::clear_commands();
765 0 : confaccessor::gethandler()->ResetData();
766 0 : confaccessor::set_total_objects(0);
767 :
768 : /// Disconnecting models from views
769 :
770 0 : for (int i = 0; i < tableholder->count(); i++) {
771 0 : TableTab* CurrentTab = dynamic_cast<TableTab*>(tableholder->widget(i));
772 0 : if (CurrentTab) {
773 0 : CurrentTab->DisconnectView();
774 : }
775 : }
776 :
777 0 : FileView->setModel(NULL);
778 0 : }
779 :
780 : void
781 0 : dbe::MainWindow::apply_settings(QSettings& settings)
782 : {
783 0 : settings.beginGroup("MainWindow-layout");
784 0 : if (settings.contains("size")) {
785 0 : resize(settings.value("size").toSize());
786 : }
787 0 : if (settings.contains("pos")) {
788 0 : move(settings.value("pos").toPoint());
789 : }
790 0 : if (settings.contains("TableView")) {
791 0 : DisplayTableView->setChecked(settings.value("TableView").toBool());
792 : }
793 0 : if (settings.contains("ClassView")) {
794 0 : DisplayClassView->setChecked(settings.value("ClassView").toBool());
795 : }
796 0 : if (settings.contains("Messages")) {
797 0 : DisplayMessages->setChecked(settings.value("Messages").toBool());
798 : }
799 0 : if (settings.contains("geometry")) {
800 0 : restoreGeometry(settings.value("geometry").toByteArray());
801 : }
802 0 : if (settings.contains("state")) {
803 0 : restoreState(settings.value("state").toByteArray());
804 : }
805 0 : settings.endGroup();
806 :
807 0 : settings.beginGroup("MainWindow-checkboxes");
808 0 : if (settings.contains("tree-case-sensitive")) {
809 0 : CaseSensitiveCheckBoxTree->setChecked(settings.value("tree-case-sensitive").toBool());
810 : }
811 0 : if (settings.contains("table-case-sensitive")) {
812 0 : CaseSensitiveCheckBoxTable->setChecked(settings.value("table-case-sensitive").toBool());
813 : }
814 0 : settings.endGroup();
815 :
816 0 : StyleUtility::InitColorManagement();
817 0 : }
818 :
819 : void
820 0 : dbe::MainWindow::WriteSettings()
821 : {
822 0 : QSettings Settings("dunedaq", "dbe_main");
823 0 : Settings.beginGroup("MainWindow-layout");
824 0 : Settings.setValue("size", size());
825 0 : Settings.setValue("pos", pos());
826 0 : Settings.setValue("TableView", DisplayTableView->isChecked());
827 0 : Settings.setValue("ClassView", DisplayClassView->isChecked());
828 :
829 0 : Settings.setValue("Messages", DisplayMessages->isChecked());
830 0 : Settings.setValue("geometry", saveGeometry());
831 0 : Settings.setValue("state", saveState());
832 0 : Settings.endGroup();
833 :
834 0 : Settings.beginGroup("MainWindow-checkboxes");
835 0 : Settings.setValue("tree-case-sensitive", CaseSensitiveCheckBoxTree->isChecked());
836 0 : Settings.setValue("table-case-sensitive", CaseSensitiveCheckBoxTable->isChecked());
837 0 : Settings.endGroup();
838 0 : }
839 :
840 : void
841 0 : dbe::MainWindow::argsparse(QMap<QString, QString> const& opts)
842 : {
843 0 : if (!opts.isEmpty()) {
844 0 : dbinfo LoadConfig;
845 0 : QString FileToLoad;
846 :
847 0 : QString FileName = opts.value("f");
848 0 : QString RdbFileName = opts.value("r");
849 0 : QString RoksFileName = opts.value("o");
850 0 : QString HashVersion = opts.value("v");
851 :
852 0 : if (!FileName.isEmpty()) {
853 0 : FileToLoad = FileName;
854 0 : LoadConfig = dbinfo::oks;
855 :
856 0 : if (!HashVersion.isEmpty()) {
857 0 : ::setenv("TDAQ_DB_VERSION", QString("hash:").append(HashVersion).toStdString().c_str(), 1);
858 0 : ::setenv("OKS_GIT_PROTOCOL", "http", 1);
859 0 : isArchivedConf = true;
860 : }
861 0 : } else if (!RdbFileName.isEmpty()) {
862 0 : FileToLoad = RdbFileName;
863 0 : LoadConfig = dbinfo::rdb;
864 0 : } else if (!RoksFileName.isEmpty()) {
865 0 : FileToLoad = RoksFileName;
866 0 : LoadConfig = dbinfo::roks;
867 : }
868 :
869 0 : if (not FileToLoad.isEmpty()) {
870 0 : dbopen(FileToLoad, LoadConfig);
871 : }
872 0 : }
873 0 : }
874 :
875 : // /**
876 : // * Create Rdb menu based on the available Rdb information
877 : // */
878 : // void dbe::MainWindow::init_rdb_menu()
879 : // {
880 : // ConnectToRdb->clear();
881 :
882 : // std::list<IPCPartition> pl;
883 : // IPCPartition::getPartitions(pl);
884 : // TLOG_DEBUG(1) << "Found " << pl.size() << " partitions" ;
885 :
886 : // pl.push_front(IPCPartition("initial"));
887 :
888 : // auto f = [pl, this] () {
889 : // for ( auto it = pl.begin(); it != pl.end(); ++it )
890 : // {
891 : // lookForRDBServers ( *it );
892 : // }
893 : // };
894 :
895 : // std::thread t(f);
896 : // t.detach();
897 : // }
898 :
899 : // void dbe::MainWindow::slot_rdb_found(const QString& p, const RDBMap& rdbs) {
900 : // QMenu * part_menu = new QMenu(p);
901 :
902 : // for(auto it = rdbs.begin(); it != rdbs.end(); ++it) {
903 : // QAction * newAct = new QAction ( it.key(), part_menu );
904 :
905 : // QFont actFont = newAct->font();
906 : // if(it.value() == true) {
907 : // newAct->setToolTip ( QString ( "This is a Read-Only instance of the DB" ) );
908 : // actFont.setItalic ( true );
909 : // } else {
910 : // newAct->setToolTip ( QString ( "This is a Read/Write instance of the DB" ) );
911 : // actFont.setBold ( true );
912 : // }
913 :
914 : // newAct->setFont ( actFont );
915 :
916 : // part_menu->addAction ( newAct );
917 : // }
918 :
919 : // ConnectToRdb->addMenu ( part_menu );
920 : // }
921 :
922 : // /**
923 : // * Add rdb servers for each partition
924 : // *
925 : // * @param p is the partition source for which to populate with server information
926 : // */
927 : // void dbe::MainWindow::lookForRDBServers ( const IPCPartition & p )
928 : // {
929 : // TLOG_DEBUG(2) << "dbe::MainWindow::addRDBServers()" ;
930 :
931 : // if ( p.isValid() )
932 : // {
933 : // TLOG_DEBUG(2) << "Inserting partition = " << p.name() ;
934 :
935 : // RDBMap rdbs;
936 :
937 : // try
938 : // {
939 : // {
940 : // std::map<std::string, rdb::cursor_var> objects;
941 : // p.getObjects<rdb::cursor, ::ipc::use_cache, ::ipc::unchecked_narrow> ( objects );
942 : // std::map<std::string, rdb::cursor_var>::iterator rdb_it = objects.begin();
943 :
944 : // while ( rdb_it != objects.end() )
945 : // {
946 : // TLOG_DEBUG(2) << "Found server : " << rdb_it->first ;
947 :
948 : // rdbs.insert(QString::fromStdString(rdb_it->first), true);
949 :
950 : // ++rdb_it;
951 : // }
952 : // }
953 :
954 : // {
955 : // std::map<std::string, rdb::writer_var> objects;
956 : // p.getObjects<rdb::writer, ::ipc::use_cache, ::ipc::unchecked_narrow> ( objects );
957 : // std::map<std::string, rdb::writer_var>::iterator rdb_it = objects.begin();
958 :
959 : // while ( rdb_it != objects.end() )
960 : // {
961 : // TLOG_DEBUG(2) << "Found server : " << rdb_it->first ;
962 :
963 : // rdbs.insert(QString::fromStdString(rdb_it->first), false);
964 :
965 : // ++rdb_it;
966 : // }
967 : // }
968 : // }
969 : // catch ( daq::ipc::InvalidPartition& e )
970 : // {
971 : // ers::error ( e );
972 : // }
973 :
974 : // if(rdbs.isEmpty() == false) {
975 : // emit signal_rdb_found (QString::fromStdString(p.name()), rdbs);
976 : // }
977 : // }
978 : // }
979 :
980 : // void dbe::MainWindow::slot_rdb_selected ( QAction * action )
981 : // {
982 : // QMenu * parentMenu = qobject_cast<QMenu *> ( action->parent() );
983 :
984 : // if ( parentMenu )
985 : // {
986 : // if ( dbreload() )
987 : // {
988 : // BOOST_SCOPE_EXIT(void)
989 : // {
990 : // QApplication::restoreOverrideCursor();
991 : // }
992 : // BOOST_SCOPE_EXIT_END
993 :
994 : // QApplication::setOverrideCursor(Qt::WaitCursor);
995 :
996 : // confaccessor::setdbinfo ( action->text() + "@" + parentMenu->title(), dbinfo::rdb );
997 :
998 : // if ( dbload() )
999 : // {
1000 : // setinternals();
1001 : // build_class_tree_model();
1002 : // build_partition_tree_model();
1003 : // build_resource_tree_model();
1004 : // build_file_model();
1005 : // }
1006 : // }
1007 : // }
1008 : // }
1009 :
1010 : // void dbe::MainWindow::slot_oracle_prepare()
1011 : // {
1012 : // if ( this_oraclewidget == nullptr )
1013 : // {
1014 : // this_oraclewidget = new OracleWidget();
1015 : // connect ( this_oraclewidget, SIGNAL ( OpenOracleConfig ( const QString & ) ), this,
1016 : // SLOT ( slot_load_oracle ( const QString & ) ) );
1017 : // }
1018 :
1019 : // this_oraclewidget->raise();
1020 : // this_oraclewidget->show();
1021 : // }
1022 :
1023 : // void dbe::MainWindow::slot_load_oracle ( const QString & OracleDatabase )
1024 : // {
1025 : // if ( dbreload() )
1026 : // {
1027 : // confaccessor::setdblocation ( OracleDatabase );
1028 :
1029 : // if ( dbload() )
1030 : // {
1031 : // setinternals();
1032 : // build_class_tree_model();
1033 : // build_partition_tree_model();
1034 : // build_resource_tree_model();
1035 : // build_file_model();
1036 : // }
1037 : // }
1038 :
1039 : // if ( this_oraclewidget != nullptr )
1040 : // {
1041 : // this_oraclewidget->close();
1042 : // }
1043 : // }
1044 :
1045 : void
1046 0 : dbe::MainWindow::slot_whatisthis()
1047 : {
1048 0 : QWhatsThis::enterWhatsThisMode();
1049 0 : }
1050 :
1051 : void
1052 0 : dbe::MainWindow::slot_show_information_about_dbe()
1053 : {
1054 0 : static QString const title("About DBE");
1055 0 : static QString const msg = QString()
1056 0 : .append("DBE is an editor to work with OKS and RDB backends that manages most of the "
1057 : "hard work for you in editing the configuration database\n")
1058 0 : .append("\n\nMaintained :\t\tC&C Working group \n\t\t\t(atlas-tdaq-cc-wg@cern.ch)")
1059 0 : .append("\nProgram version:\t\t")
1060 0 : .append(dbe_compiled_version)
1061 0 : .append("\nLibraries version:\t")
1062 0 : .append("\n\t\t\tdbecore(")
1063 0 : .append(dbe_lib_core_version)
1064 0 : .append("),\n\t\t\tdbe_config_api(")
1065 0 : .append(dbe_lib_config_api_version)
1066 0 : .append("),\n\t\t\tdbe_structure(")
1067 0 : .append(dbe_lib_structure_version)
1068 0 : .append("),\n\t\t\tdbe_internal(")
1069 0 : .append(dbe_lib_internal_version)
1070 0 : .append(')')
1071 0 : .append("\nRepo commit hash:\t")
1072 0 : .append(dbe_compiled_commit);
1073 :
1074 0 : QMessageBox::about(this, title, msg);
1075 0 : }
1076 :
1077 : void
1078 0 : dbe::MainWindow::slot_show_userguide()
1079 : {
1080 0 : QDesktopServices::openUrl(QUrl("https://atlasdaq.cern.ch/dbe/"));
1081 0 : }
1082 :
1083 : void
1084 0 : dbe::MainWindow::slot_show_userchanges()
1085 : {
1086 0 : InfoWidget->setCurrentIndex(InfoWidget->indexOf(CommitedTab));
1087 0 : }
1088 :
1089 : void
1090 0 : dbe::MainWindow::slot_undo_allchanges()
1091 : {
1092 0 : UndoView->stack()->setIndex(0);
1093 0 : }
1094 :
1095 : void
1096 0 : dbe::MainWindow::slot_toggle_casesensitive_for_treeview(bool)
1097 : {
1098 0 : if (CaseSensitiveCheckBoxTree->isChecked())
1099 0 : this_treefilter->setFilterCaseSensitivity(Qt::CaseSensitive);
1100 : else {
1101 0 : this_treefilter->setFilterCaseSensitivity(Qt::CaseInsensitive);
1102 : }
1103 0 : update_total_objects();
1104 0 : }
1105 :
1106 : void
1107 0 : dbe::MainWindow::slot_model_rebuild()
1108 : {
1109 : /// Preparing data
1110 0 : confaccessor::gethandler()->ResetData();
1111 0 : confaccessor::set_total_objects(0);
1112 : /// Disconnecting models from views
1113 :
1114 0 : for (int i = 0; i < tableholder->count(); i++) {
1115 0 : TableTab* CurrentTab = dynamic_cast<TableTab*>(tableholder->widget(i));
1116 0 : if (CurrentTab) {
1117 0 : CurrentTab->DisconnectView();
1118 : }
1119 : }
1120 :
1121 0 : FileView->setModel(NULL);
1122 :
1123 0 : build_class_tree_model();
1124 0 : build_file_model();
1125 0 : }
1126 :
1127 : void
1128 0 : dbe::MainWindow::slot_filter_textchange(const QString& FilterText)
1129 : {
1130 0 : if (this_treefilter != nullptr and SearchBox->currentIndex() != 1) {
1131 0 : this_treefilter->SetFilterType(models::treeselection::RegExpFilterType);
1132 :
1133 0 : if (SearchBox->currentIndex() == 2) {
1134 0 : this_treefilter->SetFilterRestrictionLevel(1000);
1135 : } else {
1136 0 : this_treefilter->SetFilterRestrictionLevel(1);
1137 : }
1138 :
1139 0 : this_treefilter->setFilterRegExp(FilterText);
1140 : }
1141 :
1142 0 : update_total_objects();
1143 0 : }
1144 :
1145 : void
1146 0 : dbe::MainWindow::slot_filter_query()
1147 : {
1148 0 : if (this_treefilter == nullptr) {
1149 0 : return;
1150 : }
1151 :
1152 0 : QString Tmp = SearchTreeLine->text();
1153 0 : if (SearchBox->currentIndex() == 1) {
1154 0 : this_treefilter->SetFilterType(models::treeselection::ObjectFilterType);
1155 0 : std::vector<dbe::tref> Objects = ProcessQuery(Tmp);
1156 :
1157 0 : this_treefilter->SetQueryObjects(Objects);
1158 0 : this_treefilter->setFilterRegExp(Tmp);
1159 0 : update_total_objects();
1160 0 : } else {
1161 0 : this_treefilter->ResetQueryObjects();
1162 0 : slot_filter_textchange(Tmp);
1163 : }
1164 0 : }
1165 :
1166 : void
1167 0 : dbe::MainWindow::slot_filter_table_textchange(const QString& FilterText)
1168 : {
1169 0 : TableTab* CurrentTab = dynamic_cast<TableTab*>(tableholder->currentWidget());
1170 :
1171 0 : if (CurrentTab) {
1172 0 : dbe::models::tableselection* TableFilter = CurrentTab->GetTableFilter();
1173 :
1174 0 : if (TableFilter == nullptr) {
1175 : return;
1176 : }
1177 :
1178 0 : TableFilter->SetFilterType(dbe::models::tableselection::RegExpFilter);
1179 :
1180 0 : if (CaseSensitiveCheckBoxTable->isChecked())
1181 0 : TableFilter->setFilterCaseSensitivity(Qt::CaseSensitive);
1182 : else {
1183 0 : TableFilter->setFilterCaseSensitivity(Qt::CaseInsensitive);
1184 : }
1185 :
1186 0 : TableFilter->setFilterRegExp(FilterText);
1187 : }
1188 : }
1189 :
1190 : void
1191 0 : dbe::MainWindow::slot_tree_reset()
1192 : {
1193 : // Keep track of the selected tab
1194 0 : int IndexOfCurrentTab = tableholder->currentIndex();
1195 :
1196 : // Here are the all the open tabs
1197 0 : std::vector<QModelIndex> idxs;
1198 :
1199 0 : for (int i = 0; i < tableholder->count(); ++i) {
1200 0 : TableTab* CurrentTab = dynamic_cast<TableTab*>(tableholder->widget(i));
1201 0 : if (CurrentTab) {
1202 0 : if (CurrentTab->GetTableModel()) {
1203 0 : const QString& TableClassName = CurrentTab->GetTableModel()->get_class_name();
1204 0 : if (!TableClassName.isEmpty()) {
1205 0 : treenode* NodeClass = confaccessor::gethandler()->getnode(TableClassName);
1206 0 : if (NodeClass != nullptr) {
1207 0 : idxs.push_back(this_classes->getindex(NodeClass));
1208 : }
1209 : }
1210 0 : }
1211 : }
1212 : }
1213 :
1214 : // Remove all the tabs
1215 0 : while (tableholder->count() != 0) {
1216 0 : tableholder->widget(0)->deleteLater();
1217 0 : tableholder->removeTab(0);
1218 : }
1219 :
1220 : // Disconnecting models from views
1221 0 : build_class_tree_model();
1222 :
1223 : // Re-create all the tabs
1224 0 : for (const auto& idx : idxs) {
1225 0 : slot_add_tab();
1226 0 : edit_object_at(idx);
1227 : }
1228 :
1229 : // Set the current tab
1230 0 : tableholder->setCurrentIndex(IndexOfCurrentTab);
1231 0 : }
1232 :
1233 : void
1234 0 : dbe::MainWindow::update_total_objects()
1235 : {
1236 0 : int total = 0;
1237 0 : for (int item = 0; item < this_treefilter->rowCount(); item++) {
1238 0 : auto index = this_treefilter->index(item, 1);
1239 0 : auto data = this_treefilter->data(index);
1240 0 : total += data.toInt();
1241 0 : }
1242 0 : TotalObjectsLabel->setText(QString("Total Objects: %1").arg(total));
1243 0 : }
1244 :
1245 : void
1246 0 : dbe::MainWindow::closeEvent(QCloseEvent* event)
1247 : {
1248 0 : if (isArchivedConf || check_close()) {
1249 0 : WriteSettings();
1250 :
1251 0 : foreach (QWidget* widget, QApplication::allWidgets())
1252 0 : widget->close();
1253 :
1254 0 : event->accept();
1255 : } else {
1256 0 : event->ignore();
1257 : }
1258 0 : }
1259 :
1260 : std::vector<dbe::tref>
1261 0 : dbe::MainWindow::ProcessQuery(QString const& Tmp)
1262 : {
1263 0 : if (not Tmp.isEmpty()) {
1264 0 : QString const Query = QString("(this (object-id \".*%1.*\" ~=))").arg(Tmp);
1265 :
1266 0 : try {
1267 0 : std::vector<dbe::tref> result;
1268 :
1269 0 : for (std::string const& cname : dbe::config::api::info::onclass::allnames<std::vector<std::string>>()) {
1270 0 : std::vector<dbe::tref> class_matching_objects = inner::dbcontroller::gets(cname, Query.toStdString());
1271 :
1272 0 : result.insert(result.end(), class_matching_objects.begin(), class_matching_objects.end());
1273 0 : }
1274 :
1275 0 : return result;
1276 0 : } catch (dunedaq::conffwk::Exception const& ex) {
1277 0 : ers::error(ex);
1278 0 : ERROR("Query process error", dbe::config::errors::parse(ex).c_str());
1279 0 : }
1280 0 : }
1281 :
1282 0 : return {};
1283 : }
1284 :
1285 : bool
1286 0 : dbe::MainWindow::eventFilter(QObject* Target, QEvent* Event)
1287 : {
1288 0 : if (Target == SearchBox->lineEdit() && Event->type() == QEvent::MouseButtonRelease) {
1289 0 : if (!SearchBox->lineEdit()->hasSelectedText()) {
1290 0 : SearchBox->lineEdit()->selectAll();
1291 0 : return true;
1292 : }
1293 : }
1294 :
1295 : return false;
1296 : }
1297 :
1298 : bool
1299 0 : dbe::MainWindow::check_close()
1300 : {
1301 0 : bool OK = true;
1302 :
1303 0 : foreach (QWidget* widget, QApplication::allWidgets()) {
1304 0 : ObjectCreator* ObjectCreatorInstance = dynamic_cast<ObjectCreator*>(widget);
1305 0 : ObjectEditor* ObjectEditorInstance = dynamic_cast<ObjectEditor*>(widget);
1306 :
1307 0 : if (ObjectEditorInstance) {
1308 0 : OK = ObjectEditorInstance->CanCloseWindow();
1309 : }
1310 :
1311 0 : if (!OK) {
1312 : return false;
1313 : }
1314 :
1315 0 : if (ObjectCreatorInstance) {
1316 0 : OK = ObjectCreatorInstance->CanClose();
1317 : }
1318 :
1319 0 : if (!OK) {
1320 : return false;
1321 : }
1322 0 : }
1323 :
1324 0 : {
1325 0 : cptr<QUndoStack> undo_stack(confaccessor::get_commands());
1326 :
1327 0 : if (undo_stack->isClean()) {
1328 0 : if (undo_stack->count() == 0) {
1329 : return true;
1330 : } else {
1331 0 : slot_abort_changes();
1332 : return true;
1333 : }
1334 : } else {
1335 0 : int ret =
1336 0 : QMessageBox::question(0,
1337 0 : tr("DBE"),
1338 0 : QString("There are unsaved changes.\n\nDo you want to save and commit them to the DB?\n"),
1339 0 : QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel,
1340 : QMessageBox::Save);
1341 :
1342 0 : if (ret == QMessageBox::Discard) {
1343 0 : slot_abort_changes();
1344 : return true;
1345 0 : } else if (ret == QMessageBox::Save) {
1346 0 : return slot_commit_database(true);
1347 0 : } else if (ret == QMessageBox::Cancel) {
1348 : return false;
1349 : } else {
1350 : return true;
1351 : }
1352 : }
1353 0 : }
1354 : }
1355 :
1356 : void
1357 0 : dbe::MainWindow::slot_edit_object_from_class_view(QModelIndex const& ProxyIndex)
1358 : {
1359 0 : edit_object_at(this_treefilter->mapToSource(ProxyIndex));
1360 0 : }
1361 :
1362 : /**
1363 : * Takes necessary actions to load a database from a file provided
1364 : * @param dbpath is the path (absolute or relative to the DUNEDAQ_DB_PATH) of the associated file
1365 : * @return
1366 : */
1367 : bool
1368 0 : dbe::MainWindow::dbopen(QString const& dbpath, dbinfo const& loadtype)
1369 : {
1370 0 : if (dbreload()) {
1371 0 : confaccessor::setdbinfo(dbpath, loadtype);
1372 :
1373 0 : BOOST_SCOPE_EXIT(void)
1374 : {
1375 0 : QApplication::restoreOverrideCursor();
1376 0 : }
1377 0 : BOOST_SCOPE_EXIT_END
1378 :
1379 0 : QApplication::setOverrideCursor(Qt::WaitCursor);
1380 :
1381 0 : if (dbload()) {
1382 0 : setinternals();
1383 0 : build_class_tree_model();
1384 : // build_partition_tree_model();
1385 : // build_resource_tree_model();
1386 0 : build_file_model();
1387 : }
1388 0 : }
1389 :
1390 0 : return true;
1391 : }
1392 :
1393 : void
1394 0 : dbe::MainWindow::slot_open_database_from_file()
1395 : {
1396 0 : QFileDialog FileDialog(this, tr("Open File"), ".", tr("XML files (*.xml)"));
1397 0 : FileDialog.setAcceptMode(QFileDialog::AcceptOpen);
1398 0 : FileDialog.setFileMode(QFileDialog::ExistingFile);
1399 0 : FileDialog.setViewMode(QFileDialog::Detail);
1400 :
1401 0 : if (FileDialog.exec()) {
1402 0 : QStringList FilesSelected = FileDialog.selectedFiles();
1403 :
1404 0 : if (FilesSelected.size()) {
1405 0 : QString DatabasePath = FilesSelected.value(0);
1406 0 : dbopen(DatabasePath, dbinfo::oks);
1407 0 : }
1408 0 : }
1409 0 : }
1410 :
1411 : /**
1412 : * The purpose of this method is to replay local changes after the database has been externally
1413 : * modified.
1414 : *
1415 : * It is called when there is a callback from config layer.
1416 : *
1417 : * The user is being given the option to ignore the external change and proceed without bringing
1418 : * his current database to a consistent state. This will cause the database to be overwritten when
1419 : * local changes are going to be applied.
1420 : */
1421 : void
1422 0 : dbe::MainWindow::slot_process_externalchanges()
1423 : {
1424 0 : auto user_confirmation = [](QString const& msg) {
1425 0 : QMessageBox ExternalMessageBox;
1426 0 : ExternalMessageBox.setText(msg);
1427 0 : ExternalMessageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
1428 0 : ExternalMessageBox.setDefaultButton(QMessageBox::Yes);
1429 0 : return ExternalMessageBox.exec() == QMessageBox::Yes;
1430 0 : };
1431 :
1432 0 : confaccessor::t_undo_stack_cptr undo_stack = confaccessor::get_commands();
1433 :
1434 0 : auto rewind_stack = [&undo_stack]() {
1435 0 : std::vector<bool> commands_original_undo_state;
1436 :
1437 : // Loop over the commands and set their undo-state to false such that when the undostack
1438 : // index is rewind to zero they will not be undone. The purpose is to replay them on top of
1439 : // current changes.
1440 :
1441 0 : for (int i = 0; i < undo_stack->count(); ++i) {
1442 0 : if (dbe::actions::onobject const* Command = dynamic_cast<dbe::actions::onobject const*>(undo_stack->command(i))) {
1443 0 : commands_original_undo_state.push_back(Command->undoable());
1444 0 : Command->setundoable(false);
1445 : }
1446 : }
1447 :
1448 : // Rewind the command stack by setting the index to zero
1449 : // Commands will not be replayed since we have set their state to false
1450 0 : undo_stack->setIndex(0);
1451 :
1452 : // Reset the state of all commands one by one
1453 0 : {
1454 0 : auto cmdstate = commands_original_undo_state.begin();
1455 :
1456 0 : for (int i = 0; i != undo_stack->count(); ++i) {
1457 0 : if (dbe::actions::onobject const* Command =
1458 0 : dynamic_cast<dbe::actions::onobject const*>(undo_stack->command(i))) {
1459 0 : Command->setundoable(*cmdstate++);
1460 : }
1461 : }
1462 : }
1463 0 : };
1464 :
1465 : // Close active editor widgets before replaying changes
1466 0 : for (QWidget* widget : QApplication::allWidgets()) {
1467 0 : if (dynamic_cast<widgets::editors::relation*>(widget)) {
1468 0 : widget->close();
1469 : }
1470 0 : }
1471 :
1472 0 : if (undo_stack->count() != 0) {
1473 0 : const QString msg =
1474 0 : QString("External changes to the database have been applied. Do you want to replay your changes on top? ") +
1475 0 : QString(" Otherwise any local change will be lost.\n");
1476 0 : if (user_confirmation(msg)) {
1477 0 : rewind_stack();
1478 :
1479 : // Empty the internal stack and place the changes in a reverse order in a local stack
1480 0 : confaccessor::t_internal_changes_stack internal_changes_reverse_copy;
1481 0 : auto internal_changes = confaccessor::get_internal_change_stack();
1482 :
1483 0 : while (not internal_changes->empty()) {
1484 0 : internal_changes_reverse_copy.push(internal_changes->top());
1485 0 : internal_changes->pop();
1486 : }
1487 :
1488 : // Replay the commands one by one
1489 0 : for (int i = 0; i < undo_stack->count(); ++i) {
1490 0 : config_internal_change Change = internal_changes_reverse_copy.top();
1491 0 : internal_changes_reverse_copy.pop();
1492 0 : internal_changes->push(Change);
1493 :
1494 0 : try {
1495 :
1496 0 : dbe::actions::onobject const* Command = dynamic_cast<dbe::actions::onobject const*>(undo_stack->command(i));
1497 :
1498 0 : if (not Command->redoable()) {
1499 0 : undo_stack->redo();
1500 : } else {
1501 : // If the object we are trying to make the changes to does not exist it means it was deleted
1502 :
1503 0 : if ((dbe::config::api::info::has_obj(Change.classname, Change.uid) and
1504 0 : Change.request != config_internal_change::CREATED) or
1505 0 : Change.request == config_internal_change::FILE_INCLUDED or
1506 : Change.request == config_internal_change::FILE_DELETED) {
1507 : // If in virtue of external modification the object still exists and our action was not a creation
1508 0 : Command->reload();
1509 0 : undo_stack->redo();
1510 0 : } else if (not dbe::config::api::info::has_obj(Change.classname, Change.uid) and
1511 0 : Change.request == config_internal_change::CREATED) {
1512 : // If the external changes have removed the object and we have created it
1513 0 : undo_stack->redo();
1514 0 : Command->reload();
1515 : } else {
1516 : /// "Emptying" the command so it does nothing at all
1517 0 : Command->setredoable(false);
1518 0 : Command->setundoable(false);
1519 :
1520 : // Advance the stack by redoing an non-redoable (i.e. the redo action has no effect) command
1521 0 : undo_stack->redo();
1522 : }
1523 : }
1524 :
1525 0 : } catch (dunedaq::conffwk::Exception const& e) {
1526 0 : WARN("Object reference could not be changed",
1527 : dbe::config::errors::parse(e).c_str(),
1528 : "for object with UID:",
1529 : Change.uid,
1530 : "of class",
1531 0 : Change.classname);
1532 0 : } catch (...) {
1533 0 : WARN("Unknown exception during object modification",
1534 : "s",
1535 : "\n\nFor object with UID:",
1536 : Change.uid.c_str(),
1537 : "of class:",
1538 0 : Change.classname.c_str());
1539 0 : }
1540 0 : }
1541 0 : } else {
1542 0 : confaccessor::clear_commands();
1543 : }
1544 0 : } else {
1545 0 : INFO("Database reloaded due external changes", "Database consistency enforcement");
1546 0 : confaccessor::clear_commands();
1547 : }
1548 :
1549 0 : slot_tree_reset();
1550 0 : build_file_model();
1551 :
1552 : // Emit the signal for connected listeners (e.g., the object editors)
1553 0 : emit signal_externalchanges_processed();
1554 0 : }
1555 :
1556 : /**
1557 : * Permits to retrieve the main window pointer throughout the application
1558 : *
1559 : * @return a pointer of type MainWindow to the first class of type MainWindow
1560 : */
1561 : dbe::MainWindow*
1562 0 : dbe::MainWindow::findthis()
1563 : {
1564 0 : QWidgetList allwidgets = QApplication::topLevelWidgets();
1565 :
1566 0 : QWidgetList::iterator it = allwidgets.begin();
1567 0 : MainWindow* main_win = qobject_cast<MainWindow*>(*it);
1568 :
1569 0 : for (; it != allwidgets.end() and main_win == nullptr; ++it) {
1570 0 : main_win = qobject_cast<MainWindow*>(*it);
1571 : }
1572 :
1573 0 : return main_win;
1574 0 : }
1575 :
1576 : //-----------------------------------------------------------------------------------------------------------------------------
1577 :
1578 : //-----------------------------------------------------------------------------------------------------------------------------
1579 : /**
1580 : * This method permits to propagate and display messages from the messaging subsytem.
1581 : *
1582 : * It is important that the arguments are pass-by-copy because references will become invalid,
1583 : * even if they are bound to consted temporaries, once the deleter from the other thread is called.
1584 : *
1585 : */
1586 : namespace {
1587 : const int MAX_MESSAGE_LENGTH = 500;
1588 : }
1589 :
1590 : void
1591 0 : dbe::MainWindow::display_message_box(const QString& title, const QString& msg, const QMessageBox::Icon& icon)
1592 : {
1593 0 : QMessageBox mb(this);
1594 0 : mb.setIcon(icon);
1595 0 : mb.setWindowTitle(title);
1596 0 : mb.setStandardButtons(QMessageBox::Ok);
1597 0 : if (msg.length() > MAX_MESSAGE_LENGTH) {
1598 0 : QString&& truncated_msg = msg.left(MAX_MESSAGE_LENGTH);
1599 0 : truncated_msg.append("...");
1600 0 : mb.setText(
1601 : "<b>The message has been truncated because it is too long, look at the details for the full message</b>");
1602 0 : mb.setInformativeText(truncated_msg);
1603 0 : mb.setDetailedText(msg);
1604 0 : } else {
1605 0 : mb.setText(msg);
1606 : }
1607 :
1608 0 : mb.exec();
1609 0 : }
1610 :
1611 : /**
1612 : * This method permits to propagate and display messages from the messaging subsytem.
1613 : *
1614 : * It is important that the arguments are pass-by-copy because references will become invalid,
1615 : * even if they are bound to consted temporaries, once the deleter from the other thread is called.
1616 : *
1617 : */
1618 : void
1619 0 : dbe::MainWindow::slot_information_message(QString const title, QString const msg)
1620 : {
1621 0 : display_message_box(title, msg, QMessageBox::Icon::Information);
1622 0 : }
1623 :
1624 : /**
1625 : * This method permits to propagate and display messages from the messaging subsytem.
1626 : *
1627 : * It is important that the arguments are pass-by-copy because references will become invalid,
1628 : * even if they are bound to consted temporaries, once the deleter from the other thread is called.
1629 : *
1630 : */
1631 : void
1632 0 : dbe::MainWindow::slot_error_message(QString const title, QString const msg)
1633 : {
1634 0 : display_message_box(title, msg, QMessageBox::Icon::Critical);
1635 0 : }
1636 :
1637 : /**
1638 : * This method permits to propagate and display messages from the messaging subsytem.
1639 : *
1640 : * It is important that the arguments are pass-by-copy because references will become invalid,
1641 : * even if they are bound to consted temporaries, once the deleter from the other thread is called.
1642 : *
1643 : */
1644 : void
1645 0 : dbe::MainWindow::slot_warning_message(QString const title, QString const msg)
1646 : {
1647 0 : display_message_box(title, msg, QMessageBox::Icon::Warning);
1648 0 : }
1649 :
1650 : //-----------------------------------------------------------------------------------------------------------------------------
1651 :
1652 : cptr<dbe::CustomTreeView>
1653 0 : dbe::MainWindow::get_view() const
1654 : {
1655 0 : return cptr<CustomTreeView>(TreeView);
1656 : }
1657 :
1658 : void
1659 0 : dbe::MainWindow::slot_batch_change_start()
1660 : {
1661 0 : m_batch_change_in_progress = true;
1662 0 : }
1663 :
1664 : void
1665 0 : dbe::MainWindow::slot_batch_change_stop(const QList<QPair<QString, QString>>& objs)
1666 : {
1667 0 : std::vector<dbe::dref> objects;
1668 0 : for (const auto& o : objs) {
1669 0 : objects.push_back(inner::dbcontroller::get({ o.second.toStdString(), o.first.toStdString() }));
1670 : }
1671 :
1672 : // This allows to not reset the main tree
1673 0 : this_classes->objectsUpdated(objects);
1674 :
1675 : // In this case the corresponding trees are reset
1676 : // In order to apply the same policy as in the class tree
1677 : // the subtree_proxy class needs to be completed with proper
1678 : // implementation of slots when objects are modified
1679 :
1680 : // Proper "refresh" of table tabs
1681 0 : for (int i = 0; i < tableholder->count(); i++) {
1682 0 : TableTab* CurrentTab = dynamic_cast<TableTab*>(tableholder->widget(i));
1683 0 : if (CurrentTab) {
1684 0 : dbe::models::table* m = CurrentTab->GetTableModel();
1685 0 : if (m) {
1686 0 : m->objectsUpdated(objects);
1687 : }
1688 : }
1689 : }
1690 :
1691 0 : emit signal_batch_change_stopped(objs);
1692 :
1693 0 : m_batch_change_in_progress = false;
1694 0 : }
1695 :
1696 : void
1697 0 : dbe::MainWindow::slot_toggle_commit_button()
1698 : {
1699 0 : if (isArchivedConf == false) {
1700 0 : const auto& uncommittedFiles = confaccessor::uncommitted_files();
1701 :
1702 0 : if (uncommittedFiles.empty() == true) {
1703 0 : Commit->setEnabled(false);
1704 0 : Commit->setToolTip("There is nothing to commit");
1705 : } else {
1706 0 : Commit->setEnabled(true);
1707 :
1708 0 : std::string l;
1709 0 : for (const std::string& f : uncommittedFiles) {
1710 0 : l += " " + f + "\n";
1711 : }
1712 :
1713 0 : Commit->setToolTip(QString::fromStdString("Commit changes.\nHere are the uncommitted files:\n" + l));
1714 0 : }
1715 :
1716 0 : build_file_model();
1717 :
1718 0 : } else {
1719 0 : Commit->setEnabled(false);
1720 : }
1721 0 : }
1722 :
1723 : void
1724 0 : dbe::MainWindow::slot_update_committed_files(const std::list<std::string>& files, const std::string& msg)
1725 : {
1726 0 : for (const std::string& f : files) {
1727 0 : CommittedTable->insertRow(0);
1728 0 : CommittedTable->setItem(0, 0, new QTableWidgetItem(QString::fromStdString(f)));
1729 0 : CommittedTable->setItem(0, 1, new QTableWidgetItem(QString::fromStdString(msg)));
1730 0 : CommittedTable->setItem(
1731 0 : 0, 2, new QTableWidgetItem(QDate::currentDate().toString() + " " + QTime::currentTime().toString()));
1732 : }
1733 :
1734 0 : CommittedTable->resizeColumnsToContents();
1735 0 : }
1736 :
1737 : bool
1738 0 : dbe::MainWindow::check_ready() const
1739 : {
1740 0 : return not m_batch_change_in_progress;
1741 : }
1742 :
1743 : void
1744 0 : dbe::MainWindow::slot_loaded_db_file(QString file)
1745 : {
1746 0 : allFiles.insert(file);
1747 0 : }
1748 :
1749 : void
1750 0 : dbe::MainWindow::slot_launch_preferences()
1751 : {
1752 0 : auto prefs = new Preferences();
1753 0 : prefs->show();
1754 0 : }
|