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