1 /**
2  * UGENE - Integrated Bioinformatics Tools.
3  * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4  * http://ugene.net
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  */
21 
22 #include "TaxonomySupport.h"
23 
24 #include <QAbstractItemModel>
25 #include <QFileInfo>
26 #include <QPushButton>
27 #include <QVBoxLayout>
28 #include <QtCore/QVariant>
29 #include <QtWidgets/QAction>
30 #include <QtWidgets/QApplication>
31 #include <QtWidgets/QButtonGroup>
32 #include <QtWidgets/QDialog>
33 #include <QtWidgets/QHeaderView>
34 #include <QtWidgets/QWidget>
35 
36 #include <U2Core/AppContext.h>
37 #include <U2Core/AppResources.h>
38 #include <U2Core/AppSettings.h>
39 #include <U2Core/BaseDocumentFormats.h>
40 #include <U2Core/Counter.h>
41 #include <U2Core/DataPathRegistry.h>
42 #include <U2Core/DocumentImport.h>
43 #include <U2Core/DocumentModel.h>
44 #include <U2Core/DocumentUtils.h>
45 #include <U2Core/FailTask.h>
46 #include <U2Core/FileAndDirectoryUtils.h>
47 #include <U2Core/GObject.h>
48 #include <U2Core/GObjectTypes.h>
49 #include <U2Core/GUrlUtils.h>
50 #include <U2Core/IOAdapter.h>
51 #include <U2Core/IOAdapterUtils.h>
52 #include <U2Core/L10n.h>
53 #include <U2Core/QObjectScopedPointer.h>
54 #include <U2Core/TaskSignalMapper.h>
55 #include <U2Core/Timer.h>
56 #include <U2Core/U2OpStatusUtils.h>
57 #include <U2Core/U2SafePoints.h>
58 
59 #include <U2Designer/DelegateEditors.h>
60 
61 #include <U2Formats/BAMUtils.h>
62 #include <U2Formats/FastaFormat.h>
63 #include <U2Formats/FastqFormat.h>
64 
65 #include <U2Gui/HelpButton.h>
66 
67 #include <U2Lang/WorkflowEnv.h>
68 
69 namespace U2 {
70 namespace LocalWorkflow {
71 
72 const QString TaxonomySupport::TAXONOMY_CLASSIFICATION_SLOT_ID("tax-data");
73 static const QString CLASSIFICATION_SLOT_TYPE_ID("tax-classification");
74 
75 const TaxID TaxonomyTree::UNDEFINED_ID = (TaxID)-1;
76 const TaxID TaxonomyTree::UNCLASSIFIED_ID = 0;
77 
78 TaxonomyTree *TaxonomyTree::the_tree = nullptr;
79 const int RANK_SHIFT = (sizeof(TaxID) - sizeof(char)) * 8;
80 const TaxID RANK_MASK = 0xFF << RANK_SHIFT;
81 
82 /********************************************************************/
83 /*TaxonomySupport*/
84 /********************************************************************/
85 
TAXONOMY_CLASSIFICATION_SLOT()86 const Descriptor TaxonomySupport::TAXONOMY_CLASSIFICATION_SLOT() {
87     return Descriptor(TAXONOMY_CLASSIFICATION_SLOT_ID, tr("Taxonomy classification data"), tr("Taxonomy classification data"));
88 }
89 
TAXONOMY_CLASSIFICATION_TYPE()90 DataTypePtr TaxonomySupport::TAXONOMY_CLASSIFICATION_TYPE() {
91     DataTypeRegistry *dtr = Workflow::WorkflowEnv::getDataTypeRegistry();
92     assert(dtr);
93     static bool startup = true;
94     if (startup) {
95         dtr->registerEntry(DataTypePtr(new DataType(CLASSIFICATION_SLOT_TYPE_ID, tr("Taxonomy classification data"), tr("Taxonomy classification data"))));
96         startup = false;
97     }
98     return dtr->getById(CLASSIFICATION_SLOT_TYPE_ID);
99 }
100 
getInstance()101 TaxonomyTree *TaxonomyTree::getInstance() {
102     if (the_tree == nullptr) {
103         // fixme data race???
104         the_tree = load(new TaxonomyTree);
105     }
106     return the_tree;
107 }
108 
getName(TaxID id) const109 QString TaxonomyTree::getName(TaxID id) const {
110     if (unsigned(names.size()) > id) {
111         return names.at(id);
112     }
113     algoLog.info(QString("Unknown taxon ID requested: %1").arg(id));
114     return QString("Unknown taxon ID");
115 }
116 
getRank(TaxID id) const117 QString TaxonomyTree::getRank(TaxID id) const {
118     if (unsigned(nodes.size()) > id) {
119         return ranks.at((nodes.at(id) & RANK_MASK) >> RANK_SHIFT);
120     }
121     algoLog.info(QString("Unknown taxon ID requested: %1").arg(id));
122     return QString("Unknown taxon ID");
123 }
124 
getParent(TaxID id) const125 TaxID TaxonomyTree::getParent(TaxID id) const {
126     if (unsigned(nodes.size()) > id) {
127         return nodes.at(id) & ~RANK_MASK;
128     }
129     algoLog.info(QString("Unknown taxon ID requested: %1").arg(id));
130     return UNDEFINED_ID;
131 }
132 
getChildren(TaxID id) const133 QList<TaxID> TaxonomyTree::getChildren(TaxID id) const {
134     return childs.values(id);
135 }
136 
getNamesListSize() const137 int TaxonomyTree::getNamesListSize() const {
138     return names.size();
139 }
140 
contains(TaxID id) const141 bool TaxonomyTree::contains(TaxID id) const {
142     return unsigned(nodes.size()) > id;
143 }
144 
isValid() const145 bool TaxonomyTree::isValid() const {
146     return valid;
147 }
148 
match(TaxID id,QSet<TaxID> filter)149 TaxID TaxonomyTree::match(TaxID id, QSet<TaxID> filter) {
150     // first try fastpath
151     if (id >= unsigned(nodes.size())) {
152         return UNDEFINED_ID;
153     }
154     if (filter.contains(id)) {
155         return id;
156     }
157     // then go searching by hierarchy
158     QList<TaxID> parents;
159     {
160         TaxID parent = id;
161         while (parent > 1) {
162             if (unsigned(nodes.size()) > parent) {
163                 parents << parent;
164                 parent = getParent(parent);
165             } else {
166                 algoLog.error(TaxonomySupport::tr("Broken taxonomy tree: %1").arg(id));
167                 break;
168             }
169         }
170     }
171     for (const TaxID &parent : qAsConst(parents)) {
172         if (filter.contains(parent)) {
173             return parent;
174         }
175     }
176     return UNDEFINED_ID;
177 }
178 
TaxonomyTree()179 TaxonomyTree::TaxonomyTree()
180     : valid(false) {
181 }
182 
183 class TaxonNameComparator {
184 public:
TaxonNameComparator(TaxonomyTree * tree)185     TaxonNameComparator(TaxonomyTree *tree)
186         : tree(tree) {
187     }
operator ()(const TaxID left,const TaxID right) const188     bool operator()(const TaxID left, const TaxID right) const {
189         return tree->getName(left).compare(tree->getName(right));
190     }
191 
192 private:
193     TaxonomyTree *tree;
194 };
195 
load(TaxonomyTree * tree)196 TaxonomyTree *TaxonomyTree::load(TaxonomyTree *tree) {
197     U2DataPathRegistry *dataPathRegistry = AppContext::getDataPathRegistry();
198     SAFE_POINT(nullptr != dataPathRegistry, "U2DataPathRegistry is NULL", tree);
199 
200     U2DataPath *taxonomyDataPath = dataPathRegistry->getDataPathByName(NgsReadsClassificationPlugin::TAXONOMY_DATA_ID);
201     CHECK_EXT(nullptr != taxonomyDataPath && taxonomyDataPath->isValid(), algoLog.details(QString("Taxonomy data are not configured")), tree);
202 
203     bool hasError = false;
204 
205     QString nodesUrl = taxonomyDataPath->getPathByName(NgsReadsClassificationPlugin::TAXON_NODES_ITEM_ID);
206     QFile nodesFile(nodesUrl);
207     if (!nodesFile.open(QIODevice::ReadOnly)) {
208         algoLog.error(QString("Cannot open taxonomy classification data: %1").arg(nodesUrl.isEmpty() ? NgsReadsClassificationPlugin::TAXON_NODES_ITEM_ID : nodesUrl));
209         hasError = true;
210     } else {
211         GTIMER(cvar, tvar, "TaxonomyTree::nodes");
212         QList<TaxID> &nodes = tree->nodes;
213         nodes.reserve(2000000);
214         QByteArray line;
215         while ((line = nodesFile.readLine()).size() != 0) {
216             QList<QByteArray> row = line.split('|');
217             if (row.size() > 3) {
218                 bool ok = true;
219                 TaxID taxID = row[0].trimmed().toUInt(&ok);
220                 if (ok) {
221                     TaxID parentID = row[1].trimmed().toUInt(&ok);
222                     if (ok) {
223                         if (unsigned(nodes.size()) <= taxID) {
224                             nodes.reserve(taxID + 1000);
225                         }
226                         while (unsigned(nodes.size()) <= taxID) {
227                             nodes.append(0);
228                         }
229 
230                         QString rank = row[2].trimmed();
231                         int rankID = tree->ranks.indexOf(rank);
232                         if (rankID < 0) {
233                             rankID = tree->ranks.size();
234                             tree->ranks << rank;
235                             assert(tree->ranks.size() < 0xFF);
236                             assert(rankID == tree->ranks.indexOf(rank));
237                         }
238 
239                         // Hack to skip "cellular organisms" node, "to follow NCBI taxonomy browser"
240                         const TaxID CELL_ORGMS_ID = 131567;
241                         if (parentID == CELL_ORGMS_ID) {
242                             parentID = 1;
243                         }
244                         if (taxID == CELL_ORGMS_ID) {
245                             assert(rank == "no rank");
246                             assert(parentID == 1);
247                         }
248 
249                         assert(parentID == (parentID & ~RANK_MASK));
250                         nodes[taxID] = parentID | (rankID << RANK_SHIFT);
251                         assert(tree->getParent(taxID) == parentID);
252                         assert(tree->getRank(taxID) == rank);
253 
254                         if (taxID != 1 && taxID != CELL_ORGMS_ID) {
255                             tree->childs.insert(parentID, taxID);
256                         }
257                         continue;
258                     }
259                 }
260             }
261             algoLog.error(QString("Broken nodes.dmp file : %1").arg(nodesUrl));
262             hasError = true;
263             break;
264         }
265         nodesFile.close();
266     }
267 
268     QString namesUrl = taxonomyDataPath->getPathByName(NgsReadsClassificationPlugin::TAXON_NAMES_ITEM_ID);
269     QFile namesFile(namesUrl);
270     if (!namesFile.open(QIODevice::ReadOnly)) {
271         algoLog.error(QString("Cannot open taxonomy classification data: %1").arg(namesUrl.isEmpty() ? NgsReadsClassificationPlugin::TAXON_NAMES_ITEM_ID : namesUrl));
272         hasError = true;
273     } else {
274         GTIMER(cvar, tvar, "TaxonomyTree::names");
275         QStringList &names = tree->names;
276         QByteArray line;
277         names.reserve(2000000);
278         while ((line = namesFile.readLine()).size() != 0) {
279             const QList<QByteArray> row = line.split('|');
280             bool ok = true;
281             if (row.size() > 3) {
282                 if (row[3].trimmed() == "scientific name") {
283                     TaxID taxID = row[0].trimmed().toUInt(&ok);
284                     if (ok) {
285                         QByteArray name = row[1].trimmed();
286                         if (unsigned(names.size()) <= taxID) {
287                             names.reserve(taxID + 1000);
288                         }
289                         while (unsigned(names.size()) <= taxID) {
290                             names.append(QString());
291                         }
292                         if (!names.at(taxID).isEmpty()) {
293                             algoLog.error(QString("Non-unique scientific name for taxon ID : %1").arg(taxID));
294                         }
295                         names[taxID] = name;
296                     }
297                 }
298             } else {
299                 ok = false;
300             }
301             if (!ok) {
302                 algoLog.error(QString("Broken names.dmp file : %1").arg(namesUrl));
303                 hasError = true;
304                 break;
305             }
306         }
307         namesFile.close();
308     }
309     tree->valid = !hasError;
310     return tree;
311 }
312 
313 ////////////////////////// WD GUI ////////////////////////
314 
315 class TreeItem;
316 
317 /********************************************************************/
318 /*TaxonomyTreeModel*/
319 /********************************************************************/
320 
321 namespace {
322 
taxIdLessThan(const TaxID a,const TaxID b)323 bool taxIdLessThan(const TaxID a, const TaxID b) {
324     return TaxonomyTree::getInstance()->getName(a) < TaxonomyTree::getInstance()->getName(b);
325 }
326 
327 }  // namespace
328 
TaxonomyTreeModel(const QString & data,QObject * parent)329 TaxonomyTreeModel::TaxonomyTreeModel(const QString &data, QObject *parent)
330     : QAbstractItemModel(parent), tree(TaxonomyTree::getInstance()) {
331     QStringList taxons = data.split(";", QString::SkipEmptyParts);
332     for (const QString &idStr : qAsConst(taxons)) {
333         selected.insert(idStr.toInt());
334     }
335     for (const TaxID &id : qAsConst(selected)) {
336         TaxID parentTaxId = tree->getParent(id);
337         while (parentTaxId > 1) {
338             tristate.insert(parentTaxId, id);
339             parentTaxId = tree->getParent(parentTaxId);
340         }
341     }
342 }
343 
getChildrenSorted(TaxID id) const344 QList<TaxID> TaxonomyTreeModel::getChildrenSorted(TaxID id) const {
345     QList<TaxID> values = tree->getChildren(id);
346     if (values.size() > 1) {
347         // std::sort(values.begin(), values.end(), TaxonNameComparator(tree));
348         std::sort(values.begin(), values.end(), taxIdLessThan);
349     }
350     return values;
351 }
352 
columnCount(const QModelIndex &) const353 int TaxonomyTreeModel::columnCount(const QModelIndex &) const {
354     return 3;
355 }
356 
getSelected() const357 QString TaxonomyTreeModel::getSelected() const {
358     QString res;
359     foreach (TaxID id, selected) {
360         res += QString::number(id) + ";";
361     }
362     if (!res.isEmpty()) {
363         res.chop(1);
364     }
365     return res;
366 }
367 
setData(const QModelIndex & index,const QVariant & v,int role)368 bool TaxonomyTreeModel::setData(const QModelIndex &index, const QVariant &v, int role) {
369     if (role != Qt::CheckStateRole || index.column() != 0)
370         return false;
371 
372     TaxID item = static_cast<TaxID>(index.internalId());
373     bool result = false;
374     bool old = selected.contains(item);
375     int value = v.toInt();
376     if (value == Qt::PartiallyChecked) {
377         // workaround for Qt5.4: https://bugreports.qt.io/browse/QTBUG-43473
378         value = Qt::Checked;
379     }
380     algoLog.info(QString("check %1 for %2 ").arg(value).arg(item));
381 
382     if (value == Qt::Unchecked && old) {
383         selected.remove(item);
384         result = true;
385     } else if (value == Qt::Checked && !old) {
386         selected << item;
387         result = true;
388     }
389 
390     if (result) {
391         QVector<int> checkRole(1, Qt::CheckStateRole);
392         emit dataChanged(index, index, checkRole);
393 
394         QList<TaxID> children = getChildrenSorted(item);
395         if (children.size() != 0) {
396             emit dataChanged(createIndex(0, 0, children.first()), createIndex(children.size() - 1, 3, children.last()));
397         }
398         TaxID parent = tree->getParent(item);
399         while (parent > 1) {
400             if (value == Qt::Checked) {
401                 tristate.insert(parent, item);
402             } else {
403                 tristate.remove(parent, item);
404             }
405             emit dataChanged(createIndex(0, 0, parent), createIndex(0, 3, parent), checkRole);
406             parent = tree->getParent(parent);
407         }
408     }
409     return result;
410 }
411 
data(const QModelIndex & index,int role) const412 QVariant TaxonomyTreeModel::data(const QModelIndex &index, int role) const {
413     if (!index.isValid())
414         return QVariant();
415 
416     TaxID item = static_cast<TaxID>(index.internalId());
417     if (role == Qt::CheckStateRole && index.column() == 0) {
418         while (item > 1) {
419             if (selected.contains(item)) {
420                 return Qt::Checked;
421             }
422             item = tree->getParent(item);
423         }
424         item = static_cast<TaxID>(index.internalId());
425         return tristate.contains(item) ? Qt::PartiallyChecked : Qt::Unchecked;
426     }
427     if (role == Qt::DisplayRole) {
428         switch (index.column()) {
429             case 0:
430                 return tree->getName(item);
431             case 1:
432                 return tree->getRank(item);
433             case 2:
434                 return item;
435         }
436     }
437     return QVariant();
438 }
439 
flags(const QModelIndex & index) const440 Qt::ItemFlags TaxonomyTreeModel::flags(const QModelIndex &index) const {
441     if (!index.isValid())
442         return 0;
443 
444     Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
445 
446     TaxID item = static_cast<TaxID>(index.internalId());
447 
448     if (!selected.contains(item)) {
449         while (item > 1) {
450             item = tree->getParent(item);
451             if (selected.contains(item)) {
452                 flags &= ~Qt::ItemIsEnabled;
453                 break;
454             }
455         }
456     }
457 
458     if (index.column() == 0) {
459         flags |= Qt::ItemIsUserCheckable | Qt::ItemIsAutoTristate;
460     }
461 
462     return flags;
463 }
464 
headerData(int section,Qt::Orientation orientation,int role) const465 QVariant TaxonomyTreeModel::headerData(int section, Qt::Orientation orientation, int role) const {
466     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
467         switch (section) {
468             case 0:
469                 return TaxonomySupport::tr("Taxon name");
470             case 1:
471                 return TaxonomySupport::tr("Rank");
472             case 2:
473                 return TaxonomySupport::tr("Taxon ID");
474         }
475     }
476 
477     return QVariant();
478 }
479 
index(int row,int column,const QModelIndex & parent) const480 QModelIndex TaxonomyTreeModel::index(int row, int column, const QModelIndex &parent)
481     const {
482     if (!hasIndex(row, column, parent))
483         return QModelIndex();
484 
485     TaxID parentItem;
486 
487     if (!parent.isValid())
488         parentItem = 1;
489     else
490         parentItem = static_cast<TaxID>(parent.internalId());
491 
492     QList<TaxID> children = getChildrenSorted(parentItem);
493     if (row < children.size())
494         return createIndex(row, column, children.at(row));
495     else
496         return QModelIndex();
497 }
498 
parent(const QModelIndex & index) const499 QModelIndex TaxonomyTreeModel::parent(const QModelIndex &index) const {
500     if (!index.isValid())
501         return QModelIndex();
502 
503     TaxID childItem = static_cast<TaxID>(index.internalId());
504     TaxID parentItem = tree->getParent(childItem);
505 
506     if (parentItem == 1)
507         return QModelIndex();
508 
509     QList<TaxID> siblings = getChildrenSorted(tree->getParent(parentItem));
510     int row = siblings.indexOf(parentItem);
511     if (row >= 0)
512         return createIndex(row, 0, parentItem);
513     // else todo assert
514 
515     return QModelIndex();
516 }
517 
rowCount(const QModelIndex & parent) const518 int TaxonomyTreeModel::rowCount(const QModelIndex &parent) const {
519     TaxID parentItem;
520     if (parent.column() > 0)
521         return 0;
522 
523     if (!parent.isValid())
524         parentItem = 1;
525     else
526         parentItem = static_cast<TaxID>(parent.internalId());
527 
528     return tree->getChildren(parentItem).size();
529 }
530 
531 /********************************************************************/
532 /*TaxonomyDelegate*/
533 /********************************************************************/
534 
535 static const QString PLACEHOLDER("Select IDs...");
536 
TaxonomyDelegate(QObject * parent)537 TaxonomyDelegate::TaxonomyDelegate(QObject *parent)
538     : PropertyDelegate(parent) {
539 }
540 
getDisplayValue(const QVariant & value) const541 QVariant TaxonomyDelegate::getDisplayValue(const QVariant &value) const {
542     QString str = value.value<QString>();
543     return str.isEmpty() ? PLACEHOLDER : str;
544 }
545 
createEditor(QWidget * parent,const QStyleOptionViewItem &,const QModelIndex &) const546 QWidget *TaxonomyDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const {
547     TaxonomyPropertyWidget *editor = new TaxonomyPropertyWidget(parent);
548     connect(editor, SIGNAL(si_valueChanged(QVariant)), SLOT(sl_commit()));
549     return editor;
550 }
551 
createWizardWidget(U2OpStatus &,QWidget * parent) const552 PropertyWidget *TaxonomyDelegate::createWizardWidget(U2OpStatus &, QWidget *parent) const {
553     return new TaxonomyPropertyWidget(parent);
554 }
555 
setEditorData(QWidget * editor,const QModelIndex & index) const556 void TaxonomyDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const {
557     const QVariant value = index.model()->data(index, ConfigurationEditor::ItemValueRole);
558     TaxonomyPropertyWidget *propertyWidget = qobject_cast<TaxonomyPropertyWidget *>(editor);
559     propertyWidget->setValue(value);
560 }
561 
setModelData(QWidget * editor,QAbstractItemModel * model,const QModelIndex & index) const562 void TaxonomyDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {
563     TaxonomyPropertyWidget *propertyWidget = qobject_cast<TaxonomyPropertyWidget *>(editor);
564     model->setData(index, propertyWidget->value(), ConfigurationEditor::ItemValueRole);
565 }
566 
clone()567 PropertyDelegate *TaxonomyDelegate::clone() {
568     return new TaxonomyDelegate(parent());
569 }
570 
sl_commit()571 void TaxonomyDelegate::sl_commit() {
572     TaxonomyPropertyWidget *editor = qobject_cast<TaxonomyPropertyWidget *>(sender());
573     CHECK(editor != nullptr, );
574     emit commitData(editor);
575 }
576 
577 /********************************************************************/
578 /*TaxonomyPropertyWidget*/
579 /********************************************************************/
580 
TaxonomyPropertyWidget(QWidget * parent,DelegateTags * tags)581 TaxonomyPropertyWidget::TaxonomyPropertyWidget(QWidget *parent, DelegateTags *tags)
582     : PropertyWidget(parent, tags) {
583     lineEdit = new QLineEdit(this);
584     lineEdit->setPlaceholderText(PLACEHOLDER);
585     lineEdit->setReadOnly(true);
586     lineEdit->setObjectName("lineEdit");
587     lineEdit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
588 
589     addMainWidget(lineEdit);
590 
591     toolButton = new QToolButton(this);
592     toolButton->setObjectName("toolButton");
593     toolButton->setText("...");
594     toolButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
595     connect(toolButton, SIGNAL(clicked()), SLOT(sl_showDialog()));
596     layout()->addWidget(toolButton);
597 
598     setObjectName("TaxonomyPropertyWidget");
599 }
600 
value()601 QVariant TaxonomyPropertyWidget::value() {
602     return text;
603 }
604 
setValue(const QVariant & value)605 void TaxonomyPropertyWidget::setValue(const QVariant &value) {
606     text = value.value<QString>();
607     lineEdit->setText(text);
608 }
609 
sl_showDialog()610 void TaxonomyPropertyWidget::sl_showDialog() {
611     QObjectScopedPointer<TaxonSelectionDialog> dialog(new TaxonSelectionDialog(text, this));
612     if (QDialog::Accepted == dialog->exec()) {
613         CHECK(!dialog.isNull(), );
614         text = dialog->getValue();
615         lineEdit->setText(text);
616         emit si_valueChanged(value());
617     }
618 }
619 
620 /********************************************************************/
621 /*TaxonSelectionDialog*/
622 /********************************************************************/
623 
TaxonSelectionDialog(const QString & value,QWidget * parent)624 TaxonSelectionDialog::TaxonSelectionDialog(const QString &value, QWidget *parent)
625     : QDialog(parent) {
626     if (objectName().isEmpty()) {
627         setObjectName(QStringLiteral("TaxonSelectionDialog"));
628     }
629     mainLayout = new QVBoxLayout(this);
630     mainLayout->setObjectName(QStringLiteral("mainLayout"));
631     mainLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);
632 
633     treeView = new QTreeView(this);
634     treeModel = new TaxonomyTreeModel(value);  // fixme delete
635     treeView->setModel(treeModel);
636     for (int column = 0; column < treeModel->columnCount(); ++column) {
637         treeView->resizeColumnToContents(column);
638     }
639     treeView->header()->resizeSection(0, 370);
640     treeView->header()->resizeSection(1, 120);
641     mainLayout->addWidget(treeView);
642 
643     buttonBox = new QDialogButtonBox(this);
644     buttonBox->setObjectName(QStringLiteral("buttonBox"));
645     buttonBox->setOrientation(Qt::Horizontal);
646     buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
647 
648     mainLayout->addWidget(buttonBox);
649 
650     setWindowTitle(QApplication::translate("TaxonSelectionDialog", "Select Taxa", 0));
651     QObject::connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
652     QObject::connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
653 
654     QMetaObject::connectSlotsByName(this);
655 
656     new HelpButton(this, buttonBox, "43");
657     buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Select"));
658     buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
659 
660     //  adjustSize();
661     resize(580, 440);
662     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
663 }
664 
getValue() const665 QString TaxonSelectionDialog::getValue() const {
666     return treeModel->getSelected();
667 }
668 
669 }  // namespace LocalWorkflow
670 }  // namespace U2
671