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