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 <limits>
23 
24 #include <QAction>
25 #include <QApplication>
26 #include <QMenu>
27 #include <QMessageBox>
28 #include <QScrollArea>
29 #include <QToolBar>
30 #include <QVBoxLayout>
31 
32 #include <U2Core/AnnotationSelection.h>
33 #include <U2Core/AnnotationSettings.h>
34 #include <U2Core/AnnotationTableObject.h>
35 #include <U2Core/AppContext.h>
36 #include <U2Core/AutoAnnotationsSupport.h>
37 #include <U2Core/ClipboardController.h>
38 #include <U2Core/DNASequenceObject.h>
39 #include <U2Core/DNASequenceSelection.h>
40 #include <U2Core/GObjectUtils.h>
41 #include <U2Core/L10n.h>
42 #include <U2Core/ModifySequenceObjectTask.h>
43 #include <U2Core/ProjectModel.h>
44 #include <U2Core/QObjectScopedPointer.h>
45 #include <U2Core/RemoveAnnotationsTask.h>
46 #include <U2Core/ReverseSequenceTask.h>
47 #include <U2Core/SelectionUtils.h>
48 #include <U2Core/SequenceUtils.h>
49 #include <U2Core/Settings.h>
50 #include <U2Core/TaskSignalMapper.h>
51 #include <U2Core/Timer.h>
52 #include <U2Core/U2AlphabetUtils.h>
53 #include <U2Core/U2OpStatusUtils.h>
54 #include <U2Core/U2SequenceUtils.h>
55 
56 #include <U2Gui/CreateObjectRelationDialogController.h>
57 #include <U2Gui/EditSequenceDialogController.h>
58 #include <U2Gui/EditSettingsDialog.h>
59 #include <U2Gui/GUIUtils.h>
60 #include <U2Gui/OPWidgetFactoryRegistry.h>
61 #include <U2Gui/OptionsPanel.h>
62 #include <U2Gui/PositionSelector.h>
63 #include <U2Gui/RemovePartFromSequenceDialogController.h>
64 
65 #include <U2View/CodonTable.h>
66 #include <U2View/FindPatternWidgetFactory.h>
67 #include <U2View/SecStructPredictUtils.h>
68 
69 #include "ADVAnnotationCreation.h"
70 #include "ADVClipboard.h"
71 #include "ADVConstants.h"
72 #include "ADVSequenceObjectContext.h"
73 #include "ADVSingleSequenceWidget.h"
74 #include "ADVSyncViewManager.h"
75 #include "AnnotatedDNAView.h"
76 #include "AnnotatedDNAViewFactory.h"
77 #include "AnnotatedDNAViewState.h"
78 #include "AnnotatedDNAViewTasks.h"
79 #include "AnnotationsTreeView.h"
80 #include "AutoAnnotationUtils.h"
81 #include "DetView.h"
82 #include "DetViewSequenceEditor.h"
83 #include "GraphMenu.h"
84 
85 namespace U2 {
86 
AnnotatedDNAView(const QString & viewName,const QList<U2SequenceObject * > & dnaObjects)87 AnnotatedDNAView::AnnotatedDNAView(const QString &viewName, const QList<U2SequenceObject *> &dnaObjects)
88     : GObjectView(AnnotatedDNAViewFactory::ID, viewName) {
89     timerId = 0;
90     hadExpandableSequenceWidgetsLastResize = false;
91 
92     annotationSelection = new AnnotationSelection(this);
93     annotationGroupSelection = new AnnotationGroupSelection(this);
94 
95     clipb = nullptr;
96 
97     mainSplitter = nullptr;
98     scrollArea = nullptr;
99     posSelector = nullptr;
100     posSelectorWidgetAction = nullptr;
101     annotationsView = nullptr;
102     activeSequenceWidget = nullptr;
103     replacedSeqWidget = nullptr;
104 
105     codonTableView = new CodonTableView(this);
106     connect(this, SIGNAL(si_activeSequenceWidgetChanged(ADVSequenceWidget *, ADVSequenceWidget *)), codonTableView, SLOT(sl_onActiveSequenceChanged(ADVSequenceWidget *, ADVSequenceWidget *)));
107     createCodonTableAction();
108     createAnnotationAction = (new ADVAnnotationCreation(this))->getCreateAnnotationAction();
109 
110     posSelectorAction = new QAction(QIcon(":core/images/goto.png"), tr("Go to position..."), this);
111     posSelectorAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_G));
112     posSelectorAction->setShortcutContext(Qt::WindowShortcut);
113     posSelectorAction->setObjectName(ADV_GOTO_ACTION);
114     connect(posSelectorAction, SIGNAL(triggered()), SLOT(sl_onShowPosSelectorRequest()));
115 
116     toggleHLAction = new QAction("", this);
117     connect(toggleHLAction, SIGNAL(triggered()), SLOT(sl_toggleHL()));
118 
119     removeAnnsAndQsAction = new QAction("", this);
120     removeAnnsAndQsAction->setShortcut(QKeySequence(Qt::Key_Delete));
121     removeAnnsAndQsAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
122 
123     syncViewManager = new ADVSyncViewManager(this);
124 
125     foreach (U2SequenceObject *dnaObj, dnaObjects) {
126         addObject(dnaObj);
127     }
128 
129     findPatternAction = new ADVGlobalAction(this, QIcon(":core/images/find_dialog.png"), tr("Find pattern..."), 10);
130     findPatternAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_F));
131     findPatternAction->setShortcutContext(Qt::WindowShortcut);
132     connect(findPatternAction, SIGNAL(triggered()), SLOT(sl_onFindPatternClicked()));
133 
134     editSettingsAction = new QAction(tr("Annotation settings on editing..."), this);
135     editSettingsAction->setObjectName(ACTION_EDIT_SEQUENCE_SETTINGS);
136     connect(editSettingsAction, SIGNAL(triggered()), this, SLOT(sl_editSettings()));
137 
138     addSequencePart = new QAction(tr("Insert subsequence..."), this);
139     addSequencePart->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_I));
140     addSequencePart->setObjectName(ACTION_EDIT_INSERT_SUBSEQUENCE);
141     connect(addSequencePart, SIGNAL(triggered()), this, SLOT(sl_addSequencePart()));
142 
143     removeSequencePart = new QAction(tr("Remove subsequence..."), this);
144     removeSequencePart->setObjectName(ACTION_EDIT_REMOVE_SUBSEQUENCE);
145     connect(removeSequencePart, SIGNAL(triggered()), this, SLOT(sl_removeSequencePart()));
146 
147     replaceSequencePart = new QAction(tr("Replace subsequence..."), this);
148     replaceSequencePart->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_R));
149     replaceSequencePart->setObjectName(ACTION_EDIT_REPLACE_SUBSEQUENCE);
150     connect(replaceSequencePart, SIGNAL(triggered()), this, SLOT(sl_replaceSequencePart()));
151 
152     removeSequenceObjectAction = new QAction(tr("Selected sequence from view"), this);
153     removeSequenceObjectAction->setObjectName(ACTION_EDIT_SELECT_SEQUENCE_FROM_VIEW);
154     connect(removeSequenceObjectAction, SIGNAL(triggered()), SLOT(sl_removeSelectedSequenceObject()));
155 
156     reverseComplementSequenceAction = new QAction(tr("Complementary (5'-3') sequence"), this);
157     reverseComplementSequenceAction->setShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_R));
158     reverseComplementSequenceAction->setObjectName(ACTION_EDIT_RESERVE_COMPLEMENT_SEQUENCE);
159     connect(reverseComplementSequenceAction, SIGNAL(triggered()), SLOT(sl_reverseComplementSequence()));
160 
161     reverseSequenceAction = new QAction(tr("Reverse (3'-5') sequence"), this);
162     reverseSequenceAction->setObjectName(ACTION_EDIT_RESERVE_SEQUENCE);
163     connect(reverseSequenceAction, SIGNAL(triggered()), SLOT(sl_reverseSequence()));
164 
165     complementSequenceAction = new QAction(tr("Complementary (3'-5') sequence"), this);
166     complementSequenceAction->setObjectName(ACTION_EDIT_COMPLEMENT_SEQUENCE);
167     connect(complementSequenceAction, SIGNAL(triggered()), SLOT(sl_complementSequence()));
168 
169     SecStructPredictViewAction::createAction(this);
170 }
171 
createPasteAction()172 QAction *AnnotatedDNAView::createPasteAction() {
173     QAction *action = ADVClipboard::createPasteSequenceAction(this);
174     connect(action, SIGNAL(triggered()), this, SLOT(sl_paste()));
175     return action;
176 }
177 
createWidget()178 QWidget *AnnotatedDNAView::createWidget() {
179     GTIMER(c1, t1, "AnnotatedDNAView::createWidget");
180     assert(scrollArea == nullptr);
181 
182     mainSplitter = new QSplitter(Qt::Vertical);
183     mainSplitter->setObjectName("annotated_DNA_splitter");
184     connect(mainSplitter, SIGNAL(splitterMoved(int, int)), SLOT(sl_splitterMoved(int, int)));
185 
186     mainSplitter->addWidget(codonTableView);
187     mainSplitter->setCollapsible(mainSplitter->indexOf(codonTableView), false);
188 
189     mainSplitter->setContextMenuPolicy(Qt::CustomContextMenu);
190     connect(mainSplitter, SIGNAL(customContextMenuRequested(const QPoint &)), SLOT(sl_onContextMenuRequested()));
191 
192     scrollArea = new QScrollArea();
193     scrollArea->setObjectName("annotated_DNA_scrollarea");
194     scrollArea->setWidgetResizable(true);
195     scrollArea->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
196 
197     mainSplitter->addWidget(scrollArea);
198     mainSplitter->setHandleWidth(1);  // make smaller the distance between the Annotations Editor and the  sequence sub-views
199     mainSplitter->setCollapsible(mainSplitter->indexOf(scrollArea), false);
200 
201     scrolledWidget = new QWidget(scrollArea);
202     scrolledWidgetLayout = new QVBoxLayout();
203     scrolledWidgetLayout->setContentsMargins(0, 0, 0, 0);
204     scrolledWidgetLayout->setSpacing(0);
205     scrolledWidgetLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);
206     scrolledWidget->setBackgroundRole(QPalette::Light);
207     scrolledWidget->installEventFilter(this);
208     scrolledWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
209 
210     clipb = new ADVClipboard(this);
211     QAction *pasteAction = clipb->getPasteSequenceAction();
212     pasteAction->setEnabled(false);
213     connect(pasteAction, SIGNAL(triggered()), this, SLOT(sl_paste()));
214 
215     annotationsView = new AnnotationsTreeView(this);
216     annotationsView->setParent(mainSplitter);
217     annotationsView->setObjectName("annotations_tree_view");
218     connect(annotationsView, SIGNAL(si_setCopyQualifierActionStatus(bool, QString)), clipb, SLOT(sl_setCopyQualifierActionStatus(bool, QString)));
219     connect(clipb->getCopyQualifierAction(), SIGNAL(triggered()), annotationsView, SLOT(sl_onCopyQualifierValue()));
220 
221     for (int i = 0; i < seqContexts.size(); ++i) {
222         ADVSequenceObjectContext *seqCtx = seqContexts[i];
223         ADVSingleSequenceWidget *block = new ADVSingleSequenceWidget(seqCtx, this);
224         connect(block, SIGNAL(si_titleClicked(ADVSequenceWidget *)), SLOT(sl_onSequenceWidgetTitleClicked(ADVSequenceWidget *)));
225         connect(seqCtx, SIGNAL(si_aminoTranslationChanged()), SLOT(sl_aminoTranslationChanged()));
226         block->setObjectName("ADV_single_sequence_widget_" + QString::number(i));
227         addSequenceWidget(block);
228         block->addAction(createPasteAction());
229     }
230 
231     mainSplitter->addWidget(annotationsView);
232     mainSplitter->setCollapsible(mainSplitter->indexOf(annotationsView), false);
233 
234     scrolledWidget->setLayout(scrolledWidgetLayout);
235     scrolledWidget->setObjectName("scrolled_widget_layout");
236 
237     // TODO: scroll area does not restore focus for last active child widget after Alt-Tab...
238     scrollArea->setWidget(scrolledWidget);
239 
240     mainSplitter->installEventFilter(this);
241     mainSplitter->setAcceptDrops(true);
242 
243     if (!seqViews.isEmpty()) {
244         setActiveSequenceWidget(seqViews.last());
245     }
246 
247     // add view global shortcuts
248 
249     connect(removeAnnsAndQsAction, SIGNAL(triggered()), annotationsView->removeAnnsAndQsAction, SIGNAL(triggered()));
250 
251     mainSplitter->addAction(toggleHLAction);
252     mainSplitter->addAction(removeSequenceObjectAction);
253 
254     mainSplitter->addAction(removeAnnsAndQsAction);
255 
256     mainSplitter->setWindowIcon(GObjectTypes::getTypeInfo(GObjectTypes::SEQUENCE).icon);
257 
258     // Init the Options Panel
259     optionsPanel = new OptionsPanel(this);
260     OPWidgetFactoryRegistry *opWidgetFactoryRegistry = AppContext::getOPWidgetFactoryRegistry();
261 
262     QList<OPFactoryFilterVisitorInterface *> filters;
263 
264     ADVSequenceObjectContext *ctx;
265     QList<DNAAlphabetType> alphabets;
266 
267     for (int i = 0; i < seqViews.size(); i++) {
268         if (seqViews[i] != nullptr) {
269             ctx = seqViews[i]->getActiveSequenceContext();
270             if (ctx) {
271                 const DNAAlphabet *alphabet = ctx->getAlphabet();
272                 if (alphabet) {
273                     alphabets.append(alphabet->getType());
274                 }
275             }
276         }
277     }
278     filters.append(new OPFactoryFilterVisitor(ObjViewType_SequenceView, alphabets));
279 
280     QList<OPWidgetFactory *> opWidgetFactoriesForSeqView = opWidgetFactoryRegistry->getRegisteredFactories(filters);
281     foreach (OPWidgetFactory *factory, opWidgetFactoriesForSeqView) {
282         optionsPanel->addGroup(factory);
283     }
284 
285     qDeleteAll(filters);
286     return mainSplitter;
287 }
288 
getOptionsPanel()289 OptionsPanel *AnnotatedDNAView::getOptionsPanel() {
290     return optionsPanel;
291 }
292 
sl_splitterMoved(int,int)293 void AnnotatedDNAView::sl_splitterMoved(int, int) {
294     // WORKAROUND: looks like a QT bug:
295     // ADVSequenceWidgets get paint events as needed, but scrolledWidget is over-painted by splitter's handle
296     // to reproduce it open any complex (like 3d structure) view and pull the splitter handle upward slowly
297     // -> workaround: update geometry for scrollArea or repaint main splitter's ares (todo: recheck effect)
298     mainSplitter->repaint(scrollArea->geometry());
299     mainSplitter->refresh();
300 }
301 
sl_onSequenceWidgetTitleClicked(ADVSequenceWidget * seqWidget)302 void AnnotatedDNAView::sl_onSequenceWidgetTitleClicked(ADVSequenceWidget *seqWidget) {
303     replacedSeqWidget = seqWidget;
304 }
305 
timerEvent(QTimerEvent *)306 void AnnotatedDNAView::timerEvent(QTimerEvent *) {
307     // see comment for sl_splitterMoved()
308     assert(timerId != 0);
309     killTimer(timerId);
310     timerId = 0;
311 
312     QWidget *w = scrollArea;
313     QRect orig = w->geometry();
314     QRect tmp = orig;
315     tmp.adjust(0, 0, 1, 1);
316     w->setGeometry(tmp);
317     w->setGeometry(orig);
318 }
319 
updateScrollAreaHeight()320 void AnnotatedDNAView::updateScrollAreaHeight() {
321     if (!scrolledWidget->isVisible()) {
322         return;
323     }
324 
325     int newScrollAreaMaxHeight = 0;
326     foreach (ADVSequenceWidget *v, seqViews) {
327         int widgetMaxHeight = v->maximumHeight();
328         if (widgetMaxHeight == QWIDGETSIZE_MAX) {
329             scrollArea->setMaximumHeight(QWIDGETSIZE_MAX);
330             return;
331         }
332         newScrollAreaMaxHeight += v->maximumHeight();
333     }
334     newScrollAreaMaxHeight += 2;  // magic '+2' is for the borders, without it unneccessary scroll bar will appear
335     if (newScrollAreaMaxHeight <= scrollArea->height()) {
336         scrollArea->setMaximumHeight(newScrollAreaMaxHeight);
337     }
338 }
339 
~AnnotatedDNAView()340 AnnotatedDNAView::~AnnotatedDNAView() {
341     delete posSelector;
342 }
343 
eventFilter(QObject * o,QEvent * e)344 bool AnnotatedDNAView::eventFilter(QObject *o, QEvent *e) {
345     if (o == mainSplitter) {
346         if (e->type() == QEvent::DragEnter || e->type() == QEvent::Drop) {
347             QDropEvent *de = (QDropEvent *)e;
348             const QMimeData *md = de->mimeData();
349             const GObjectMimeData *gomd = qobject_cast<const GObjectMimeData *>(md);
350             if (gomd != nullptr) {
351                 if (e->type() == QEvent::DragEnter) {
352                     de->acceptProposedAction();
353                 } else {
354                     GObject *obj = gomd->objPtr.data();
355                     if (obj != nullptr) {
356                         QString err = tryAddObject(obj);
357                         if (!err.isEmpty()) {
358                             QMessageBox::critical(nullptr, tr("Error!"), err);
359                         }
360                     }
361                 }
362             }
363         }
364     } else if (o == scrolledWidget) {
365         if (replacedSeqWidget && e->type() == QEvent::MouseMove) {
366             QMouseEvent *event = dynamic_cast<QMouseEvent *>(e);
367             if (event->buttons() == Qt::LeftButton) {
368                 seqWidgetMove(event->pos());
369             }
370         } else if (replacedSeqWidget && e->type() == QEvent::MouseButtonRelease) {
371             QMouseEvent *event = dynamic_cast<QMouseEvent *>(e);
372             if (event->buttons() == Qt::LeftButton) {
373                 finishSeqWidgetMove();
374             }
375         }
376         // try to restore mainSplitter state on sequence views fixed <-> expandable state transition. Usually this happens when user toggles sequence views.
377         if (e->type() == QEvent::Resize) {
378             bool hasExpandableSequenceWidgetsNow = false;  // expandable state: any of the sequence view widgets has unlimited height.
379             foreach (const ADVSequenceWidget *w, getSequenceWidgets()) {
380                 if (w->maximumHeight() == QWIDGETSIZE_MAX) {
381                     hasExpandableSequenceWidgetsNow = true;
382                     break;
383                 }
384             }
385             if (hasExpandableSequenceWidgetsNow != hadExpandableSequenceWidgetsLastResize) {  // transition from fixed <-> expandable state
386                 if (hasExpandableSequenceWidgetsNow) {  // try restore state from the saved sizes if possible.
387                     if (savedMainSplitterSizes.size() > 0 && savedMainSplitterSizes.size() == mainSplitter->sizes().size()) {
388                         mainSplitter->setSizes(savedMainSplitterSizes);
389                     }
390                 }
391                 hadExpandableSequenceWidgetsLastResize = hasExpandableSequenceWidgetsNow;
392             }
393             if (hasExpandableSequenceWidgetsNow) {  // update saved sizes for a future use.
394                 savedMainSplitterSizes = mainSplitter->sizes();
395             }
396         }
397         return false;
398     } else if (e->type() == QEvent::Resize) {
399         ADVSequenceWidget *v = qobject_cast<ADVSequenceWidget *>(o);
400         if (v != nullptr) {
401             updateScrollAreaHeight();
402         }
403     } else if (e->type() == QEvent::KeyPress) {
404         sl_selectionChanged();
405     }
406 
407     return false;
408 }
409 
setActiveSequenceWidget(ADVSequenceWidget * sequenceWidget)410 void AnnotatedDNAView::setActiveSequenceWidget(ADVSequenceWidget *sequenceWidget) {
411     if (sequenceWidget == activeSequenceWidget) {
412         return;
413     }
414     ADVSequenceWidget *prevFocus = activeSequenceWidget;
415     activeSequenceWidget = sequenceWidget;
416     updateMultiViewActions();
417     sl_updatePasteAction();
418     emit si_activeSequenceWidgetChanged(prevFocus, activeSequenceWidget);
419 }
420 
onCloseEvent()421 bool AnnotatedDNAView::onCloseEvent() {
422     QList<AutoAnnotationObject *> aaList = autoAnnotationsMap.values();
423     bool waitFinishedRemovedTasks = false;
424     foreach (AutoAnnotationObject *aa, aaList) {
425         bool existRemovedTask = false;
426         cancelAutoAnnotationUpdates(aa, &existRemovedTask);
427         waitFinishedRemovedTasks = waitFinishedRemovedTasks || existRemovedTask;
428     }
429     if (waitFinishedRemovedTasks) {
430         QMessageBox::information(this->getWidget(), "information", "Can not close view while there are annotations being processed");
431         return false;
432     }
433     foreach (ADVSplitWidget *w, splitWidgets) {
434         bool canClose = w->onCloseEvent();
435         if (!canClose) {
436             return false;
437         }
438     }
439     emit si_onClose(this);
440     return true;
441 }
442 
onObjectRemoved(GObject * o)443 bool AnnotatedDNAView::onObjectRemoved(GObject *o) {
444     if (o->getGObjectType() == GObjectTypes::ANNOTATION_TABLE) {
445         AnnotationTableObject *ao = qobject_cast<AnnotationTableObject *>(o);
446         annotationSelection->removeObjectAnnotations(ao);
447         foreach (ADVSequenceObjectContext *seqCtx, seqContexts) {
448             if (seqCtx->getAnnotationObjects().contains(ao)) {
449                 seqCtx->removeAnnotationObject(ao);
450                 break;
451             }
452         }
453         annotations.removeOne(ao);
454         emit si_annotationObjectRemoved(ao);
455     } else if (o->getGObjectType() == GObjectTypes::SEQUENCE) {
456         U2SequenceObject *seqObj = qobject_cast<U2SequenceObject *>(o);
457         ADVSequenceObjectContext *seqCtx = getSequenceContext(seqObj);
458         if (seqCtx != nullptr) {
459             foreach (ADVSequenceWidget *w, seqCtx->getSequenceWidgets()) {
460                 removeSequenceWidget(w);
461             }
462             QSet<AnnotationTableObject *> aObjs = seqCtx->getAnnotationObjects();
463             foreach (AnnotationTableObject *ao, aObjs) {
464                 removeObject(ao);
465             }
466             emit si_sequenceRemoved(seqCtx);
467             seqContexts.removeOne(seqCtx);
468             removeAutoAnnotations(seqCtx);
469             delete seqCtx;
470         }
471     }
472 
473     GObjectView::onObjectRemoved(o);
474     return seqContexts.isEmpty();
475 }
476 
addADVAction(ADVGlobalAction * a1)477 void AnnotatedDNAView::addADVAction(ADVGlobalAction *a1) {
478     for (int i = 0; i < advActions.size(); i++) {
479         ADVGlobalAction *a2 = advActions[i];
480         int p1 = a1->getPosition();
481         int p2 = a2->getPosition();
482         if (p1 < p2 || (p1 == p2 && a1->text() < a2->text())) {
483             advActions.insert(i, a1);
484             return;
485         }
486     }
487     advActions.append(a1);
488 }
489 
buildStaticToolbar(QToolBar * tb)490 void AnnotatedDNAView::buildStaticToolbar(QToolBar *tb) {
491     tb->addAction(createAnnotationAction);
492 
493     tb->addSeparator();
494     tb->addAction(clipb->getCopySequenceAction());
495     tb->addAction(clipb->getCopyComplementAction());
496     tb->addAction(clipb->getCopyTranslationAction());
497     tb->addAction(clipb->getCopyComplementTranslationAction());
498     tb->addAction(clipb->getCopyAnnotationSequenceAction());
499     tb->addAction(clipb->getCopyAnnotationSequenceTranslationAction());
500     tb->addAction(clipb->getCopyQualifierAction());
501     tb->addAction(clipb->getPasteSequenceAction());
502     tb->addSeparator();
503 
504     if (posSelector == nullptr && !seqContexts.isEmpty()) {
505         qint64 len = seqContexts.first()->getSequenceLength();
506         posSelector = new PositionSelector(tb, 1, len);
507         connect(posSelector, SIGNAL(si_positionChanged(int)), SLOT(sl_onPosChangeRequest(int)));
508         posSelectorWidgetAction = tb->addWidget(posSelector);
509     } else {
510         tb->addAction(posSelectorWidgetAction);
511     }
512 
513     tb->addSeparator();
514     syncViewManager->updateToolbar1(tb);
515     tb->addSeparator();
516 
517     foreach (ADVGlobalAction *a, advActions) {
518         if (a->getFlags().testFlag(ADVGlobalActionFlag_AddToToolbar)) {
519             tb->addAction(a);
520             QWidget *w = tb->widgetForAction(a);
521             if (w) {
522                 w->setObjectName(a->objectName() + "_widget");
523             }
524         }
525     }
526 
527     GObjectView::buildStaticToolbar(tb);
528 
529     tb->addSeparator();
530     syncViewManager->updateToolbar2(tb);
531 }
532 
buildMenu(QMenu * m,const QString & type)533 void AnnotatedDNAView::buildMenu(QMenu *m, const QString &type) {
534     if (type != GObjectViewMenuType::STATIC) {
535         GObjectView::buildMenu(m, type);
536         return;
537     }
538     m->addAction(posSelectorAction);
539     clipb->addCopyMenu(m);
540     m->addSeparator();
541     addAddMenu(m);
542     addAnalyseMenu(m);
543     addAlignMenu(m);
544     addExportMenu(m);
545     addEditMenu(m);
546     addRemoveMenu(m);
547     m->addSeparator();
548 
549     annotationsView->adjustStaticMenu(m);
550 
551     GObjectView::buildMenu(m, type);
552 }
553 
addAnalyseMenu(QMenu * m)554 void AnnotatedDNAView::addAnalyseMenu(QMenu *m) {
555     QMenu *am = m->addMenu(tr("Analyze"));
556     am->menuAction()->setObjectName(ADV_MENU_ANALYSE);
557     foreach (ADVGlobalAction *a, advActions) {
558         if (a->getFlags().testFlag(ADVGlobalActionFlag_AddToAnalyseMenu)) {
559             am->addAction(a);
560         }
561     }
562 }
563 
addAddMenu(QMenu * m)564 void AnnotatedDNAView::addAddMenu(QMenu *m) {
565     QMenu *am = m->addMenu(tr("Add"));
566     am->menuAction()->setObjectName(ADV_MENU_ADD);
567     am->addAction(createAnnotationAction);
568 }
569 
addExportMenu(QMenu * m)570 void AnnotatedDNAView::addExportMenu(QMenu *m) {
571     QMenu *em = m->addMenu(tr("Export"));
572     em->menuAction()->setObjectName(ADV_MENU_EXPORT);
573 }
574 
addAlignMenu(QMenu * m)575 void AnnotatedDNAView::addAlignMenu(QMenu *m) {
576     QMenu *am = m->addMenu(tr("Align"));
577     am->menuAction()->setObjectName(ADV_MENU_ALIGN);
578 }
579 
addRemoveMenu(QMenu * m)580 void AnnotatedDNAView::addRemoveMenu(QMenu *m) {
581     QMenu *rm = m->addMenu(tr("Remove"));
582     rm->menuAction()->setObjectName(ADV_MENU_REMOVE);
583 
584     rm->addAction(removeSequenceObjectAction);
585 }
586 
addEditMenu(QMenu * m)587 void AnnotatedDNAView::addEditMenu(QMenu *m) {
588     ADVSequenceObjectContext *seqCtx = getActiveSequenceContext();
589     SAFE_POINT(seqCtx != nullptr, "Sequence in focus is NULL", );
590 
591     U2SequenceObject *seqObj = seqCtx->getSequenceObject();
592     SAFE_POINT(seqObj != nullptr, "Sequence object in focus is NULL", );
593 
594     Document *curDoc = seqObj->getDocument();
595     SAFE_POINT(curDoc != nullptr, "Current document is NULL", );
596 
597     QMenu *editMenu = m->addMenu(tr("Edit"));
598     editMenu->setEnabled(!(curDoc->findGObjectByType(GObjectTypes::SEQUENCE).isEmpty() || seqObj->isStateLocked()));
599     editMenu->menuAction()->setObjectName(ADV_MENU_EDIT);
600 
601     QAction *editAction = getEditActionFromSequenceWidget(activeSequenceWidget);
602     if (editAction != nullptr) {
603         editMenu->addAction(editAction);
604     }
605     if (annotationSelection->getAnnotations().size() == 1 && annotationsView->editAction->isEnabled()) {
606         editMenu->addAction(annotationsView->editAction);
607     }
608     editMenu->addAction(editSettingsAction);
609     editMenu->addSeparator();
610 
611     editMenu->addAction(addSequencePart);
612     editMenu->addAction(replaceSequencePart);
613     sl_selectionChanged();
614     editMenu->addAction(removeSequencePart);
615     editMenu->addSeparator();
616 
617     if (seqObj->getAlphabet()->isNucleic() && seqCtx->getComplementTT() != nullptr) {
618         QMenu *replaceMenu = editMenu->addMenu(tr("Replace the whole sequence by"));
619         replaceMenu->menuAction()->setObjectName(ADV_MENU_REPLACE_WHOLE_SEQUENCE);
620         replaceMenu->addAction(reverseComplementSequenceAction);
621         replaceMenu->addSeparator();
622         replaceMenu->addAction(complementSequenceAction);
623         replaceMenu->addAction(reverseSequenceAction);
624     }
625 }
626 
updateViewTask(const QString & stateName,const QVariantMap & stateData)627 Task *AnnotatedDNAView::updateViewTask(const QString &stateName, const QVariantMap &stateData) {
628     return new UpdateAnnotatedDNAViewTask(this, stateName, stateData);
629 }
630 
saveState()631 QVariantMap AnnotatedDNAView::saveState() {
632     if (closing) {
633         return QVariantMap();
634     }
635     QVariantMap state = AnnotatedDNAViewState::saveState(this);
636     foreach (ADVSequenceWidget *sw, seqViews) {
637         sw->saveState(state);
638     }
639     foreach (ADVSplitWidget *w, splitWidgets) {
640         w->saveState(state);
641     }
642     annotationsView->saveState(state);
643     return state;
644 }
645 
saveWidgetState()646 void AnnotatedDNAView::saveWidgetState() {
647     annotationsView->saveWidgetState();
648 }
649 
canAddObject(GObject * obj)650 bool AnnotatedDNAView::canAddObject(GObject *obj) {
651     if (GObjectView::canAddObject(obj)) {
652         return true;
653     }
654     if (isChildWidgetObject(obj)) {
655         return true;
656     }
657     if (obj->getGObjectType() == GObjectTypes::SEQUENCE) {
658         return true;
659     }
660     if (obj->getGObjectType() != GObjectTypes::ANNOTATION_TABLE) {
661         return false;
662     }
663     // todo: add annotations related to sequence object not in view (sobj) and add 'sobj' too the view ?
664     bool hasRelation = false;
665     foreach (ADVSequenceObjectContext *soc, seqContexts) {
666         if (obj->hasObjectRelation(soc->getSequenceObject(), ObjectRole_Sequence)) {
667             hasRelation = true;
668             break;
669         }
670     }
671     return hasRelation;
672 }
673 
isChildWidgetObject(GObject * obj) const674 bool AnnotatedDNAView::isChildWidgetObject(GObject *obj) const {
675     foreach (ADVSequenceWidget *lv, seqViews) {
676         SAFE_POINT(lv != nullptr, "AnnotatedDNAView::isChildWidgetObject::No sequence widget", false);
677         if (lv->isWidgetOnlyObject(obj)) {
678             return true;
679         }
680     }
681     foreach (ADVSplitWidget *sw, splitWidgets) {
682         SAFE_POINT(sw != nullptr, "AnnotatedDNAView::isChildWidgetObject::No split widget", false);
683         if (sw->acceptsGObject(obj)) {
684             return true;
685         }
686     }
687     return false;
688 }
689 
addSequenceWidget(ADVSequenceWidget * v)690 void AnnotatedDNAView::addSequenceWidget(ADVSequenceWidget *v) {
691     assert(!seqViews.contains(v));
692     seqViews.append(v);
693 
694     QAction *editAction = getEditActionFromSequenceWidget(v);
695     SAFE_POINT(editAction != nullptr, "Edit action is not found", );
696 
697     connect(editAction, SIGNAL(triggered()), SLOT(sl_updatePasteAction()));
698 
699     QList<ADVSequenceObjectContext *> contexts = v->getSequenceContexts();
700     foreach (ADVSequenceObjectContext *c, contexts) {
701         c->addSequenceWidget(v);
702         addAutoAnnotations(c);
703         addGraphs(c);
704         connect(c->getSequenceSelection(), SIGNAL(si_selectionChanged(LRegionsSelection *, QVector<U2Region>, QVector<U2Region>)), SLOT(sl_selectionChanged()));
705         clipb->connectSequence(c);
706     }
707     scrolledWidgetLayout->addWidget(v);
708     v->setVisible(true);
709     v->installEventFilter(this);
710     updateScrollAreaHeight();
711     updateMultiViewActions();
712     emit si_sequenceWidgetAdded(v);
713 }
714 
removeSequenceWidget(ADVSequenceWidget * sequenceWidget)715 void AnnotatedDNAView::removeSequenceWidget(ADVSequenceWidget *sequenceWidget) {
716     int widgetIndex = seqViews.indexOf(sequenceWidget);
717     SAFE_POINT(widgetIndex >= 0, "removeSequenceWidget is called for an unknown widget", );
718 
719     if (activeSequenceWidget == sequenceWidget) {
720         int newActiveWidgetIndex = widgetIndex + 1 < seqViews.size() ? widgetIndex + 1 : widgetIndex - 1;
721         setActiveSequenceWidget(newActiveWidgetIndex < 0 ? nullptr : seqViews[newActiveWidgetIndex]);
722     }
723 
724     // remove widget
725     seqViews.removeOne(sequenceWidget);
726     sequenceWidget->hide();
727 
728     QList<ADVSequenceObjectContext *> contexts = sequenceWidget->getSequenceContexts();
729     foreach (ADVSequenceObjectContext *c, contexts) {
730         c->removeSequenceWidget(sequenceWidget);
731         disconnect(c->getSequenceSelection(), SIGNAL(si_selectionChanged(LRegionsSelection *, QVector<U2Region>, QVector<U2Region>)));
732     }
733     updateMultiViewActions();
734     emit si_sequenceWidgetRemoved(sequenceWidget);
735     scrolledWidgetLayout->removeWidget(sequenceWidget);
736     delete sequenceWidget;  // v->deleteLater(); //problem: updates for 'v' after seqCtx is destroyed
737     updateScrollAreaHeight();
738 }
739 
updateMultiViewActions()740 void AnnotatedDNAView::updateMultiViewActions() {
741     bool canRemoveActiveSequence = seqViews.size() > 1 && activeSequenceWidget != nullptr && activeSequenceWidget->getActiveSequenceContext() != nullptr;
742     removeSequenceObjectAction->setEnabled(canRemoveActiveSequence);
743 
744     if (posSelector != nullptr) {
745         qint64 currentSequenceLength = 0;
746         if (activeSequenceWidget != nullptr && activeSequenceWidget->getActiveSequenceContext() != nullptr) {
747             currentSequenceLength = activeSequenceWidget->getActiveSequenceContext()->getSequenceLength();
748         }
749         posSelector->updateRange(1, currentSequenceLength);
750     }
751 }
752 
sl_updatePasteAction()753 void AnnotatedDNAView::sl_updatePasteAction() {
754     CHECK(activeSequenceWidget != nullptr, );
755 
756     QAction *editAction = getEditActionFromSequenceWidget(activeSequenceWidget);
757     SAFE_POINT(editAction != nullptr, "Edit action is not found", );
758 
759     const bool isEditModeChecked = editAction->isChecked();
760 
761     QAction *pasteAction = clipb->getPasteSequenceAction();
762     SAFE_POINT(pasteAction != nullptr, "Paste action is NULL", );
763 
764     pasteAction->setEnabled(isEditModeChecked);
765 }
766 
sl_relatedObjectRelationChanged()767 void AnnotatedDNAView::sl_relatedObjectRelationChanged() {
768     GObject *o = qobject_cast<GObject *>(sender());
769     CHECK(o != nullptr, );
770     QList<AnnotationTableObject *> currentAnnotations = getAnnotationObjects(false);
771     QList<GObject *> objectsToAdd;
772     QList<GObject *> allObjs = GObjectUtils::findObjectsRelatedToObjectByRole(o, GObjectTypes::ANNOTATION_TABLE, ObjectRole_Sequence, GObjectUtils::findAllObjects(UOF_LoadedOnly, GObjectTypes::ANNOTATION_TABLE), UnloadedObjectFilter::UOF_LoadedOnly);
773 
774     foreach (GObject *obj, allObjs) {
775         if (!currentAnnotations.contains(qobject_cast<AnnotationTableObject *>(obj))) {
776             objectsToAdd << obj;
777         }
778     }
779 
780     foreach (GObject *obj, objectsToAdd) {
781         QString error = addObject(obj);
782         if (!error.isEmpty()) {
783             coreLog.error(error);
784         }
785     }
786 }
787 
sl_onContextMenuRequested()788 void AnnotatedDNAView::sl_onContextMenuRequested() {
789     QMenu m;
790 
791     m.addAction(posSelectorAction);
792 
793     m.addSeparator()->setObjectName("FIRST_SEP");
794     clipb->addCopyMenu(&m);
795     m.addSeparator()->setObjectName(ADV_MENU_SECTION1_SEP);
796     addAddMenu(&m);
797     addAnalyseMenu(&m);
798     addAlignMenu(&m);
799     addExportMenu(&m);
800     addEditMenu(&m);
801     addRemoveMenu(&m);
802     m.addSeparator()->setObjectName(ADV_MENU_SECTION2_SEP);
803 
804     if (annotationSelection->getAnnotations().size() == 1) {
805         Annotation *a = annotationSelection->getAnnotations().first();
806         const SharedAnnotationData &aData = a->getData();
807         AnnotationSettingsRegistry *registry = AppContext::getAnnotationsSettingsRegistry();
808         AnnotationSettings *as = registry->getAnnotationSettings(aData);
809         if (as->visible) {
810             toggleHLAction->setText(tr("Disable '%1' highlighting").arg(aData->name));
811         } else {
812             toggleHLAction->setText(tr("Enable '%1' highlighting").arg(aData->name));
813         }
814 
815         const QIcon icon = GUIUtils::createSquareIcon(as->color, 10);
816         toggleHLAction->setIcon(icon);
817 
818         toggleHLAction->setObjectName("toggle_HL_action");
819         m.addAction(toggleHLAction);
820     }
821 
822     if (activeSequenceWidget != nullptr) {
823         activeSequenceWidget->buildPopupMenu(m);
824     }
825     emit si_buildMenu(this, &m, GObjectViewMenuType::CONTEXT);
826 
827     m.exec(QCursor::pos());
828 }
829 
sl_onFindPatternClicked()830 void AnnotatedDNAView::sl_onFindPatternClicked() {
831     OptionsPanel *optionsPanel = getOptionsPanel();
832     SAFE_POINT(optionsPanel != nullptr, "Internal error: options panel is NULL"
833                                         " when pattern search has been initiated!", );
834 
835     const QString &findPatternGroupId = FindPatternWidgetFactory::getGroupId();
836     optionsPanel->openGroupById(findPatternGroupId);
837 }
838 
sl_toggleHL()839 void AnnotatedDNAView::sl_toggleHL() {
840     if (annotationSelection->isEmpty()) {
841         return;
842     }
843     const Annotation *a = annotationSelection->getAnnotations().first();
844     AnnotationSettingsRegistry *registry = AppContext::getAnnotationsSettingsRegistry();
845     AnnotationSettings *as = registry->getAnnotationSettings(a->getData());
846     as->visible = !as->visible;
847     registry->changeSettings(QList<AnnotationSettings *>() << as, true);
848 }
849 
tryAddObject(GObject * o)850 QString AnnotatedDNAView::tryAddObject(GObject *o) {
851     if (o->getGObjectType() == GObjectTypes::UNLOADED) {
852         AppContext::getTaskScheduler()->registerTopLevelTask(new AddToViewTask(this, o));
853         return "";
854     }
855     QList<ADVSequenceObjectContext *> rCtx;
856     if (o->getGObjectType() == GObjectTypes::ANNOTATION_TABLE) {
857         rCtx = findRelatedSequenceContexts(o);
858         if (rCtx.isEmpty()) {
859             // ask user if to create new association
860             QObjectScopedPointer<CreateObjectRelationDialogController> d = new CreateObjectRelationDialogController(o, getSequenceGObjectsWithContexts(), ObjectRole_Sequence, true, tr("Select sequence to associate annotations with:"));
861             d->exec();
862             CHECK(!d.isNull(), "");
863             bool objectAlreadyAdded = d->relationIsSet;
864             rCtx = findRelatedSequenceContexts(o);
865             if (rCtx.isEmpty() || objectAlreadyAdded) {
866                 return "";
867             }
868         }
869     }
870     return addObject(o);
871 }
872 
addObject(GObject * o)873 QString AnnotatedDNAView::addObject(GObject *o) {
874     QList<ADVSequenceObjectContext *> rCtx;
875     if (o->getGObjectType() == GObjectTypes::ANNOTATION_TABLE) {
876         rCtx = findRelatedSequenceContexts(o);
877         if (rCtx.isEmpty()) {
878             return tr("No sequence object found for annotations");
879         }
880     }
881     QString res = GObjectView::addObject(o);
882     if (!res.isEmpty()) {
883         return res;
884     }
885 
886     bool internalViewObject = isChildWidgetObject(o);
887     if (internalViewObject) {
888         return "";
889     }
890 
891     if (o->getGObjectType() == GObjectTypes::SEQUENCE) {
892         U2SequenceObject *dnaObj = qobject_cast<U2SequenceObject *>(o);
893         U2OpStatusImpl status;
894         if (!dnaObj->isValidDbiObject(status)) {
895             return "";
896         }
897         ADVSequenceObjectContext *sc = new ADVSequenceObjectContext(this, dnaObj);
898         seqContexts.append(sc);
899         // if mainSplitter==NULL -> its view initialization and widgets will be added later
900         if (mainSplitter != nullptr && !isChildWidgetObject(dnaObj)) {
901             ADVSingleSequenceWidget *block = new ADVSingleSequenceWidget(sc, this);
902             connect(block, SIGNAL(si_titleClicked(ADVSequenceWidget *)), SLOT(sl_onSequenceWidgetTitleClicked(ADVSequenceWidget *)));
903             block->setObjectName("ADV_single_sequence_widget_" + QString::number(seqViews.count()));
904             addSequenceWidget(block);
905             block->addAction(createPasteAction());
906             setActiveSequenceWidget(block);
907         }
908         addRelatedAnnotations(sc);
909         emit si_sequenceAdded(sc);
910         connect(o, SIGNAL(si_relatedObjectRelationChanged()), SLOT(sl_relatedObjectRelationChanged()));
911     } else if (o->getGObjectType() == GObjectTypes::ANNOTATION_TABLE) {
912         AnnotationTableObject *ao = qobject_cast<AnnotationTableObject *>(o);
913         SAFE_POINT(ao != nullptr, "Invalid annotation table!", QString());
914         annotations.append(ao);
915         foreach (ADVSequenceObjectContext *sc, rCtx) {
916             sc->addAnnotationObject(ao);
917         }
918         emit si_annotationObjectAdded(ao);
919     }
920     return "";
921 }
922 
findRelatedSequenceContexts(GObject * obj) const923 QList<ADVSequenceObjectContext *> AnnotatedDNAView::findRelatedSequenceContexts(GObject *obj) const {
924     QList<GObject *> relatedObjects = GObjectUtils::selectRelations(obj, GObjectTypes::SEQUENCE, ObjectRole_Sequence, objects, UOF_LoadedOnly);
925     QList<ADVSequenceObjectContext *> res;
926     foreach (GObject *seqObj, relatedObjects) {
927         U2SequenceObject *dnaObj = qobject_cast<U2SequenceObject *>(seqObj);
928         ADVSequenceObjectContext *ctx = getSequenceContext(dnaObj);
929         res.append(ctx);
930     }
931     return res;
932 }
933 
sl_onPosChangeRequest(int pos)934 void AnnotatedDNAView::sl_onPosChangeRequest(int pos) {
935     uiLog.trace(QString("ADV: center change request: %1").arg(pos));
936     ADVSequenceWidget *seqBlock = getActiveSequenceWidget();
937     assert(seqBlock != nullptr);
938     seqBlock->centerPosition(pos - 1);
939 }
940 
sl_onShowPosSelectorRequest()941 void AnnotatedDNAView::sl_onShowPosSelectorRequest() {
942     ADVSequenceObjectContext *seqCtx = getActiveSequenceContext();
943     assert(seqCtx != nullptr);
944 
945     QObjectScopedPointer<QDialog> dlg = new QDialog(getWidget());
946     dlg->setModal(true);
947     dlg->setWindowTitle(tr("Go to Position"));
948 
949     PositionSelector *ps = new PositionSelector(dlg.data(), 1, seqCtx->getSequenceLength(), true);
950     connect(ps, SIGNAL(si_positionChanged(int)), SLOT(sl_onPosChangeRequest(int)));
951 
952     dlg->exec();
953 }
954 
insertWidgetIntoSplitter(ADVSplitWidget * splitWidget)955 void AnnotatedDNAView::insertWidgetIntoSplitter(ADVSplitWidget *splitWidget) {
956     assert(mainSplitter != nullptr);
957     if (splitWidgets.contains(splitWidget)) {
958         return;
959     }
960     mainSplitter->insertWidget(0, splitWidget);
961     mainSplitter->setStretchFactor(0, 1);
962     splitWidgets.append(splitWidget);
963 }
964 
unregisterSplitWidget(ADVSplitWidget * splitWidget)965 void AnnotatedDNAView::unregisterSplitWidget(ADVSplitWidget *splitWidget) {
966     splitWidgets.removeOne(splitWidget);
967 }
968 
getSequenceContext(AnnotationTableObject * obj) const969 ADVSequenceObjectContext *AnnotatedDNAView::getSequenceContext(AnnotationTableObject *obj) const {
970     SAFE_POINT(getAnnotationObjects(true).contains(obj),
971                "Unexpected annotation table detected!",
972                nullptr);
973     foreach (ADVSequenceObjectContext *seqCtx, seqContexts) {
974         QSet<AnnotationTableObject *> aObjs = seqCtx->getAnnotationObjects(true);
975         if (aObjs.contains(obj)) {
976             return seqCtx;
977         }
978     }
979     return nullptr;
980 }
981 
getActiveSequenceContext() const982 ADVSequenceObjectContext *AnnotatedDNAView::getActiveSequenceContext() const {
983     ADVSequenceWidget *w = getActiveSequenceWidget();
984     return w == nullptr ? nullptr : w->getActiveSequenceContext();
985 }
986 
getSequenceContext(U2SequenceObject * obj) const987 ADVSequenceObjectContext *AnnotatedDNAView::getSequenceContext(U2SequenceObject *obj) const {
988     foreach (ADVSequenceObjectContext *seqCtx, seqContexts) {
989         if (seqCtx->getSequenceObject() == obj) {
990             return seqCtx;
991         }
992     }
993     return nullptr;
994 }
995 
getSequenceContext(const GObjectReference & r) const996 ADVSequenceObjectContext *AnnotatedDNAView::getSequenceContext(const GObjectReference &r) const {
997     foreach (ADVSequenceObjectContext *seqCtx, seqContexts) {
998         GObjectReference ref(seqCtx->getSequenceObject());
999         if (ref == r) {
1000             return seqCtx;
1001         }
1002     }
1003     return nullptr;
1004 }
1005 
addRelatedAnnotations(ADVSequenceObjectContext * seqCtx)1006 void AnnotatedDNAView::addRelatedAnnotations(ADVSequenceObjectContext *seqCtx) {
1007     QList<GObject *> allLoadedAnnotations = GObjectUtils::findAllObjects(UOF_LoadedOnly, GObjectTypes::ANNOTATION_TABLE);
1008     QList<GObject *> loadedAndRelatedAnnotations = GObjectUtils::findObjectsRelatedToObjectByRole(
1009         seqCtx->getSequenceObject(),
1010         GObjectTypes::ANNOTATION_TABLE,
1011         ObjectRole_Sequence,
1012         allLoadedAnnotations,
1013         UOF_LoadedOnly);
1014 
1015     foreach (GObject *ao, loadedAndRelatedAnnotations) {
1016         if (objects.contains(ao)) {
1017             seqCtx->addAnnotationObject(qobject_cast<AnnotationTableObject *>(ao));
1018         } else {
1019             addObject(ao);
1020         }
1021     }
1022 }
1023 
addAutoAnnotations(ADVSequenceObjectContext * seqCtx)1024 void AnnotatedDNAView::addAutoAnnotations(ADVSequenceObjectContext *seqCtx) {
1025     AutoAnnotationObject *aa = new AutoAnnotationObject(seqCtx->getSequenceObject(), seqCtx->getAminoTT(), seqCtx);
1026     seqCtx->addAutoAnnotationObject(aa->getAnnotationObject());
1027     autoAnnotationsMap.insert(seqCtx, aa);
1028 
1029     emit si_annotationObjectAdded(aa->getAnnotationObject());
1030 
1031     foreach (ADVSequenceWidget *w, seqCtx->getSequenceWidgets()) {
1032         AutoAnnotationsADVAction *aaAction = new AutoAnnotationsADVAction(w, aa);
1033         w->addADVSequenceWidgetAction(aaAction);
1034     }
1035 }
1036 
removeAutoAnnotations(ADVSequenceObjectContext * seqCtx)1037 void AnnotatedDNAView::removeAutoAnnotations(ADVSequenceObjectContext *seqCtx) {
1038     AutoAnnotationObject *aa = autoAnnotationsMap.take(seqCtx);
1039     cancelAutoAnnotationUpdates(aa);
1040     emit si_annotationObjectRemoved(aa->getAnnotationObject());
1041     delete aa;
1042 }
1043 
cancelAutoAnnotationUpdates(AutoAnnotationObject * aa,bool * removeTaskExist)1044 void AnnotatedDNAView::cancelAutoAnnotationUpdates(AutoAnnotationObject *aa, bool *removeTaskExist) {
1045     QList<Task *> tasks = AppContext::getTaskScheduler()->getTopLevelTasks();
1046     foreach (Task *t, tasks) {
1047         AutoAnnotationsUpdateTask *aaUpdateTask = qobject_cast<AutoAnnotationsUpdateTask *>(t);
1048         if (aaUpdateTask != nullptr) {
1049             if (aaUpdateTask->getAutoAnnotationObject() == aa && !aaUpdateTask->isFinished()) {
1050                 aaUpdateTask->setAutoAnnotationInvalid();
1051                 aaUpdateTask->cancel();
1052                 if (removeTaskExist) {
1053                     *removeTaskExist = false;
1054                     foreach (const QPointer<Task> &subTask, aaUpdateTask->getSubtasks()) {
1055                         RemoveAnnotationsTask *rTask = qobject_cast<RemoveAnnotationsTask *>(subTask.data());
1056                         if (rTask && !rTask->isFinished()) {
1057                             *removeTaskExist = true;
1058                         }
1059                     }
1060                 }
1061             }
1062         }
1063     }
1064 }
1065 
1066 /**
1067  * Adds common graphs menu to the current for each sequence
1068  */
addGraphs(ADVSequenceObjectContext * seqCtx)1069 void AnnotatedDNAView::addGraphs(ADVSequenceObjectContext *seqCtx) {
1070     foreach (ADVSequenceWidget *seqWidget, seqCtx->getSequenceWidgets()) {
1071         ADVSingleSequenceWidget *singleSeqWidget = qobject_cast<ADVSingleSequenceWidget *>(seqWidget);
1072         SAFE_POINT(singleSeqWidget != nullptr, "singleSeqWidget is NULL", );
1073         GraphMenuAction *graphMenuAction = new GraphMenuAction(singleSeqWidget->getSequenceObject()->getAlphabet());
1074         if (singleSeqWidget != nullptr) {
1075             singleSeqWidget->addADVSequenceWidgetActionToViewsToolbar(graphMenuAction);
1076         } else {
1077             seqWidget->addADVSequenceWidgetAction(graphMenuAction);
1078         }
1079     }
1080 }
1081 
sl_onDocumentAdded(Document * d)1082 void AnnotatedDNAView::sl_onDocumentAdded(Document *d) {
1083     GObjectView::sl_onDocumentAdded(d);
1084     importDocAnnotations(d);
1085 }
1086 
importDocAnnotations(Document * doc)1087 void AnnotatedDNAView::importDocAnnotations(Document *doc) {
1088     QList<GObject *> docObjects = doc->getObjects();
1089 
1090     foreach (GObject *obj, objects) {
1091         if (obj->getGObjectType() != GObjectTypes::SEQUENCE) {
1092             continue;
1093         }
1094         QList<GObject *> relatedAnns = GObjectUtils::findObjectsRelatedToObjectByRole(obj, GObjectTypes::ANNOTATION_TABLE, ObjectRole_Sequence, docObjects, UOF_LoadedOnly);
1095         foreach (GObject *annObj, relatedAnns) {
1096             addObject(annObj);
1097         }
1098     }
1099 }
1100 
seqWidgetMove(const QPoint & pos)1101 void AnnotatedDNAView::seqWidgetMove(const QPoint &pos) {
1102     SAFE_POINT(replacedSeqWidget, "Moving the NULL widget", );
1103     CHECK_EXT(seqViews.contains(replacedSeqWidget), replacedSeqWidget = nullptr, );
1104 
1105     int index = seqViews.indexOf(replacedSeqWidget);
1106     QRect replacedWidgetRect = replacedSeqWidget->geometry();
1107     CHECK(!replacedWidgetRect.contains(pos), );
1108 
1109     QRect prevWidgetRect;
1110     // If previous widget exists, define its rectangle
1111     if (index > 0) {
1112         prevWidgetRect = seqViews[index - 1]->geometry();
1113     }
1114 
1115     QRect nextWidgetRect;
1116     // If next widget exists, define its rectangle
1117     if (index < seqViews.count() - 1) {
1118         nextWidgetRect = seqViews[index + 1]->geometry();
1119     }
1120 
1121     if (prevWidgetRect.isValid() && pos.y() < prevWidgetRect.center().y()) {
1122         seqViews.swap(index - 1, index);
1123         scrolledWidgetLayout->insertWidget(index - 1, scrolledWidgetLayout->takeAt(index)->widget());
1124     }
1125     if (nextWidgetRect.isValid() && pos.y() > nextWidgetRect.top()) {
1126         seqViews.swap(index, index + 1);
1127         scrolledWidgetLayout->insertWidget(index, scrolledWidgetLayout->takeAt(index + 1)->widget());
1128     }
1129 }
1130 
finishSeqWidgetMove()1131 void AnnotatedDNAView::finishSeqWidgetMove() {
1132     replacedSeqWidget = nullptr;
1133 }
1134 
createCodonTableAction()1135 void AnnotatedDNAView::createCodonTableAction() {
1136     QAction *showCodonTableAction = new ADVGlobalAction(this, QIcon(":core/images/codon_table.png"), tr("Show codon table"), INT_MAX - 1, ADVGlobalActionFlag_AddToToolbar);
1137     showCodonTableAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_B));
1138     showCodonTableAction->setShortcutContext(Qt::WindowShortcut);
1139     connect(showCodonTableAction, SIGNAL(triggered()), codonTableView, SLOT(sl_setVisible()));
1140     showCodonTableAction->setObjectName("Codon table");
1141     showCodonTableAction->setCheckable(true);
1142 }
1143 
sl_onDocumentLoadedStateChanged()1144 void AnnotatedDNAView::sl_onDocumentLoadedStateChanged() {
1145     Document *d = qobject_cast<Document *>(sender());
1146     importDocAnnotations(d);
1147     GObjectView::sl_onDocumentLoadedStateChanged();
1148 }
1149 
getSequenceObjectsWithContexts() const1150 QList<U2SequenceObject *> AnnotatedDNAView::getSequenceObjectsWithContexts() const {
1151     QList<U2SequenceObject *> res;
1152     foreach (ADVSequenceObjectContext *cx, seqContexts) {
1153         res.append(cx->getSequenceObject());
1154     }
1155     return res;
1156 }
1157 
getSequenceGObjectsWithContexts() const1158 QList<GObject *> AnnotatedDNAView::getSequenceGObjectsWithContexts() const {
1159     QList<GObject *> res;
1160     foreach (ADVSequenceObjectContext *cx, seqContexts) {
1161         res.append(cx->getSequenceObject());
1162     }
1163     return res;
1164 }
1165 
updateState(const AnnotatedDNAViewState & s)1166 void AnnotatedDNAView::updateState(const AnnotatedDNAViewState &s) {
1167     if (!s.isValid()) {
1168         return;
1169     }
1170     QList<GObjectReference> objs = s.getSequenceObjects();
1171     QVector<U2Region> regs = s.getSequenceSelections();
1172     assert(objs.size() == regs.size());
1173 
1174     // TODO: sync seq object lists
1175     // TODO: sync annotation object lists
1176 
1177     for (int i = 0; i < objs.size(); i++) {
1178         const GObjectReference &ref = objs[i];
1179         const U2Region &reg = regs[i];
1180         ADVSequenceObjectContext *seqCtx = getSequenceContext(ref);
1181         if (seqCtx == nullptr) {
1182             continue;
1183         }
1184         U2Region wholeSeq(0, seqCtx->getSequenceLength());
1185         U2Region finalSel = reg.intersect(wholeSeq);
1186         seqCtx->getSequenceSelection()->setRegion(finalSel);
1187     }
1188     foreach (ADVSequenceWidget *sw, seqViews) {
1189         sw->updateState(s.stateData);
1190     }
1191 
1192     foreach (ADVSplitWidget *w, splitWidgets) {
1193         w->updateState(s.stateData);
1194     }
1195 
1196     annotationsView->updateState(s.stateData);
1197 }
1198 
sl_editSettings()1199 void AnnotatedDNAView::sl_editSettings() {
1200     Settings *s = AppContext::getSettings();
1201     SAFE_POINT(s != nullptr, L10N::nullPointerError("AppContext::settings"), );
1202     EditSettings settings;
1203     settings.annotationStrategy =
1204         (U1AnnotationUtils::AnnotationStrategyForResize)s->getValue(QString(SEQ_EDIT_SETTINGS_ROOT) + SEQ_EDIT_SETTINGS_ANNOTATION_STRATEGY,
1205                                                                     U1AnnotationUtils::AnnotationStrategyForResize_Resize)
1206             .toInt();
1207     settings.recalculateQualifiers = s->getValue(QString(SEQ_EDIT_SETTINGS_ROOT) + SEQ_EDIT_SETTINGS_RECALC_QUALIFIERS, false).toBool();
1208 
1209     QObjectScopedPointer<EditSettingsDialog> dlg = new EditSettingsDialog(settings, getActiveSequenceWidget());
1210     int res = dlg->exec();
1211     SAFE_POINT(!dlg.isNull(), "EditSettingsDialog is null!", );
1212 
1213     if (res == QDialog::Accepted) {
1214         const EditSettings &newSettings = dlg->getSettings();
1215         s->setValue(QString(SEQ_EDIT_SETTINGS_ROOT) + SEQ_EDIT_SETTINGS_ANNOTATION_STRATEGY, newSettings.annotationStrategy);
1216         s->setValue(QString(SEQ_EDIT_SETTINGS_ROOT) + SEQ_EDIT_SETTINGS_RECALC_QUALIFIERS, newSettings.recalculateQualifiers);
1217     }
1218 }
1219 
sl_addSequencePart()1220 void AnnotatedDNAView::sl_addSequencePart() {
1221     ADVSequenceObjectContext *seqCtx = getActiveSequenceContext();
1222     U2SequenceObject *seqObj = seqCtx->getSequenceObject();
1223 
1224     EditSequencDialogConfig cfg;
1225 
1226     cfg.mode = EditSequenceMode_Insert;
1227     cfg.source = U2Region(0, seqObj->getSequenceLength());
1228     cfg.alphabet = seqObj->getAlphabet();
1229     cfg.position = 1;
1230 
1231     ADVSingleSequenceWidget *wgt = qobject_cast<ADVSingleSequenceWidget *>(activeSequenceWidget);
1232     if (wgt != nullptr) {
1233         QList<GSequenceLineView *> views = wgt->getLineViews();
1234         foreach (GSequenceLineView *v, views) {
1235             if (v->hasFocus()) {
1236                 cfg.position = v->getLastPressPos();
1237                 break;
1238             }
1239         }
1240     }
1241 
1242     const QVector<U2Region> &selection = seqCtx->getSequenceSelection()->getSelectedRegions();
1243     cfg.selectionRegions = selection;
1244 
1245     QObjectScopedPointer<EditSequenceDialogController> dialog = new EditSequenceDialogController(cfg, getActiveSequenceWidget());
1246     const int result = dialog->exec();
1247     CHECK(!dialog.isNull(), );
1248     CHECK(result == QDialog::Accepted, );
1249 
1250     Task *t = new ModifySequenceContentTask(dialog->getDocumentFormatId(), seqObj, U2Region(dialog->getPosToInsert(), 0), dialog->getNewSequence(), dialog->recalculateQualifiers(), dialog->getAnnotationStrategy(), dialog->getDocumentPath(), dialog->mergeAnnotations());
1251     connect(t, SIGNAL(si_stateChanged()), SLOT(sl_sequenceModifyTaskStateChanged()));
1252     AppContext::getTaskScheduler()->registerTopLevelTask(t);
1253 
1254     seqCtx->getSequenceSelection()->clear();
1255 }
1256 
sl_removeSequencePart()1257 void AnnotatedDNAView::sl_removeSequencePart() {
1258     ADVSequenceObjectContext *seqCtx = getActiveSequenceContext();
1259     U2SequenceObject *seqObj = seqCtx->getSequenceObject();
1260 
1261     Document *curDoc = seqObj->getDocument();
1262     U2Region source(0, seqObj->getSequenceLength());
1263 
1264     U2Region selection = source;
1265     if (seqCtx->getSequenceSelection()->getSelectedRegions().size() > 0) {
1266         selection = seqCtx->getSequenceSelection()->getSelectedRegions().first();
1267     }
1268 
1269     QObjectScopedPointer<RemovePartFromSequenceDialogController> dialog = new RemovePartFromSequenceDialogController(selection, source, curDoc->getURLString(), getActiveSequenceWidget());
1270     const int result = dialog->exec();
1271     CHECK(!dialog.isNull(), );
1272     CHECK(result == QDialog::Accepted, );
1273 
1274     Task *t = nullptr;
1275     if (dialog->modifyCurrentDocument()) {
1276         t = new ModifySequenceContentTask(dialog->getDocumentFormatId(), seqObj, dialog->getRegionToDelete(), DNASequence(), dialog->recalculateQualifiers(), dialog->getStrategy(), seqObj->getDocument()->getURL());
1277         connect(t, SIGNAL(si_stateChanged()), SLOT(sl_sequenceModifyTaskStateChanged()));
1278     } else {
1279         t = new ModifySequenceContentTask(dialog->getDocumentFormatId(), seqObj, dialog->getRegionToDelete(), DNASequence(), dialog->recalculateQualifiers(), dialog->getStrategy(), dialog->getNewDocumentPath(), dialog->mergeAnnotations());
1280     }
1281     SAFE_POINT(t != nullptr, L10N::nullPointerError("Edit sequence task"), );
1282     AppContext::getTaskScheduler()->registerTopLevelTask(t);
1283 
1284     seqCtx->getSequenceSelection()->clear();
1285 }
1286 
sl_replaceSequencePart()1287 void AnnotatedDNAView::sl_replaceSequencePart() {
1288     ADVSequenceObjectContext *seqCtx = getActiveSequenceContext();
1289     U2SequenceObject *seqObj = seqCtx->getSequenceObject();
1290 
1291     if (seqCtx->getSequenceSelection()->getSelectedRegions().isEmpty()) {
1292         return;
1293     }
1294 
1295     EditSequencDialogConfig cfg;
1296 
1297     cfg.mode = EditSequenceMode_Replace;
1298     cfg.source = U2Region(0, seqObj->getSequenceLength());
1299     cfg.alphabet = seqObj->getAlphabet();
1300     U2Region selection = seqCtx->getSequenceSelection()->getSelectedRegions().first();
1301     cfg.initialText = seqObj->getSequenceData(selection);
1302     cfg.position = 1;
1303 
1304     cfg.selectionRegions.append(selection);
1305 
1306     QObjectScopedPointer<EditSequenceDialogController> dlg = new EditSequenceDialogController(cfg, getActiveSequenceWidget());
1307     const int result = dlg->exec();
1308     CHECK(!dlg.isNull(), );
1309 
1310     CHECK(result == QDialog::Accepted, );
1311 
1312     Task *t = new ModifySequenceContentTask(dlg->getDocumentFormatId(), seqObj, selection, dlg->getNewSequence(), dlg->recalculateQualifiers(), dlg->getAnnotationStrategy(), dlg->getDocumentPath(), dlg->mergeAnnotations());
1313     connect(t, SIGNAL(si_stateChanged()), SLOT(sl_sequenceModifyTaskStateChanged()));
1314     AppContext::getTaskScheduler()->registerTopLevelTask(t);
1315     seqCtx->getSequenceSelection()->clear();
1316 }
1317 
sl_removeSelectedSequenceObject()1318 void AnnotatedDNAView::sl_removeSelectedSequenceObject() {
1319     ADVSequenceWidget *sw = getActiveSequenceWidget();
1320     ADVSequenceObjectContext *soc = sw->getActiveSequenceContext();
1321     U2SequenceObject *so = soc->getSequenceObject();
1322     removeObject(so);
1323 }
1324 
getAnnotationObjects(bool includeAutoAnnotations) const1325 QList<AnnotationTableObject *> AnnotatedDNAView::getAnnotationObjects(bool includeAutoAnnotations) const {
1326     QList<AnnotationTableObject *> result = annotations;
1327     if (includeAutoAnnotations) {
1328         foreach (AutoAnnotationObject *aa, autoAnnotationsMap.values()) {
1329             result += aa->getAnnotationObject();
1330         }
1331     }
1332     return result;
1333 }
1334 
updateAutoAnnotations()1335 void AnnotatedDNAView::updateAutoAnnotations() {
1336     QList<AutoAnnotationObject *> autoAnnotations = autoAnnotationsMap.values();
1337     foreach (AutoAnnotationObject *aa, autoAnnotations) {
1338         aa->updateAll();
1339     }
1340 }
1341 
sl_sequenceModifyTaskStateChanged()1342 void AnnotatedDNAView::sl_sequenceModifyTaskStateChanged() {
1343     Task *t = qobject_cast<Task *>(sender());
1344     if (nullptr == t) {
1345         return;
1346     }
1347 
1348     if (t->getState() == Task::State_Finished && !(t->hasError() || t->isCanceled())) {
1349         updateAutoAnnotations();
1350         // TODO: there must be better way to do this
1351         bool reverseComplementTask = false;
1352         if (qobject_cast<ReverseComplementSequenceTask *>(t) != nullptr ||
1353             qobject_cast<ReverseSequenceTask *>(t) != nullptr ||
1354             qobject_cast<ComplementSequenceTask *>(t) != nullptr) {
1355             reverseComplementTask = true;
1356         }
1357 
1358         ADVSequenceObjectContext *seqCtx = getActiveSequenceContext();
1359         if (reverseComplementTask && seqCtx != nullptr) {
1360             const QVector<U2Region> regions = seqCtx->getSequenceSelection()->getSelectedRegions();
1361             if (regions.count() == 1) {
1362                 const U2Region r = regions.first();
1363                 foreach (ADVSequenceWidget *w, seqCtx->getSequenceWidgets()) {
1364                     w->centerPosition((int)r.startPos);
1365                 }
1366             }
1367         }
1368 
1369         ModifySequenceContentTask *modifyContentTask = qobject_cast<ModifySequenceContentTask *>(t);
1370         if (modifyContentTask != nullptr) {
1371             qint64 seqSizeDelta = modifyContentTask->getSequenceLengthDelta();
1372             if (seqSizeDelta > 0) {  // try keeping all maximized zooms in max state
1373                 U2Region newMaxRange(0, modifyContentTask->getSequenceObject()->getSequenceLength());
1374                 U2Region oldMaxRange(0, newMaxRange.length - seqSizeDelta);
1375                 foreach (ADVSequenceObjectContext *ctx, seqContexts) {
1376                     if (ctx->getSequenceGObject() == modifyContentTask->getSequenceObject()) {
1377                         foreach (ADVSequenceWidget *w, seqCtx->getSequenceWidgets()) {
1378                             if (w->getVisibleRange() == oldMaxRange) {
1379                                 w->setVisibleRange(newMaxRange);
1380                             }
1381                         }
1382                     }
1383                 }
1384             }
1385         }
1386         updateMultiViewActions();
1387         emit si_sequenceModified(seqCtx);
1388     }
1389 }
1390 
sl_paste()1391 void AnnotatedDNAView::sl_paste() {
1392     PasteFactory *pasteFactory = AppContext::getPasteFactory();
1393     SAFE_POINT(pasteFactory != nullptr, "pasteFactory is null", );
1394 
1395     ADVSingleSequenceWidget *wgt = qobject_cast<ADVSingleSequenceWidget *>(activeSequenceWidget);
1396     CHECK(wgt != nullptr, );
1397 
1398     DetView *detView = wgt->getDetView();
1399     SAFE_POINT(detView, "DetView is unexpectedly NULL", );
1400     CHECK(detView->hasFocus(), );
1401     SAFE_POINT(detView->getEditor(), "DetViewEditor is NULL", );
1402     CHECK(detView->getEditor()->isEditMode(), );
1403 
1404     PasteTask *task = pasteFactory->createPasteTask(false);
1405     CHECK(task != nullptr, );
1406     connect(new TaskSignalMapper(task), SIGNAL(si_taskFinished(Task *)), detView->getEditor(), SLOT(sl_paste(Task *)));
1407     AppContext::getTaskScheduler()->registerTopLevelTask(task);
1408 }
1409 
onObjectRenamed(GObject * obj,const QString & oldName)1410 void AnnotatedDNAView::onObjectRenamed(GObject *obj, const QString &oldName) {
1411     if (obj->getGObjectType() == GObjectTypes::SEQUENCE) {
1412         // 1. update title
1413         OpenAnnotatedDNAViewTask::updateTitle(this);
1414 
1415         // 2. update components
1416         U2SequenceObject *seqObj = qobject_cast<U2SequenceObject *>(obj);
1417         ADVSequenceObjectContext *ctx = getSequenceContext(seqObj);
1418         foreach (ADVSequenceWidget *w, ctx->getSequenceWidgets()) {
1419             w->onSequenceObjectRenamed(oldName);
1420         }
1421     }
1422 }
1423 
sl_reverseComplementSequence()1424 void AnnotatedDNAView::sl_reverseComplementSequence() {
1425     reverseComplementSequence();
1426 }
1427 
sl_reverseSequence()1428 void AnnotatedDNAView::sl_reverseSequence() {
1429     reverseComplementSequence(true, false);
1430 }
1431 
sl_complementSequence()1432 void AnnotatedDNAView::sl_complementSequence() {
1433     reverseComplementSequence(false, true);
1434 }
1435 
sl_selectionChanged()1436 void AnnotatedDNAView::sl_selectionChanged() {
1437     ADVSequenceObjectContext *seqCtx = getActiveSequenceContext();
1438     CHECK(seqCtx != nullptr, );
1439     DNASequenceSelection *selection = qobject_cast<DNASequenceSelection *>(sender());
1440     CHECK(selection != nullptr && seqCtx->getSequenceGObject() == selection->getSequenceObject(), );
1441 
1442     replaceSequencePart->setEnabled(!seqCtx->getSequenceSelection()->isEmpty());
1443 }
1444 
sl_aminoTranslationChanged()1445 void AnnotatedDNAView::sl_aminoTranslationChanged() {
1446     ADVSequenceObjectContext *seqCtx = getActiveSequenceContext();
1447     U2SequenceObject *seqObj = seqCtx->getSequenceObject();
1448     QList<AutoAnnotationObject *> autoAnnotations = autoAnnotationsMap.values();
1449     foreach (AutoAnnotationObject *aa, autoAnnotations) {
1450         if (aa->getSequenceObject() == seqObj) {
1451             aa->updateTranslationDependent(seqCtx->getAminoTT());
1452         }
1453     }
1454 }
1455 
reverseComplementSequence(bool reverse,bool complement)1456 void AnnotatedDNAView::reverseComplementSequence(bool reverse, bool complement) {
1457     ADVSequenceObjectContext *seqCtx = getActiveSequenceContext();
1458     U2SequenceObject *seqObj = seqCtx->getSequenceObject();
1459     QList<AnnotationTableObject *> annotationObjects = seqCtx->getAnnotationObjects(false).toList();
1460 
1461     DNATranslation *complTT = nullptr;
1462     if (seqObj->getAlphabet()->isNucleic()) {
1463         complTT = seqCtx->getComplementTT();
1464     }
1465 
1466     Task *t = nullptr;
1467     if (reverse && complement) {
1468         t = new ReverseComplementSequenceTask(seqObj, annotationObjects, seqCtx->getSequenceSelection(), complTT);
1469     } else if (reverse) {
1470         t = new ReverseSequenceTask(seqObj, annotationObjects, seqCtx->getSequenceSelection());
1471     } else if (complement) {
1472         t = new ComplementSequenceTask(seqObj, annotationObjects, seqCtx->getSequenceSelection(), complTT);
1473     }
1474 
1475     AppContext::getTaskScheduler()->registerTopLevelTask(t);
1476     connect(t, SIGNAL(si_stateChanged()), SLOT(sl_sequenceModifyTaskStateChanged()));
1477 }
1478 
getEditActionFromSequenceWidget(ADVSequenceWidget * seqWgt)1479 QAction *AnnotatedDNAView::getEditActionFromSequenceWidget(ADVSequenceWidget *seqWgt) {
1480     ADVSingleSequenceWidget *wgt = qobject_cast<ADVSingleSequenceWidget *>(seqWgt);
1481     SAFE_POINT(wgt != nullptr, "ADVSingleSequenceWidget is NULL", nullptr);
1482 
1483     DetView *detView = wgt->getDetView();
1484     SAFE_POINT(detView != nullptr, "DetView is NULL", nullptr);
1485 
1486     DetViewSequenceEditor *editor = detView->getEditor();
1487     SAFE_POINT(editor != nullptr, "DetViewSequenceEditor is NULL", nullptr);
1488 
1489     QAction *editAction = editor->getEditAction();
1490     SAFE_POINT(editAction != nullptr, "EditAction is NULL", nullptr);
1491 
1492     return editAction;
1493 }
1494 
areAnnotationsInRange(const QList<Annotation * > & toCheck)1495 bool AnnotatedDNAView::areAnnotationsInRange(const QList<Annotation *> &toCheck) {
1496     foreach (Annotation *a, toCheck) {
1497         QList<ADVSequenceObjectContext *> relatedSeqObjects = findRelatedSequenceContexts(a->getGObject());
1498         foreach (ADVSequenceObjectContext *seq, relatedSeqObjects) {
1499             SAFE_POINT(seq != nullptr, "Sequence is NULL", true);
1500             foreach (const U2Region &r, a->getRegions()) {
1501                 if (r.endPos() > seq->getSequenceLength()) {
1502                     return false;
1503                 }
1504             }
1505         }
1506     }
1507     return true;
1508 }
1509 
getActiveSequenceWidget() const1510 ADVSequenceWidget *AnnotatedDNAView::getActiveSequenceWidget() const {
1511     return activeSequenceWidget;
1512 }
1513 
1514 }  // namespace U2
1515