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 <math.h>
23
24 #include <QColorDialog>
25 #include <QDesktopWidget>
26
27 #include <U2Core/AppContext.h>
28 #include <U2Core/DNAAlphabet.h>
29 #include <U2Core/DNASequenceObject.h>
30 #include <U2Core/GObjectUtils.h>
31 #include <U2Core/ProjectModel.h>
32 #include <U2Core/QObjectScopedPointer.h>
33 #include <U2Core/U2SafePoints.h>
34 #include <U2Core/global.h>
35
36 #include <U2Gui/CreateAnnotationWidgetController.h>
37 #include <U2Gui/DialogUtils.h>
38 #include <U2Gui/HelpButton.h>
39 #include <U2Gui/LastUsedDirHelper.h>
40 #include <U2Gui/U2FileDialog.h>
41
42 #include <U2View/ADVSequenceObjectContext.h>
43 #include <U2View/AnnotatedDNAView.h>
44
45 #include "DotPlotDialog.h"
46 #include "DotPlotTasks.h"
47
48 namespace U2 {
49
DotPlotDialog(QWidget * parent,AnnotatedDNAView * currentADV,int minLen,int identity,ADVSequenceObjectContext * sequenceX,ADVSequenceObjectContext * sequenceY,bool dir,bool inv,const QColor & dColor,const QColor & iColor,bool hideLoadSequences)50 DotPlotDialog::DotPlotDialog(QWidget *parent, AnnotatedDNAView *currentADV, int minLen, int identity, ADVSequenceObjectContext *sequenceX, ADVSequenceObjectContext *sequenceY, bool dir, bool inv, const QColor &dColor, const QColor &iColor, bool hideLoadSequences)
51 : QDialog(parent), xSeq(sequenceX), ySeq(sequenceY), adv(currentADV), directColor(dColor), invertedColor(iColor), openSequenceTask(nullptr) {
52 setupUi(this);
53
54 new HelpButton(this, buttonBox, "65929583");
55 buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK"));
56 buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
57
58 SAFE_POINT(adv != nullptr, "DotPlotDialog called without view context!", );
59
60 directCheckBox->setChecked(dir);
61 invertedCheckBox->setChecked(inv);
62
63 updateColors();
64
65 // set algorithms
66 algoCombo->addItem(tr("Auto"), RFAlgorithm_Auto);
67 algoCombo->addItem(tr("Suffix index"), RFAlgorithm_Suffix);
68 algoCombo->addItem(tr("Diagonals"), RFAlgorithm_Diagonal);
69
70 minLenBox->setValue(minLen);
71 identityBox->setValue(identity);
72
73 connect(minLenHeuristicsButton, SIGNAL(clicked()), SLOT(sl_minLenHeuristics()));
74 connect(hundredPercentButton, SIGNAL(clicked()), SLOT(sl_hundredPercent()));
75
76 connect(directCheckBox, SIGNAL(clicked()), SLOT(sl_directInvertedCheckBox()));
77 connect(invertedCheckBox, SIGNAL(clicked()), SLOT(sl_directInvertedCheckBox()));
78
79 connect(directColorButton, SIGNAL(clicked()), SLOT(sl_directColorButton()));
80 connect(invertedColorButton, SIGNAL(clicked()), SLOT(sl_invertedColorButton()));
81
82 connect(directDefaultColorButton, SIGNAL(clicked()), SLOT(sl_directDefaultColorButton()));
83 connect(invertedDefaultColorButton, SIGNAL(clicked()), SLOT(sl_invertedDefaultColorButton()));
84
85 connect(loadSequenceButton, SIGNAL(clicked()), SLOT(sl_loadSequenceButton()));
86
87 // listen to project modification to update list of available sequence objects.
88 Project *project = AppContext::getProject();
89 connect(project, SIGNAL(si_documentAdded(Document *)), SLOT(sl_documentAddedOrRemoved()));
90 connect(project, SIGNAL(si_documentRemoved(Document *)), SLOT(sl_documentAddedOrRemoved()));
91 reconnectAllProjectDocuments();
92 updateSequenceSelectors();
93
94 connect(xAxisCombo, SIGNAL(currentIndexChanged(int)), SLOT(sl_sequenceSelectorIndexChanged()));
95 connect(yAxisCombo, SIGNAL(currentIndexChanged(int)), SLOT(sl_sequenceSelectorIndexChanged()));
96 sl_sequenceSelectorIndexChanged();
97
98 if (hideLoadSequences) {
99 loadSequenceButton->hide();
100 }
101 }
102
reconnectAllProjectDocuments()103 void DotPlotDialog::reconnectAllProjectDocuments() {
104 Project *project = AppContext::getProject();
105 foreach (Document *d, project->getDocuments()) {
106 d->disconnect(this);
107 connect(d, SIGNAL(si_objectAdded(GObject *)), SLOT(sl_objectAddedOrRemoved()));
108 connect(d, SIGNAL(si_objectRemoved(GObject *)), SLOT(sl_objectAddedOrRemoved()));
109 connect(d, SIGNAL(si_loadedStateChanged()), SLOT(sl_loadedStateChanged()));
110 }
111 }
112
updateSequenceSelectors()113 void DotPlotDialog::updateSequenceSelectors() {
114 xAxisCombo->clear();
115 yAxisCombo->clear();
116
117 int xSeqIndex = -1, ySeqIndex = -1, curIndex = 0;
118
119 // sequences in the project
120 QList<GObject *> sequenceObjects = GObjectUtils::findAllObjects(UOF_LoadedOnly, GObjectTypes::SEQUENCE);
121 foreach (GObject *obj, sequenceObjects) {
122 U2SequenceObject *seqObj = qobject_cast<U2SequenceObject *>(obj);
123 QString name = seqObj->getGObjectName();
124
125 xAxisCombo->addItem(name);
126 yAxisCombo->addItem(name);
127
128 if (xSeq && (xSeq->getSequenceGObject() == seqObj)) {
129 xSeqIndex = curIndex;
130 }
131 if (ySeq && (ySeq->getSequenceGObject() == seqObj)) {
132 ySeqIndex = curIndex;
133 }
134 curIndex++;
135 }
136
137 if (xSeqIndex >= 0) {
138 xAxisCombo->setCurrentIndex(xSeqIndex);
139 }
140 if (ySeqIndex >= 0) {
141 yAxisCombo->setCurrentIndex(ySeqIndex);
142 } else if (sequenceObjects.size() > 1) { // choose the second sequence for Y axis by default
143 yAxisCombo->setCurrentIndex(1);
144 }
145 }
146
sl_documentAddedOrRemoved()147 void DotPlotDialog::sl_documentAddedOrRemoved() {
148 reconnectAllProjectDocuments();
149 updateSequenceSelectors();
150 }
151
sl_objectAddedOrRemoved()152 void DotPlotDialog::sl_objectAddedOrRemoved() {
153 updateSequenceSelectors();
154 }
155
sl_loadedStateChanged()156 void DotPlotDialog::sl_loadedStateChanged() {
157 updateSequenceSelectors();
158 }
159
sl_sequenceSelectorIndexChanged()160 void DotPlotDialog::sl_sequenceSelectorIndexChanged() {
161 int xIdx = xAxisCombo->currentIndex();
162 int yIdx = yAxisCombo->currentIndex();
163
164 QList<GObject *> sequenceObjects = GObjectUtils::findAllObjects(UOF_LoadedOnly, GObjectTypes::SEQUENCE);
165 SAFE_POINT(xIdx >= 0 && xIdx < sequenceObjects.length(), QString("DotPlotDialog: index is out of range: %1").arg(xIdx), );
166 SAFE_POINT(yIdx >= 0 && yIdx < sequenceObjects.length(), QString("DotPlotDialog: index is out of range: %1").arg(yIdx), );
167
168 U2SequenceObject *objX = qobject_cast<U2SequenceObject *>(sequenceObjects[xIdx]);
169 U2SequenceObject *objY = qobject_cast<U2SequenceObject *>(sequenceObjects[yIdx]);
170 if (!objX->getAlphabet()->isNucleic() || !objY->getAlphabet()->isNucleic()) {
171 invertedCheckBox->setDisabled(true);
172 invertedColorButton->setDisabled(true);
173 invertedDefaultColorButton->setDisabled(true);
174 } else {
175 invertedCheckBox->setDisabled(false);
176 invertedColorButton->setDisabled(false);
177 invertedDefaultColorButton->setDisabled(false);
178 }
179 int defaultWindow = qMin(objX->getSequenceLength(), objY->getSequenceLength());
180 defaultWindow = defaultWindow < 100 ? defaultWindow : 100;
181 if (minLenBox->value() > defaultWindow) {
182 minLenBox->setValue(defaultWindow);
183 }
184 }
185
accept()186 void DotPlotDialog::accept() {
187 int xIdx = xAxisCombo->currentIndex();
188 int yIdx = yAxisCombo->currentIndex();
189
190 QList<GObject *> sequenceObjects = GObjectUtils::findAllObjects(UOF_LoadedOnly, GObjectTypes::SEQUENCE);
191 SAFE_POINT(xIdx >= 0 && xIdx < sequenceObjects.length(), QString("DotPlotDialog: index is out of range: %1").arg(xIdx), );
192 SAFE_POINT(yIdx >= 0 && yIdx < sequenceObjects.length(), QString("DotPlotDialog: index is out of range: %1").arg(yIdx), );
193
194 U2SequenceObject *objX = qobject_cast<U2SequenceObject *>(sequenceObjects[xIdx]);
195 U2SequenceObject *objY = qobject_cast<U2SequenceObject *>(sequenceObjects[yIdx]);
196
197 if (!isObjectInADV(objX)) {
198 adv->addObject(objX);
199 }
200
201 if (!isObjectInADV(objY)) {
202 adv->addObject(objY);
203 }
204
205 xSeq = adv->getSequenceContext(objX);
206 ySeq = adv->getSequenceContext(objY);
207
208 QDialog::accept();
209 }
210
sl_minLenHeuristics()211 void DotPlotDialog::sl_minLenHeuristics() {
212 identityBox->setValue(100);
213
214 // formula used here: nVariations / lenVariations = wantedResCount (==1000)
215 // where nVariations == area size
216 // lenVariations = 4^len where len is result
217 // so we have len = ln(nVariations/wantedResCount)/ln(4)
218
219 int xIdx = xAxisCombo->currentIndex();
220 int yIdx = yAxisCombo->currentIndex();
221
222 QList<GObject *> sequenceObjects = GObjectUtils::findAllObjects(UOF_LoadedOnly, GObjectTypes::SEQUENCE);
223 SAFE_POINT(xIdx >= 0 && xIdx < sequenceObjects.length(), QString("DotPlotDialog: index is out of range: %1").arg(xIdx), );
224 SAFE_POINT(yIdx >= 0 && yIdx < sequenceObjects.length(), QString("DotPlotDialog: index is out of range: %1").arg(yIdx), );
225
226 U2SequenceObject *objX = qobject_cast<U2SequenceObject *>(sequenceObjects[xIdx]);
227 U2SequenceObject *objY = qobject_cast<U2SequenceObject *>(sequenceObjects[yIdx]);
228
229 qint64 xSeqLen = objX->getSequenceLength();
230 qint64 ySeqLen = objY->getSequenceLength();
231
232 double nVariations = xSeqLen * ySeqLen;
233 double resCount = 1000;
234 double len = log(nVariations / resCount) / log(double(4));
235
236 minLenBox->setValue((int)len);
237 }
238
sl_hundredPercent()239 void DotPlotDialog::sl_hundredPercent() {
240 identityBox->setValue(100);
241 }
242
getMismatches() const243 int DotPlotDialog::getMismatches() const {
244 return (100 - identityBox->value()) * minLenBox->value() / 100;
245 }
246
247 // which algorithm
getAlgo() const248 RFAlgorithm DotPlotDialog::getAlgo() const {
249 if (algoCheck->isChecked()) {
250 int index = algoCombo->currentIndex();
251 return RFAlgorithm(algoCombo->itemData(index).toInt());
252 }
253 return RFAlgorithm_Auto;
254 }
255
getMinLen() const256 int DotPlotDialog::getMinLen() const {
257 return minLenBox->value();
258 }
259
isDirect() const260 bool DotPlotDialog::isDirect() const {
261 return directCheckBox->isChecked();
262 }
263
isInverted() const264 bool DotPlotDialog::isInverted() const {
265 return invertedCheckBox->isChecked() && invertedCheckBox->isEnabled();
266 }
267
sl_directInvertedCheckBox()268 void DotPlotDialog::sl_directInvertedCheckBox() {
269 buttonBox->button(QDialogButtonBox::Ok)->setEnabled(isDirect() || isInverted());
270 }
271
272 static const QString COLOR_STYLE("QPushButton { background-color: %1 }");
273
sl_directColorButton()274 void DotPlotDialog::sl_directColorButton() {
275 QObjectScopedPointer<QColorDialog> d = new QColorDialog(directColor, this);
276 d->exec();
277 CHECK(!d.isNull(), );
278
279 if (QDialog::Accepted == d->result()) {
280 directColor = d->selectedColor();
281 directCheckBox->setChecked(true);
282 }
283
284 updateColors();
285 }
286
sl_invertedColorButton()287 void DotPlotDialog::sl_invertedColorButton() {
288 QObjectScopedPointer<QColorDialog> d = new QColorDialog(invertedColor, this);
289 d->exec();
290 CHECK(!d.isNull(), );
291
292 if (QDialog::Accepted == d->result()) {
293 invertedColor = d->selectedColor();
294 invertedCheckBox->setChecked(true);
295 }
296
297 updateColors();
298 }
299
sl_directDefaultColorButton()300 void DotPlotDialog::sl_directDefaultColorButton() {
301 directColor = QColor();
302 directCheckBox->setChecked(true);
303 updateColors();
304 }
305
sl_invertedDefaultColorButton()306 void DotPlotDialog::sl_invertedDefaultColorButton() {
307 invertedColor = QColor();
308 invertedCheckBox->setChecked(true);
309 updateColors();
310 }
311
sl_loadSequenceButton()312 void DotPlotDialog::sl_loadSequenceButton() {
313 QString filter = DialogUtils::prepareDocumentsFileFilterByObjType(GObjectTypes::SEQUENCE, true);
314 LastUsedDirHelper lod("DotPlot file");
315 lod.url = U2FileDialog::getOpenFileName(this, tr("Open file"), lod.dir, filter);
316 if (!lod.url.isEmpty()) {
317 Task *tasks = new Task("Adding document to the project", TaskFlag_NoRun);
318
319 if (!AppContext::getProject()) {
320 tasks->addSubTask(AppContext::getProjectLoader()->createNewProjectTask());
321 }
322
323 QVariantMap hints;
324 hints[ProjectLoaderHint_LoadWithoutView] = false;
325 hints[ProjectLoaderHint_LoadUnloadedDocument] = true;
326 openSequenceTask = AppContext::getProjectLoader()->openWithProjectTask(lod.url, hints);
327 if (openSequenceTask == nullptr) {
328 return;
329 }
330 tasks->addSubTask(openSequenceTask);
331
332 connect(AppContext::getTaskScheduler(), SIGNAL(si_stateChanged(Task *)), SLOT(sl_loadTaskStateChanged(Task *)));
333
334 AppContext::getTaskScheduler()->registerTopLevelTask(tasks);
335 }
336 }
337
sl_loadTaskStateChanged(Task * t)338 void DotPlotDialog::sl_loadTaskStateChanged(Task *t) {
339 DotPlotLoadDocumentsTask *loadTask = qobject_cast<DotPlotLoadDocumentsTask *>(t);
340 if (loadTask == nullptr) {
341 return;
342 }
343 if (loadTask->getStateInfo().hasError()) {
344 QMessageBox::critical(this, tr("Error"), tr("Error opening files"));
345 return;
346 }
347 }
348
updateColors()349 void DotPlotDialog::updateColors() {
350 directColorButton->setStyleSheet(COLOR_STYLE.arg(directColor.name()));
351 invertedColorButton->setStyleSheet(COLOR_STYLE.arg(invertedColor.name()));
352 }
353
isObjectInADV(GObject * obj)354 bool DotPlotDialog::isObjectInADV(GObject *obj) {
355 SAFE_POINT(obj != nullptr, "Object is NULL in DotPlotDialog::isObjectInADV(GObject* obj)", false);
356
357 return adv->containsObject(obj);
358 }
359
getGObjectByName(const QString & gObjectName)360 GObject *DotPlotDialog::getGObjectByName(const QString &gObjectName) {
361 QList<GObject *> allSequences = GObjectUtils::findAllObjects(UOF_LoadedOnly, GObjectTypes::SEQUENCE);
362 GObject *obj = nullptr;
363 foreach (GObject *s, allSequences) {
364 if (gObjectName == s->getGObjectName()) {
365 obj = s;
366 }
367 }
368 return obj;
369 }
370
371 } // namespace U2
372