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 "MaConsensusMismatchController.h"
23 
24 #include <U2Algorithm/MSAConsensusAlgorithm.h>
25 
26 #include <U2Core/AppContext.h>
27 #include <U2Core/Counter.h>
28 #include <U2Core/DNASequenceSelection.h>
29 
30 #include <U2Gui/GUIUtils.h>
31 #include <U2Gui/Notification.h>
32 
33 #include "MSAEditorConsensusCache.h"
34 #include "McaEditor.h"
35 #include "McaEditorSequenceArea.h"
36 #include "ov_msa/view_rendering/MaEditorSequenceArea.h"
37 #include "ov_sequence/SequenceObjectContext.h"
38 
39 namespace U2 {
40 
MaConsensusMismatchController(QObject * p,const QSharedPointer<MSAEditorConsensusCache> & consCache,MaEditor * editor)41 MaConsensusMismatchController::MaConsensusMismatchController(QObject *p,
42                                                              const QSharedPointer<MSAEditorConsensusCache> &consCache,
43                                                              MaEditor *editor)
44     : QObject(p),
45       consCache(consCache),
46       editor(editor),
47       nextMismatch(nullptr),
48       prevMismatch(nullptr) {
49     mismatchCache = QBitArray(editor->getAlignmentLen(), false);
50     connect(consCache.data(), SIGNAL(si_cachedItemUpdated(int, char)), SLOT(sl_updateItem(int, char)));
51     connect(consCache.data(), SIGNAL(si_cacheResized(int)), SLOT(sl_resize(int)));
52 
53     nextMismatch = new QAction(QIcon(":core/images/mismatch-forward.png"), tr("Jump to next variation"), this);
54     nextMismatch->setObjectName("next_mismatch");
55     nextMismatch->setShortcut(Qt::CTRL + Qt::ALT + Qt::Key_V);
56     GUIUtils::updateActionToolTip(nextMismatch);
57     connect(nextMismatch, SIGNAL(triggered(bool)), SLOT(sl_next()));
58 
59     prevMismatch = new QAction(QIcon(":core/images/mismatch-backward.png"), tr("Jump to previous variation"), this);
60     prevMismatch->setShortcut(Qt::CTRL + Qt::ALT + Qt::SHIFT + Qt::Key_V);
61     prevMismatch->setObjectName("prev_mismatch");
62     GUIUtils::updateActionToolTip(prevMismatch);
63     connect(prevMismatch, SIGNAL(triggered(bool)), SLOT(sl_prev()));
64 }
65 
isMismatch(int pos) const66 bool MaConsensusMismatchController::isMismatch(int pos) const {
67     SAFE_POINT(0 <= pos && pos < mismatchCache.size(), "Invalid pos", false);
68     return mismatchCache[pos];
69 }
70 
getPrevMismatchAction() const71 QAction *MaConsensusMismatchController::getPrevMismatchAction() const {
72     return prevMismatch;
73 }
74 
getNextMismatchAction() const75 QAction *MaConsensusMismatchController::getNextMismatchAction() const {
76     return nextMismatch;
77 }
78 
sl_updateItem(int pos,char c)79 void MaConsensusMismatchController::sl_updateItem(int pos, char c) {
80     SAFE_POINT(0 <= pos && pos < mismatchCache.size(), "Invalid pos", );
81     mismatchCache[pos] = c != MSAConsensusAlgorithm::INVALID_CONS_CHAR && editor->getReferenceCharAt(pos) != c;
82 }
83 
sl_resize(int newSize)84 void MaConsensusMismatchController::sl_resize(int newSize) {
85     mismatchCache.resize(newSize);
86     mismatchCache.fill(false);
87 }
88 
sl_next()89 void MaConsensusMismatchController::sl_next() {
90     GCounter::increment("Jump to next variation", editor->getFactoryId());
91     selectNextMismatch(Forward);
92 }
93 
sl_prev()94 void MaConsensusMismatchController::sl_prev() {
95     GCounter::increment("Jump to previous variation", editor->getFactoryId());
96     selectNextMismatch(Backward);
97 }
98 
selectNextMismatch(NavigationDirection direction)99 void MaConsensusMismatchController::selectNextMismatch(NavigationDirection direction) {
100     McaEditor *mcaEditor = qobject_cast<McaEditor *>(editor);
101     CHECK(mcaEditor != nullptr, );
102 
103     SequenceObjectContext *ctx = mcaEditor->getReferenceContext();
104     int initialPos = -1;
105 
106     if (ctx->getSequenceSelection()->isEmpty()) {
107         // find next/prev from visible range
108         MaEditorSequenceArea *seqArea = mcaEditor->getUI()->getSequenceArea();
109         initialPos = seqArea->getFirstVisibleBase() != 0 ? seqArea->getFirstVisibleBase() - 1 : mismatchCache.size() - 1;
110     } else {
111         // find next/prev from referenece selection
112         DNASequenceSelection *selection = ctx->getSequenceSelection();
113         initialPos = selection->getSelectedRegions().first().startPos;
114     }
115 
116     int pos = initialPos;
117     do {
118         switch (direction) {
119             case Forward:
120                 pos++;
121                 if (pos == mismatchCache.size()) {
122                     pos = 0;
123                 }
124                 break;
125             default:
126                 pos--;
127                 if (pos == -1) {
128                     pos = mismatchCache.size() - 1;
129                 }
130                 break;
131         }
132         consCache->updateCacheItem(pos);
133         if (mismatchCache[pos] == true) {
134             emit si_selectMismatch(pos);
135             return;
136         }
137     } while (pos != initialPos);
138     NotificationStack::addNotification(tr("There are no variations in the consensus sequence."), Info_Not);
139 }
140 
141 }  // namespace U2
142