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 "GSequenceLineViewAnnotated.h"
23 
24 #include <QApplication>
25 #include <QMenu>
26 #include <QPainterPath>
27 #include <QToolTip>
28 
29 #include <U2Core/AnnotationData.h>
30 #include <U2Core/AnnotationModification.h>
31 #include <U2Core/AnnotationSettings.h>
32 #include <U2Core/AnnotationTableObject.h>
33 #include <U2Core/AppContext.h>
34 #include <U2Core/DNASequenceObject.h>
35 #include <U2Core/DNASequenceSelection.h>
36 #include <U2Core/GenbankFeatures.h>
37 #include <U2Core/Timer.h>
38 #include <U2Core/U1AnnotationUtils.h>
39 #include <U2Core/U2SafePoints.h>
40 
41 #include <U2Formats/GenbankLocationParser.h>
42 
43 #include <U2Gui/GUIUtils.h>
44 
45 #include "ADVSequenceObjectContext.h"
46 
47 namespace U2 {
48 
GSequenceLineViewAnnotated(QWidget * p,SequenceObjectContext * ctx)49 GSequenceLineViewAnnotated::GSequenceLineViewAnnotated(QWidget *p, SequenceObjectContext *ctx)
50     : GSequenceLineView(p, ctx) {
51     const QSet<AnnotationTableObject *> aObjs = ctx->getAnnotationObjects(true);
52     foreach (const AnnotationTableObject *ao, aObjs) {
53         connectAnnotationObject(ao);
54     }
55     connect(ctx->getAnnotationsSelection(),
56             SIGNAL(si_selectionChanged(AnnotationSelection *, const QList<Annotation *> &, const QList<Annotation *> &)),
57             SLOT(sl_onAnnotationSelectionChanged(AnnotationSelection *, const QList<Annotation *> &, const QList<Annotation *> &)));
58 
59     connect(ctx, SIGNAL(si_annotationObjectAdded(AnnotationTableObject *)), SLOT(sl_onAnnotationObjectAdded(AnnotationTableObject *)));
60     connect(ctx, SIGNAL(si_annotationObjectRemoved(AnnotationTableObject *)), SLOT(sl_onAnnotationObjectRemoved(AnnotationTableObject *)));
61     connect(ctx, SIGNAL(si_annotationActivated(Annotation *, int)), SLOT(sl_onAnnotationActivated(Annotation *, int)));
62 
63     connect(AppContext::getAnnotationsSettingsRegistry(), SIGNAL(si_annotationSettingsChanged(const QStringList &)), SLOT(sl_onAnnotationSettingsChanged(const QStringList &)));
64 }
65 
connectAnnotationObject(const AnnotationTableObject * ao)66 void GSequenceLineViewAnnotated::connectAnnotationObject(const AnnotationTableObject *ao) {
67     connect(ao, SIGNAL(si_onAnnotationsAdded(const QList<Annotation *> &)), SLOT(sl_onAnnotationsAdded(const QList<Annotation *> &)));
68     connect(ao, SIGNAL(si_onAnnotationsRemoved(const QList<Annotation *> &)), SLOT(sl_onAnnotationsRemoved(const QList<Annotation *> &)));
69     connect(ao, SIGNAL(si_onAnnotationsInGroupRemoved(const QList<Annotation *> &, AnnotationGroup *)), SLOT(sl_onAnnotationsInGroupRemoved(const QList<Annotation *> &, AnnotationGroup *)));
70     connect(ao, SIGNAL(si_onAnnotationsModified(const QList<AnnotationModification> &)), SLOT(sl_onAnnotationsModified(const QList<AnnotationModification> &)));
71 }
72 
sl_onAnnotationSettingsChanged(const QStringList &)73 void GSequenceLineViewAnnotated::sl_onAnnotationSettingsChanged(const QStringList &) {
74     addUpdateFlags(GSLV_UF_AnnotationsChanged);
75     update();
76 }
77 
sl_onAnnotationObjectAdded(AnnotationTableObject * o)78 void GSequenceLineViewAnnotated::sl_onAnnotationObjectAdded(AnnotationTableObject *o) {
79     connectAnnotationObject(o);
80     sl_onAnnotationsAdded(o->getAnnotations());
81 }
82 
sl_onAnnotationObjectRemoved(AnnotationTableObject * o)83 void GSequenceLineViewAnnotated::sl_onAnnotationObjectRemoved(AnnotationTableObject *o) {
84     o->disconnect(this);
85     sl_onAnnotationsRemoved(o->getAnnotations());
86 }
87 
sl_onAnnotationsAdded(const QList<Annotation * > & l)88 void GSequenceLineViewAnnotated::sl_onAnnotationsAdded(const QList<Annotation *> &l) {
89     GTIMER(c2, t2, "GSequenceLineViewAnnotated::sl_onAnnotationsAdded");
90     registerAnnotations(l);
91     addUpdateFlags(GSLV_UF_AnnotationsChanged);
92     update();
93 }
94 
sl_onAnnotationsRemoved(const QList<Annotation * > & l)95 void GSequenceLineViewAnnotated::sl_onAnnotationsRemoved(const QList<Annotation *> &l) {
96     unregisterAnnotations(l);
97     addUpdateFlags(GSLV_UF_AnnotationsChanged);
98     update();
99 }
100 
sl_onAnnotationsInGroupRemoved(const QList<Annotation * > & l,AnnotationGroup *)101 void GSequenceLineViewAnnotated::sl_onAnnotationsInGroupRemoved(const QList<Annotation *> &l, AnnotationGroup *) {
102     ClearAnnotationsTask *task = new ClearAnnotationsTask(l, this);
103     AppContext::getTaskScheduler()->registerTopLevelTask(task);
104 }
105 
ClearAnnotationsTask(const QList<Annotation * > & list,GSequenceLineViewAnnotated * view)106 ClearAnnotationsTask::ClearAnnotationsTask(const QList<Annotation *> &list, GSequenceLineViewAnnotated *view)
107     : Task("Clear annotations", TaskFlag_None), l(list), view(view) {
108 }
109 
run()110 void ClearAnnotationsTask::run() {
111     view->unregisterAnnotations(l);
112 }
113 
report()114 Task::ReportResult ClearAnnotationsTask::report() {
115     view->addUpdateFlags(GSLV_UF_AnnotationsChanged);
116     view->update();
117     return ReportResult_Finished;
118 }
119 
sl_onAnnotationActivated(U2::Annotation * annotation,int regionIndex)120 void GSequenceLineViewAnnotated::sl_onAnnotationActivated(U2::Annotation *annotation, int regionIndex) {
121     const QSet<AnnotationTableObject *> aos = ctx->getAnnotationObjects(true);
122     if (aos.contains(annotation->getGObject())) {
123         ensureVisible(annotation, regionIndex);
124     }
125 }
126 
sl_onAnnotationSelectionChanged(AnnotationSelection *,const QList<Annotation * > & _added,const QList<Annotation * > & _removed)127 void GSequenceLineViewAnnotated::sl_onAnnotationSelectionChanged(AnnotationSelection * /*as*/, const QList<Annotation *> &_added, const QList<Annotation *> &_removed) {
128     const QSet<AnnotationTableObject *> aos = ctx->getAnnotationObjects(true);
129 
130     bool changed = false;
131     const QList<Annotation *> added = ctx->selectRelatedAnnotations(_added);
132     const QList<Annotation *> removed = ctx->selectRelatedAnnotations(_removed);
133 
134     if (!changed) {
135         foreach (Annotation *a, added) {
136             if (aos.contains(a->getGObject()) && isAnnotationVisible(a)) {
137                 changed = true;
138                 break;
139             }
140         }
141         if (!changed) {
142             foreach (Annotation *a, removed) {
143                 if (aos.contains(a->getGObject()) && isAnnotationVisible(a)) {
144                     changed = true;
145                     break;
146                 }
147             }
148         }
149     }
150 
151     if (changed) {
152         addUpdateFlags(GSLV_UF_SelectionChanged);
153         update();
154     }
155 }
156 
isAnnotationVisible(const Annotation * a) const157 bool GSequenceLineViewAnnotated::isAnnotationVisible(const Annotation *a) const {
158     foreach (const U2Region &r, a->getRegions()) {
159         if (visibleRange.intersects(r)) {
160             return true;
161         }
162     }
163     return false;
164 }
165 
findAnnotationsByCoord(const QPoint & coord) const166 QList<Annotation *> GSequenceLineViewAnnotated::findAnnotationsByCoord(const QPoint &coord) const {
167     auto renderAreaAnnotated = qobject_cast<GSequenceLineViewAnnotatedRenderArea *>(renderArea);
168     SAFE_POINT(renderAreaAnnotated != nullptr, "GSequenceLineViewAnnotated must have GSequenceLineViewAnnotatedRenderArea!", QList<Annotation *>());
169     return renderAreaAnnotated->findAnnotationsByCoord(coord);
170 }
171 
mousePressEvent(QMouseEvent * me)172 void GSequenceLineViewAnnotated::mousePressEvent(QMouseEvent *me) {
173     setFocus();
174     const QPoint renderAreaPoint = toRenderAreaPoint(me->pos());
175     const QPoint p = toRenderAreaPoint(me->pos());
176     const Qt::KeyboardModifiers km = QApplication::keyboardModifiers();
177     const bool singleBaseSelectionMode = km.testFlag(Qt::AltModifier);
178     bool annotationEvent = false;  // true if mouse pressed in some annotation area
179     if (renderArea->rect().contains(p) && me->button() == Qt::LeftButton && !singleBaseSelectionMode) {
180         const Qt::KeyboardModifiers mouseEventModifiers = me->modifiers();
181         const bool controlOrShiftPressed = mouseEventModifiers.testFlag(Qt::ControlModifier) || mouseEventModifiers.testFlag(Qt::ShiftModifier);
182         QList<Annotation *> annotations = findAnnotationsByCoord(p);
183         annotationEvent = !annotations.isEmpty();
184         if ((!controlOrShiftPressed || !annotationEvent) && cursor().shape() == Qt::ArrowCursor) {
185             ctx->getAnnotationsSelection()->clear();
186             ctx->getSequenceSelection()->clear();
187             ctx->emitClearSelectedAnnotationRegions();
188         }
189         if (annotationEvent && cursor().shape() == Qt::ArrowCursor) {
190             Annotation *annotation = annotations.first();
191             if (annotations.size() > 1) {
192                 AnnotationSettingsRegistry *asr = AppContext::getAnnotationsSettingsRegistry();
193                 QMenu popup;
194                 foreach (const Annotation *as, annotations) {
195                     const SharedAnnotationData &aData = as->getData();
196                     const U2Region r = as->getRegions().first();
197                     const QString text = aData->name + QString(" [%1, %2]").arg(r.startPos + 1).arg(r.endPos());
198                     AnnotationSettings *asettings = asr->getAnnotationSettings(aData);
199                     const QIcon icon = GUIUtils::createSquareIcon(asettings->color, 10);
200                     popup.addAction(icon, text);
201                 }
202                 QAction *a = popup.exec(QCursor::pos());
203                 if (nullptr == a) {
204                     annotation = nullptr;
205                 } else {
206                     int idx = popup.actions().indexOf(a);
207                     annotation = annotations[idx];
208                 }
209             }
210             if (nullptr != annotation) {
211                 QVector<U2Region> annotationRegions = annotation->getRegions();
212                 bool processAllRegions = U1AnnotationUtils::isAnnotationContainsJunctionPoint(annotation, seqLen);
213                 if (processAllRegions) {
214                     ctx->emitAnnotationActivated(annotation, -1);
215                 } else {
216                     qint64 mousePressPos = renderArea->coordToPos(renderAreaPoint);
217                     for (int i = 0; i < annotationRegions.size(); i++) {
218                         const U2Region &region = annotationRegions[i];
219                         if (region.contains(mousePressPos)) {
220                             ctx->emitAnnotationActivated(annotation, i);
221                         }
222                     }
223                 }
224             }
225         }
226     }
227     // a hint to parent class: if mouse action leads to annotation selection -> skip selection handling for mouse press
228     ignoreMouseSelectionEvents = annotationEvent;
229     GSequenceLineView::mousePressEvent(me);
230     ignoreMouseSelectionEvents = false;
231 }
232 
mouseDoubleClickEvent(QMouseEvent * me)233 void GSequenceLineViewAnnotated::mouseDoubleClickEvent(QMouseEvent *me) {
234     const QPoint renderAreaPoint = toRenderAreaPoint(me->pos());
235     lastPressPos = renderArea->coordToPos(renderAreaPoint);
236     QList<Annotation *> selection = findAnnotationsByCoord(renderAreaPoint);
237     if (selection.isEmpty()) {
238         GSequenceLineView::mouseDoubleClickEvent(me);
239         return;
240     }
241     Annotation *annotation = selection.first();
242     // Using any of modifiers (compatibility with older UGENE behavior).
243     bool expandSelection = me->modifiers() == Qt::ControlModifier || me->modifiers() == Qt::ShiftModifier;
244     if (!expandSelection) {
245         ctx->emitClearSelectedAnnotationRegions();
246     }
247     const QVector<U2Region> annotationRegions = annotation->getRegions();
248     foreach (const U2Region &region, annotationRegions) {
249         CHECK_CONTINUE(region.contains(lastPressPos));
250 
251         ctx->emitAnnotationDoubleClicked(annotation, annotationRegions.indexOf(region));
252         break;
253     }
254 }
255 
256 //! VIEW_RENDERER_REFACTORING: used only in CV, doubled in SequenceViewAnnotetedRenderer.
257 //! Apply renederer logic to CV and remove this method.
prepareAnnotationText(const SharedAnnotationData & a,const AnnotationSettings * as)258 QString GSequenceLineViewAnnotated::prepareAnnotationText(const SharedAnnotationData &a, const AnnotationSettings *as) {
259     if (!as->showNameQuals || as->nameQuals.isEmpty()) {
260         return a->name;
261     }
262     QVector<U2Qualifier> qs;
263     foreach (const QString &qn, as->nameQuals) {
264         qs.clear();
265         a->findQualifiers(qn, qs);
266         if (!qs.isEmpty()) {
267             QString res = qs[0].value;
268             return res;
269         }
270     }
271     return a->name;
272 }
273 
findAnnotationsInRange(const U2Region & range) const274 QList<Annotation *> GSequenceLineViewAnnotated::findAnnotationsInRange(const U2Region &range) const {
275     QList<Annotation *> result;
276     const QSet<AnnotationTableObject *> aObjs = ctx->getAnnotationObjects(true);
277     foreach (AnnotationTableObject *ao, aObjs) {
278         result << ao->getAnnotationsByRegion(range);
279     }
280     return result;
281 }
282 
283 //////////////////////////////////////////////////////////////////////////
284 /// Renderer
285 
GSequenceLineViewAnnotatedRenderArea(GSequenceLineViewAnnotated * sequenceLineView)286 GSequenceLineViewAnnotatedRenderArea::GSequenceLineViewAnnotatedRenderArea(GSequenceLineViewAnnotated *sequenceLineView)
287     : GSequenceLineViewRenderArea(sequenceLineView), sequenceLineViewAnnotated(sequenceLineView) {
288     afNormal = new QFont("Courier", 10);
289     afSmall = new QFont("Arial", 8);
290 
291     afmNormal = new QFontMetrics(*afNormal, this);
292     afmSmall = new QFontMetrics(*afSmall, this);
293 
294     afNormalCharWidth = afmNormal->width('w');
295     afSmallCharWidth = afmSmall->width('w');
296 
297     QLinearGradient gradient(0, 0, 0, 1);  // vertical
298     gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
299     gradient.setColorAt(0.00, QColor(255, 255, 255, 120));
300     gradient.setColorAt(0.50, QColor(0, 0, 0, 0));
301     gradient.setColorAt(0.70, QColor(0, 0, 0, 0));
302     gradient.setColorAt(1.00, QColor(0, 0, 0, 70));
303     gradientMaskBrush = QBrush(gradient);
304 }
305 
~GSequenceLineViewAnnotatedRenderArea()306 GSequenceLineViewAnnotatedRenderArea::~GSequenceLineViewAnnotatedRenderArea() {
307     delete afmNormal;
308     delete afNormal;
309     delete afmSmall;
310     delete afSmall;
311 }
312 
registerAnnotations(const QList<Annotation * > &)313 void GSequenceLineViewAnnotated::registerAnnotations(const QList<Annotation *> &) {
314 }
315 
unregisterAnnotations(const QList<Annotation * > &)316 void GSequenceLineViewAnnotated::unregisterAnnotations(const QList<Annotation *> &) {
317 }
318 
ensureVisible(Annotation * a,int locationIdx)319 void GSequenceLineViewAnnotated::ensureVisible(Annotation *a, int locationIdx) {
320     QVector<U2Region> location = a->getRegions();
321     SAFE_POINT(locationIdx < location.size(), "Invalid annotation location on the widget!", );
322     if (-1 == locationIdx) {
323         foreach (const U2Region &r, location) {
324             if (visibleRange.intersects(r)) {
325                 return;
326             }
327         }
328     }
329     const U2Region &region = location[qMax(0, locationIdx)];
330     if (!visibleRange.intersects(region)) {
331         const qint64 pos = a->getStrand().isCompementary() ? region.endPos() : region.startPos;
332         setCenterPos(qBound(qint64(0), pos, seqLen - 1));
333     }
334 }
335 
event(QEvent * e)336 bool GSequenceLineViewAnnotated::event(QEvent *e) {
337     if (e->type() == QEvent::ToolTip) {
338         QHelpEvent *he = static_cast<QHelpEvent *>(e);
339         QString tip = createToolTip(he);
340         if (!tip.isEmpty()) {
341             QToolTip::showText(he->globalPos(), tip);
342         }
343         return true;
344     }
345     return GSequenceLineView::event(e);
346 }
347 
createToolTip(QHelpEvent * e)348 QString GSequenceLineViewAnnotated::createToolTip(QHelpEvent *e) {
349     const int ROWS_LIMIT = 25;
350     QList<Annotation *> la = findAnnotationsByCoord(e->pos());
351     QList<SharedAnnotationData> annotationList;
352     if (la.isEmpty()) {
353         return QString();
354     } else {
355         // fetch annotation data before further processing in order to improve performance
356         foreach (const Annotation *annotation, la) {
357             annotationList << annotation->getData();
358         }
359     }
360     QString tip = "<table>";
361     int rows = 0;
362     if (annotationList.size() > 1) {
363         foreach (const SharedAnnotationData &ad, annotationList) {
364             rows += ad->qualifiers.size() + 1;
365         }
366     }
367 
368     const bool skipDetails = (rows > ROWS_LIMIT);
369     rows = 0;
370     foreach (const SharedAnnotationData &ad, annotationList) {
371         if (++rows > ROWS_LIMIT) {
372             break;
373         }
374         AnnotationSettingsRegistry *registry = AppContext::getAnnotationsSettingsRegistry();
375         const QColor acl = registry->getAnnotationSettings(ad->name)->color;
376         tip += "<tr><td bgcolor=" + acl.name() + " bordercolor=black width=15></td><td><big>" + ad->name + "</big></td></tr>";
377 
378         if (skipDetails) {
379             tip += "<tr><td/><td>...</td>";
380             rows++;
381         } else {
382             tip += "<tr><td></td><td><b>Location</b> = " + U1AnnotationUtils::buildLocationString(ad) + "</td></tr>";
383             tip += "<tr><td/><td>";
384             tip += Annotation::getQualifiersTip(ad, ROWS_LIMIT - rows, getSequenceObject(), ctx->getComplementTT(), ctx->getAminoTT());
385             tip += "</td></tr>";
386             rows += ad->qualifiers.size();
387         }
388     }
389     tip += "</table>";
390     if (rows > ROWS_LIMIT) {
391         tip += "<hr> <div align=center>" + tr("etc ...") + "</div>";
392     }
393     return tip;
394 }
395 
sl_onAnnotationsModified(const QList<AnnotationModification> & annotationModifications)396 void GSequenceLineViewAnnotated::sl_onAnnotationsModified(const QList<AnnotationModification> &annotationModifications) {
397     foreach (const AnnotationModification &annotationModification, annotationModifications) {
398         if (annotationModification.type == AnnotationModification_LocationChanged ||
399             annotationModification.type == AnnotationModification_NameChanged ||
400             annotationModification.type == AnnotationModification_TypeChanged) {
401             addUpdateFlags(GSLV_UF_AnnotationsChanged);
402             update();
403             break;
404         }
405     }
406 }
407 
isAnnotationSelectionInVisibleRange() const408 bool GSequenceLineViewAnnotated::isAnnotationSelectionInVisibleRange() const {
409     const QSet<AnnotationTableObject *> aos = ctx->getAnnotationObjects(true);
410     AnnotationSelection *as = ctx->getAnnotationsSelection();
411     foreach (const Annotation *annotation, as->getAnnotations()) {
412         if (!aos.contains(annotation->getGObject())) {
413             continue;
414         }
415         if (isAnnotationVisible(annotation)) {
416             return true;
417         }
418     }
419     return false;
420 }
421 
GSequenceLineViewGridAnnotationRenderArea(GSequenceLineViewAnnotated * sequenceLineView)422 GSequenceLineViewGridAnnotationRenderArea::GSequenceLineViewGridAnnotationRenderArea(GSequenceLineViewAnnotated *sequenceLineView)
423     : GSequenceLineViewAnnotatedRenderArea(sequenceLineView) {
424 }
425 
findAnnotationsByCoord(const QPoint & coord) const426 QList<Annotation *> GSequenceLineViewGridAnnotationRenderArea::findAnnotationsByCoord(const QPoint &coord) const {
427     QList<Annotation *> resultAnnotationList;
428     CHECK(rect().contains(coord), resultAnnotationList);
429 
430     AnnotationSettingsRegistry *annotationsSettingsRegistry = AppContext::getAnnotationsSettingsRegistry();
431     const qint64 pos = coordToPos(coord);
432     qint64 uncertaintyLength = 0;
433     SequenceObjectContext *sequenceContext = sequenceLineViewAnnotated->getSequenceContext();
434     qint64 sequenceLength = sequenceContext->getSequenceLength();
435     U2Region visibleRange = view->getVisibleRange();
436     if (visibleRange.length > width()) {
437         double scale = getCurrentScale();
438         uncertaintyLength = static_cast<qint64>(1 / scale);
439         SAFE_POINT(uncertaintyLength < sequenceLength, "Invalid uncertaintyLength for the given seqLen!", resultAnnotationList);
440     }
441     U2Region pointRegion(pos - uncertaintyLength, 1 + 2 * uncertaintyLength);  // A region of sequence covered by the 'QPoint& coord'.
442     const QSet<AnnotationTableObject *> annotationObjectSet = sequenceContext->getAnnotationObjects(true);
443     for (const AnnotationTableObject *annotationObject : qAsConst(annotationObjectSet)) {
444         for (Annotation *annotation : annotationObject->getAnnotationsByRegion(pointRegion)) {
445             const SharedAnnotationData &aData = annotation->getData();
446             const QVector<U2Region> locationRegionList = aData->getRegions();
447             for (int i = 0, n = locationRegionList.size(); i < n; i++) {
448                 const U2Region &locationRegion = locationRegionList[i];
449                 if (locationRegion.intersects(pointRegion) || pointRegion.startPos == locationRegion.endPos()) {
450                     // now check pixel precise coords for boundaries.
451                     if (pos == locationRegion.startPos) {
452                         int posStartX = posToCoord(locationRegion.startPos, true);
453                         if (coord.x() < posStartX) {
454                             continue;
455                         }
456                     } else if (pos == locationRegion.endPos()) {
457                         // Use endPos() - 1 to avoid moving to the next line in multiline views.
458                         int posEndX = posToCoord(locationRegion.endPos() - 1, true) + getCharWidth();
459                         if (coord.x() >= posEndX) {
460                             continue;
461                         }
462                     }
463                     AnnotationSettings *annotationSettings = annotationsSettingsRegistry->getAnnotationSettings(aData);
464                     if (annotationSettings->visible && checkAnnotationRegionContainsYPoint(coord.y(), annotation, i, annotationSettings)) {
465                         resultAnnotationList.append(annotation);  // select whole annotation (all regions)
466                         break;
467                     }
468                 }
469             }
470         }
471     }
472     return resultAnnotationList;
473 }
474 
checkAnnotationRegionContainsYPoint(int y,Annotation * annotation,int locationRegionIndex,const AnnotationSettings * annotationSettings) const475 bool GSequenceLineViewGridAnnotationRenderArea::checkAnnotationRegionContainsYPoint(int y, Annotation *annotation, int locationRegionIndex, const AnnotationSettings *annotationSettings) const {
476     QList<U2Region> yRegionList = getAnnotationYRegions(annotation, locationRegionIndex, annotationSettings);
477     for (const U2Region &region : qAsConst(yRegionList)) {
478         if (region.contains(y)) {
479             return true;
480         }
481     }
482     return false;
483 }
484 
485 }  // namespace U2
486