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 "MSAEditor.h"
23
24 #include <QDropEvent>
25
26 #include <U2Core/AddSequencesToAlignmentTask.h>
27 #include <U2Core/AppContext.h>
28 #include <U2Core/DNASequenceObject.h>
29 #include <U2Core/GObjectSelection.h>
30 #include <U2Core/GUrlUtils.h>
31 #include <U2Core/QObjectScopedPointer.h>
32 #include <U2Core/Settings.h>
33 #include <U2Core/TaskWatchdog.h>
34 #include <U2Core/U2AlphabetUtils.h>
35 #include <U2Core/U2Mod.h>
36 #include <U2Core/U2OpStatusUtils.h>
37
38 #include <U2Gui/DialogUtils.h>
39 #include <U2Gui/ExportImageDialog.h>
40 #include <U2Gui/GUIUtils.h>
41 #include <U2Gui/GroupHeaderImageWidget.h>
42 #include <U2Gui/GroupOptionsWidget.h>
43 #include <U2Gui/OPWidgetFactoryRegistry.h>
44 #include <U2Gui/OptionsPanel.h>
45 #include <U2Gui/OptionsPanelWidget.h>
46 #include <U2Gui/ProjectView.h>
47
48 #include <U2View/ColorSchemaSettingsController.h>
49 #include <U2View/FindPatternMsaWidgetFactory.h>
50
51 #include "MSAEditorOffsetsView.h"
52 #include "MSAEditorSequenceArea.h"
53 #include "MaEditorFactory.h"
54 #include "MaEditorNameList.h"
55 #include "MaEditorTasks.h"
56 #include "export/MSAImageExportTask.h"
57 #include "highlighting/MsaSchemesMenuBuilder.h"
58 #include "move_to_object/MoveToObjectMaController.h"
59 #include "overview/MaEditorOverviewArea.h"
60 #include "realign_to_alignment/RealignSequencesInAlignmentTask.h"
61 #include "view_rendering/MaEditorConsensusArea.h"
62 #include "view_rendering/MaEditorSelection.h"
63 #include "view_rendering/MaEditorSequenceArea.h"
64
65 namespace U2 {
66
67 const QString MsaEditorMenuType::ALIGN("msa-editor-menu-align");
68 const QString MsaEditorMenuType::ALIGN_SEQUENCES_TO_ALIGNMENT("msa-editor-menu-align-sequences-to-alignment");
69
MSAEditor(const QString & viewName,MultipleSequenceAlignmentObject * obj)70 MSAEditor::MSAEditor(const QString &viewName, MultipleSequenceAlignmentObject *obj)
71 : MaEditor(MsaEditorFactory::ID, viewName, obj),
72 treeManager(this) {
73 selectionController = new MaEditorSelectionController(this);
74
75 gotoAction = nullptr;
76 searchInSequencesAction = nullptr;
77 searchInSequenceNamesAction = nullptr;
78
79 sortByNameAscendingAction = new QAction(tr("By name"), this);
80 sortByNameAscendingAction->setObjectName("action_sort_by_name");
81 sortByNameAscendingAction->setToolTip(tr("Sort selected sequences range or the whole alignment by name, ascending"));
82 connect(sortByNameAscendingAction, SIGNAL(triggered()), SLOT(sl_sortSequencesByName()));
83
84 sortByNameDescendingAction = new QAction(tr("By name, descending"), this);
85 sortByNameDescendingAction->setObjectName("action_sort_by_name_descending");
86 sortByNameDescendingAction->setToolTip(tr("Sort selected sequences range or the whole alignment by name, descending"));
87 connect(sortByNameDescendingAction, SIGNAL(triggered()), SLOT(sl_sortSequencesByName()));
88
89 sortByLengthAscendingAction = new QAction(tr("By length"), this);
90 sortByLengthAscendingAction->setObjectName("action_sort_by_length");
91 sortByLengthAscendingAction->setToolTip(tr("Sort selected sequences range or the whole alignment by length, ascending"));
92 connect(sortByLengthAscendingAction, SIGNAL(triggered()), SLOT(sl_sortSequencesByLength()));
93
94 sortByLengthDescendingAction = new QAction(tr("By length, descending"), this);
95 sortByLengthDescendingAction->setObjectName("action_sort_by_length_descending");
96 sortByLengthDescendingAction->setToolTip(tr("Sort selected sequences range or the whole alignment by length, descending"));
97 connect(sortByLengthDescendingAction, SIGNAL(triggered()), SLOT(sl_sortSequencesByLength()));
98
99 sortByLeadingGapAscendingAction = new QAction(tr("By leading gap"), this);
100 sortByLeadingGapAscendingAction->setObjectName("action_sort_by_leading_gap");
101 sortByLeadingGapAscendingAction->setToolTip(tr("Sort selected sequences range or the whole alignment by leading gap, ascending"));
102 connect(sortByLeadingGapAscendingAction, SIGNAL(triggered()), SLOT(sl_sortSequencesByLeadingGap()));
103
104 sortByLeadingGapDescendingAction = new QAction(tr("By leading gap, descending"), this);
105 sortByLeadingGapDescendingAction->setObjectName("action_sort_by_leading_gap_descending");
106 sortByLeadingGapDescendingAction->setToolTip(tr("Sort selected sequences range or the whole alignment by leading gap, descending"));
107 connect(sortByLeadingGapDescendingAction, SIGNAL(triggered()), SLOT(sl_sortSequencesByLeadingGap()));
108
109 openCustomSettingsAction = new QAction(tr("Create new color scheme"), this);
110 openCustomSettingsAction->setObjectName("Create new color scheme");
111 connect(openCustomSettingsAction, SIGNAL(triggered()), SLOT(sl_showCustomSettings()));
112
113 sortGroupsBySizeAscendingAction = new QAction(tr("Sort groups, small first"), this);
114 sortGroupsBySizeAscendingAction->setObjectName("action_sort_groups_by_size_ascending");
115 sortGroupsBySizeAscendingAction->setToolTip(tr("Sort groups by number of sequences in the group, ascending"));
116 connect(sortGroupsBySizeAscendingAction, SIGNAL(triggered()), SLOT(sl_sortGroupsBySize()));
117
118 sortGroupsBySizeDescendingAction = new QAction(tr("Sort groups, large first"), this);
119 sortGroupsBySizeDescendingAction->setObjectName("action_sort_groups_by_size_descending");
120 sortGroupsBySizeDescendingAction->setToolTip(tr("Sort groups by number of sequences in the group, descending"));
121 connect(sortGroupsBySizeDescendingAction, SIGNAL(triggered()), SLOT(sl_sortGroupsBySize()));
122
123 saveScreenshotAction = new QAction(QIcon(":/core/images/cam2.png"), tr("Export as image"), this);
124 saveScreenshotAction->setObjectName("export_msa_as_image_action");
125 connect(saveScreenshotAction, &QAction::triggered, this, &MSAEditor::sl_exportImage);
126
127 initZoom();
128 initFont();
129
130 buildTreeAction = new QAction(QIcon(":/core/images/phylip.png"), tr("Build Tree"), this);
131 buildTreeAction->setObjectName("Build Tree");
132 buildTreeAction->setEnabled(!isAlignmentEmpty());
133 connect(maObject, SIGNAL(si_rowsRemoved(const QList<qint64> &)), SLOT(sl_rowsRemoved(const QList<qint64> &)));
134 connect(buildTreeAction, SIGNAL(triggered()), SLOT(sl_buildTree()));
135
136 realignSomeSequenceAction = new QAction(QIcon(":/core/images/realign_some_sequences.png"), tr("Realign sequence(s) to other sequences"), this);
137 realignSomeSequenceAction->setObjectName("Realign sequence(s) to other sequences");
138
139 pairwiseAlignmentWidgetsSettings = new PairwiseAlignmentWidgetsSettings;
140 if (maObject->getAlphabet() != nullptr) {
141 pairwiseAlignmentWidgetsSettings->customSettings.insert("alphabet", maObject->getAlphabet()->getId());
142 }
143
144 convertDnaToRnaAction = new QAction(tr("Convert to RNA alphabet (T->U)"), this);
145 convertDnaToRnaAction->setObjectName("convertDnaToRnaAction");
146 convertDnaToRnaAction->setToolTip(tr("Convert alignment from DNA to RNA alphabet: replace T with U"));
147 connect(convertDnaToRnaAction, SIGNAL(triggered()), SLOT(sl_convertBetweenDnaAndRnaAlphabets()));
148
149 convertRnaToDnaAction = new QAction(tr("Convert to DNA alphabet (U->T)"), this);
150 convertRnaToDnaAction->setObjectName("convertRnaToDnaAction");
151 convertRnaToDnaAction->setToolTip(tr("Convert alignment from RNA to DNA alphabet: replace U with T"));
152 connect(convertRnaToDnaAction, SIGNAL(triggered()), SLOT(sl_convertBetweenDnaAndRnaAlphabets()));
153
154 convertRawToDnaAction = new QAction(tr("Convert RAW to DNA alphabet"), this);
155 convertRawToDnaAction->setObjectName("convertRawToDnaAction");
156 convertRawToDnaAction->setToolTip(tr("Convert alignment from RAW to DNA alphabet: use N for unknown symbols"));
157 connect(convertRawToDnaAction, SIGNAL(triggered()), SLOT(sl_convertRawToDnaAlphabet()));
158
159 convertRawToAminoAction = new QAction(tr("Convert RAW to Amino alphabet"), this);
160 convertRawToAminoAction->setObjectName("convertRawToAminoAction");
161 convertRawToAminoAction->setToolTip(tr("Convert alignment from RAW to Amino alphabet: use X for unknown symbols"));
162 connect(convertRawToAminoAction, SIGNAL(triggered()), SLOT(sl_convertRawToAminoAlphabet()));
163
164 updateActions();
165 }
166
updateActions()167 void MSAEditor::updateActions() {
168 MaEditor::updateActions();
169 bool isReadOnly = maObject->isStateLocked();
170
171 sortByNameAscendingAction->setEnabled(!isReadOnly);
172 sortByNameDescendingAction->setEnabled(!isReadOnly);
173 sortByLengthAscendingAction->setEnabled(!isReadOnly);
174 sortByLengthDescendingAction->setEnabled(!isReadOnly);
175
176 if (alignSequencesToAlignmentAction != nullptr) {
177 alignSequencesToAlignmentAction->setEnabled(!isReadOnly);
178 }
179 buildTreeAction->setEnabled(!isReadOnly && !isAlignmentEmpty());
180 sl_updateRealignAction();
181
182 auto alphabetId = maObject->getAlphabet()->getId();
183 convertDnaToRnaAction->setEnabled(!isReadOnly && alphabetId == BaseDNAAlphabetIds::NUCL_DNA_DEFAULT());
184 convertRnaToDnaAction->setEnabled(!isReadOnly && alphabetId == BaseDNAAlphabetIds::NUCL_RNA_DEFAULT());
185 convertRawToDnaAction->setEnabled(!isReadOnly && alphabetId == BaseDNAAlphabetIds::RAW());
186 convertRawToAminoAction->setEnabled(!isReadOnly && alphabetId == BaseDNAAlphabetIds::RAW());
187
188 // Sorting of groups is enabled only on "group by content" mode.
189 // This 'virtual' mode is 100% managed by MSA Editor and is not saved to file.
190 bool isGroupBySequenceContent = getRowOrderMode() == MaEditorRowOrderMode::Sequence;
191 sortGroupsBySizeAscendingAction->setEnabled(isGroupBySequenceContent);
192 sortGroupsBySizeDescendingAction->setEnabled(isGroupBySequenceContent);
193 }
194
sl_buildTree()195 void MSAEditor::sl_buildTree() {
196 treeManager.buildTreeWithDialog();
197 }
198
onObjectRemoved(GObject * obj)199 bool MSAEditor::onObjectRemoved(GObject *obj) {
200 bool result = GObjectView::onObjectRemoved(obj);
201
202 obj->disconnect(ui->getSequenceArea());
203 obj->disconnect(ui->getConsensusArea());
204 obj->disconnect(ui->getEditorNameList());
205 return result;
206 }
207
onObjectRenamed(GObject *,const QString &)208 void MSAEditor::onObjectRenamed(GObject *, const QString &) {
209 // update title
210 OpenMaEditorTask::updateTitle(this);
211 }
212
onCloseEvent()213 bool MSAEditor::onCloseEvent() {
214 if (ui->getOverviewArea() != nullptr) {
215 ui->getOverviewArea()->cancelRendering();
216 }
217 return true;
218 }
219
getRowByViewRowIndex(int viewRowIndex) const220 MultipleSequenceAlignmentRow MSAEditor::getRowByViewRowIndex(int viewRowIndex) const {
221 int maRowIndex = collapseModel->getMaRowIndexByViewRowIndex(viewRowIndex);
222 return getMaObject()->getMsaRow(maRowIndex);
223 }
224
~MSAEditor()225 MSAEditor::~MSAEditor() {
226 delete pairwiseAlignmentWidgetsSettings;
227 }
228
buildStaticToolbar(QToolBar * tb)229 void MSAEditor::buildStaticToolbar(QToolBar *tb) {
230 tb->addAction(ui->copyFormattedSelectionAction);
231
232 tb->addAction(saveAlignmentAction);
233 tb->addAction(saveAlignmentAsAction);
234
235 tb->addAction(zoomInAction);
236 tb->addAction(zoomOutAction);
237 tb->addAction(zoomToSelectionAction);
238 tb->addAction(resetZoomAction);
239
240 tb->addAction(showOverviewAction);
241 tb->addAction(changeFontAction);
242
243 tb->addAction(saveScreenshotAction);
244 tb->addAction(buildTreeAction);
245 tb->addAction(alignAction);
246 tb->addAction(alignSequencesToAlignmentAction);
247 tb->addAction(realignSomeSequenceAction);
248
249 GObjectView::buildStaticToolbar(tb);
250 }
251
buildMenu(QMenu * m,const QString & type)252 void MSAEditor::buildMenu(QMenu *m, const QString &type) {
253 if (type != MsaEditorMenuType::STATIC) {
254 GObjectView::buildMenu(m, type);
255 return;
256 }
257 addAppearanceMenu(m);
258
259 addNavigationMenu(m);
260
261 addLoadMenu(m);
262
263 addCopyPasteMenu(m);
264 addEditMenu(m);
265 addSortMenu(m);
266
267 addAlignMenu(m);
268 addTreeMenu(m);
269 addStatisticsMenu(m);
270
271 addExportMenu(m);
272
273 addAdvancedMenu(m);
274
275 GObjectView::buildMenu(m, type);
276
277 GUIUtils::disableEmptySubmenus(m);
278 }
279
addCopyPasteMenu(QMenu * m)280 void MSAEditor::addCopyPasteMenu(QMenu *m) {
281 MaEditor::addCopyPasteMenu(m);
282
283 QMenu *copyMenu = GUIUtils::findSubMenu(m, MSAE_MENU_COPY);
284 SAFE_POINT(copyMenu != nullptr, "copyMenu is null", );
285
286 const MaEditorSelection &selection = getSelection();
287 ui->copySelectionAction->setDisabled(selection.isEmpty());
288
289 // TODO:? move the signal emit point to a correct location.
290 auto sequenceArea = qobject_cast<MSAEditorSequenceArea *>(ui->getSequenceArea());
291 SAFE_POINT(sequenceArea != nullptr, "sequenceArea is null", );
292 emit sequenceArea->si_copyFormattedChanging(!selection.isEmpty());
293
294 copyMenu->addAction(ui->copySelectionAction);
295 ui->copyFormattedSelectionAction->setDisabled(selection.isEmpty());
296 copyMenu->addAction(ui->copyFormattedSelectionAction);
297 copyMenu->addAction(copyConsensusAction);
298 copyMenu->addAction(copyConsensusWithGapsAction);
299 copyMenu->addSeparator();
300 copyMenu->addAction(ui->pasteAction);
301 copyMenu->addAction(ui->pasteBeforeAction);
302 copyMenu->addSeparator();
303 copyMenu->addAction(ui->cutSelectionAction);
304
305 copyMenu->addSeparator();
306 MaEditorNameList *nameList = ui->getEditorNameList();
307 copyMenu->addAction(nameList->copyWholeRowAction);
308 }
309
addEditMenu(QMenu * m)310 void MSAEditor::addEditMenu(QMenu *m) {
311 QMenu *menu = m->addMenu(tr("Edit"));
312 menu->menuAction()->setObjectName(MSAE_MENU_EDIT);
313 }
314
addSortMenu(QMenu * m)315 void MSAEditor::addSortMenu(QMenu *m) {
316 QMenu *menu = m->addMenu(tr("Sort"));
317 menu->menuAction()->setObjectName(MSAE_MENU_SORT);
318 menu->addAction(sortByNameAscendingAction);
319 menu->addAction(sortByNameDescendingAction);
320 menu->addAction(sortByLengthAscendingAction);
321 menu->addAction(sortByLengthDescendingAction);
322 menu->addAction(sortByLeadingGapAscendingAction);
323 menu->addAction(sortByLeadingGapDescendingAction);
324
325 if (getRowOrderMode() == MaEditorRowOrderMode::Sequence) {
326 menu->addSeparator();
327 menu->addAction(sortGroupsBySizeDescendingAction);
328 menu->addAction(sortGroupsBySizeAscendingAction);
329 }
330 }
331
addExportMenu(QMenu * m)332 void MSAEditor::addExportMenu(QMenu *m) {
333 MaEditor::addExportMenu(m);
334 QMenu *em = GUIUtils::findSubMenu(m, MSAE_MENU_EXPORT);
335 SAFE_POINT(em != nullptr, "Export menu not found", );
336 em->addAction(saveScreenshotAction);
337 }
338
addAppearanceMenu(QMenu * m)339 void MSAEditor::addAppearanceMenu(QMenu *m) {
340 QMenu *appearanceMenu = m->addMenu(tr("Appearance"));
341 appearanceMenu->menuAction()->setObjectName(MSAE_MENU_APPEARANCE);
342
343 appearanceMenu->addAction(showOverviewAction);
344 auto offsetsController = ui->getOffsetsViewController();
345 if (offsetsController != nullptr) {
346 appearanceMenu->addAction(offsetsController->toggleColumnsViewAction);
347 }
348 appearanceMenu->addSeparator();
349 appearanceMenu->addAction(zoomInAction);
350 appearanceMenu->addAction(zoomOutAction);
351 appearanceMenu->addAction(zoomToSelectionAction);
352 appearanceMenu->addAction(resetZoomAction);
353 appearanceMenu->addSeparator();
354
355 addColorsMenu(appearanceMenu);
356 addHighlightingMenu(appearanceMenu);
357 appearanceMenu->addSeparator();
358
359 appearanceMenu->addAction(changeFontAction);
360 appearanceMenu->addSeparator();
361
362 appearanceMenu->addAction(clearSelectionAction);
363 }
364
addColorsMenu(QMenu * m)365 void MSAEditor::addColorsMenu(QMenu *m) {
366 QMenu *colorsSchemeMenu = m->addMenu(tr("Colors"));
367 colorsSchemeMenu->menuAction()->setObjectName("Colors");
368 colorsSchemeMenu->setIcon(QIcon(":core/images/color_wheel.png"));
369 auto sequenceArea = ui->getSequenceArea();
370 foreach (QAction *a, sequenceArea->colorSchemeMenuActions) {
371 MsaSchemesMenuBuilder::addActionOrTextSeparatorToMenu(a, colorsSchemeMenu);
372 }
373 colorsSchemeMenu->addSeparator();
374
375 QMenu *customColorSchemaMenu = new QMenu(tr("Custom schemes"), colorsSchemeMenu);
376 customColorSchemaMenu->menuAction()->setObjectName("Custom schemes");
377
378 foreach (QAction *a, sequenceArea->customColorSchemeMenuActions) {
379 MsaSchemesMenuBuilder::addActionOrTextSeparatorToMenu(a, customColorSchemaMenu);
380 }
381
382 if (!sequenceArea->customColorSchemeMenuActions.isEmpty()) {
383 customColorSchemaMenu->addSeparator();
384 }
385
386 customColorSchemaMenu->addAction(openCustomSettingsAction);
387
388 colorsSchemeMenu->addMenu(customColorSchemaMenu);
389 m->insertMenu(GUIUtils::findAction(m->actions(), MSAE_MENU_EDIT), colorsSchemeMenu);
390 }
391
addHighlightingMenu(QMenu * m)392 void MSAEditor::addHighlightingMenu(QMenu *m) {
393 QMenu *highlightSchemeMenu = new QMenu(tr("Highlighting"), nullptr);
394
395 highlightSchemeMenu->menuAction()->setObjectName("Highlighting");
396
397 auto sequenceArea = ui->getSequenceArea();
398 foreach (QAction *a, sequenceArea->highlightingSchemeMenuActions) {
399 MsaSchemesMenuBuilder::addActionOrTextSeparatorToMenu(a, highlightSchemeMenu);
400 }
401 highlightSchemeMenu->addSeparator();
402 highlightSchemeMenu->addAction(sequenceArea->useDotsAction);
403 m->insertMenu(GUIUtils::findAction(m->actions(), MSAE_MENU_EDIT), highlightSchemeMenu);
404 }
405
addNavigationMenu(QMenu * m)406 void MSAEditor::addNavigationMenu(QMenu *m) {
407 QMenu *navMenu = m->addMenu(tr("Navigation"));
408 navMenu->menuAction()->setObjectName(MSAE_MENU_NAVIGATION);
409 navMenu->addAction(gotoAction);
410 navMenu->addSeparator();
411 navMenu->addAction(searchInSequencesAction);
412 navMenu->addAction(searchInSequenceNamesAction);
413 }
414
addTreeMenu(QMenu * m)415 void MSAEditor::addTreeMenu(QMenu *m) {
416 QMenu *em = m->addMenu(tr("Tree"));
417 // em->setIcon(QIcon(":core/images/tree.png"));
418 em->menuAction()->setObjectName(MSAE_MENU_TREES);
419 em->addAction(buildTreeAction);
420 }
421
addAdvancedMenu(QMenu * m)422 void MSAEditor::addAdvancedMenu(QMenu *m) {
423 QMenu *menu = m->addMenu(tr("Advanced"));
424 menu->menuAction()->setObjectName(MSAE_MENU_ADVANCED);
425
426 if (convertDnaToRnaAction->isEnabled()) {
427 menu->addAction(convertDnaToRnaAction);
428 } else if (convertRnaToDnaAction->isEnabled()) {
429 menu->addAction(convertRnaToDnaAction);
430 }
431 }
432
addStatisticsMenu(QMenu * m)433 void MSAEditor::addStatisticsMenu(QMenu *m) {
434 QMenu *em = m->addMenu(tr("Statistics"));
435 em->setIcon(QIcon(":core/images/chart_bar.png"));
436 em->menuAction()->setObjectName(MSAE_MENU_STATISTICS);
437 }
438
getUI() const439 MsaEditorWgt *MSAEditor::getUI() const {
440 return qobject_cast<MsaEditorWgt *>(ui);
441 }
442
createWidget()443 QWidget *MSAEditor::createWidget() {
444 Q_ASSERT(ui == nullptr);
445 ui = new MsaEditorWgt(this);
446
447 QString objName = "msa_editor_" + maObject->getGObjectName();
448 ui->setObjectName(objName);
449
450 initActions();
451
452 connect(ui, SIGNAL(customContextMenuRequested(const QPoint &)), SLOT(sl_onContextMenuRequested(const QPoint &)));
453
454 gotoAction = new QAction(QIcon(":core/images/goto.png"), tr("Go to position…"), this);
455 gotoAction->setObjectName("action_go_to_position");
456 gotoAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_G));
457 gotoAction->setShortcutContext(Qt::WindowShortcut);
458 gotoAction->setToolTip(QString("%1 (%2)").arg(gotoAction->text()).arg(gotoAction->shortcut().toString()));
459 connect(gotoAction, SIGNAL(triggered()), ui->getSequenceArea(), SLOT(sl_goto()));
460
461 searchInSequencesAction = new QAction(QIcon(":core/images/find_dialog.png"), tr("Search in sequences…"), this);
462 searchInSequencesAction->setObjectName("search_in_sequences");
463 searchInSequencesAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_F));
464 searchInSequencesAction->setShortcutContext(Qt::WindowShortcut);
465 searchInSequencesAction->setToolTip(QString("%1 (%2)").arg(searchInSequencesAction->text()).arg(searchInSequencesAction->shortcut().toString()));
466 connect(searchInSequencesAction, SIGNAL(triggered()), this, SLOT(sl_searchInSequences()));
467
468 searchInSequenceNamesAction = new QAction(QIcon(":core/images/find_dialog.png"), tr("Search in sequence names…"), this);
469 searchInSequenceNamesAction->setObjectName("search_in_sequence_names");
470 searchInSequenceNamesAction->setShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_F));
471 searchInSequenceNamesAction->setShortcutContext(Qt::WindowShortcut);
472 searchInSequenceNamesAction->setToolTip(QString("%1 (%2)").arg(searchInSequenceNamesAction->text()).arg(searchInSequenceNamesAction->shortcut().toString()));
473 connect(searchInSequenceNamesAction, SIGNAL(triggered()), this, SLOT(sl_searchInSequenceNames()));
474
475 alignAction = new QAction(QIcon(":core/images/align.png"), tr("Align"), this);
476 alignAction->setObjectName("Align");
477 connect(alignAction, SIGNAL(triggered()), this, SLOT(sl_align()));
478
479 alignSequencesToAlignmentAction = new QAction(QIcon(":/core/images/add_to_alignment.png"), tr("Align sequence(s) to this alignment"), this);
480 alignSequencesToAlignmentAction->setObjectName("Align sequence(s) to this alignment");
481 connect(alignSequencesToAlignmentAction, SIGNAL(triggered()), this, SLOT(sl_addToAlignment()));
482
483 setAsReferenceSequenceAction = new QAction(tr("Set this sequence as reference"), this);
484 setAsReferenceSequenceAction->setObjectName("set_seq_as_reference");
485 connect(setAsReferenceSequenceAction, SIGNAL(triggered()), SLOT(sl_setSeqAsReference()));
486
487 unsetReferenceSequenceAction = new QAction(tr("Unset reference sequence"), this);
488 unsetReferenceSequenceAction->setObjectName("unset_reference");
489 connect(unsetReferenceSequenceAction, SIGNAL(triggered()), SLOT(sl_unsetReferenceSeq()));
490
491 optionsPanel = new OptionsPanel(this);
492 OPWidgetFactoryRegistry *opWidgetFactoryRegistry = AppContext::getOPWidgetFactoryRegistry();
493
494 QList<OPFactoryFilterVisitorInterface *> filters;
495 filters.append(new OPFactoryFilterVisitor(ObjViewType_AlignmentEditor));
496
497 QList<OPWidgetFactory *> opWidgetFactories = opWidgetFactoryRegistry->getRegisteredFactories(filters);
498 foreach (OPWidgetFactory *factory, opWidgetFactories) {
499 optionsPanel->addGroup(factory);
500 }
501
502 connect(realignSomeSequenceAction, SIGNAL(triggered()), this, SLOT(sl_realignSomeSequences()));
503 connect(maObject, SIGNAL(si_alphabetChanged(const MaModificationInfo &, const DNAAlphabet *)), SLOT(sl_updateRealignAction()));
504 connect(getSelectionController(),
505 SIGNAL(si_selectionChanged(const MaEditorSelection &, const MaEditorSelection &)),
506 SLOT(sl_updateRealignAction()));
507
508 qDeleteAll(filters);
509
510 connect(ui, SIGNAL(si_showTreeOP()), SLOT(sl_showTreeOP()));
511 connect(ui, SIGNAL(si_hideTreeOP()), SLOT(sl_hideTreeOP()));
512 sl_hideTreeOP();
513
514 treeManager.loadRelatedTrees();
515
516 new MoveToObjectMaController(this);
517
518 initDragAndDropSupport();
519 updateActions();
520 return ui;
521 }
522
sl_onContextMenuRequested(const QPoint &)523 void MSAEditor::sl_onContextMenuRequested(const QPoint & /*pos*/) {
524 QMenu m;
525
526 addAppearanceMenu(&m);
527 addNavigationMenu(&m);
528 addLoadMenu(&m);
529 addCopyPasteMenu(&m);
530 addEditMenu(&m);
531 addSortMenu(&m);
532 m.addSeparator();
533
534 addAlignMenu(&m);
535 addTreeMenu(&m);
536 addStatisticsMenu(&m);
537 addExportMenu(&m);
538 addAdvancedMenu(&m);
539
540 m.addSeparator();
541 snp.clickPoint = QCursor::pos();
542 const QPoint nameMapped = ui->getEditorNameList()->mapFromGlobal(snp.clickPoint);
543 const qint64 hoverRowId = (0 <= nameMapped.y()) ? ui->getEditorNameList()->sequenceIdAtPos(nameMapped) : U2MsaRow::INVALID_ROW_ID;
544 if ((hoverRowId != getReferenceRowId() || U2MsaRow::INVALID_ROW_ID == getReferenceRowId()) && hoverRowId != U2MsaRow::INVALID_ROW_ID) {
545 m.addAction(setAsReferenceSequenceAction);
546 }
547 if (U2MsaRow::INVALID_ROW_ID != getReferenceRowId()) {
548 m.addAction(unsetReferenceSequenceAction);
549 }
550 m.addSeparator();
551
552 emit si_buildMenu(this, &m, MsaEditorMenuType::CONTEXT);
553
554 GUIUtils::disableEmptySubmenus(&m);
555
556 m.exec(QCursor::pos());
557 }
558
sl_showTreeOP()559 void MSAEditor::sl_showTreeOP() {
560 OptionsPanelWidget *opWidget = dynamic_cast<OptionsPanelWidget *>(optionsPanel->getMainWidget());
561 if (opWidget == nullptr) {
562 return;
563 }
564
565 QWidget *addTreeGroupWidget = opWidget->findOptionsWidgetByGroupId("OP_MSA_ADD_TREE_WIDGET");
566 if (addTreeGroupWidget != nullptr) {
567 addTreeGroupWidget->hide();
568 opWidget->closeOptionsPanel();
569 }
570 QWidget *addTreeHeader = opWidget->findHeaderWidgetByGroupId("OP_MSA_ADD_TREE_WIDGET");
571 if (addTreeHeader != nullptr) {
572 addTreeHeader->hide();
573 }
574
575 GroupHeaderImageWidget *header = opWidget->findHeaderWidgetByGroupId("OP_MSA_TREES_WIDGET");
576 if (header != nullptr) {
577 header->show();
578 header->changeState();
579 }
580 }
581
sl_hideTreeOP()582 void MSAEditor::sl_hideTreeOP() {
583 OptionsPanelWidget *opWidget = dynamic_cast<OptionsPanelWidget *>(optionsPanel->getMainWidget());
584 if (opWidget == nullptr) {
585 return;
586 }
587 GroupHeaderImageWidget *header = opWidget->findHeaderWidgetByGroupId("OP_MSA_TREES_WIDGET");
588 QWidget *groupWidget = opWidget->findOptionsWidgetByGroupId("OP_MSA_TREES_WIDGET");
589 bool openAddTreeGroup = groupWidget != nullptr;
590 header->hide();
591
592 GroupHeaderImageWidget *addTreeHeader = opWidget->findHeaderWidgetByGroupId("OP_MSA_ADD_TREE_WIDGET");
593 if (addTreeHeader != nullptr) {
594 addTreeHeader->show();
595 if (openAddTreeGroup) {
596 addTreeHeader->changeState();
597 }
598 }
599 }
600
eventFilter(QObject *,QEvent * e)601 bool MSAEditor::eventFilter(QObject *, QEvent *e) {
602 if (e->type() == QEvent::DragEnter || e->type() == QEvent::Drop) {
603 QDropEvent *de = (QDropEvent *)e;
604 const QMimeData *md = de->mimeData();
605 const GObjectMimeData *gomd = qobject_cast<const GObjectMimeData *>(md);
606 if (gomd != nullptr) {
607 CHECK(!maObject->isStateLocked(), false)
608 U2SequenceObject *dnaObj = qobject_cast<U2SequenceObject *>(gomd->objPtr.data());
609 if (dnaObj != nullptr) {
610 if (U2AlphabetUtils::deriveCommonAlphabet(dnaObj->getAlphabet(), maObject->getAlphabet()) == nullptr) {
611 return false;
612 }
613 if (e->type() == QEvent::DragEnter) {
614 de->acceptProposedAction();
615 } else {
616 U2OpStatusImpl os;
617 DNASequence seq = dnaObj->getWholeSequence(os);
618 seq.alphabet = dnaObj->getAlphabet();
619 Task *task = new AddSequenceObjectsToAlignmentTask(getMaObject(), QList<DNASequence>() << seq);
620 TaskWatchdog::trackResourceExistence(maObject, task, tr("A problem occurred during adding sequences. The multiple alignment is no more available."));
621 AppContext::getTaskScheduler()->registerTopLevelTask(task);
622 }
623 }
624 }
625 }
626
627 return false;
628 }
629
initDragAndDropSupport()630 void MSAEditor::initDragAndDropSupport() {
631 SAFE_POINT(ui != nullptr, QString("MSAEditor::ui is not initialized in MSAEditor::initDragAndDropSupport"), );
632 ui->setAcceptDrops(true);
633 ui->installEventFilter(this);
634 }
635
sl_align()636 void MSAEditor::sl_align() {
637 QMenu menu;
638 emit si_buildMenu(this, &menu, MsaEditorMenuType::ALIGN);
639 menu.exec(QCursor::pos());
640 }
641
sl_addToAlignment()642 void MSAEditor::sl_addToAlignment() {
643 QMenu menu;
644 emit si_buildMenu(this, &menu, MsaEditorMenuType::ALIGN_SEQUENCES_TO_ALIGNMENT);
645 menu.exec(QCursor::pos());
646 }
647
sl_searchInSequences()648 void MSAEditor::sl_searchInSequences() {
649 auto optionsPanel = getOptionsPanel();
650 SAFE_POINT(optionsPanel != nullptr, "Internal error: options panel is NULL"
651 " when search in sequences was initiated!", );
652 QVariantMap options = FindPatternMsaWidgetFactory::getOptionsToActivateSearchInSequences();
653 optionsPanel->openGroupById(FindPatternMsaWidgetFactory::getGroupId(), options);
654 }
655
sl_searchInSequenceNames()656 void MSAEditor::sl_searchInSequenceNames() {
657 auto optionsPanel = getOptionsPanel();
658 SAFE_POINT(optionsPanel != nullptr, "Internal error: options panel is NULL"
659 " when search in sequence names was initiated!", );
660 QVariantMap options = FindPatternMsaWidgetFactory::getOptionsToActivateSearchInNames();
661 optionsPanel->openGroupById(FindPatternMsaWidgetFactory::getGroupId(), options);
662 }
663
sl_realignSomeSequences()664 void MSAEditor::sl_realignSomeSequences() {
665 const MaEditorSelection &selection = getSelection();
666 QList<int> selectedMaRowIndexes = collapseModel->getMaRowIndexesFromSelectionRects(selection.getRectList());
667 QList<qint64> selectedRowIds = maObject->getRowIdsByRowIndexes(selectedMaRowIndexes);
668 auto realignTask = new RealignSequencesInAlignmentTask(getMaObject(), selectedRowIds.toSet());
669 TaskWatchdog::trackResourceExistence(maObject, realignTask, tr("A problem occurred during realigning sequences. The multiple alignment is no more available."));
670 AppContext::getTaskScheduler()->registerTopLevelTask(realignTask);
671 }
672
sl_setSeqAsReference()673 void MSAEditor::sl_setSeqAsReference() {
674 QPoint menuCallPos = snp.clickPoint;
675 QPoint nameMapped = ui->getEditorNameList()->mapFromGlobal(menuCallPos);
676 if (nameMapped.y() >= 0) {
677 qint64 newRowId = ui->getEditorNameList()->sequenceIdAtPos(nameMapped);
678 if (U2MsaRow::INVALID_ROW_ID != newRowId && newRowId != snp.seqId) {
679 setReference(newRowId);
680 }
681 }
682 }
683
sl_unsetReferenceSeq()684 void MSAEditor::sl_unsetReferenceSeq() {
685 if (U2MsaRow::INVALID_ROW_ID != getReferenceRowId()) {
686 setReference(U2MsaRow::INVALID_ROW_ID);
687 }
688 }
689
sl_rowsRemoved(const QList<qint64> & rowIds)690 void MSAEditor::sl_rowsRemoved(const QList<qint64> &rowIds) {
691 foreach (qint64 rowId, rowIds) {
692 if (getReferenceRowId() == rowId) {
693 sl_unsetReferenceSeq();
694 break;
695 }
696 }
697 }
698
sl_updateRealignAction()699 void MSAEditor::sl_updateRealignAction() {
700 if (maObject->isStateLocked() || maObject->getAlphabet()->isRaw() || ui == nullptr) {
701 realignSomeSequenceAction->setDisabled(true);
702 return;
703 }
704 const MaEditorSelection &selection = getSelection();
705 int selectionWidth = selection.getWidth();
706 int selectedRowsCount = selection.getCountOfSelectedRows();
707 bool isWholeSequenceSelection = selectionWidth == maObject->getLength() && selectedRowsCount >= 1;
708 bool isAllRowsSelection = selectedRowsCount == collapseModel->getViewRowCount();
709 realignSomeSequenceAction->setEnabled(isWholeSequenceSelection && !isAllRowsSelection);
710 }
711
buildTree()712 void MSAEditor::buildTree() {
713 sl_buildTree();
714 }
715
getReferenceRowName() const716 QString MSAEditor::getReferenceRowName() const {
717 const MultipleAlignment alignment = getMaObject()->getMultipleAlignment();
718 U2OpStatusImpl os;
719 const int refSeq = alignment->getRowIndexByRowId(getReferenceRowId(), os);
720 return (U2MsaRow::INVALID_ROW_ID != refSeq) ? alignment->getRowNames().at(refSeq) : QString();
721 }
722
getReferenceCharAt(int pos) const723 char MSAEditor::getReferenceCharAt(int pos) const {
724 CHECK(getReferenceRowId() != U2MsaRow::INVALID_ROW_ID, '\n');
725
726 U2OpStatusImpl os;
727 const int refSeq = maObject->getMultipleAlignment()->getRowIndexByRowId(getReferenceRowId(), os);
728 SAFE_POINT_OP(os, '\n');
729
730 return maObject->getMultipleAlignment()->charAt(refSeq, pos);
731 }
732
sl_showCustomSettings()733 void MSAEditor::sl_showCustomSettings() {
734 AppContext::getAppSettingsGUI()->showSettingsDialog(ColorSchemaSettingsPageId);
735 }
736
sortSequences(const MultipleAlignment::SortType & sortType,const MultipleAlignment::Order & sortOrder)737 void MSAEditor::sortSequences(const MultipleAlignment::SortType &sortType, const MultipleAlignment::Order &sortOrder) {
738 MultipleSequenceAlignmentObject *msaObject = getMaObject();
739 CHECK(!msaObject->isStateLocked(), );
740
741 MultipleSequenceAlignment msa = msaObject->getMultipleAlignmentCopy();
742 const MaEditorSelection &selection = getSelection();
743 QRect selectionRect = selection.toRect();
744 U2Region sortRange = selectionRect.height() <= 1 ? U2Region() : U2Region(selectionRect.y(), selectionRect.height());
745 msa->sortRows(sortType, sortOrder, sortRange);
746
747 // Switch into 'Original' ordering mode.
748 getUI()->getSequenceArea()->sl_toggleSequenceRowOrder(false);
749
750 QStringList rowNames = msa->getRowNames();
751 if (rowNames != msaObject->getMultipleAlignment()->getRowNames()) {
752 U2OpStatusImpl os;
753 msaObject->updateRowsOrder(os, msa->getRowsIds());
754 }
755 }
756
sl_sortSequencesByName()757 void MSAEditor::sl_sortSequencesByName() {
758 MultipleAlignment::Order order = sender() == sortByNameDescendingAction ? MultipleAlignment::Descending : MultipleAlignment::Ascending;
759 sortSequences(MultipleAlignment::SortByName, order);
760 }
761
sl_sortSequencesByLength()762 void MSAEditor::sl_sortSequencesByLength() {
763 MultipleAlignment::Order order = sender() == sortByLengthDescendingAction ? MultipleAlignment::Descending : MultipleAlignment::Ascending;
764 sortSequences(MultipleAlignment::SortByLength, order);
765 }
766
sl_sortSequencesByLeadingGap()767 void MSAEditor::sl_sortSequencesByLeadingGap() {
768 MultipleAlignment::Order order = sender() == sortByLeadingGapDescendingAction ? MultipleAlignment::Descending : MultipleAlignment::Ascending;
769 sortSequences(MultipleAlignment::SortByLeadingGap, order);
770 }
771
sl_convertBetweenDnaAndRnaAlphabets()772 void MSAEditor::sl_convertBetweenDnaAndRnaAlphabets() {
773 CHECK(!maObject->isStateLocked(), )
774
775 auto alphabetId = maObject->getAlphabet()->getId();
776 bool isDnaAlphabet = alphabetId == BaseDNAAlphabetIds::NUCL_DNA_DEFAULT();
777 bool isRnaAlphabet = alphabetId == BaseDNAAlphabetIds::NUCL_RNA_DEFAULT();
778 CHECK(isDnaAlphabet || isRnaAlphabet, );
779
780 auto msaObject = getMaObject();
781 auto alphabetRegistry = AppContext::getDNAAlphabetRegistry();
782 U2OpStatus2Log os;
783 U2UseCommonUserModStep userModStep(msaObject->getEntityRef(), os);
784 auto resultAlphabet = alphabetRegistry->findById(isDnaAlphabet ? BaseDNAAlphabetIds::NUCL_RNA_DEFAULT() : BaseDNAAlphabetIds::NUCL_DNA_DEFAULT());
785 char fromChar = isDnaAlphabet ? 'T' : 'U';
786 char toChar = isDnaAlphabet ? 'U' : 'T';
787 msaObject->replaceAllCharacters(fromChar, toChar, resultAlphabet);
788 }
789
sl_convertRawToDnaAlphabet()790 void MSAEditor::sl_convertRawToDnaAlphabet() {
791 CHECK(!maObject->isStateLocked(), )
792
793 auto alphabetId = maObject->getAlphabet()->getId();
794 CHECK(alphabetId == BaseDNAAlphabetIds::RAW(), );
795
796 auto msaObject = getMaObject();
797 auto alphabetRegistry = AppContext::getDNAAlphabetRegistry();
798 U2OpStatus2Log os;
799 U2UseCommonUserModStep userModStep(msaObject->getEntityRef(), os);
800 auto resultAlphabet = alphabetRegistry->findById(BaseDNAAlphabetIds::NUCL_DNA_DEFAULT());
801 QByteArray replacementMap(256, '\0');
802 replacementMap['U'] = 'T';
803 msaObject->morphAlphabet(resultAlphabet, replacementMap);
804 }
805
sl_convertRawToAminoAlphabet()806 void MSAEditor::sl_convertRawToAminoAlphabet() {
807 CHECK(!maObject->isStateLocked(), )
808
809 auto alphabetId = maObject->getAlphabet()->getId();
810 CHECK(alphabetId == BaseDNAAlphabetIds::RAW(), );
811
812 auto msaObject = getMaObject();
813 auto alphabetRegistry = AppContext::getDNAAlphabetRegistry();
814 U2OpStatus2Log os;
815 U2UseCommonUserModStep userModStep(msaObject->getEntityRef(), os);
816 auto resultAlphabet = alphabetRegistry->findById(BaseDNAAlphabetIds::AMINO_DEFAULT());
817 msaObject->morphAlphabet(resultAlphabet);
818 }
819
sl_sortGroupsBySize()820 void MSAEditor::sl_sortGroupsBySize() {
821 groupsSortOrder = sender() == sortGroupsBySizeAscendingAction ? GroupsSortOrder::Ascending : GroupsSortOrder::Descending;
822 updateCollapseModel();
823 }
824
825 // TODO: move this function into MSA?
826 /* Groups rows by similarity. Two rows are considered equal if their sequences are equal with ignoring of gaps. */
groupRowsBySimilarity(const QList<MultipleAlignmentRow> & msaRows)827 static QList<QList<int>> groupRowsBySimilarity(const QList<MultipleAlignmentRow> &msaRows) {
828 QList<QList<int>> rowGroups;
829 QSet<int> mappedRows; // contains indexes of the already processed rows.
830 for (int i = 0; i < msaRows.size(); i++) {
831 if (mappedRows.contains(i)) {
832 continue;
833 }
834 const MultipleAlignmentRow &row = msaRows[i];
835 QList<int> rowGroup;
836 rowGroup << i;
837 for (int j = i + 1; j < msaRows.size(); j++) {
838 const MultipleAlignmentRow &next = msaRows[j];
839 if (!mappedRows.contains(j) && MultipleAlignmentRowData::isEqualsIgnoreGaps(next.data(), row.data())) {
840 rowGroup << j;
841 mappedRows.insert(j);
842 }
843 }
844 rowGroups << rowGroup;
845 }
846 return rowGroups;
847 }
848
updateCollapseModel()849 void MSAEditor::updateCollapseModel() {
850 if (rowOrderMode == MaEditorRowOrderMode::Original) {
851 // Synchronize collapsible model with a current alignment.
852 collapseModel->reset(getMaRowIds());
853 return;
854 } else if (rowOrderMode == MaEditorRowOrderMode::Free) {
855 // Check if the modification is compatible with the current view state: all rows have view properties assigned. Reset to the Original order if not.
856 QSet<qint64> maRowIds = getMaRowIds().toSet();
857 QSet<qint64> viewModelRowIds = collapseModel->getAllRowIds();
858 if (viewModelRowIds != maRowIds) {
859 rowOrderMode = MaEditorRowOrderMode::Original;
860 collapseModel->reset(getMaRowIds());
861 }
862 return;
863 }
864
865 SAFE_POINT(rowOrderMode == MaEditorRowOrderMode::Sequence, "Unexpected row order mode", );
866
867 // Order and group rows by sequence content.
868 MultipleSequenceAlignmentObject *msaObject = getMaObject();
869 QList<QList<int>> rowGroups = groupRowsBySimilarity(msaObject->getRows());
870 QVector<MaCollapsibleGroup> newCollapseGroups;
871
872 QSet<qint64> maRowIdsOfNonCollapsedRowsBefore;
873 for (int i = 0; i < collapseModel->getGroupCount(); i++) {
874 const MaCollapsibleGroup *group = collapseModel->getCollapsibleGroup(i);
875 if (!group->isCollapsed) {
876 maRowIdsOfNonCollapsedRowsBefore += group->maRowIds.toSet();
877 }
878 }
879 for (int i = 0; i < rowGroups.size(); i++) {
880 const QList<int> &maRowsInGroup = rowGroups[i];
881 QList<qint64> maRowIdsInGroup = msaObject->getMultipleAlignment()->getRowIdsByRowIndexes(maRowsInGroup);
882 bool isCollapsed = !maRowIdsOfNonCollapsedRowsBefore.contains(maRowIdsInGroup[0]);
883 newCollapseGroups << MaCollapsibleGroup(maRowsInGroup, maRowIdsInGroup, isCollapsed);
884 }
885 if (groupsSortOrder != GroupsSortOrder::Original) {
886 std::stable_sort(newCollapseGroups.begin(), newCollapseGroups.end(), [&](const MaCollapsibleGroup &g1, const MaCollapsibleGroup &g2) {
887 int size1 = g1.maRowIds.size();
888 int size2 = g2.maRowIds.size();
889 return groupsSortOrder == GroupsSortOrder::Ascending ? size1 < size2 : size2 < size1;
890 });
891 }
892 collapseModel->update(newCollapseGroups);
893 }
894
setRowOrderMode(MaEditorRowOrderMode mode)895 void MSAEditor::setRowOrderMode(MaEditorRowOrderMode mode) {
896 if (mode == rowOrderMode) {
897 return;
898 }
899 MaEditor::setRowOrderMode(mode);
900 freeModeMasterMarkersSet.clear();
901 updateCollapseModel();
902 updateActions();
903 }
904
getFreeModeMasterMarkersSet() const905 const QSet<QObject *> &MSAEditor::getFreeModeMasterMarkersSet() const {
906 return freeModeMasterMarkersSet;
907 }
908
addFreeModeMasterMarker(QObject * marker)909 void MSAEditor::addFreeModeMasterMarker(QObject *marker) {
910 freeModeMasterMarkersSet.insert(marker);
911 }
912
removeFreeModeMasterMarker(QObject * marker)913 void MSAEditor::removeFreeModeMasterMarker(QObject *marker) {
914 freeModeMasterMarkersSet.remove(marker);
915 }
916
getSelectionController() const917 MaEditorSelectionController *MSAEditor::getSelectionController() const {
918 return selectionController;
919 }
920
sl_exportImage()921 void MSAEditor::sl_exportImage() {
922 MSAImageExportController controller(ui);
923 QWidget *parentWidget = (QWidget *)AppContext::getMainWindow()->getQMainWindow();
924 QString fileName = GUrlUtils::fixFileName(maObject->getGObjectName());
925 QObjectScopedPointer<ExportImageDialog> dlg = new ExportImageDialog(&controller,
926 ExportImageDialog::MSA,
927 fileName,
928 ExportImageDialog::NoScaling,
929 parentWidget);
930 dlg->exec();
931 }
932
933 } // namespace U2
934