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 "ConvertToSQLiteDialog.h"
23
24 #include <QDesktopWidget>
25 #include <QMessageBox>
26 #include <QTextEdit>
27
28 #include <U2Core/AppContext.h>
29 #include <U2Core/BaseDocumentFormats.h>
30 #include <U2Core/DocumentUtils.h>
31 #include <U2Core/FileAndDirectoryUtils.h>
32 #include <U2Core/FormatUtils.h>
33 #include <U2Core/GUrlUtils.h>
34 #include <U2Core/ProjectModel.h>
35 #include <U2Core/QObjectScopedPointer.h>
36 #include <U2Core/Task.h>
37 #include <U2Core/Theme.h>
38 #include <U2Core/U2SafePoints.h>
39
40 #include <U2Gui/DialogUtils.h>
41 #include <U2Gui/HelpButton.h>
42 #include <U2Gui/ObjectViewModel.h>
43 #include <U2Gui/SaveDocumentController.h>
44 #include <U2Gui/U2FileDialog.h>
45
46 #include "BAMDbiPlugin.h"
47 #include "BaiReader.h"
48 #include "LoadBamInfoTask.h"
49
50 namespace U2 {
51 namespace BAM {
52
53 static const QString DIR_HELPER_DOMAIN("ConvertToSQLiteDialog");
54
ConvertToSQLiteDialog(const GUrl & _sourceUrl,BAMInfo & _bamInfo,bool sam)55 ConvertToSQLiteDialog::ConvertToSQLiteDialog(const GUrl &_sourceUrl, BAMInfo &_bamInfo, bool sam)
56 : QDialog(QApplication::activeWindow()),
57 saveController(nullptr),
58 sourceUrl(_sourceUrl),
59 bamInfo(_bamInfo) {
60 ui.setupUi(this);
61 new HelpButton(this, ui.buttonBox, "65929794");
62 ui.buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Import"));
63 ui.buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
64
65 if (sam) {
66 setWindowTitle(tr("Import SAM File"));
67 } else {
68 setWindowTitle(tr("Import BAM File"));
69 }
70 this->setObjectName("Import BAM File");
71
72 const QString warningMessageStyleSheet("color: " + Theme::successColorLabelStr() + "; font: bold;");
73 ui.indexNotAvailableLabel->setStyleSheet(warningMessageStyleSheet);
74 ui.referenceWarningLabel->setStyleSheet(warningMessageStyleSheet);
75
76 initSaveController();
77
78 connect(ui.bamInfoButton, SIGNAL(clicked()), SLOT(sl_bamInfoButtonClicked()));
79 connect(ui.refUrlButton, SIGNAL(clicked()), SLOT(sl_refUrlButtonClicked()));
80 connect(ui.selectAllToolButton, SIGNAL(clicked()), SLOT(sl_selectAll()));
81 connect(ui.selectNoneToolButton, SIGNAL(clicked()), SLOT(sl_unselectAll()));
82 connect(ui.inverseSelectionToolButton, SIGNAL(clicked()), SLOT(sl_inverseSelection()));
83 ui.indexNotAvailableLabel->setVisible(sam ? false : !bamInfo.hasIndex());
84
85 if (sam && bamInfo.getHeader().getReferences().isEmpty()) {
86 hideReferencesTable();
87 } else {
88 hideReferenceUrl();
89 hideReferenceMessage();
90 ui.tableWidget->setColumnCount(3);
91 ui.tableWidget->setRowCount(bamInfo.getHeader().getReferences().count());
92 QStringList header;
93 header << BAMDbiPlugin::tr("Assembly name") << BAMDbiPlugin::tr("Length") << BAMDbiPlugin::tr("URI");
94 ui.tableWidget->setHorizontalHeaderLabels(header);
95 ui.tableWidget->horizontalHeader()->setStretchLastSection(true);
96 {
97 int i = 0;
98 foreach (const Header::Reference &ref, bamInfo.getHeader().getReferences()) {
99 QTableWidgetItem *checkbox = new QTableWidgetItem();
100 checkbox->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
101 checkbox->setText(ref.getName());
102 ui.tableWidget->setItem(i, 0, checkbox);
103 QTableWidgetItem *item = new QTableWidgetItem(FormatUtils::formatNumberWithSeparators(ref.getLength()));
104 item->setFlags(Qt::ItemIsEnabled);
105 ui.tableWidget->setItem(i, 1, item);
106 ui.tableWidget->setCellWidget(i, 2, new QLabel("<a href=\"" + ref.getUri() + "\">" + ref.getUri() + "</a>"));
107 checkbox->setCheckState(Qt::Checked);
108 i++;
109 }
110 }
111 ui.tableWidget->verticalHeader()->setDefaultSectionSize(QFontMetrics(QFont()).height() + 5);
112 }
113 QPushButton *okButton = ui.buttonBox->button(QDialogButtonBox::Ok);
114 ui.importUnmappedBox->setCheckState(bamInfo.isUnmappedSelected() ? Qt::Checked : Qt::Unchecked);
115 ui.sourceUrlView->setText(QDir::cleanPath(sourceUrl.getURLString()));
116 okButton->setFocus();
117 connect(ui.tableWidget, SIGNAL(itemChanged(QTableWidgetItem *)), SLOT(sl_assemblyCheckChanged(QTableWidgetItem *)));
118
119 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
120 adjustSize();
121 if (ui.tableWidget->isHidden()) {
122 setFixedHeight(height());
123 }
124 setMinimumWidth(600);
125 }
126
hideReferenceUrl()127 void ConvertToSQLiteDialog::hideReferenceUrl() {
128 ui.refUrlLabel->hide();
129 ui.refUrlEdit->hide();
130 ui.refUrlButton->hide();
131 }
132
hideReferencesTable()133 void ConvertToSQLiteDialog::hideReferencesTable() {
134 ui.selectionButtons->hide();
135 ui.tableWidget->hide();
136 }
137
hideReferenceMessage()138 void ConvertToSQLiteDialog::hideReferenceMessage() {
139 ui.referenceWarningLabel->setVisible(false);
140 }
141
sl_selectAll()142 void ConvertToSQLiteDialog::sl_selectAll() {
143 for (int i = 0; i < bamInfo.getSelected().count(); i++) {
144 ui.tableWidget->item(i, 0)->setCheckState(Qt::Checked);
145 }
146 }
147
sl_unselectAll()148 void ConvertToSQLiteDialog::sl_unselectAll() {
149 for (int i = 0; i < bamInfo.getSelected().count(); i++) {
150 ui.tableWidget->item(i, 0)->setCheckState(Qt::Unchecked);
151 }
152 }
sl_inverseSelection()153 void ConvertToSQLiteDialog::sl_inverseSelection() {
154 for (int i = 0; i < bamInfo.getSelected().count(); i++) {
155 QTableWidgetItem *item = ui.tableWidget->item(i, 0);
156 item->setCheckState(item->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked);
157 }
158 }
159
sl_bamInfoButtonClicked()160 void ConvertToSQLiteDialog::sl_bamInfoButtonClicked() {
161 const Header &header = bamInfo.getHeader();
162 QObjectScopedPointer<QDialog> dialog = new QDialog(this);
163 dialog->setWindowTitle(BAMDbiPlugin::tr("%1 file info").arg(sourceUrl.getURLString()));
164 dialog->setLayout(new QVBoxLayout());
165
166 {
167 QTableWidget *table = new QTableWidget();
168 table->setColumnCount(2);
169 table->setHorizontalHeaderLabels(QStringList() << BAMDbiPlugin::tr("Property name") << BAMDbiPlugin::tr("Value"));
170 table->horizontalHeader()->setStretchLastSection(true);
171 table->verticalHeader()->setVisible(false);
172
173 QList<QPair<QString, QString>> list;
174 QString sort;
175 switch (header.getSortingOrder()) {
176 case Header::Unsorted:
177 sort = BAMDbiPlugin::tr("Unsorted");
178 break;
179 case Header::Unknown:
180 sort = BAMDbiPlugin::tr("Unknown");
181 break;
182 case Header::Coordinate:
183 sort = BAMDbiPlugin::tr("Coordinate");
184 break;
185 case Header::QueryName:
186 sort = BAMDbiPlugin::tr("Query name");
187 break;
188 }
189
190 list << QPair<QString, QString>(BAMDbiPlugin::tr("URL"), sourceUrl.getURLString())
191 << QPair<QString, QString>(BAMDbiPlugin::tr("Format version"), header.getFormatVersion().text)
192 << QPair<QString, QString>(BAMDbiPlugin::tr("Sorting order"), sort);
193
194 table->setRowCount(list.count());
195 {
196 for (int i = 0; i < list.count(); i++) {
197 const QPair<QString, QString> &pair = list.at(i);
198 QTableWidgetItem *item = new QTableWidgetItem(pair.first);
199 item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
200 table->setItem(i, 0, item);
201 item = new QTableWidgetItem(pair.second);
202 item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
203 table->setItem(i, 1, item);
204 }
205 }
206 dialog->layout()->addWidget(table);
207 }
208
209 {
210 QTableWidget *table = new QTableWidget();
211 table->setColumnCount(9);
212 table->setHorizontalHeaderLabels(QStringList() << BAMDbiPlugin::tr("Sequencing center") << BAMDbiPlugin::tr("Description") << BAMDbiPlugin::tr("Date")
213 << BAMDbiPlugin::tr("Library") << BAMDbiPlugin::tr("Programs") << BAMDbiPlugin::tr("Predicted median insert size") << BAMDbiPlugin::tr("Platform/technology")
214 << BAMDbiPlugin::tr("Platform unit") << BAMDbiPlugin::tr("Sample"));
215 table->horizontalHeader()->setStretchLastSection(true);
216
217 int i = 0;
218 foreach (const Header::ReadGroup &rg, header.getReadGroups()) {
219 QStringList rgList;
220 rgList << QString(rg.getSequencingCenter()) << QString(rg.getDescription()) << QString(rg.getDate().toString()) << QString(rg.getLibrary())
221 << QString(rg.getPlatform()) << QString(rg.getPredictedInsertSize()) << QString(rg.getPlatform()) << QString(rg.getPlatformUnit()) << QString(rg.getSample());
222 int j = 0;
223 foreach (const QString &s, rgList) {
224 QTableWidgetItem *item = new QTableWidgetItem(s);
225 item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
226 table->setItem(i, j, item);
227 j++;
228 }
229 i++;
230 }
231 dialog->layout()->addWidget(new QLabel(BAMDbiPlugin::tr("Read groups:")));
232 dialog->layout()->addWidget(table);
233 }
234
235 {
236 QTableWidget *table = new QTableWidget();
237 table->setColumnCount(4);
238 table->setHorizontalHeaderLabels(QStringList() << BAMDbiPlugin::tr("Name") << BAMDbiPlugin::tr("Version") << BAMDbiPlugin::tr("Command") << BAMDbiPlugin::tr("Previous ID"));
239 table->horizontalHeader()->setStretchLastSection(true);
240
241 int i = 0;
242 foreach (const Header::Program &pg, header.getPrograms()) {
243 QStringList pgList;
244 pgList << QString(pg.getName()) << QString(pg.getVersion()) << QString(pg.getCommandLine()) << QString(pg.getPreviousId());
245 int j = 0;
246 foreach (const QString &s, pgList) {
247 QTableWidgetItem *item = new QTableWidgetItem(s);
248 item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
249 table->setItem(i, j, item);
250 j++;
251 }
252 i++;
253 }
254 dialog->layout()->addWidget(new QLabel(BAMDbiPlugin::tr("Programs:")));
255 dialog->layout()->addWidget(table);
256 }
257 dialog->resize(qMin(600, QApplication::desktop()->screenGeometry().width()), dialog->sizeHint().height());
258 dialog->exec();
259 }
260
sl_refUrlButtonClicked()261 void ConvertToSQLiteDialog::sl_refUrlButtonClicked() {
262 GUrl currentUrl = ui.refUrlEdit->text();
263 if (ui.refUrlEdit->text().isEmpty()) {
264 currentUrl = sourceUrl;
265 }
266 QString dir = currentUrl.dirPath() + "/" + currentUrl.baseFileName();
267 QString value;
268 #ifdef Q_OS_DARWIN
269 if (qgetenv(ENV_GUI_TEST).toInt() == 1 && qgetenv(ENV_USE_NATIVE_DIALOGS).toInt() == 0) {
270 value = U2FileDialog::getOpenFileName(this, QObject::tr("Reference File"), dir, "", 0, QFileDialog::DontUseNativeDialog);
271 } else
272 #endif
273 value = U2FileDialog::getOpenFileName(this, QObject::tr("Reference File"), dir);
274 if (!value.isEmpty()) {
275 ui.refUrlEdit->setText(value);
276 hideReferenceMessage();
277 }
278 }
279
sl_assemblyCheckChanged(QTableWidgetItem * item)280 void ConvertToSQLiteDialog::sl_assemblyCheckChanged(QTableWidgetItem *item) {
281 bamInfo.getSelected()[item->row()] = (item->checkState() == Qt::Checked);
282 }
283
getDestinationUrl() const284 const GUrl &ConvertToSQLiteDialog::getDestinationUrl() const {
285 return destinationUrl;
286 }
287
getReferenceUrl() const288 QString ConvertToSQLiteDialog::getReferenceUrl() const {
289 return ui.refUrlEdit->text();
290 }
291
addToProject() const292 bool ConvertToSQLiteDialog::addToProject() const {
293 return ui.addToProjectBox->isChecked();
294 }
295
hideAddToProjectOption()296 void ConvertToSQLiteDialog::hideAddToProjectOption() {
297 ui.addToProjectBox->hide();
298 }
299
referenceFromFile()300 bool ConvertToSQLiteDialog::referenceFromFile() {
301 return ui.refUrlEdit->isVisible();
302 }
303
checkReferencesState()304 bool ConvertToSQLiteDialog::checkReferencesState() {
305 if (referenceFromFile()) {
306 return true;
307 } else {
308 bool selected = false;
309 foreach (const bool &i, bamInfo.getSelected()) {
310 if (i) {
311 selected = true;
312 break;
313 }
314 }
315 if (!selected && !bamInfo.isUnmappedSelected()) {
316 QMessageBox::critical(this, windowTitle(), BAMDbiPlugin::tr("Please select assemblies to be imported"));
317 return false;
318 }
319 }
320 return true;
321 }
322
initSaveController()323 void ConvertToSQLiteDialog::initSaveController() {
324 SaveDocumentControllerConfig config;
325 config.defaultDomain = DIR_HELPER_DOMAIN;
326 config.defaultFileName = sourceUrl.dirPath() + "/" + QFileInfo(sourceUrl.fileName()).completeBaseName() + ".ugenedb";
327 config.defaultFormatId = BaseDocumentFormats::UGENEDB;
328 config.fileDialogButton = ui.destinationUrlButton;
329 config.fileNameEdit = ui.destinationUrlEdit;
330 config.parentWidget = this;
331 config.saveTitle = BAMDbiPlugin::tr("Destination UGENEDB File");
332
333 const QList<DocumentFormatId> formats = QList<DocumentFormatId>() << BaseDocumentFormats::UGENEDB;
334
335 saveController = new SaveDocumentController(config, formats, this);
336 }
337
338 namespace {
checkWritePermissions(const QString & fileUrl)339 bool checkWritePermissions(const QString &fileUrl) {
340 QDir dir = QFileInfo(fileUrl).dir();
341 if (!dir.exists()) {
342 bool created = dir.mkpath(dir.absolutePath());
343 CHECK(created, false);
344 }
345 return FileAndDirectoryUtils::isDirectoryWritable(dir.absolutePath());
346 }
347 } // namespace
348
accept()349 void ConvertToSQLiteDialog::accept() {
350 destinationUrl = GUrl(saveController->getSaveFileName());
351 bamInfo.setUnmappedSelected(ui.importUnmappedBox->checkState() == Qt::Checked);
352 if (destinationUrl.isEmpty()) {
353 ui.destinationUrlEdit->setFocus(Qt::OtherFocusReason);
354 QMessageBox::critical(this, windowTitle(), BAMDbiPlugin::tr("Destination URL is not specified"));
355 } else if (!destinationUrl.isLocalFile()) {
356 ui.destinationUrlEdit->setFocus(Qt::OtherFocusReason);
357 QMessageBox::critical(this, windowTitle(), BAMDbiPlugin::tr("Destination URL must point to a local file"));
358 } else if (!checkWritePermissions(destinationUrl.getURLString())) {
359 ui.destinationUrlEdit->setFocus(Qt::OtherFocusReason);
360 QMessageBox::critical(this, windowTitle(), BAMDbiPlugin::tr("Destination URL folder has not write permissions"));
361 } else {
362 if (!checkReferencesState()) {
363 return;
364 }
365
366 Project *prj = AppContext::getProject();
367 if (prj != nullptr) {
368 Document *destDoc = prj->findDocumentByURL(destinationUrl);
369 if (destDoc != nullptr && destDoc->isLoaded() && !GObjectViewUtils::findViewsWithAnyOfObjects(destDoc->getObjects()).isEmpty()) {
370 QMessageBox::critical(this, windowTitle(), BAMDbiPlugin::tr("There is opened view with destination file.\n"
371 "Close it or choose different file"));
372 ui.destinationUrlEdit->setFocus(Qt::OtherFocusReason);
373 return;
374 }
375 }
376 QFileInfo destinationDir(QFileInfo(destinationUrl.getURLString()).path());
377 if (!destinationDir.isWritable()) {
378 ui.destinationUrlEdit->setFocus(Qt::OtherFocusReason);
379 QMessageBox::critical(this, windowTitle(), BAMDbiPlugin::tr("Destination folder '%1' is not writable, please choose different destination URL").arg(destinationDir.absoluteFilePath()));
380 return;
381 }
382
383 if (QFile::exists(destinationUrl.getURLString())) {
384 int result = QMessageBox::question(this, windowTitle(), BAMDbiPlugin::tr("Destination file already exists.\n"
385 "To overwrite the file, press 'Replace'.\n"
386 "To append data to existing file press 'Append'."),
387 BAMDbiPlugin::tr("Replace"),
388 BAMDbiPlugin::tr("Append"),
389 BAMDbiPlugin::tr("Cancel"),
390 2);
391 switch (result) {
392 case 0: {
393 bool ok = QFile::remove(destinationUrl.getURLString());
394 if (!ok) {
395 QMessageBox::critical(this, windowTitle(), BAMDbiPlugin::tr("Destination file '%1' cannot be removed").arg(destinationUrl.getURLString()));
396 return;
397 }
398 }
399 QDialog::accept();
400 break;
401 case 1:
402 QDialog::accept();
403 break;
404 }
405 } else {
406 QDialog::accept();
407 }
408 }
409 }
410
411 } // namespace BAM
412 } // namespace U2
413