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 "AutoAnnotationUtils.h"
23 
24 #include <U2Core/AnnotationTableObject.h>
25 #include <U2Core/AppContext.h>
26 #include <U2Core/AutoAnnotationsSupport.h>
27 #include <U2Core/DNASequenceObject.h>
28 #include <U2Core/U1AnnotationUtils.h>
29 #include <U2Core/U2SafePoints.h>
30 
31 #include <U2Gui/MainWindow.h>
32 #include <U2Gui/ObjectViewModel.h>
33 
34 #include <U2View/ADVAnnotationCreation.h>
35 
36 #include "ADVSequenceObjectContext.h"
37 #include "AnnotatedDNAView.h"
38 
39 namespace U2 {
40 
41 const QString AutoAnnotationsADVAction::ACTION_NAME("AutoAnnotationUpdateAction");
42 
43 #define AUTO_ANNOTATION_GROUP_NAME "AutoAnnotatationGroupName"
44 
AutoAnnotationsADVAction(ADVSequenceWidget * v,AutoAnnotationObject * obj)45 AutoAnnotationsADVAction::AutoAnnotationsADVAction(ADVSequenceWidget *v,
46                                                    AutoAnnotationObject *obj)
47     : ADVSequenceWidgetAction(ACTION_NAME, tr("Automatic annotations highlighting")), aaObj(obj), updatesCount(0) {
48     seqWidget = v;
49     addToBar = true;
50 
51     menu = new QMenu();
52     setIcon(QIcon(":core/images/predefined_annotation_groups.png"));
53     setMenu(menu);
54 
55     connect(aaObj, SIGNAL(si_updateStarted()), SLOT(sl_autoAnnotationUpdateStarted()));
56     connect(aaObj, SIGNAL(si_updateFinished()), SLOT(sl_autoAnnotationUpdateFinished()));
57 
58     selectAllAction = new QAction(tr("Select all"), this);
59     connect(selectAllAction, SIGNAL(triggered()), SLOT(sl_onSelectAll()));
60 
61     deselectAllAction = new QAction(tr("Deselect all"), this);
62     connect(deselectAllAction, SIGNAL(triggered()), SLOT(sl_onDeselectAll()));
63 
64     updateMenu();
65 
66     aaObj->updateAll();
67 }
68 
69 #define MAX_SEQ_SIZE_TO_ENABLE_AUTO_ANNOTATIONS 10000
70 
updateMenu()71 void AutoAnnotationsADVAction::updateMenu() {
72     AutoAnnotationConstraints constraints;
73     if (seqWidget->getSequenceContexts().count() > 0) {
74         constraints.alphabet = seqWidget->getSequenceContexts().first()->getAlphabet();
75     }
76 
77     // Auto annotations should not be enabled by default for very large sequences
78     // or when there are many small sequences. This flag controls such behavior.
79     bool largeSequence = false;
80 
81     if (seqWidget->getSequenceObjects().count() > 0) {
82         constraints.hints = seqWidget->getSequenceObjects().first()->getGHints();
83 
84         int totalLen = 0;
85 
86         AnnotatedDNAView *view = seqWidget->getAnnotatedDNAView();
87         if (view != nullptr) {
88             const QList<ADVSequenceObjectContext *> &ctxList = view->getSequenceContexts();
89             foreach (const ADVSequenceObjectContext *ctx, ctxList) {
90                 totalLen += ctx->getSequenceLength();
91             }
92         }
93         largeSequence = totalLen > MAX_SEQ_SIZE_TO_ENABLE_AUTO_ANNOTATIONS;
94     }
95 
96     QList<AutoAnnotationsUpdater *> updaters = AppContext::getAutoAnnotationsSupport()->getAutoAnnotationUpdaters();
97     if (updaters.count() == 0) {
98         setEnabled(false);
99         return;
100     }
101     foreach (AutoAnnotationsUpdater *updater, updaters) {
102         QAction *toggleAction = new QAction(updater->getName(), this);
103         toggleAction->setObjectName(updater->getName());
104         toggleAction->setProperty(AUTO_ANNOTATION_GROUP_NAME, updater->getGroupName());
105         bool enabled = updater->checkConstraints(constraints);
106         toggleAction->setEnabled(enabled);
107         toggleAction->setCheckable(true);
108         bool checked = updater->isEnabledByDefault() && !largeSequence;
109         toggleAction->setChecked(checked);
110         aaObj->setGroupEnabled(updater->getGroupName(), checked);
111         connect(toggleAction, SIGNAL(toggled(bool)), SLOT(sl_toggle(bool)));
112         menu->addAction(toggleAction);
113     }
114 
115     menu->update();
116 }
117 
sl_toggle(bool toggled)118 void AutoAnnotationsADVAction::sl_toggle(bool toggled) {
119     QAction *action = qobject_cast<QAction *>(sender());
120     if (action == nullptr) {
121         return;
122     }
123     AutoAnnotationsUpdater *updater = AppContext::getAutoAnnotationsSupport()->findUpdaterByName(action->text());
124     if (updater != nullptr) {
125         QString groupName = updater->getGroupName();
126         aaObj->setGroupEnabled(groupName, toggled);
127         aaObj->updateGroup(groupName);
128         updater->setEnabledByDefault(toggled);
129     }
130 }
131 
sl_onSelectAll()132 void AutoAnnotationsADVAction::sl_onSelectAll() {
133     QList<QAction *> actions = getToggleActions();
134     foreach (QAction *action, actions) {
135         if (!action->isChecked()) {
136             action->trigger();
137         }
138     }
139 }
140 
sl_onDeselectAll()141 void AutoAnnotationsADVAction::sl_onDeselectAll() {
142     QList<QAction *> actions = getToggleActions();
143     foreach (QAction *action, actions) {
144         if (action->isChecked()) {
145             action->trigger();
146         }
147     }
148 }
149 
~AutoAnnotationsADVAction()150 AutoAnnotationsADVAction::~AutoAnnotationsADVAction() {
151     menu->clear();
152     delete menu;
153     menu = nullptr;
154 }
155 
getToggleActions()156 QList<QAction *> AutoAnnotationsADVAction::getToggleActions() {
157     return menu->actions();
158 }
159 
findToggleAction(const QString & groupName)160 QAction *AutoAnnotationsADVAction::findToggleAction(const QString &groupName) {
161     QList<QAction *> toggleActions = menu->actions();
162     foreach (QAction *tAction, toggleActions) {
163         if (tAction->property(AUTO_ANNOTATION_GROUP_NAME) == groupName) {
164             return tAction;
165         }
166     }
167     return nullptr;
168 }
169 
addUpdaterToMenu(AutoAnnotationsUpdater * updater)170 void AutoAnnotationsADVAction::addUpdaterToMenu(AutoAnnotationsUpdater *updater) {
171     AutoAnnotationConstraints constraints;
172     if (seqWidget->getSequenceContexts().count() > 0) {
173         constraints.alphabet = seqWidget->getSequenceContexts().first()->getAlphabet();
174     }
175     if (seqWidget->getSequenceObjects().count() > 0) {
176         constraints.hints = seqWidget->getSequenceObjects().first()->getGHints();
177     }
178 
179     QAction *toggleAction = new QAction(updater->getName(), this);
180     toggleAction->setProperty(AUTO_ANNOTATION_GROUP_NAME, updater->getGroupName());
181     bool enabled = updater->checkConstraints(constraints);
182     toggleAction->setEnabled(enabled);
183     toggleAction->setCheckable(true);
184     bool checked = updater->isEnabledByDefault();
185     toggleAction->setChecked(checked);
186     aaObj->setGroupEnabled(updater->getGroupName(), checked);
187     connect(toggleAction, SIGNAL(toggled(bool)), SLOT(sl_toggle(bool)));
188     menu->addAction(toggleAction);
189 
190     menu->update();
191 }
192 
sl_autoAnnotationUpdateStarted()193 void AutoAnnotationsADVAction::sl_autoAnnotationUpdateStarted() {
194     setEnabled(false);
195     updatesCount++;
196 }
197 
sl_autoAnnotationUpdateFinished()198 void AutoAnnotationsADVAction::sl_autoAnnotationUpdateFinished() {
199     updatesCount--;
200     if (updatesCount == 0) {
201         setEnabled(true);
202     }
203 }
204 
205 //////////////////////////////////////////////////////////////////////////
206 
findAutoAnnotationsToggleAction(ADVSequenceObjectContext * ctx,const QString & groupName)207 QAction *AutoAnnotationUtils::findAutoAnnotationsToggleAction(ADVSequenceObjectContext *ctx, const QString &groupName) {
208     foreach (ADVSequenceWidget *w, ctx->getSequenceWidgets()) {
209         ADVSequenceWidgetAction *advAction = w->getADVSequenceWidgetAction(AutoAnnotationsADVAction::ACTION_NAME);
210         if (advAction == nullptr) {
211             continue;
212         }
213         AutoAnnotationsADVAction *aaAction = qobject_cast<AutoAnnotationsADVAction *>(advAction);
214         assert(aaAction != nullptr);
215         QList<QAction *> toggleActions = aaAction->getToggleActions();
216         foreach (QAction *tAction, toggleActions) {
217             if (tAction->property(AUTO_ANNOTATION_GROUP_NAME) == groupName) {
218                 return tAction;
219             }
220         }
221     }
222 
223     return nullptr;
224 }
225 
triggerAutoAnnotationsUpdate(ADVSequenceObjectContext * ctx,const QString & aaGroupName)226 void AutoAnnotationUtils::triggerAutoAnnotationsUpdate(ADVSequenceObjectContext *ctx, const QString &aaGroupName) {
227     AutoAnnotationsADVAction *aaAction = findAutoAnnotationADVAction(ctx);
228 
229     if (aaAction != nullptr && !aaAction->isEnabled()) {
230         return;
231     }
232 
233     assert(aaAction != nullptr);
234     if (aaAction) {
235         QAction *updateAction = aaAction->findToggleAction(aaGroupName);
236         assert(updateAction != nullptr);
237 
238         if (!updateAction) {
239             return;
240         }
241 
242         if (!updateAction->isChecked()) {
243             updateAction->trigger();
244         } else {
245             AutoAnnotationsUpdater *updater = AppContext::getAutoAnnotationsSupport()->findUpdaterByGroupName(aaGroupName);
246             if (updater != nullptr) {
247                 aaAction->getAAObj()->updateGroup(aaGroupName);
248             }
249         }
250     }
251 }
252 
findAutoAnnotationADVAction(ADVSequenceObjectContext * ctx)253 AutoAnnotationsADVAction *AutoAnnotationUtils::findAutoAnnotationADVAction(ADVSequenceObjectContext *ctx) {
254     foreach (ADVSequenceWidget *w, ctx->getSequenceWidgets()) {
255         ADVSequenceWidgetAction *advAction = w->getADVSequenceWidgetAction(AutoAnnotationsADVAction::ACTION_NAME);
256         if (advAction == nullptr) {
257             continue;
258         } else {
259             return qobject_cast<AutoAnnotationsADVAction *>(advAction);
260         }
261     }
262 
263     return nullptr;
264 }
265 
getAutoAnnotationToggleActions(ADVSequenceObjectContext * ctx)266 QList<QAction *> AutoAnnotationUtils::getAutoAnnotationToggleActions(ADVSequenceObjectContext *ctx) {
267     QList<QAction *> res;
268 
269     foreach (ADVSequenceWidget *w, ctx->getSequenceWidgets()) {
270         ADVSequenceWidgetAction *advAction = w->getADVSequenceWidgetAction(AutoAnnotationsADVAction::ACTION_NAME);
271         if (advAction == nullptr) {
272             continue;
273         }
274         AutoAnnotationsADVAction *aaAction = qobject_cast<AutoAnnotationsADVAction *>(advAction);
275         assert(aaAction != nullptr);
276         res = aaAction->getToggleActions();
277 
278         int selectedCount = 0;
279         foreach (QAction *a, res) {
280             if (a->isChecked()) {
281                 selectedCount += 1;
282             }
283         }
284 
285         if (selectedCount == res.size()) {
286             res.append(aaAction->getDeselectAllAction());
287         } else {
288             res.append(aaAction->getSelectAllAction());
289         }
290     }
291 
292     return res;
293 }
294 
295 //////////////////////////////////////////////////////////////////////////
296 
ExportAutoAnnotationsGroupTask(AnnotationGroup * ag,GObjectReference & ref,ADVSequenceObjectContext * ctx,const QString & annDescription)297 ExportAutoAnnotationsGroupTask::ExportAutoAnnotationsGroupTask(AnnotationGroup *ag,
298                                                                GObjectReference &ref,
299                                                                ADVSequenceObjectContext *ctx,
300                                                                const QString &annDescription)
301     : Task("ExportAutoAnnotationsGroupTask", TaskFlags_NR_FOSCOE),
302       aGroup(ag),
303       aRef(ref),
304       seqCtx(ctx),
305       createTask(nullptr),
306       annDescription(annDescription) {
307     SAFE_POINT_EXT(nullptr != ag, stateInfo.setError(tr("Invalid annotation group provided")), );
308 }
309 
prepare()310 void ExportAutoAnnotationsGroupTask::prepare() {
311     QList<Annotation *> annotationList;
312     aGroup->findAllAnnotationsInGroupSubTree(annotationList);
313 
314     QList<SharedAnnotationData> newAnnotationDataList;
315     for (const Annotation *annotation : qAsConst(annotationList)) {
316         SharedAnnotationData data(new AnnotationData(*(annotation->getData())));
317         U1AnnotationUtils::addDescriptionQualifier(data, annDescription);
318         newAnnotationDataList.append(data);
319     }
320 
321     CHECK(!newAnnotationDataList.isEmpty(), );
322 
323     bool selectNewAnnotations = newAnnotationDataList.size() < 100;
324     createTask = new ADVCreateAnnotationsTask(seqCtx->getAnnotatedDNAView(), aRef, aGroup->getName(), newAnnotationDataList, selectNewAnnotations);
325     addSubTask(createTask);
326 }
327 
onSubTaskFinished(Task * subTask)328 QList<Task *> ExportAutoAnnotationsGroupTask::onSubTaskFinished(Task *subTask) {
329     QList<Task *> res;
330 
331     if (!subTask->isFinished() || subTask->hasError() || subTask->isCanceled()) {
332         return res;
333     }
334 
335     if (subTask == createTask) {
336         QAction *toggleAction = AutoAnnotationUtils::findAutoAnnotationsToggleAction(seqCtx, aGroup->getName());
337         if (toggleAction != nullptr && toggleAction->isChecked()) {
338             toggleAction->trigger();
339         }
340     }
341 
342     return res;
343 }
344 
345 }  // namespace U2
346