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 "MultipleDocumentsReadingModeSelectorController.h"
23
24 #include <QDir>
25
26 #include <U2Core/AppContext.h>
27 #include <U2Core/BaseDocumentFormats.h>
28 #include <U2Core/DocumentModel.h>
29 #include <U2Core/DocumentUtils.h>
30 #include <U2Core/GUrlUtils.h>
31 #include <U2Core/IOAdapter.h>
32 #include <U2Core/LocalFileAdapter.h>
33 #include <U2Core/ProjectModel.h>
34 #include <U2Core/U2SafePoints.h>
35
36 #include <U2Gui/HelpButton.h>
37 #include <U2Gui/SaveDocumentController.h>
38
39 namespace U2 {
40
41 struct GUrlLess {
operator ()U2::GUrlLess42 bool operator()(GUrl a, GUrl b) const {
43 return a.getURLString() < b.getURLString();
44 }
45 };
46
adjustReadingMode(QVariantMap & props,QList<GUrl> & urls,const QMap<QString,qint64> & headerSequenceLengths)47 bool MultipleDocumentsReadingModeSelectorController::adjustReadingMode(QVariantMap &props, QList<GUrl> &urls, const QMap<QString, qint64> &headerSequenceLengths) {
48 std::sort(urls.begin(), urls.end(), GUrlLess());
49
50 MultipleDocumentsReadingModeDialog d(urls, QApplication::activeWindow());
51 return d.setupGUI(urls, props, headerSequenceLengths);
52 }
53
mergeDocumentOption(const FormatDetectionResult & formatResult,QMap<QString,qint64> * headerSequenceLengths)54 bool MultipleDocumentsReadingModeSelectorController::mergeDocumentOption(const FormatDetectionResult &formatResult, QMap<QString, qint64> *headerSequenceLengths) {
55 QVariantMap docHints = formatResult.rawDataCheckResult.properties;
56 if (formatResult.format == nullptr) {
57 return false;
58 }
59 if (formatResult.format->getFormatId() == BaseDocumentFormats::PLAIN_GENBANK) {
60 if (docHints.value(RawDataCheckResult_Sequence) == false) {
61 static const int MAX_LINE = 8192;
62 char buff[MAX_LINE + 1] = {0};
63
64 QScopedPointer<LocalFileAdapterFactory> factory(new LocalFileAdapterFactory());
65 QScopedPointer<IOAdapter> io(factory->createIOAdapter());
66 if (!io->open(formatResult.url, IOAdapterMode_Read)) {
67 return false;
68 }
69 bool terminatorFound = false;
70 io->readLine(buff, MAX_LINE, &terminatorFound);
71 if (!terminatorFound) {
72 return false;
73 }
74 QString line = QString(QByteArray(buff));
75 QStringList words = line.split(QRegExp("\\s"), QString::SkipEmptyParts);
76 if (words.size() < 3) { // origin len not defined
77 return false;
78 }
79 bool isLenDefined = false;
80 qint64 seqLen = words[2].toLongLong(&isLenDefined);
81
82 if (!isLenDefined || seqLen <= 0) {
83 return false;
84 }
85 (*headerSequenceLengths)[formatResult.url.getURLString()] = seqLen;
86 return true;
87 }
88 }
89 return docHints.value(RawDataCheckResult_Sequence).toBool();
90 }
91
MultipleDocumentsReadingModeDialog(const QList<GUrl> & _urls,QWidget * parent)92 MultipleDocumentsReadingModeDialog::MultipleDocumentsReadingModeDialog(const QList<GUrl> &_urls, QWidget *parent)
93 : QDialog(parent),
94 saveController(nullptr),
95 urls(_urls) {
96 }
97
setupNewUrl()98 QString MultipleDocumentsReadingModeDialog::setupNewUrl() {
99 QString urlStr = newDocUrl->text();
100 if (urlStr.isEmpty()) {
101 GUrl url = urls.at(0);
102 urlStr = url.getURLString();
103 }
104 QFileInfo fi(urlStr);
105 urlStr = fi.dir().path();
106
107 QString extension4MergedDocument;
108 if (mergeMode->isChecked()) {
109 extension4MergedDocument = AppContext::getDocumentFormatRegistry()->getFormatById(BaseDocumentFormats::PLAIN_GENBANK)->getSupportedDocumentFileExtensions().first();
110 } else if (join2alignmentMode->isChecked()) {
111 extension4MergedDocument = AppContext::getDocumentFormatRegistry()->getFormatById(BaseDocumentFormats::CLUSTAL_ALN)->getSupportedDocumentFileExtensions().first();
112 }
113
114 return urlStr + "/merged_document." + extension4MergedDocument;
115 }
116
setupGUI(QList<GUrl> & _urls,QVariantMap & props,const QMap<QString,qint64> & headerSequenceLengths)117 bool MultipleDocumentsReadingModeDialog::setupGUI(QList<GUrl> &_urls, QVariantMap &props, const QMap<QString, qint64> &headerSequenceLengths) {
118 setModal(true);
119 setupUi(this);
120 new HelpButton(this, buttonBox, "65929449");
121 buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK"));
122 buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
123
124 // doesn't matter from what position, because excluded fileName all path of documents are the same
125 CHECK(!urls.isEmpty(), false);
126
127 connect(separateMode, SIGNAL(toggled(bool)), SLOT(sl_optionChanged()));
128 connect(mergeMode, SIGNAL(toggled(bool)), SLOT(sl_optionChanged()));
129 connect(join2alignmentMode, SIGNAL(toggled(bool)), SLOT(sl_optionChanged()));
130 connect(upperButton, SIGNAL(clicked()), SLOT(sl_onMoveUp()));
131 connect(bottomButton, SIGNAL(clicked()), SLOT(sl_onMoveDown()));
132
133 upperButton->setIcon(QIcon(":ugene/images/up.png"));
134 bottomButton->setIcon(QIcon(":ugene/images/down.png"));
135
136 for (int i = 0; i < urls.size(); ++i) {
137 listDocuments->addItem(new QListWidgetItem(QString("%1. ").arg(i + 1) + urls.at(i).fileName(), listDocuments));
138 }
139
140 int rc = exec();
141
142 if (rc == QDialog::Rejected) {
143 return false;
144 }
145
146 if (separateMode->isChecked() || saveController->getSaveFileName().isEmpty()) {
147 return true;
148 }
149
150 deleteAllNumPrefix();
151 _urls.clear();
152
153 for (int i = 0; i < listDocuments->count(); ++i) {
154 _urls << findUrlByFileName(listDocuments->item(i)->text());
155 }
156
157 listDocuments->clear();
158
159 if (mergeMode->isChecked()) {
160 props[DocumentReadingMode_SequenceMergeGapSize] = fileGap->value();
161 props[ProjectLoaderHint_MultipleFilesMode_RealDocumentFormat] = BaseDocumentFormats::PLAIN_GENBANK;
162
163 foreach (const GUrl &url, _urls) {
164 props[RawDataCheckResult_HeaderSequenceLength + url.getURLString()] = headerSequenceLengths.value(url.getURLString(), -1);
165 }
166 } else if (join2alignmentMode->isChecked()) {
167 props[ProjectLoaderHint_MultipleFilesMode_RealDocumentFormat] = BaseDocumentFormats::CLUSTAL_ALN;
168 props[DocumentReadingMode_SequenceAsAlignmentHint] = true;
169 }
170 props[ProjectLoaderHint_MultipleFilesMode_URLDocument] = saveController->getSaveFileName();
171 props[ProjectLoaderHint_MultipleFilesMode_SaveDocumentFlag] = saveBox->isChecked();
172 props[ProjectLoaderHint_MultipleFilesMode_Flag] = true;
173
174 QStringList urlsStr;
175 foreach (GUrl url, _urls) {
176 urlsStr << url.getURLString();
177 }
178
179 props[ProjectLoaderHint_MultipleFilesMode_URLsDocumentConsistOf] = urlsStr;
180
181 _urls.clear();
182 _urls << GUrl(saveController->getSaveFileName());
183
184 return true;
185 }
186
findUrlByFileName(const QString & fileName)187 QString MultipleDocumentsReadingModeDialog::findUrlByFileName(const QString &fileName) {
188 foreach (GUrl url, urls) {
189 QString _fileName = url.fileName();
190 if (_fileName == fileName) {
191 return url.getURLString();
192 }
193 }
194 assert(false && "Error finding url by file name");
195 return "";
196 }
197
sl_optionChanged()198 void MultipleDocumentsReadingModeDialog::sl_optionChanged() {
199 bool isNewDocInfoAvailable = !separateMode->isChecked();
200
201 saveBox->setEnabled(isNewDocInfoAvailable);
202 listDocuments->setEnabled(isNewDocInfoAvailable);
203 upperButton->setEnabled(isNewDocInfoAvailable);
204 bottomButton->setEnabled(isNewDocInfoAvailable);
205 newDocLabel->setEnabled(isNewDocInfoAvailable);
206 newDocUrl->setEnabled(isNewDocInfoAvailable);
207
208 bool mergeInfoAvailable = mergeMode->isChecked();
209 fileGap->setEnabled(mergeInfoAvailable);
210 mergeModeLabel->setEnabled(mergeInfoAvailable);
211
212 if (mergeMode->isChecked()) {
213 delete saveController;
214 initSequenceSaveController();
215 } else if (join2alignmentMode->isChecked()) {
216 delete saveController;
217 initMsaSaveController();
218 } else {
219 return;
220 }
221 }
222
initSequenceSaveController()223 void MultipleDocumentsReadingModeDialog::initSequenceSaveController() {
224 SaveDocumentControllerConfig config;
225 config.defaultDomain = "SaveMergeDocumentsAsOneDocument";
226 config.defaultFileName = setupNewUrl();
227 config.defaultFormatId = BaseDocumentFormats::PLAIN_GENBANK;
228 config.fileDialogButton = toolButton;
229 config.fileNameEdit = newDocUrl;
230 config.parentWidget = this;
231 config.saveTitle = tr("Select file to save new document");
232 config.rollOutProjectUrls = true;
233
234 const QList<DocumentFormatId> formats = QList<DocumentFormatId>() << BaseDocumentFormats::PLAIN_GENBANK;
235
236 saveController = new SaveDocumentController(config, formats, this);
237 }
238
initMsaSaveController()239 void MultipleDocumentsReadingModeDialog::initMsaSaveController() {
240 SaveDocumentControllerConfig config;
241 config.defaultDomain = "SaveMergeDocumentsAsOneDocument";
242 config.defaultFileName = setupNewUrl();
243 config.defaultFormatId = BaseDocumentFormats::CLUSTAL_ALN;
244 config.fileDialogButton = toolButton;
245 config.fileNameEdit = newDocUrl;
246 config.parentWidget = this;
247 config.saveTitle = tr("Select file to save new document");
248 config.rollOutProjectUrls = true;
249
250 const QList<DocumentFormatId> formats = QList<DocumentFormatId>() << BaseDocumentFormats::CLUSTAL_ALN;
251
252 saveController = new SaveDocumentController(config, formats, this);
253 }
254
sl_onMoveUp()255 void MultipleDocumentsReadingModeDialog::sl_onMoveUp() {
256 QListWidgetItem *item = listDocuments->currentItem();
257 if (item == nullptr) {
258 return;
259 }
260 int row = listDocuments->row(item);
261 if (row == 0) {
262 return;
263 }
264
265 QListWidgetItem *newCurrent = new QListWidgetItem(item->text());
266 listDocuments->insertItem(row - 1, newCurrent);
267 listDocuments->removeItemWidget(item);
268 delete item;
269
270 changeNumPrefix();
271 listDocuments->setCurrentItem(newCurrent);
272 }
273
sl_onMoveDown()274 void MultipleDocumentsReadingModeDialog::sl_onMoveDown() {
275 QListWidgetItem *item = listDocuments->currentItem();
276 if (item == nullptr) {
277 return;
278 }
279 int row = listDocuments->row(item);
280 if (row == listDocuments->count() - 1) {
281 return;
282 }
283
284 QListWidgetItem *newCurrent = new QListWidgetItem(item->text());
285 listDocuments->insertItem(row + 2, newCurrent);
286 listDocuments->removeItemWidget(item);
287 delete item;
288
289 changeNumPrefix();
290 listDocuments->setCurrentItem(newCurrent);
291 }
292
changeNumPrefix()293 void MultipleDocumentsReadingModeDialog::changeNumPrefix() {
294 for (int i = 0; i < listDocuments->count(); ++i) {
295 listDocuments->item(i)->setText(QString("%1. ").arg(i + 1) + deleteNumPrefix(listDocuments->item(i)->text()));
296 }
297 }
298
deleteNumPrefix(QString prefixString)299 QString MultipleDocumentsReadingModeDialog::deleteNumPrefix(QString prefixString) {
300 QString toDelete = prefixString.split(" ")[0]; // number with space
301 prefixString.remove(0, toDelete.size() + 1);
302 return prefixString;
303 }
304
deleteAllNumPrefix()305 void MultipleDocumentsReadingModeDialog::deleteAllNumPrefix() {
306 for (int i = 0; i < listDocuments->count(); ++i) {
307 listDocuments->item(i)->setText(deleteNumPrefix(listDocuments->item(i)->text()));
308 }
309 }
310
311 } // namespace U2
312