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 "GenomeAssemblyDialog.h"
23
24 #include <QKeyEvent>
25 #include <QMessageBox>
26
27 #include <U2Algorithm/GenomeAssemblyRegistry.h>
28
29 #include <U2Core/AppContext.h>
30 #include <U2Core/BaseDocumentFormats.h>
31 #include <U2Core/DocumentModel.h>
32 #include <U2Core/DocumentUtils.h>
33 #include <U2Core/ExternalToolRegistry.h>
34 #include <U2Core/FileAndDirectoryUtils.h>
35 #include <U2Core/GUrlUtils.h>
36 #include <U2Core/U2SafePoints.h>
37
38 #include <U2Gui/AppSettingsGUI.h>
39 #include <U2Gui/HelpButton.h>
40 #include <U2Gui/LastUsedDirHelper.h>
41 #include <U2Gui/U2FileDialog.h>
42
43 #include <U2View/DnaAssemblyGUIExtension.h>
44 #include <U2View/DnaAssemblyUtils.h>
45
46 namespace U2 {
47
48 QString GenomeAssemblyDialog::methodName;
49 QString GenomeAssemblyDialog::library;
50
GenomeAssemblyDialog(QWidget * p)51 GenomeAssemblyDialog::GenomeAssemblyDialog(QWidget *p)
52 : QDialog(p),
53 assemblyRegistry(AppContext::getGenomeAssemblyAlgRegistry()),
54 customGUI(nullptr) {
55 setupUi(this);
56
57 QMap<QString, QString> helpPagesMap;
58 helpPagesMap.insert("SPAdes", "65930903");
59 new ComboboxDependentHelpButton(this, buttonBox, methodNamesBox, helpPagesMap);
60 buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Start"));
61 buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
62
63 QStringList names = assemblyRegistry->getRegisteredAlgorithmIds();
64 methodNamesBox->addItems(names);
65 // TODO: change the way default method is set
66 if (names.size() > 0) {
67 int res = -1;
68 if (!methodName.isEmpty()) {
69 res = methodNamesBox->findText(methodName);
70 }
71 if (-1 == res) {
72 methodNamesBox->setCurrentIndex(names.size() - 1);
73 } else {
74 methodNamesBox->setCurrentIndex(res);
75 }
76 }
77
78 // libraryComboBox->addItems(GenomeAssemblyUtils::getLibraryTypes());
79
80 QHeaderView *header1 = propertiesReadsTable->header();
81 QHeaderView *header2 = leftReadsTable->header();
82 QHeaderView *header3 = rightReadsTable->header();
83
84 header1->setStretchLastSection(false);
85 header2->setStretchLastSection(false);
86 header3->setStretchLastSection(false);
87 header1->setSectionsClickable(false);
88 header1->setSectionResizeMode(0, QHeaderView::Stretch);
89 header2->setSectionsClickable(false);
90 header2->setSectionResizeMode(0, QHeaderView::Stretch);
91 header3->setSectionsClickable(false);
92 header3->setSectionResizeMode(0, QHeaderView::Stretch);
93
94 sl_onLibraryTypeChanged();
95 sl_onAlgorithmChanged(methodNamesBox->currentText());
96
97 connect(addLeftButton, SIGNAL(clicked()), SLOT(sl_onAddShortReadsButtonClicked()));
98 connect(addRightButton, SIGNAL(clicked()), SLOT(sl_onAddShortReadsButtonClicked()));
99 connect(removeLeftButton, SIGNAL(clicked()), SLOT(sl_onRemoveShortReadsButtonClicked()));
100 connect(removeRightButton, SIGNAL(clicked()), SLOT(sl_onRemoveShortReadsButtonClicked()));
101 connect(setResultDirNameButton, SIGNAL(clicked()), SLOT(sl_onOutDirButtonClicked()));
102 connect(methodNamesBox, SIGNAL(currentIndexChanged(const QString &)), SLOT(sl_onAlgorithmChanged(const QString &)));
103 connect(libraryComboBox, SIGNAL(currentIndexChanged(int)), SLOT(sl_onLibraryTypeChanged()));
104
105 QString defaultOutputDir = GUrlUtils::getDefaultDataPath() + "/" + methodNamesBox->currentText() + "_output";
106 resultDirNameEdit->setText(GUrlUtils::rollFileName(defaultOutputDir, QSet<QString>() << defaultOutputDir));
107
108 if (!library.isEmpty()) {
109 int index = libraryComboBox->findText(library);
110 if (index != -1) {
111 libraryComboBox->setCurrentIndex(index);
112 }
113 }
114 }
115
updateState()116 void GenomeAssemblyDialog::updateState() {
117 addGuiExtension();
118 }
119
updateProperties()120 void GenomeAssemblyDialog::updateProperties() {
121 int numProperties = propertiesReadsTable->topLevelItemCount();
122 int numberOfReads = leftReadsTable->topLevelItemCount();
123 // if(GenomeAssemblyUtils::hasRightReads(libraryComboBox->currentText())){
124 // numberOfReads = qMax(leftReadsTable->topLevelItemCount(), rightReadsTable->topLevelItemCount());
125 // }
126 if (numProperties > numberOfReads) {
127 // remove items
128 for (int i = numProperties - 1; i >= numberOfReads; --i) {
129 propertiesReadsTable->takeTopLevelItem(i);
130 }
131 } else if (numProperties < numberOfReads) {
132 // add items
133 for (int i = numProperties; i < numberOfReads; i++) {
134 ReadPropertiesItem *item = new ReadPropertiesItem(propertiesReadsTable);
135 item->setLibraryType(libraryComboBox->currentText());
136 ReadPropertiesItem::addItemToTable(item, propertiesReadsTable);
137 }
138 }
139 // update numbers
140 numProperties = propertiesReadsTable->topLevelItemCount();
141 for (int i = 0; i < numProperties; ++i) {
142 QTreeWidgetItem *item = propertiesReadsTable->topLevelItem(i);
143 item->setData(0, 0, i + 1);
144 }
145 }
146
addReads(QStringList fileNames,QTreeWidget * readsWidget)147 void GenomeAssemblyDialog::addReads(QStringList fileNames, QTreeWidget *readsWidget) {
148 foreach (const QString &f, fileNames) {
149 QTreeWidgetItem *item = new QTreeWidgetItem();
150 item->setToolTip(0, f);
151 item->setText(0, GUrl(f).fileName());
152 item->setData(0, Qt::UserRole, f);
153 readsWidget->addTopLevelItem(item);
154 item->setSizeHint(0, QComboBox().sizeHint());
155 }
156
157 updateProperties();
158 }
159
sl_onAddShortReadsButtonClicked()160 void GenomeAssemblyDialog::sl_onAddShortReadsButtonClicked() {
161 QTreeWidget *readsWidget = nullptr;
162 QObject *obj = sender();
163 if (obj == addLeftButton) {
164 readsWidget = leftReadsTable;
165 } else if (obj == addRightButton) {
166 readsWidget = rightReadsTable;
167 } else {
168 return;
169 }
170
171 LastUsedDirHelper lod("AssemblyReads");
172 QStringList fileNames;
173 #ifdef Q_OS_DARWIN
174 if (qgetenv(ENV_GUI_TEST).toInt() == 1 && qgetenv(ENV_USE_NATIVE_DIALOGS).toInt() == 0) {
175 fileNames = U2FileDialog::getOpenFileNames(this, tr("Add short reads"), lod.dir, QString(), 0, QFileDialog::DontUseNativeDialog);
176 } else
177 #endif
178 fileNames = U2FileDialog::getOpenFileNames(this, tr("Add short reads"), lod.dir);
179 if (fileNames.isEmpty()) {
180 return;
181 }
182 lod.url = fileNames.at(fileNames.count() - 1);
183
184 addReads(fileNames, readsWidget);
185 }
186
accept()187 void GenomeAssemblyDialog::accept() {
188 bool validated = true;
189 if (nullptr != customGUI) {
190 QString error;
191 if (!customGUI->isParametersOk(error)) {
192 if (!error.isEmpty()) {
193 QMessageBox::information(this, tr("Genome Assembly"), error);
194 }
195 validated = false;
196 }
197 }
198
199 if (resultDirNameEdit->text().isEmpty()) {
200 QMessageBox::information(this, tr("Genome Assembly"), tr("Result assembly folder is not set!"));
201 validated = false;
202 } else {
203 // if(GenomeAssemblyUtils::hasRightReads(libraryComboBox->currentText())){
204 // if(leftReadsTable->topLevelItemCount() == 0 && rightReadsTable->topLevelItemCount() == 0){
205 // QMessageBox::information(this, tr("Genome Assembly"),
206 // tr("No reads. Please, add file(s) with short reads.") );
207 // validated = false;
208 // }
209
210 // if(leftReadsTable->topLevelItemCount() != rightReadsTable->topLevelItemCount()){
211 // QMessageBox::information(this, tr("Genome Assembly"),
212 // tr("In the paired-end mode a number of lift and right reads must be equal.") );
213 // validated = false;
214 // }
215
216 // }else{
217 // if(leftReadsTable->topLevelItemCount() == 0){
218 // QMessageBox::information(this, tr("Genome Assembly"),
219 // tr("No reads. Please, add file(s) with short reads.") );
220 // validated = false;
221 // }
222 // }
223
224 if (validated) {
225 library = libraryComboBox->currentText();
226 library = libraryComboBox->currentText();
227
228 // check formats
229 QStringList reads;
230 int numItems = leftReadsTable->topLevelItemCount();
231 for (int i = 0; i < numItems; ++i) {
232 reads.append(leftReadsTable->topLevelItem(i)->data(0, Qt::UserRole).toString());
233 }
234
235 numItems = rightReadsTable->topLevelItemCount();
236 for (int i = 0; i < numItems; ++i) {
237 reads.append(rightReadsTable->topLevelItem(i)->data(0, Qt::UserRole).toString());
238 }
239
240 GenomeAssemblyAlgorithmEnv *env = AppContext::getGenomeAssemblyAlgRegistry()->getAlgorithm(methodNamesBox->currentText());
241 SAFE_POINT(nullptr != env, "Unknown algorithm: " + methodNamesBox->currentText(), );
242 QStringList formats = env->getReadsFormats();
243
244 foreach (const QString &r, reads) {
245 const QString detectedFormat = FileAndDirectoryUtils::detectFormat(r);
246 if (detectedFormat.isEmpty()) {
247 QMessageBox::information(this, tr("Genome Assembly"), tr("Unknown file format of %1.").arg(r));
248 return;
249 }
250
251 if (!formats.contains(detectedFormat)) {
252 QMessageBox::information(this, tr("Genome Assembly"), tr("File format of %1 is %2. Supported file formats of reads: %3.").arg(r).arg(detectedFormat).arg(formats.join(", ")));
253 return;
254 }
255 }
256 QString outputDirUrl = resultDirNameEdit->text();
257 QDir d(outputDirUrl);
258 if (!d.exists()) {
259 if (!d.mkdir(outputDirUrl)) {
260 QMessageBox::information(this, tr("Genome Assembly"), tr("Unable to create output folder for result assembly.\r\nDirectory Path: %1").arg(outputDirUrl));
261 }
262 }
263 QDialog::accept();
264 }
265 }
266 }
267
getAlgorithmName()268 const QString GenomeAssemblyDialog::getAlgorithmName() {
269 return methodNamesBox->currentText();
270 }
271
getOutDir()272 const QString GenomeAssemblyDialog::getOutDir() {
273 return resultDirNameEdit->text();
274 }
275
getReads()276 QList<AssemblyReads> GenomeAssemblyDialog::getReads() {
277 QList<AssemblyReads> result;
278
279 int numProperties = propertiesReadsTable->topLevelItemCount();
280 int numLeftReads = leftReadsTable->topLevelItemCount();
281 int numRightReads = rightReadsTable->topLevelItemCount();
282
283 for (int i = 0; i < numProperties; i++) {
284 AssemblyReads read;
285 QTreeWidgetItem *item = propertiesReadsTable->topLevelItem(i);
286 ReadPropertiesItem *pitem = dynamic_cast<ReadPropertiesItem *>(item);
287 if (pitem) {
288 // read.libNumber = pitem->getNumber();
289 // read.libType = pitem->getType();
290 read.orientation = pitem->getOrientation();
291 read.libName = libraryComboBox->currentText();
292
293 if (i < numLeftReads) {
294 read.left << leftReadsTable->topLevelItem(i)->data(0, Qt::UserRole).toString();
295 if (i < numRightReads) {
296 read.right << rightReadsTable->topLevelItem(i)->data(0, Qt::UserRole).toString();
297 }
298 result.append(read);
299 }
300 }
301 }
302 return result;
303 }
304
sl_onRemoveShortReadsButtonClicked()305 void GenomeAssemblyDialog::sl_onRemoveShortReadsButtonClicked() {
306 QTreeWidget *readsWidget = nullptr;
307 QObject *obj = sender();
308 if (obj == removeLeftButton) {
309 readsWidget = leftReadsTable;
310 } else if (obj == removeRightButton) {
311 readsWidget = rightReadsTable;
312 } else {
313 return;
314 }
315
316 int currentRow = readsWidget->currentIndex().row();
317 readsWidget->takeTopLevelItem(currentRow);
318
319 updateProperties();
320 }
321
sl_onOutDirButtonClicked()322 void GenomeAssemblyDialog::sl_onOutDirButtonClicked() {
323 LastUsedDirHelper lod("assemblyRes");
324
325 lod.url = U2FileDialog::getExistingDirectory(this, tr("Select output folder"), lod.dir);
326 if (lod.url.isEmpty()) {
327 return;
328 }
329
330 resultDirNameEdit->setText(lod.url);
331 }
332
sl_onAlgorithmChanged(const QString & text)333 void GenomeAssemblyDialog::sl_onAlgorithmChanged(const QString &text) {
334 methodName = text;
335 updateState();
336 }
337
getCustomSettings()338 QMap<QString, QVariant> GenomeAssemblyDialog::getCustomSettings() {
339 if (customGUI != nullptr) {
340 return customGUI->getGenomeAssemblyCustomSettings();
341 } else {
342 return QMap<QString, QVariant>();
343 }
344 }
345
addGuiExtension()346 void GenomeAssemblyDialog::addGuiExtension() {
347 static const int insertPos = verticalLayout->count() - 2;
348
349 int macFixDelta = 50;
350
351 // cleanup previous extension
352 if (customGUI != nullptr) {
353 layout()->removeWidget(customGUI);
354 setMinimumHeight(minimumHeight() - customGUI->minimumHeight());
355 delete customGUI;
356 customGUI = nullptr;
357 macFixDelta = 0;
358 }
359
360 // insert new extension widget
361 GenomeAssemblyAlgorithmEnv *env = assemblyRegistry->getAlgorithm(methodNamesBox->currentText());
362
363 if (nullptr == env) {
364 adjustSize();
365 return;
366 }
367
368 GenomeAssemblyGUIExtensionsFactory *gui = env->getGUIExtFactory();
369 if (gui != nullptr && gui->hasMainWidget()) {
370 customGUI = gui->createMainWidget(this);
371 int extensionMinWidth = customGUI->sizeHint().width();
372 int extensionMinHeight = customGUI->sizeHint().height();
373 customGUI->setMinimumWidth(extensionMinWidth);
374 customGUI->setMinimumHeight(extensionMinHeight);
375 verticalLayout->insertWidget(insertPos, customGUI);
376 // adjust sizes
377 // HACK: add 50 to min height when dialog first shown, 50 to width always (fix for Mac OS)
378 // TODO: handle margins in proper way so this hack not needed
379 setMinimumHeight(customGUI->minimumHeight() + minimumHeight() + macFixDelta);
380 if (minimumWidth() < customGUI->minimumWidth() + 50) {
381 setMinimumWidth(customGUI->minimumWidth() + 50);
382 };
383
384 customGUI->show();
385 adjustSize();
386 } else {
387 adjustSize();
388 }
389 }
390
sl_onLibraryTypeChanged()391 void GenomeAssemblyDialog::sl_onLibraryTypeChanged() {
392 QString libraryType = libraryComboBox->currentText();
393 // if(GenomeAssemblyUtils::hasRightReads(libraryType)){
394 // rightReadsTable->setEnabled(true);
395 // addRightButton->setEnabled(true);
396 // removeRightButton->setEnabled(true);
397 // }else{
398 // rightReadsTable->setEnabled(false);
399 // addRightButton->setEnabled(false);
400 // removeRightButton->setEnabled(false);
401 // }
402
403 int size = propertiesReadsTable->topLevelItemCount();
404 for (int i = 0; i < size; i++) {
405 QTreeWidgetItem *item = propertiesReadsTable->topLevelItem(i);
406 ReadPropertiesItem *pitem = dynamic_cast<ReadPropertiesItem *>(item);
407 if (pitem) {
408 pitem->setLibraryType(libraryType);
409 }
410 }
411
412 updateProperties();
413 }
414
ReadPropertiesItem(QTreeWidget * widget)415 ReadPropertiesItem::ReadPropertiesItem(QTreeWidget *widget)
416 : QTreeWidgetItem(widget) {
417 typeBox = new QComboBox(widget);
418 // typeBox->addItems(GenomeAssemblyUtils::getPairTypes());
419
420 orientationBox = new QComboBox(widget);
421 orientationBox->addItems(GenomeAssemblyUtils::getOrientationTypes());
422 }
423
getNumber() const424 QString ReadPropertiesItem::getNumber() const {
425 return data(0, 0).toString();
426 }
427
getType() const428 QString ReadPropertiesItem::getType() const {
429 return typeBox->currentText();
430 }
431
getOrientation() const432 QString ReadPropertiesItem::getOrientation() const {
433 return orientationBox->currentText();
434 }
435
setLibraryType(const QString & libraryType)436 void ReadPropertiesItem::setLibraryType(const QString &libraryType) {
437 if (GenomeAssemblyUtils::isLibraryPaired(libraryType)) {
438 orientationBox->setEnabled(true);
439 typeBox->setEnabled(true);
440 } else {
441 orientationBox->setEnabled(false);
442 typeBox->setEnabled(false);
443 }
444 }
445
addItemToTable(ReadPropertiesItem * item,QTreeWidget * treeWidget)446 void ReadPropertiesItem::addItemToTable(ReadPropertiesItem *item, QTreeWidget *treeWidget) {
447 treeWidget->addTopLevelItem(item);
448 treeWidget->setItemWidget(item, 1, item->typeBox);
449 treeWidget->setItemWidget(item, 2, item->orientationBox);
450 }
451
452 } // namespace U2
453