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 "MSAHighlightingTab.h"
23 
24 #include <QCheckBox>
25 #include <QComboBox>
26 #include <QDoubleSpinBox>
27 #include <QLabel>
28 #include <QRadioButton>
29 #include <QStandardItemModel>
30 #include <QToolButton>
31 #include <QVBoxLayout>
32 
33 #include <U2Algorithm/MsaColorScheme.h>
34 #include <U2Algorithm/MsaHighlightingScheme.h>
35 
36 #include <U2Core/AppContext.h>
37 #include <U2Core/DNAAlphabet.h>
38 #include <U2Core/U2SafePoints.h>
39 
40 #include <U2Gui/GroupedComboBoxDelegate.h>
41 #include <U2Gui/ShowHideSubgroupWidget.h>
42 #include <U2Gui/U2WidgetStateStorage.h>
43 
44 #include <U2View/MSAEditor.h>
45 #include <U2View/MSAEditorSequenceArea.h>
46 
47 namespace U2 {
48 
49 static const int ITEMS_SPACING = 6;
50 static const int TITLE_SPACING = 1;
51 
initVBoxLayout(QWidget * w)52 static inline QVBoxLayout *initVBoxLayout(QWidget *w) {
53     QVBoxLayout *layout = new QVBoxLayout;
54     layout->setContentsMargins(0, 0, 0, 0);
55     layout->setSpacing(5);
56 
57     w->setLayout(layout);
58     return layout;
59 }
60 
initHBoxLayout(QWidget * w)61 static inline QHBoxLayout *initHBoxLayout(QWidget *w) {
62     QHBoxLayout *layout = new QHBoxLayout;
63     layout->setContentsMargins(0, 0, 0, 0);
64 
65     w->setLayout(layout);
66     return layout;
67 }
68 
createColorGroup()69 QWidget *MSAHighlightingTab::createColorGroup() {
70     QWidget *group = new QWidget(this);
71 
72     QVBoxLayout *layout = initVBoxLayout(group);
73     colorSchemeController = new MsaSchemeComboBoxController<MsaColorSchemeFactory, MsaColorSchemeRegistry>(msa, AppContext::getMsaColorSchemeRegistry(), this);
74     colorSchemeController->getComboBox()->setObjectName("colorScheme");
75     colorSchemeController->getComboBox()->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
76 
77     colorThresholdLabel = new QLabel(tr("Threshold"));
78 
79     colorThresholdSlider = new QSlider(Qt::Horizontal, this);
80     colorThresholdSlider->setMinimum(1);
81     colorThresholdSlider->setMaximum(999);
82     colorThresholdSlider->setValue(500);
83     colorThresholdSlider->setObjectName("colorThresholdSlider");
84 
85     colorSpinBox = new QDoubleSpinBox();
86     colorSpinBox->setMinimum(0.1);
87     colorSpinBox->setMaximum(99.9);
88     colorSpinBox->setSingleStep(0.1);
89     colorSpinBox->setValue(50.0);
90     colorSpinBox->setDecimals(1);
91     colorSpinBox->setObjectName("colorSpinBox");
92 
93     QHBoxLayout *horizontalLayout = new QHBoxLayout();
94     horizontalLayout->addWidget(colorThresholdSlider);
95     horizontalLayout->addWidget(colorSpinBox);
96     horizontalLayout->setSpacing(10);
97 
98     layout->addWidget(colorSchemeController->getComboBox());
99     layout->addSpacing(TITLE_SPACING);
100     layout->addSpacing(TITLE_SPACING);
101     layout->addWidget(colorThresholdLabel);
102     layout->addLayout(horizontalLayout);
103 
104     layout->addSpacing(ITEMS_SPACING);
105 
106     return group;
107 }
108 
createHighlightingGroup()109 QWidget *MSAHighlightingTab::createHighlightingGroup() {
110     QWidget *group = new QWidget(this);
111 
112     QVBoxLayout *layout = initVBoxLayout(group);
113     highlightingSchemeController = new MsaSchemeComboBoxController<MsaHighlightingSchemeFactory, MsaHighlightingSchemeRegistry>(msa, AppContext::getMsaHighlightingSchemeRegistry(), this);
114     highlightingSchemeController->getComboBox()->setObjectName("highlightingScheme");
115 
116     hint = new QLabel("");
117     hint->setWordWrap(true);
118     hint->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
119 
120     useDots = new QCheckBox(tr("Use dots"));
121     useDots->setObjectName("useDots");
122 
123     exportHighlightning = new QToolButton();
124     exportHighlightning->setText(tr("Export"));
125     exportHighlightning->setObjectName("exportHighlightning");
126     exportHighlightning->setMinimumWidth(198);
127     exportHighlightning->setMinimumHeight(23);
128 
129     QWidget *buttonAndSpacer = new QWidget(this);
130     QHBoxLayout *layout2 = initHBoxLayout(buttonAndSpacer);
131     layout2->addWidget(exportHighlightning);
132     // layout2->addSpacerItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
133 
134     lessMoreLabel = new QLabel(tr("Highlight characters with conservation level:"));
135     lessMoreLabel->setWordWrap(true);
136 
137     thresholdMoreRb = new QRadioButton(QString::fromWCharArray(L"\x2265") + tr(" threshold"));
138     thresholdLessRb = new QRadioButton(QString::fromWCharArray(L"\x2264") + tr(" threshold"));
139     thresholdMoreRb->setObjectName("thresholdMoreRb");
140     thresholdLessRb->setObjectName("thresholdLessRb");
141 
142     highlightingThresholdSlider = new QSlider(Qt::Horizontal, this);
143     highlightingThresholdSlider->setMinimum(0);
144     highlightingThresholdSlider->setMaximum(100);
145     highlightingThresholdSlider->setValue(50);
146     highlightingThresholdSlider->setTickPosition(QSlider::TicksRight);
147     highlightingThresholdSlider->setObjectName("thresholdSlider");
148 
149     thresholdLabel = new QLabel(tr("Threshold: %1%").arg(highlightingThresholdSlider->value()), this);
150 
151     layout->setSpacing(ITEMS_SPACING);
152     layout->addSpacing(TITLE_SPACING);
153     layout->addWidget(highlightingSchemeController->getComboBox());
154     layout->addWidget(thresholdLabel);
155     layout->addWidget(highlightingThresholdSlider);
156     layout->addWidget(lessMoreLabel);
157     layout->addWidget(thresholdLessRb);
158     layout->addWidget(thresholdMoreRb);
159     layout->addWidget(useDots);
160 
161 #ifdef Q_OS_DARWIN
162     layout->addSpacerItem(new QSpacerItem(40, 8, QSizePolicy::Expanding, QSizePolicy::Minimum));
163 #endif
164     layout->addWidget(buttonAndSpacer);
165     layout->addWidget(hint);
166 
167     return group;
168 }
169 
MSAHighlightingTab(MSAEditor * m)170 MSAHighlightingTab::MSAHighlightingTab(MSAEditor *m)
171     : msa(m), savableTab(this, GObjectViewUtils::findViewByName(m->getName())) {
172     setObjectName("HighlightingOptionsPanelWidget");
173     QVBoxLayout *mainLayout = initVBoxLayout(this);
174     mainLayout->setSpacing(0);
175 
176     QWidget *colorGroup = new ShowHideSubgroupWidget("COLOR", tr("Color"), createColorGroup(), true);
177     mainLayout->addWidget(colorGroup);
178 
179     QWidget *highlightingGroup = new ShowHideSubgroupWidget("HIGHLIGHTING", tr("Highlighting"), createHighlightingGroup(), true);
180     mainLayout->addWidget(highlightingGroup);
181 
182     seqArea = msa->getUI()->getSequenceArea();
183 
184     savableTab.disableSavingForWidgets(QStringList()
185                                        << highlightingThresholdSlider->objectName()
186                                        << highlightingSchemeController->getComboBox()->objectName()
187                                        << colorSchemeController->getComboBox()->objectName()
188                                        << colorThresholdSlider->objectName()
189                                        << colorSpinBox->objectName());
190     U2WidgetStateStorage::restoreWidgetState(savableTab);
191 
192     sl_sync();
193 
194     connect(colorSchemeController, SIGNAL(si_dataChanged(const QString &)), seqArea, SLOT(sl_changeColorSchemeOutside(const QString &)));
195     connect(highlightingSchemeController, SIGNAL(si_dataChanged(const QString &)), seqArea, SLOT(sl_changeColorSchemeOutside(const QString &)));
196     connect(useDots, SIGNAL(stateChanged(int)), seqArea, SLOT(sl_triggerUseDots()));
197 
198     connect(seqArea, SIGNAL(si_highlightingChanged()), SLOT(sl_sync()));
199 
200     MsaColorSchemeRegistry *msaColorSchemeRegistry = AppContext::getMsaColorSchemeRegistry();
201     connect(msaColorSchemeRegistry, SIGNAL(si_customSettingsChanged()), SLOT(sl_refreshSchemes()));
202 
203     connect(m, SIGNAL(si_referenceSeqChanged(qint64)), SLOT(sl_updateHint()));
204     connect(m->getMaObject(), SIGNAL(si_alphabetChanged(MaModificationInfo, const DNAAlphabet *)), SLOT(sl_refreshSchemes()));
205 
206     connect(highlightingSchemeController->getComboBox(), SIGNAL(currentIndexChanged(const QString &)), SLOT(sl_updateHint()));
207     connect(colorSchemeController->getComboBox(), SIGNAL(currentIndexChanged(const QString &)), SLOT(sl_updateColorSchemeWidgets()));
208     connect(exportHighlightning, SIGNAL(clicked()), SLOT(sl_exportHighlightningClicked()));
209 
210     connect(colorThresholdSlider, SIGNAL(valueChanged(int)), SLOT(sl_colorParametersChanged()));
211     connect(colorSpinBox, SIGNAL(valueChanged(double)), SLOT(sl_colorParametersChanged()));
212     connect(this, SIGNAL(si_colorSchemeChanged()), seqArea, SLOT(sl_completeRedraw()));
213 
214     connect(highlightingThresholdSlider, SIGNAL(valueChanged(int)), SLOT(sl_highlightingParametersChanged()));
215     connect(thresholdMoreRb, SIGNAL(toggled(bool)), SLOT(sl_highlightingParametersChanged()));
216     connect(thresholdLessRb, SIGNAL(toggled(bool)), SLOT(sl_highlightingParametersChanged()));
217 
218     sl_updateHint();
219     sl_highlightingParametersChanged();
220 }
221 
sl_sync()222 void MSAHighlightingTab::sl_sync() {
223     MsaColorScheme *s = seqArea->getCurrentColorScheme();
224     SAFE_POINT(s != nullptr, "Current scheme is NULL", );
225     SAFE_POINT(s->getFactory() != nullptr, "Current scheme color factory is NULL", );
226 
227     colorSchemeController->getComboBox()->blockSignals(true);
228     colorSchemeController->setCurrentItemById(s->getFactory()->getId());
229     colorSchemeController->getComboBox()->blockSignals(false);
230 
231     MsaHighlightingScheme *sh = seqArea->getCurrentHighlightingScheme();
232     SAFE_POINT(sh != nullptr, "Current highlighting scheme is NULL!", );
233     SAFE_POINT(sh->getFactory() != nullptr, "Current highlighting scheme factory is NULL!", );
234 
235     highlightingSchemeController->getComboBox()->blockSignals(true);
236     highlightingSchemeController->setCurrentItemById(sh->getFactory()->getId());
237     highlightingSchemeController->getComboBox()->blockSignals(false);
238 
239     useDots->blockSignals(true);
240     useDots->setChecked(seqArea->getUseDotsCheckedState());
241     useDots->blockSignals(false);
242 
243     sl_updateHint();
244     sl_updateColorSchemeWidgets();
245 }
246 
sl_updateHint()247 void MSAHighlightingTab::sl_updateHint() {
248     MsaHighlightingScheme *s = seqArea->getCurrentHighlightingScheme();
249     SAFE_POINT(s->getFactory() != nullptr, "Highlighting factory is NULL!", );
250 
251     QVariantMap highlightingSettings;
252     if (s->getFactory()->isNeedThreshold()) {
253         thresholdLabel->show();
254         highlightingThresholdSlider->show();
255         thresholdLessRb->show();
256         thresholdMoreRb->show();
257         lessMoreLabel->show();
258         bool ok = false;
259         int thresholdValue = s->getSettings().value(MsaHighlightingScheme::THRESHOLD_PARAMETER_NAME).toInt(&ok);
260         assert(ok);
261         highlightingThresholdSlider->setValue(thresholdValue);
262         bool lessThenThreshold = s->getSettings().value(MsaHighlightingScheme::LESS_THAN_THRESHOLD_PARAMETER_NAME, thresholdLessRb->isChecked()).toBool();
263         thresholdLessRb->setChecked(lessThenThreshold);
264         thresholdMoreRb->setChecked(!lessThenThreshold);
265         highlightingSettings.insert(MsaHighlightingScheme::THRESHOLD_PARAMETER_NAME, thresholdValue);
266         highlightingSettings.insert(MsaHighlightingScheme::LESS_THAN_THRESHOLD_PARAMETER_NAME, lessThenThreshold);
267     } else {
268         thresholdLabel->hide();
269         highlightingThresholdSlider->hide();
270         thresholdLessRb->hide();
271         thresholdMoreRb->hide();
272         lessMoreLabel->hide();
273     }
274     if (U2MsaRow::INVALID_ROW_ID == msa->getReferenceRowId() && !seqArea->getCurrentHighlightingScheme()->getFactory()->isRefFree()) {
275         hint->setText(tr("Info: set a reference sequence."));
276         hint->setStyleSheet(
277             "color: green;"
278             "font: bold;");
279         exportHighlightning->setDisabled(true);
280         return;
281     }
282     hint->setText("");
283     if (s->getFactory()->isRefFree()) {
284         hint->setText(tr("Info: export is not available for the selected highlighting."));
285         hint->setStyleSheet(
286             "color: green;"
287             "font: bold;");
288         exportHighlightning->setDisabled(true);
289     } else {
290         exportHighlightning->setEnabled(true);
291     }
292     s->applySettings(highlightingSettings);
293 }
294 
sl_updateColorSchemeWidgets()295 void MSAHighlightingTab::sl_updateColorSchemeWidgets() {
296     MsaColorScheme *currentColorScheme = seqArea->getCurrentColorScheme();
297     SAFE_POINT(currentColorScheme != nullptr, "Current Color Scheme is NULL!", );
298 
299     const MsaColorSchemeFactory *factory = currentColorScheme->getFactory();
300     SAFE_POINT(factory != nullptr, "Current Color Scheme factory is NULL!", );
301 
302     if (factory->isThresholdNeeded()) {
303         colorThresholdLabel->show();
304         colorThresholdSlider->show();
305         colorSpinBox->show();
306     } else {
307         colorThresholdLabel->hide();
308         colorThresholdSlider->hide();
309         colorSpinBox->hide();
310     }
311 }
312 
sl_exportHighlightningClicked()313 void MSAHighlightingTab::sl_exportHighlightningClicked() {
314     msa->exportHighlighted();
315 }
316 
sl_colorParametersChanged()317 void MSAHighlightingTab::sl_colorParametersChanged() {
318     QSignalBlocker thresholdBlocker(colorThresholdSlider);
319     Q_UNUSED(thresholdBlocker);
320     QSignalBlocker spinBoxBlocker(colorSpinBox);
321     Q_UNUSED(spinBoxBlocker);
322 
323     double thresholdValue = colorSpinBox->value();
324     if (sender() == colorThresholdSlider) {
325         int sliderValue = colorThresholdSlider->value();
326         thresholdValue = double(sliderValue) / 10;
327         colorSpinBox->setValue(thresholdValue);
328     } else if (sender() == colorSpinBox) {
329         int sliderNewValue = int(thresholdValue * 10);
330         colorThresholdSlider->setValue(sliderNewValue);
331     }
332     MsaColorScheme *currentColorScheme = seqArea->getCurrentColorScheme();
333     SAFE_POINT(currentColorScheme != nullptr, "Current Color Scheme is NULL!", );
334 
335     QVariantMap settings;
336     settings.insert(MsaColorScheme::THRESHOLD_PARAMETER_NAME, thresholdValue);
337     currentColorScheme->applySettings(settings);
338     emit si_colorSchemeChanged();
339 }
340 
sl_highlightingParametersChanged()341 void MSAHighlightingTab::sl_highlightingParametersChanged() {
342     QVariantMap highlightingSettings;
343     thresholdLabel->setText(tr("Threshold: %1%").arg(highlightingThresholdSlider->value()));
344     MsaHighlightingScheme *s = seqArea->getCurrentHighlightingScheme();
345     highlightingSettings.insert(MsaHighlightingScheme::THRESHOLD_PARAMETER_NAME, highlightingThresholdSlider->value());
346     highlightingSettings.insert(MsaHighlightingScheme::LESS_THAN_THRESHOLD_PARAMETER_NAME, thresholdLessRb->isChecked());
347     s->applySettings(highlightingSettings);
348     seqArea->sl_changeColorSchemeOutside(colorSchemeController->getComboBox()->currentData().toString());
349 }
350 
sl_refreshSchemes()351 void MSAHighlightingTab::sl_refreshSchemes() {
352     colorSchemeController->init();
353     highlightingSchemeController->init();
354     sl_sync();
355 }
356 
357 }  // namespace U2
358