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 "ADVSyncViewManager.h"
23 
24 #include <U2Core/AnnotationTableObject.h>
25 #include <U2Core/Counter.h>
26 #include <U2Core/DNASequenceSelection.h>
27 
28 #include "ADVSequenceObjectContext.h"
29 #include "ADVSingleSequenceWidget.h"
30 #include "AnnotatedDNAView.h"
31 #include "AutoAnnotationUtils.h"
32 #include "PanView.h"
33 
34 namespace U2 {
35 
ADVSyncViewManager(AnnotatedDNAView * v)36 ADVSyncViewManager::ADVSyncViewManager(AnnotatedDNAView *v)
37     : QObject(v), adv(v) {
38     assert(v->getSequenceContexts().isEmpty());
39 
40     recursion = false;
41     selectionRecursion = false;
42 
43     lockByStartPosAction = new QAction(tr("Lock scales: visible range start"), this);
44     lockByStartPosAction->setObjectName("Lock scales: visible range start");
45     connect(lockByStartPosAction, SIGNAL(triggered()), SLOT(sl_lock()));
46     lockByStartPosAction->setCheckable(true);
47 
48     lockBySeqSelAction = new QAction(tr("Lock scales: selected sequence"), this);
49     lockBySeqSelAction->setObjectName("Lock scales: selected sequence");
50     connect(lockBySeqSelAction, SIGNAL(triggered()), SLOT(sl_lock()));
51     lockBySeqSelAction->setCheckable(true);
52 
53     lockByAnnSelAction = new QAction(tr("Lock scales: selected annotation"), this);
54     lockByAnnSelAction->setObjectName("Lock scales: selected annotation");
55     connect(lockByAnnSelAction, SIGNAL(triggered()), SLOT(sl_lock()));
56     lockByAnnSelAction->setCheckable(true);
57 
58     lockActionGroup = new QActionGroup(this);
59     lockActionGroup->addAction(lockByStartPosAction);
60     lockActionGroup->addAction(lockBySeqSelAction);
61     lockActionGroup->addAction(lockByAnnSelAction);
62     lockActionGroup->setExclusive(true);
63 
64     syncByStartPosAction = new QAction(tr("Adjust scales: visible range start"), this);
65     syncByStartPosAction->setObjectName("Adjust scales: visible range start");
66     connect(syncByStartPosAction, SIGNAL(triggered()), SLOT(sl_sync()));
67 
68     syncBySeqSelAction = new QAction(tr("Adjust scales: selected sequence"), this);
69     syncBySeqSelAction->setObjectName("Adjust scales: selected sequence");
70     connect(syncBySeqSelAction, SIGNAL(triggered()), SLOT(sl_sync()));
71 
72     syncByAnnSelAction = new QAction(tr("Adjust scales: selected annotation"), this);
73     syncByAnnSelAction->setObjectName("Adjust scales: selected annotation");
74     connect(syncByAnnSelAction, SIGNAL(triggered()), SLOT(sl_sync()));
75 
76     lockMenu = new QMenu(tr("Lock scales"));
77     lockMenu->setIcon(QIcon(":core/images/lock_scales.png"));
78     lockMenu->addActions(lockActionGroup->actions());
79 
80     syncMenu = new QMenu(tr("Adjust scales"));
81     syncMenu->setIcon(QIcon(":core/images/sync_scales.png"));
82     syncMenu->addAction(syncByStartPosAction);
83     syncMenu->addAction(syncBySeqSelAction);
84     syncMenu->addAction(syncByAnnSelAction);
85 
86     lockButton = new QToolButton();
87     lockButton->setObjectName("Lock scales");
88     lockButton->setCheckable(true);
89     connect(lockButton, SIGNAL(clicked()), SLOT(sl_lock()));
90     lockButton->setDefaultAction(lockMenu->menuAction());
91     lockButton->setCheckable(true);
92 
93     syncButton = new QToolButton();
94     syncButton->setObjectName("Adjust scales");
95     connect(syncButton, SIGNAL(clicked()), SLOT(sl_sync()));
96     syncButton->setDefaultAction(syncMenu->menuAction());
97 
98     lockButtonTBAction = nullptr;
99     syncButtonTBAction = nullptr;
100 
101     // auto-annotations highlighting ops
102 
103     toggleAutoAnnotationsMenu = new QMenu("Global automatic annotation highlighting");
104     toggleAutoAnnotationsMenu->setIcon(QIcon(":core/images/predefined_annotation_groups.png"));
105     connect(toggleAutoAnnotationsMenu, SIGNAL(aboutToShow()), SLOT(sl_updateAutoAnnotationsMenu()));
106 
107     toggleAutoAnnotationsButton = new QToolButton();
108     toggleAutoAnnotationsButton->setObjectName("toggleAutoAnnotationsButton");
109     toggleAutoAnnotationsButton->setDefaultAction(toggleAutoAnnotationsMenu->menuAction());
110     toggleAutoAnnotationsButton->setPopupMode(QToolButton::InstantPopup);
111 
112     toggleAutoAnnotationsAction = nullptr;
113 
114     // visual mode ops
115     toggleAllAction = new QAction("Toggle All sequence views", this);
116     toggleAllAction->setObjectName("toggleAllSequenceViews");
117     connect(toggleAllAction, SIGNAL(triggered()), SLOT(sl_toggleVisualMode()));
118 
119     toggleOveAction = new QAction("Toggle Overview", this);
120     toggleOveAction->setObjectName("toggleOverview");
121     connect(toggleOveAction, SIGNAL(triggered()), SLOT(sl_toggleVisualMode()));
122 
123     togglePanAction = new QAction("Toggle Zoom view", this);
124     togglePanAction->setObjectName("toggleZoomView");
125     connect(togglePanAction, SIGNAL(triggered()), SLOT(sl_toggleVisualMode()));
126 
127     toggleDetAction = new QAction("Toggle Details view", this);
128     toggleDetAction->setObjectName("toggleDetailsView");
129     connect(toggleDetAction, SIGNAL(triggered()), SLOT(sl_toggleVisualMode()));
130 
131     toggleViewButtonAction = nullptr;
132     toggleViewButtonMenu = new QMenu(tr("Toggle views"));
133     toggleViewButtonMenu->setIcon(QIcon(":core/images/adv_widget_menu.png"));
134 
135     toggleViewButtonMenu->addAction(toggleAllAction);  //-> behavior can be not clear to user
136     toggleViewButtonMenu->addAction(toggleOveAction);
137     toggleViewButtonMenu->addAction(togglePanAction);
138     toggleViewButtonMenu->addAction(toggleDetAction);
139     connect(toggleViewButtonMenu, SIGNAL(aboutToShow()), SLOT(sl_updateVisualMode()));
140 
141     toggleViewButton = new QToolButton();
142     toggleViewButton->setObjectName("toggleViewButton");
143     toggleViewButton->setDefaultAction(toggleViewButtonMenu->menuAction());
144     toggleViewButton->setPopupMode(QToolButton::InstantPopup);
145 
146     updateEnabledState();
147 
148     connect(adv, SIGNAL(si_sequenceWidgetAdded(ADVSequenceWidget *)), SLOT(sl_sequenceWidgetAdded(ADVSequenceWidget *)));
149     connect(adv, SIGNAL(si_sequenceWidgetRemoved(ADVSequenceWidget *)), SLOT(sl_sequenceWidgetRemoved(ADVSequenceWidget *)));
150 }
151 
~ADVSyncViewManager()152 ADVSyncViewManager::~ADVSyncViewManager() {
153     delete lockButton;
154     delete syncButton;
155     delete syncMenu;
156     delete lockMenu;
157 
158     delete toggleAutoAnnotationsButton;
159     delete toggleAutoAnnotationsMenu;
160 
161     delete toggleViewButton;
162     delete toggleViewButtonMenu;
163 }
164 
updateToolbar1(QToolBar * tb)165 void ADVSyncViewManager::updateToolbar1(QToolBar *tb) {
166     if (lockButtonTBAction == nullptr) {
167         lockButtonTBAction = tb->addWidget(lockButton);
168         syncButtonTBAction = tb->addWidget(syncButton);
169     } else {
170         tb->addAction(lockButtonTBAction);
171         tb->addAction(syncButtonTBAction);
172     }
173 }
174 
updateToolbar2(QToolBar * tb)175 void ADVSyncViewManager::updateToolbar2(QToolBar *tb) {
176     if (toggleAutoAnnotationsAction == nullptr) {
177         updateAutoAnnotationActions();
178         toggleAutoAnnotationsAction = tb->addWidget(toggleAutoAnnotationsButton);
179     } else {
180         tb->addAction(toggleAutoAnnotationsAction);
181     }
182 
183     if (toggleViewButtonAction == nullptr) {
184         toggleViewButtonAction = tb->addWidget(toggleViewButton);
185     } else {
186         tb->addAction(toggleViewButtonAction);
187     }
188 }
189 
updateEnabledState()190 void ADVSyncViewManager::updateEnabledState() {
191     bool enabled = getViewsFromADV().size() > 1;
192     syncButton->setEnabled(enabled);
193     lockButton->setEnabled(enabled);
194 }
195 
sl_sequenceWidgetAdded(ADVSequenceWidget * w)196 void ADVSyncViewManager::sl_sequenceWidgetAdded(ADVSequenceWidget *w) {
197     ADVSingleSequenceWidget *sw = qobject_cast<ADVSingleSequenceWidget *>(w);
198     if (sw == nullptr) {
199         return;
200     }
201     unlock();
202     if (toggleAutoAnnotationsAction != nullptr) {
203         updateAutoAnnotationActions();
204     }
205 }
206 
sl_sequenceWidgetRemoved(ADVSequenceWidget * w)207 void ADVSyncViewManager::sl_sequenceWidgetRemoved(ADVSequenceWidget *w) {
208     ADVSingleSequenceWidget *sw = qobject_cast<ADVSingleSequenceWidget *>(w);
209     if (sw == nullptr) {
210         return;
211     }
212     unlock();
213     updateAutoAnnotationActions();
214 }
215 
unlock()216 void ADVSyncViewManager::unlock() {
217     foreach (ADVSingleSequenceWidget *sw, views) {
218         sw->getPanView()->disconnect(this);
219         sw->getSequenceSelection()->disconnect(this);
220     }
221     views.clear();
222     updateEnabledState();
223 }
224 
getViewsFromADV() const225 QList<ADVSingleSequenceWidget *> ADVSyncViewManager::getViewsFromADV() const {
226     QList<ADVSingleSequenceWidget *> res;
227     foreach (ADVSequenceWidget *w, adv->getSequenceWidgets()) {
228         ADVSingleSequenceWidget *sw = qobject_cast<ADVSingleSequenceWidget *>(w);
229         if (sw != nullptr) {
230             res.append(sw);
231         }
232     }
233     return res;
234 }
235 
sl_rangeChanged()236 void ADVSyncViewManager::sl_rangeChanged() {
237     if (recursion) {
238         return;
239     }
240     recursion = true;
241 
242     PanView *activePan = qobject_cast<PanView *>(sender());
243     const U2Region &activeRange = activePan->getVisibleRange();
244     int activeOffset = activePan->getSyncOffset();
245     foreach (ADVSingleSequenceWidget *sw, views) {
246         PanView *pan = sw->getPanView();
247         if (pan == activePan) {
248             continue;
249         }
250         int panOffset = pan->getSyncOffset();
251         int resultOffset = panOffset - activeOffset;
252         qint64 seqLen = pan->getSequenceLength();
253         qint64 newStart = qBound(qint64(0), activeRange.startPos + resultOffset, seqLen);
254         qint64 nVisible = qMin(activeRange.length, seqLen);
255         if (newStart + nVisible > seqLen) {
256             newStart = seqLen - nVisible;
257         }
258         assert(newStart >= 0 && newStart + nVisible <= seqLen);
259         pan->setVisibleRange(U2Region(newStart, nVisible));
260     }
261 
262     recursion = false;
263 }
264 
sl_lock()265 void ADVSyncViewManager::sl_lock() {
266     GCOUNTER(tvar, "SequenceView::SyncViewManager::Lock scales");
267     QObject *s = sender();
268     bool buttonClicked = (s == lockButton);
269 
270     SyncMode m = SyncMode_Start;
271     if (lockButton->isChecked()) {
272         unlock();
273     } else {
274         if (s == lockBySeqSelAction) {
275             m = SyncMode_SeqSel;
276         } else if (s == lockByAnnSelAction) {
277             m = SyncMode_AnnSel;
278         } else if (s == lockButton) {
279             m = detectSyncMode();
280         }
281         sync(true, m);
282     }
283 
284     if (buttonClicked) {
285         QAction *checkedAction = lockActionGroup->checkedAction();
286         if (nullptr == checkedAction) {
287             toggleCheckedAction(m);
288         } else {
289             checkedAction->toggle();
290         }
291         lockButton->toggle();
292     } else {
293         lockButton->setChecked(lockActionGroup->checkedAction() != nullptr);
294     }
295 }
296 
sl_sync()297 void ADVSyncViewManager::sl_sync() {
298     GCOUNTER(tvar, "SequenceView::SyncViewManager::Adjust scales");
299     QObject *s = sender();
300     SyncMode m = SyncMode_Start;
301     if (s == syncBySeqSelAction) {
302         m = SyncMode_SeqSel;
303     } else if (s == syncByAnnSelAction) {
304         m = SyncMode_AnnSel;
305     } else if (s == syncButton) {
306         m = detectSyncMode();
307     }
308     sync(false, m);
309 }
310 
sync(bool lock,SyncMode m)311 void ADVSyncViewManager::sync(bool lock, SyncMode m) {
312     ADVSingleSequenceWidget *focusedW = qobject_cast<ADVSingleSequenceWidget *>(adv->getActiveSequenceWidget());
313     if (focusedW == nullptr) {
314         return;
315     }
316 
317     QList<ADVSingleSequenceWidget *> seqs = getViewsFromADV();
318     QVector<int> offsets(seqs.size());
319 
320     // offset here ==> new panview start pos
321     // dOffset is used to keep focused sequence unchanged
322     U2Region focusedRange;
323     int dOffset = 0;
324     for (int i = 0; i < seqs.size(); i++) {
325         int offset = 0;
326         ADVSingleSequenceWidget *seqW = seqs[i];
327         switch (m) {
328             case SyncMode_Start:
329                 offset = seqW->getVisibleRange().startPos;
330                 break;
331             case SyncMode_SeqSel:
332                 offset = offsetBySeqSel(seqW);
333                 break;
334             case SyncMode_AnnSel:
335                 offset = offsetByAnnSel(seqW);
336                 break;
337         }
338         offsets[i] = offset;
339         if (seqW == focusedW) {
340             focusedRange = focusedW->getVisibleRange();
341             dOffset = offset - focusedRange.startPos;
342         }
343     }
344     assert(!focusedRange.isEmpty());
345     for (int i = 0; i < seqs.size(); i++) {
346         ADVSingleSequenceWidget *seqW = seqs[i];
347         int offset = offsets[i] - dOffset;
348         PanView *pan = seqW->getPanView();
349         if (seqW != focusedW) {
350             pan->setNumBasesVisible(focusedRange.length);
351             pan->setStartPos(offset);
352         }
353         if (lock) {
354             DNASequenceSelection *selection = seqW->getSequenceContext()->getSequenceSelection();
355             connect(selection,
356                     SIGNAL(si_selectionChanged(LRegionsSelection *, const QVector<U2Region> &, const QVector<U2Region> &)),
357                     SLOT(sl_onSelectionChanged(LRegionsSelection *, const QVector<U2Region> &, const QVector<U2Region> &)));
358             pan->setSyncOffset(offset);
359             connect(pan, SIGNAL(si_visibleRangeChanged()), SLOT(sl_rangeChanged()));
360             views.append(seqW);
361         }
362     }
363 }
364 
offsetBySeqSel(ADVSingleSequenceWidget * w) const365 int ADVSyncViewManager::offsetBySeqSel(ADVSingleSequenceWidget *w) const {
366     DNASequenceSelection *seqSel = w->getSequenceContext()->getSequenceSelection();
367     if (seqSel->isEmpty()) {
368         return w->getVisibleRange().startPos;
369     }
370     return seqSel->getSelectedRegions().first().startPos;
371 }
372 
offsetByAnnSel(ADVSingleSequenceWidget * w) const373 int ADVSyncViewManager::offsetByAnnSel(ADVSingleSequenceWidget *w) const {
374     int pos = findSelectedAnnotationPos(w);
375     if (pos == -1) {
376         return w->getVisibleRange().startPos;
377     }
378     return pos;
379 }
380 
findSelectedAnnotationPos(ADVSingleSequenceWidget * w) const381 int ADVSyncViewManager::findSelectedAnnotationPos(ADVSingleSequenceWidget *w) const {
382     AnnotationSelection *as = w->getSequenceContext()->getAnnotationsSelection();
383     const QSet<AnnotationTableObject *> &objs = w->getSequenceContext()->getAnnotationObjects(true);
384     foreach (const Annotation *annotation, as->getAnnotations()) {
385         AnnotationTableObject *obj = annotation->getGObject();
386         if (objs.contains(obj)) {
387             return annotation->getStrand().isCompementary() ? annotation->getRegions().last().endPos() : annotation->getRegions().first().startPos;
388         }
389     }
390     return -1;
391 }
392 
detectSyncMode() const393 ADVSyncViewManager::SyncMode ADVSyncViewManager::detectSyncMode() const {
394     ADVSingleSequenceWidget *focusedW = qobject_cast<ADVSingleSequenceWidget *>(adv->getActiveSequenceWidget());
395     assert(focusedW != nullptr);
396     QList<ADVSingleSequenceWidget *> seqs = getViewsFromADV();
397 
398     // if current sequence + any other sequence have annotation selection -> sync by annotation
399     if (findSelectedAnnotationPos(focusedW) != -1) {
400         foreach (ADVSingleSequenceWidget *sw, seqs) {
401             if (sw != focusedW && findSelectedAnnotationPos(sw) != -1) {
402                 return SyncMode_AnnSel;
403             }
404         }
405     }
406 
407     // if current sequence + any other sequence have sequence selection -> sync by annotation
408     if (!focusedW->getSequenceContext()->getSequenceSelection()->isEmpty()) {
409         foreach (ADVSingleSequenceWidget *sw, seqs) {
410             if (sw != focusedW && !sw->getSequenceContext()->getSequenceSelection()->isEmpty()) {
411                 return SyncMode_SeqSel;
412             }
413         }
414     }
415     // else sync by start pos
416     return SyncMode_Start;
417 }
418 
sl_updateVisualMode()419 void ADVSyncViewManager::sl_updateVisualMode() {
420     // if have at least 1 visible -> hide all
421     bool haveVisiblePan = false;
422     bool haveVisibleDet = false;
423     bool haveVisibleView = false;
424     bool haveVisibleOve = false;
425     foreach (ADVSingleSequenceWidget *sw, getViewsFromADV()) {
426         haveVisiblePan = haveVisiblePan || !sw->isPanViewCollapsed();
427         haveVisibleDet = haveVisibleDet || !sw->isDetViewCollapsed();
428         haveVisibleView = haveVisibleView || !sw->isViewCollapsed();
429         haveVisibleOve = haveVisibleOve || !sw->isOverviewCollapsed();
430     }
431     toggleAllAction->setText(haveVisibleView ? tr("Hide all sequences") : tr("Show all sequences"));
432     togglePanAction->setText(haveVisiblePan ? tr("Hide all zoom views") : tr("Show all zoom views"));
433     toggleDetAction->setText(haveVisibleDet ? tr("Hide all details") : tr("Show all details"));
434     toggleOveAction->setText(haveVisibleOve ? tr("Hide all overviews") : tr("Show all overviews"));
435 }
436 
sl_toggleVisualMode()437 void ADVSyncViewManager::sl_toggleVisualMode() {
438     // if have at least 1 visible -> hide all
439     bool haveVisibleNav = false;
440     bool haveVisiblePan = false;
441     bool haveVisibleDet = false;
442     bool haveVisibleView = false;
443 
444     QList<ADVSingleSequenceWidget *> views = getViewsFromADV();
445     foreach (ADVSingleSequenceWidget *sw, views) {
446         haveVisibleDet = haveVisibleDet || !sw->isDetViewCollapsed();
447         haveVisibleView = haveVisibleView || !sw->isViewCollapsed();
448         haveVisiblePan = haveVisiblePan || !sw->isPanViewCollapsed();
449         haveVisibleNav = haveVisibleNav || !sw->isOverviewCollapsed();
450     }
451 
452     QObject *s = sender();
453     foreach (ADVSingleSequenceWidget *sw, views) {
454         if (s == toggleOveAction) {
455             sw->setOverviewCollapsed(haveVisibleNav);
456         } else if (s == togglePanAction) {
457             sw->setPanViewCollapsed(haveVisiblePan);
458         } else if (s == toggleDetAction) {
459             sw->setDetViewCollapsed(haveVisibleDet);
460         } else {
461             sw->setViewCollapsed(haveVisibleView);
462         }
463     }
464 }
465 
sl_onSelectionChanged(LRegionsSelection * sel,const QVector<U2Region> & added,const QVector<U2Region> &)466 void ADVSyncViewManager::sl_onSelectionChanged(LRegionsSelection *sel, const QVector<U2Region> &added, const QVector<U2Region> &) {
467     Q_UNUSED(sel);
468     if (selectionRecursion) {
469         return;
470     }
471 
472     selectionRecursion = true;
473 
474     ADVSingleSequenceWidget *focusedW = qobject_cast<ADVSingleSequenceWidget *>(adv->getActiveSequenceWidget());
475     if (focusedW == nullptr) {
476         return;
477     }
478     for (int i = 0; i < views.size(); ++i) {
479         ADVSingleSequenceWidget *w = views[i];
480         if (w == focusedW) {
481             continue;
482         }
483 
484         int offset = focusedW->getVisibleRange().startPos - w->getVisibleRange().startPos;
485 
486         DNASequenceSelection *selection = w->getSequenceSelection();
487         selection->clear();
488         qint64 seqLen = w->getSequenceLength();
489         foreach (U2Region r, added) {
490             r.startPos -= offset;
491 
492             if (r.startPos < 0) {
493                 r.startPos = 0;
494             }
495 
496             if (r.endPos() > seqLen) {
497                 r.length = seqLen - r.startPos;
498             }
499             if (r.length > 0) {
500                 selection->addRegion(r);
501             }
502         }
503     }
504 
505     selectionRecursion = false;
506 }
507 
toggleCheckedAction(SyncMode mode)508 void ADVSyncViewManager::toggleCheckedAction(SyncMode mode) {
509     switch (mode) {
510         case SyncMode_AnnSel:
511             lockByAnnSelAction->toggle();
512             break;
513         case SyncMode_SeqSel:
514             lockBySeqSelAction->toggle();
515             break;
516         default:
517             lockByStartPosAction->toggle();
518     }
519 }
520 
updateAutoAnnotationActions()521 void ADVSyncViewManager::updateAutoAnnotationActions() {
522     aaActionMap.clear();
523     toggleAutoAnnotationsMenu->clear();
524 
525     foreach (ADVSequenceWidget *w, adv->getSequenceWidgets()) {
526         QList<ADVSequenceWidgetAction *> actions = w->getADVSequenceWidgetActions();
527         bool active = false;
528         foreach (ADVSequenceWidgetAction *action, actions) {
529             AutoAnnotationsADVAction *aaAction = qobject_cast<AutoAnnotationsADVAction *>(action);
530             if (aaAction != nullptr) {
531                 QList<QAction *> aaToggleActions = aaAction->getToggleActions();
532                 foreach (QAction *toggleAction, aaToggleActions) {
533                     if (toggleAction->isEnabled()) {
534                         aaActionMap.insertMulti(toggleAction->text(), toggleAction);
535                         active = true;
536                     }
537                 }
538                 aaAction->setVisible(active);
539             }
540         }
541     }
542 
543     toggleAutoAnnotationsButton->setEnabled(!aaActionMap.isEmpty());
544 
545     QSet<QString> actionNames = aaActionMap.keys().toSet();
546 
547     foreach (const QString &aName, actionNames) {
548         QAction *action = new QAction(toggleAutoAnnotationsMenu);
549         action->setObjectName(aName);
550         connect(action, SIGNAL(triggered()), SLOT(sl_toggleAutoAnnotationHighlighting()));
551         toggleAutoAnnotationsMenu->addAction(action);
552     }
553 }
554 
555 #define HAVE_ENABLED_AUTOANNOTATIONS "have_enabled_autoannotations"
556 
sl_toggleAutoAnnotationHighlighting()557 void ADVSyncViewManager::sl_toggleAutoAnnotationHighlighting() {
558     QAction *menuAction = qobject_cast<QAction *>(sender());
559     if (menuAction == nullptr) {
560         return;
561     }
562     QVariant val = menuAction->property(HAVE_ENABLED_AUTOANNOTATIONS);
563     assert(val.isValid());
564     bool haveEnabledAutoAnnotations = val.toBool();
565     QList<QAction *> aaActions = aaActionMap.values(menuAction->objectName());
566     foreach (QAction *aaAction, aaActions) {
567         aaAction->setChecked(!haveEnabledAutoAnnotations);
568     }
569 }
570 
sl_updateAutoAnnotationsMenu()571 void ADVSyncViewManager::sl_updateAutoAnnotationsMenu() {
572     QList<QAction *> menuActions = toggleAutoAnnotationsMenu->actions();
573 
574     foreach (QAction *menuAction, menuActions) {
575         QString aName = menuAction->objectName();
576         bool haveEnabledAutoAnnotations = false;
577         // if have at least 1 checked  -> uncheck all
578         QList<QAction *> aaActions = aaActionMap.values(aName);
579         foreach (QAction *aaAction, aaActions) {
580             if (aaAction->isChecked()) {
581                 haveEnabledAutoAnnotations = true;
582                 break;
583             }
584         }
585 
586         if (haveEnabledAutoAnnotations) {
587             menuAction->setText(tr("Hide %1").arg(aName));
588         } else {
589             menuAction->setText(tr("Show %1").arg(aName));
590         }
591         menuAction->setProperty(HAVE_ENABLED_AUTOANNOTATIONS, haveEnabledAutoAnnotations);
592     }
593 }
594 
595 }  // namespace U2
596