Line data Source code
1 : #include "dbe/FileInfo.hpp"
2 : #include "dbe/MainWindow.hpp"
3 : #include "dbe/ObjectEditor.hpp"
4 : #include "dbe/StyleUtility.hpp"
5 : #include "dbe/confaccessor.hpp"
6 : #include "dbe/config_api.hpp"
7 : #include "dbe/config_api_commands.hpp"
8 :
9 : #include "ui_FileInfo.h"
10 :
11 : #include <QFileDialog>
12 : #include <QInputDialog>
13 : #include <QListWidgetItem>
14 : #include <QMessageBox>
15 : #include <QScopedValueRollback>
16 : #include <QString>
17 : #include <QWidget>
18 :
19 : namespace dbegraph = dbe::config::api::graph;
20 :
21 : namespace dbe {
22 :
23 : QString FileInfo::s_schema_path{ "." };
24 : QString FileInfo::s_data_path{ "." };
25 : QStringList FileInfo::s_path_list{};
26 : QList<QUrl> FileInfo::s_path_urls{};
27 : std::map<QString, std::map<QString, const tref>> FileInfo::s_obj_map{};
28 : std::map<QString, std::set<QString>> FileInfo::s_missing_schema_map{};
29 : std::map<QString, std::set<QString>> FileInfo::s_missing_data_map{};
30 :
31 : void
32 0 : FileInfo::setup_paths()
33 : {
34 0 : QString DUNEDAQ_DB_PATH = getenv("DUNEDAQ_DB_PATH");
35 0 : s_path_list.clear();
36 0 : s_path_urls.clear();
37 0 : auto path_list = DUNEDAQ_DB_PATH.split(QLatin1Char(':'), Qt::SkipEmptyParts);
38 0 : for (QString& path : path_list) {
39 0 : char* rpath = realpath(path.toStdString().c_str(), NULL);
40 0 : path = QString(rpath);
41 0 : free(rpath);
42 :
43 0 : if (!path.isEmpty()) {
44 0 : if (!path.endsWith("/")) {
45 0 : path.append("/");
46 : }
47 0 : s_path_urls.append(QUrl::fromLocalFile(path));
48 0 : s_path_list.append(path);
49 : }
50 : }
51 0 : auto dbpath = confaccessor::dbfullname();
52 0 : dbpath.truncate(dbpath.lastIndexOf("/") + 1);
53 0 : if (!s_path_list.contains(dbpath)) {
54 0 : s_path_urls.append(QUrl::fromLocalFile(dbpath));
55 0 : s_path_list.append(dbpath);
56 : }
57 0 : char* rpath_cd = realpath(".", NULL);
58 0 : auto path_cd = QString(rpath_cd);
59 0 : free(rpath_cd);
60 0 : if (!s_path_list.contains(path_cd)) {
61 0 : s_path_urls.append(QUrl::fromLocalFile(path_cd));
62 : }
63 0 : }
64 :
65 : QString
66 0 : FileInfo::prune_path(QString file)
67 : {
68 0 : if (s_path_list.isEmpty()) {
69 0 : setup_paths();
70 : }
71 0 : for (const QString& element : s_path_list) {
72 0 : if (file.startsWith(element)) {
73 0 : file.remove(element);
74 : break;
75 : }
76 : }
77 0 : return file;
78 : }
79 :
80 : bool
81 0 : FileInfo::match_path(const QString& file, const QString& top_file, const QStringList& includes)
82 : {
83 0 : if (top_file.endsWith(file)) {
84 : return true;
85 : }
86 :
87 0 : if (s_path_list.isEmpty()) {
88 0 : setup_paths();
89 : }
90 :
91 0 : QStringList candidates{ file };
92 0 : for (const auto& element : s_path_list) {
93 0 : if (file.startsWith(element)) {
94 0 : auto short_name = file;
95 0 : candidates.append(short_name.remove(element));
96 0 : }
97 : }
98 :
99 0 : for (auto cand : candidates) {
100 0 : if (includes.contains(cand)) {
101 0 : return true;
102 : }
103 0 : }
104 0 : return false;
105 0 : }
106 :
107 : QList<QUrl>
108 0 : FileInfo::get_path_urls()
109 : {
110 0 : if (s_path_urls.isEmpty()) {
111 0 : setup_paths();
112 : }
113 0 : return s_path_urls;
114 : }
115 : QStringList
116 0 : FileInfo::get_path_list()
117 : {
118 0 : if (s_path_list.isEmpty()) {
119 0 : setup_paths();
120 : }
121 0 : return s_path_list;
122 : }
123 :
124 : void
125 0 : FileInfo::parse_all_objects()
126 : {
127 0 : s_obj_map.clear();
128 0 : for (auto const& class_name : config::api::info::onclass::allnames<std::vector<std::string>>()) {
129 0 : for (auto obj : config::api::info::onclass::objects(class_name, false)) {
130 0 : auto file = prune_path(QString::fromStdString(obj.contained_in()));
131 0 : auto name = QString::fromStdString(obj.full_name());
132 0 : if (!s_obj_map.contains(file)) {
133 0 : s_obj_map.insert({ file, {} });
134 0 : s_missing_schema_map.insert({ file, {} });
135 0 : s_missing_data_map.insert({ file, {} });
136 : }
137 0 : s_obj_map.at(file).insert({ name, obj });
138 0 : }
139 0 : }
140 0 : }
141 :
142 : QString
143 0 : FileInfo::check_file_includes(const QString& filename)
144 : {
145 0 : QString message{};
146 :
147 0 : auto fname = prune_path(filename);
148 0 : if (!s_missing_schema_map.contains(fname)) {
149 0 : s_missing_schema_map.insert({ fname, {} });
150 : } else {
151 0 : s_missing_schema_map.at(fname).clear();
152 : }
153 0 : if (!s_missing_data_map.contains(fname)) {
154 0 : s_missing_data_map.insert({ fname, {} });
155 : } else {
156 0 : s_missing_data_map.at(fname).clear();
157 : }
158 0 : QStringList includes(config::api::get::file::inclusions_singlefile(filename));
159 0 : if (s_obj_map.contains(prune_path(filename))) {
160 0 : for (auto [id, obj] : s_obj_map.at(prune_path(filename))) {
161 0 : dunedaq::conffwk::class_t const& classdef = dbe::config::api::info::onclass::definition(obj.class_name(), false);
162 :
163 0 : auto schema_file = QString::fromStdString(classdef.p_schema_path);
164 0 : if (!match_path(schema_file, filename, includes)) {
165 0 : message += QString("Object <i>" + id + "</i> is of class <i>" + QString::fromStdString(obj.class_name()) +
166 0 : "</i> defined in file <b>" + schema_file + "</b> which is not included<br>");
167 0 : s_missing_schema_map.at(fname).insert(prune_path(schema_file));
168 : }
169 0 : std::vector<tref> relobjs;
170 0 : for (auto rel : classdef.p_relationships) {
171 0 : if (config::api::info::relation::is_simple(rel)) {
172 0 : try {
173 0 : relobjs.push_back(dbegraph::linked::through::relation<tref>(obj, rel));
174 0 : } catch (daq::dbe::config_object_retrieval_result_is_null const& e) {
175 : // nothing needs be done to handle cases that a relation has not been set
176 0 : }
177 : } else {
178 0 : relobjs = dbegraph::linked::through::relation<std::vector<tref>>(obj, rel);
179 : }
180 :
181 0 : for (auto relobj : relobjs) {
182 0 : auto file = QString::fromStdString(relobj.contained_in());
183 0 : if (!(match_path(file, filename, includes))) {
184 0 : message +=
185 0 : QString("Object <i>" + id + "</i> has relationship to <i>" + QString::fromStdString(relobj.full_name()) +
186 0 : "</i> in file <b>" + file + "</b> which is not included<br>");
187 0 : s_missing_data_map.at(fname).insert(prune_path(file));
188 : }
189 0 : }
190 0 : }
191 0 : }
192 : }
193 0 : return message;
194 0 : }
195 :
196 0 : FileInfo::FileInfo(QString filename, QWidget* /*parent*/)
197 0 : : m_ui(new Ui::FileInfo)
198 0 : , m_filename(filename)
199 0 : , m_uuid(QUuid::createUuid())
200 : {
201 :
202 0 : QWidget::setAttribute(Qt::WA_DeleteOnClose);
203 :
204 0 : m_ui->setupUi(this);
205 0 : m_ui->filename->setText(filename);
206 0 : if (confaccessor::check_file_rw(filename)) {
207 0 : m_ui->rstatus->setText(QString("RW"));
208 0 : m_readonly = false;
209 : } else {
210 0 : m_ui->rstatus->setText(QString("RO"));
211 0 : m_readonly = true;
212 : }
213 :
214 0 : setObjectName(filename);
215 0 : setWindowTitle("File: " + filename.section('/', -1));
216 :
217 0 : setup_paths();
218 0 : parse_includes();
219 0 : parse_objects();
220 :
221 0 : m_ui->schema_list->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
222 0 : m_ui->data_list->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
223 0 : m_ui->object_list->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
224 :
225 0 : connect(m_ui->add_schema, SIGNAL(pressed()), this, SLOT(add_schemafile()));
226 0 : connect(m_ui->add_data, SIGNAL(pressed()), this, SLOT(add_datafile()));
227 :
228 0 : connect(m_ui->add_missing_schema, SIGNAL(pressed()), this, SLOT(add_missing_schemafiles()));
229 0 : connect(m_ui->add_missing, SIGNAL(pressed()), this, SLOT(add_missing_datafiles()));
230 :
231 0 : connect(
232 0 : m_ui->schema_list, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(activate_schema_context_menu(QPoint)));
233 :
234 0 : connect(m_ui->data_list, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(activate_data_context_menu(QPoint)));
235 0 : connect(m_ui->data_list, SIGNAL(itemActivated(QListWidgetItem*)), this, SLOT(file_info_slot(QListWidgetItem*)));
236 :
237 0 : connect(m_ui->object_list, SIGNAL(itemActivated(QListWidgetItem*)), this, SLOT(edit_object_slot(QListWidgetItem*)));
238 0 : connect(
239 0 : m_ui->object_list, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(activate_object_context_menu(QPoint)));
240 :
241 0 : connect(MainWindow::findthis(), SIGNAL(signal_new_file_model()), this, SLOT(filemodel_updated()));
242 0 : }
243 :
244 : void
245 0 : FileInfo::filemodel_updated()
246 : {
247 0 : if (m_updating) {
248 : return;
249 : }
250 0 : parse_includes();
251 0 : parse_objects();
252 : }
253 :
254 : void
255 0 : FileInfo::parse_objects()
256 : {
257 0 : parse_all_objects();
258 0 : m_ui->object_list->clear();
259 0 : auto fname = prune_path(m_filename);
260 0 : if (s_obj_map.contains(fname)) {
261 0 : auto& omap = s_obj_map.at(fname);
262 0 : for (auto const& [obj_name, obj_ref] : omap) {
263 0 : auto item = new QListWidgetItem(obj_name);
264 0 : m_ui->object_list->addItem(item);
265 : }
266 : }
267 0 : m_ui->object_list->update();
268 :
269 0 : auto status = check_includes();
270 0 : if (!s_missing_schema_map.contains(fname)) {
271 0 : s_missing_schema_map.insert({ fname, {} });
272 : }
273 0 : if (!s_missing_data_map.contains(fname)) {
274 0 : s_missing_data_map.insert({ fname, {} });
275 : }
276 0 : m_ui->add_missing_schema->setVisible(!s_missing_schema_map.at(fname).empty());
277 0 : m_ui->add_missing->setVisible(!s_missing_data_map.at(fname).empty());
278 :
279 0 : m_ui->warningBox->setVisible(!status);
280 0 : }
281 :
282 : bool
283 0 : FileInfo::check_includes()
284 : {
285 0 : QString message = check_file_includes(m_filename);
286 0 : m_ui->message->setText(message);
287 0 : return message.isEmpty();
288 0 : }
289 :
290 : void
291 0 : FileInfo::parse_includes()
292 : {
293 0 : QStringList includes(config::api::get::file::inclusions_singlefile(m_filename));
294 0 : m_ui->schema_list->clear();
295 0 : m_ui->data_list->clear();
296 0 : for (auto inc : includes) {
297 0 : auto item = new QListWidgetItem(inc);
298 0 : if (!confaccessor::check_file_rw(inc)) {
299 0 : item->setForeground(QBrush(StyleUtility::FileReadOnlyForeground));
300 0 : item->setBackground(QBrush(StyleUtility::FileReadOnlyBackground));
301 : }
302 0 : if (inc.endsWith(".schema.xml")) {
303 0 : m_ui->schema_list->addItem(item);
304 : }
305 0 : if (inc.endsWith(".data.xml")) {
306 0 : m_ui->data_list->addItem(item);
307 : }
308 0 : }
309 0 : m_ui->schema_list->update();
310 0 : m_ui->data_list->update();
311 0 : }
312 :
313 : void
314 0 : FileInfo::keyPressEvent(QKeyEvent* event)
315 : {
316 0 : if (event->key() == Qt::Key_Escape) {
317 0 : close();
318 : }
319 0 : QWidget::keyPressEvent(event);
320 0 : }
321 :
322 : void
323 0 : FileInfo::accept()
324 : {
325 0 : close();
326 0 : }
327 : void
328 0 : FileInfo::reject()
329 : {
330 0 : close();
331 0 : }
332 :
333 : void
334 0 : FileInfo::edit_object_slot()
335 : {
336 0 : auto item = m_ui->object_list->currentItem();
337 0 : edit_object_slot(item);
338 0 : }
339 :
340 : void
341 0 : FileInfo::edit_object_slot(QListWidgetItem* item)
342 : {
343 0 : auto name = item->text();
344 0 : if (s_obj_map.at(prune_path(m_filename)).contains(name)) {
345 0 : for (QWidget* widget : QApplication::allWidgets()) {
346 0 : auto oe = dynamic_cast<ObjectEditor*>(widget);
347 0 : if (oe != nullptr) {
348 0 : if (oe->objectName() == name) {
349 0 : oe->raise();
350 0 : oe->setVisible(true);
351 0 : oe->activateWindow();
352 0 : return;
353 : }
354 : }
355 0 : }
356 :
357 0 : auto obj = s_obj_map.at(prune_path(m_filename)).at(name);
358 0 : auto oe = new ObjectEditor(obj);
359 0 : oe->show();
360 0 : }
361 0 : }
362 :
363 : void
364 0 : FileInfo::delete_object_slot()
365 : {
366 0 : auto name = m_ui->object_list->currentItem()->text();
367 0 : auto file = prune_path(m_filename);
368 0 : if (s_obj_map.at(file).contains(name)) {
369 0 : config::api::commands::delobj(s_obj_map.at(file).at(name), m_uuid);
370 : }
371 0 : parse_objects();
372 0 : }
373 :
374 : void
375 0 : FileInfo::rename_object_slot()
376 : {
377 0 : auto name = m_ui->object_list->currentItem()->text();
378 0 : auto file = prune_path(m_filename);
379 0 : if (s_obj_map.at(file).contains(name)) {
380 0 : QInputDialog dia(this);
381 0 : dia.setLabelText("Enter new name for " + name);
382 0 : auto code = dia.exec();
383 0 : if (code == QDialog::Accepted) {
384 0 : std::string new_name = dia.textValue().toStdString();
385 0 : config::api::commands::renobj(s_obj_map.at(file).at(name), new_name, m_uuid);
386 0 : }
387 0 : }
388 0 : parse_objects();
389 0 : }
390 :
391 : void
392 0 : FileInfo::add_datafile()
393 : {
394 0 : if (s_data_path == ".") {
395 0 : s_data_path = s_schema_path;
396 : }
397 0 : auto fd = new QFileDialog(this, tr("Open Data File"), s_data_path, tr("XML data files (*.data.xml)"));
398 0 : add_includefile(fd);
399 0 : if (fd->result() == QDialog::Accepted) {
400 0 : s_data_path = fd->directory().path();
401 : }
402 0 : }
403 : void
404 0 : FileInfo::add_schemafile()
405 : {
406 0 : if (s_schema_path == ".") {
407 0 : s_schema_path = s_data_path;
408 : }
409 0 : auto fd = new QFileDialog(this, tr("Open Schema File"), s_schema_path, tr("XML schema files (*.schema.xml)"));
410 0 : add_includefile(fd);
411 0 : if (fd->result() == QDialog::Accepted) {
412 0 : s_schema_path = fd->directory().path();
413 : }
414 0 : }
415 : void
416 0 : FileInfo::add_includefile(QFileDialog* fd)
417 : {
418 0 : fd->setFileMode(QFileDialog::ExistingFiles);
419 0 : fd->setViewMode(QFileDialog::Detail);
420 0 : fd->setAcceptMode(QFileDialog::AcceptOpen);
421 0 : fd->setSidebarUrls(s_path_urls);
422 0 : if (fd->exec() == QDialog::Accepted) {
423 0 : auto files = fd->selectedFiles();
424 0 : QScopedValueRollback<bool> rb(m_updating, true);
425 0 : for (auto file : files) {
426 0 : file = prune_path(file);
427 0 : config::api::commands::file::add(m_filename, file);
428 0 : }
429 0 : parse_includes();
430 0 : parse_objects();
431 0 : }
432 0 : }
433 :
434 : void
435 0 : FileInfo::add_missing_schemafiles()
436 : {
437 0 : QScopedValueRollback<bool> rb(m_updating, true);
438 0 : auto short_filename = prune_path(m_filename);
439 0 : if (!s_missing_schema_map.contains(short_filename)) {
440 0 : QMessageBox::warning(
441 0 : this, "Warning", QString("Missing schema map is corrupt and does not contain %1").arg(short_filename));
442 0 : return;
443 : }
444 0 : for (const auto& file : s_missing_schema_map.at(short_filename)) {
445 0 : config::api::commands::file::add(m_filename, file);
446 : }
447 0 : parse_includes();
448 0 : parse_objects();
449 0 : }
450 :
451 : void
452 0 : FileInfo::add_missing_datafiles()
453 : {
454 0 : QScopedValueRollback<bool> rb(m_updating, true);
455 0 : auto short_filename = prune_path(m_filename);
456 0 : if (!s_missing_data_map.contains(short_filename)) {
457 0 : QMessageBox::warning(
458 0 : this, "Warning", QString("Missing data map is corrupt and does not contain %1").arg(short_filename));
459 0 : return;
460 : }
461 0 : for (const auto& file : s_missing_data_map.at(prune_path(m_filename))) {
462 0 : config::api::commands::file::add(m_filename, file);
463 : }
464 0 : parse_includes();
465 0 : parse_objects();
466 0 : }
467 :
468 : void
469 0 : FileInfo::remove_schemafile_slot()
470 : {
471 0 : remove_includefile(m_ui->schema_list->currentItem()->text());
472 0 : }
473 : void
474 0 : FileInfo::remove_datafile_slot()
475 : {
476 0 : remove_includefile(m_ui->data_list->currentItem()->text());
477 0 : }
478 : void
479 0 : FileInfo::remove_includefile(const QString& file)
480 : {
481 0 : config::api::commands::file::remove(m_filename, file);
482 0 : parse_includes();
483 0 : parse_objects();
484 0 : }
485 :
486 : void
487 0 : FileInfo::file_info_slot()
488 : {
489 0 : show_file_info(m_ui->data_list->currentItem()->text());
490 0 : }
491 :
492 : void
493 0 : FileInfo::file_info_slot(QListWidgetItem* item)
494 : {
495 0 : show_file_info(item->text());
496 0 : }
497 :
498 : void
499 0 : FileInfo::file_info_slot(const QString& filename)
500 : {
501 0 : show_file_info(filename);
502 0 : }
503 :
504 : void
505 0 : FileInfo::show_file_info(const QString& filename)
506 : {
507 0 : for (QWidget* widget : QApplication::allWidgets()) {
508 0 : auto fi = dynamic_cast<FileInfo*>(widget);
509 0 : if (fi != nullptr) {
510 0 : if (fi->objectName() == filename) {
511 0 : fi->raise();
512 0 : fi->setVisible(true);
513 0 : fi->activateWindow();
514 0 : return;
515 : }
516 : }
517 0 : }
518 :
519 0 : auto fi = new FileInfo(filename);
520 0 : fi->show();
521 : }
522 :
523 : void
524 0 : FileInfo::activate_schema_context_menu(QPoint pos)
525 : {
526 0 : if (m_schema_menu == nullptr) {
527 0 : m_schema_menu = new QMenu(this);
528 :
529 0 : if (!m_readonly) {
530 0 : auto add = new QAction("Add include file", this);
531 0 : connect(add, SIGNAL(triggered()), this, SLOT(add_schemafile()));
532 0 : m_schema_menu->addAction(add);
533 :
534 0 : auto remove = new QAction("Remove include file", this);
535 0 : connect(remove, SIGNAL(triggered()), this, SLOT(remove_schemafile_slot()));
536 0 : m_schema_menu->addAction(remove);
537 : }
538 : }
539 :
540 0 : if (m_ui->schema_list->currentIndex().isValid()) {
541 0 : m_schema_menu->actions().at(1)->setVisible(true);
542 : }
543 0 : m_schema_menu->exec(m_ui->schema_list->mapToGlobal(pos));
544 0 : }
545 :
546 : void
547 0 : FileInfo::activate_data_context_menu(QPoint pos)
548 : {
549 0 : if (m_data_menu == nullptr) {
550 0 : m_data_menu = new QMenu(this);
551 :
552 0 : auto info = new QAction(tr("Show file info"), this);
553 0 : connect(info, SIGNAL(triggered()), this, SLOT(file_info_slot()));
554 0 : m_data_menu->addAction(info);
555 :
556 0 : if (!m_readonly) {
557 0 : auto add = new QAction("Add include file", this);
558 0 : connect(add, SIGNAL(triggered()), this, SLOT(add_datafile()));
559 0 : m_data_menu->addAction(add);
560 :
561 0 : auto remove = new QAction("Remove include file", this);
562 0 : connect(remove, SIGNAL(triggered()), this, SLOT(remove_datafile_slot()));
563 0 : m_data_menu->addAction(remove);
564 : }
565 : }
566 :
567 0 : if (m_ui->data_list->currentIndex().isValid()) {
568 0 : m_data_menu->actions().at(0)->setVisible(true);
569 0 : m_data_menu->actions().at(2)->setVisible(true);
570 : } else {
571 0 : m_data_menu->actions().at(0)->setVisible(false);
572 0 : m_data_menu->actions().at(2)->setVisible(false);
573 : }
574 0 : m_data_menu->exec(m_ui->data_list->mapToGlobal(pos));
575 0 : }
576 :
577 : void
578 0 : FileInfo::activate_object_context_menu(QPoint pos)
579 : {
580 0 : if (m_object_menu == nullptr) {
581 0 : m_object_menu = new QMenu(this);
582 :
583 0 : auto edit_action = new QAction(tr("&Edit object"), this);
584 0 : connect(edit_action, SIGNAL(triggered()), this, SLOT(edit_object_slot()));
585 0 : m_object_menu->addAction(edit_action);
586 :
587 0 : auto delete_action = new QAction(tr("&Delete Object"), this);
588 0 : connect(delete_action, SIGNAL(triggered()), this, SLOT(delete_object_slot()));
589 0 : m_object_menu->addAction(delete_action);
590 :
591 0 : auto rename_action = new QAction(tr("&Rename Object"), this);
592 0 : connect(rename_action, SIGNAL(triggered()), this, SLOT(rename_object_slot()));
593 0 : m_object_menu->addAction(rename_action);
594 : }
595 :
596 0 : if (m_ui->object_list->currentIndex().isValid()) {
597 0 : m_object_menu->exec(m_ui->object_list->mapToGlobal(pos));
598 : }
599 0 : }
600 :
601 : } // namespace dbe
|