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 ® = 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