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 #ifndef _U2_MA_EDITOR_SEQUENCE_AREA_
23 #define _U2_MA_EDITOR_SEQUENCE_AREA_
24 
25 #include <QColor>
26 #include <QPainter>
27 #include <QTimer>
28 #include <QWidget>
29 
30 #include <U2Core/MultipleAlignment.h>
31 #include <U2Core/U2Region.h>
32 
33 #include <U2Gui/GScrollBar.h>
34 #include <U2Gui/SelectionModificationHelper.h>
35 
36 #include "../MsaEditorUserModStepController.h"
37 
38 class QRubberBand;
39 
40 namespace U2 {
41 
42 #define SETTINGS_ROOT QString("msaeditor/")
43 #define SETTINGS_COLOR_NUCL "color_nucl"
44 #define SETTINGS_COLOR_AMINO "color_amino"
45 #define SETTINGS_COLOR_RAW "color_raw"
46 #define SETTINGS_HIGHLIGHT_NUCL "highlight_nucl"
47 #define SETTINGS_HIGHLIGHT_AMINO "highlight_amino"
48 #define SETTINGS_HIGHLIGHT_RAW "highlight_raw"
49 #define SETTINGS_COPY_FORMATTED "copyformatted"
50 
51 class GScrollBar;
52 class MaEditor;
53 class MaEditorWgt;
54 class SequenceAreaRenderer;
55 
56 class MaModificationInfo;
57 class MsaColorScheme;
58 class MaEditorSelection;
59 class MsaColorSchemeFactory;
60 class MsaHighlightingScheme;
61 class MsaHighlightingSchemeFactory;
62 
63 class U2VIEW_EXPORT MaEditorSequenceArea : public QWidget {
64     Q_OBJECT
65     friend class SequenceAreaRenderer;
66 
67 public:
68     MaEditorSequenceArea(MaEditorWgt *ui, GScrollBar *hb, GScrollBar *vb);
69     virtual ~MaEditorSequenceArea();
70 
71     MaEditor *getEditor() const;
72 
73     QSize getCanvasSize(const QList<int> &seqIdx, const U2Region &region) const;
74 
75     int getFirstVisibleBase() const;
76     int getLastVisibleBase(bool countClipped) const;
77     int getNumVisibleBases() const;
78 
79     /*
80      * Returns count of sequences that are drawn on the widget by taking into account
81      * collapsed rows.
82      */
83     int getViewRowCount() const;
84 
85     int getRowIndex(const int num) const;
86 
87     bool isAlignmentEmpty() const;
88 
89     bool isPosInRange(int position) const;
90     bool isSeqInRange(int rowNumber) const;
91     bool isInRange(const QPoint &point) const;
92 
93     /** Returns true if the given rectangle within visible [rows x columns] grid range. */
94     bool isInRange(const QRect &rect) const;
95 
96     QPoint boundWithVisibleRange(const QPoint &point) const;
97 
98     /** Returns rectangle bounded with the visible width/height range. */
99     QRect boundWithVisibleRange(const QRect &rect) const;
100 
101     bool isVisible(const QPoint &p, bool countClipped) const;
102     bool isPositionVisible(int pos, bool countClipped) const;
103     bool isRowVisible(int rowNumber, bool countClipped) const;
104 
105     /**
106      * Sets new selection from the given rect.
107      * Bounds the rect with the visible area to ensure rect is within view bounds.
108      * This method does not update selection directly but calls 'setSelection()' with a new selection instance.
109      */
110     void setSelectionRect(const QRect &newSelectionRect);
111 
112     virtual void moveSelection(int dx, int dy, bool allowSelectionResize = false);
113 
adjustReferenceLength(U2OpStatus & os)114     virtual void adjustReferenceLength(U2OpStatus &os) {
115         Q_UNUSED(os);
116     }
117 
118     /** Returns list of selected MA row indexes. */
119     QList<int> getSelectedMaRowIndexes() const;
120 
121     /** Returns MA row index of the top-most selected view row or -1 if selection is empty. */
122     int getTopSelectedMaRow() const;
123 
124     QString getCopyFormattedAlgorithmId() const;
125 
126     virtual void deleteCurrentSelection();
127 
128     /**
129      * Shifts currently selected region to @shift.
130      * If @shift > 0, the region is moved to the right and "true" is returned.
131      * If @shift <= 0, the region is moved to the left only for the available number
132      * of columns (i.e. the columns with gaps). The returned value specifies
133      * whether the region was actually moved in this case.
134      */
135     bool shiftSelectedRegion(int shift);
136 
137     void centerPos(const QPoint &point);
138     void centerPos(int pos);
139 
140     QFont getFont() const;
141 
142     void onVisibleRangeChanged();
143 
144     bool isAlignmentLocked() const;
145 
146     void drawVisibleContent(QPainter &painter);
147 
148     bool drawContent(QPainter &painter, const U2Region &columns, const QList<int> &maRows, int xStart, int yStart);
149 
150     MsaColorScheme *getCurrentColorScheme() const;
151     MsaHighlightingScheme *getCurrentHighlightingScheme() const;
152     bool getUseDotsCheckedState() const;
153 
154     QAction *getReplaceCharacterAction() const;
155 
156 public slots:
157     void sl_changeColorSchemeOutside(const QString &id);
158     void sl_delCurrentSelection();
159     void sl_changeCopyFormat(const QString &formatId);
160 
161 protected slots:
162     void sl_changeColorScheme();
163     void sl_fillCurrentSelectionWithGaps();
164 
165     void sl_alignmentChanged(const MultipleAlignment &ma, const MaModificationInfo &modInfo);
166 
167     void sl_completeUpdate();
168     void sl_completeRedraw();
169 
170     virtual void sl_updateActions() = 0;
171 
172     void sl_triggerUseDots();
173     void sl_useDots();
174 
175     void sl_registerCustomColorSchemes();
176     void sl_colorSchemeFactoryUpdated();
177     void sl_setDefaultColorScheme();
178     void sl_changeHighlightScheme();
179 
180     void sl_replaceSelectedCharacter();
181     void sl_changeSelectionColor();
182     virtual void sl_modelChanged();
183 
184 private slots:
185     void sl_hScrollBarActionPerformed();
186     void sl_onSelectionChanged(const MaEditorSelection &newSelection, const MaEditorSelection &oldSelection);
187 
188 private:
189     void setBorderCursor(const QPoint &p);
190     void moveBorder(const QPoint &p);
191 
192     int shiftRegion(int shift);
193     QList<U2MsaGap> findRemovableGapColumns(int &shift);
194     QList<U2MsaGap> findCommonGapColumns(int &numOfColumns);
195     U2MsaGap addTrailingGapColumns(int count);
196     QList<U2MsaGap> findRestorableGapColumns(const int shift);
197 
198     /**
199      * Restores view selection using cached MA selection.
200      * If the original selection can't be restored moves the selection to the top-left corner of the original.
201      */
202     void restoreViewSelectionFromMaSelection();
203 
204 signals:
205     void si_selectionChanged(const QStringList &selectedRows);
206     void si_highlightingChanged();
207     void si_visibleRangeChanged(QStringList visibleSequences, int reqHeight);
208     void si_visibleRangeChanged();
209     void si_startMaChanging();
210     void si_stopMaChanging(bool msaUpdated);
211     void si_copyFormattedChanging(bool enabled);
212     void si_collapsingModeChanged();
213 
214 protected:
215     void resizeEvent(QResizeEvent *event);
216     void paintEvent(QPaintEvent *event);
217     void wheelEvent(QWheelEvent *event);
218     void mousePressEvent(QMouseEvent *event);
219     void mouseReleaseEvent(QMouseEvent *event);
220     void mouseMoveEvent(QMouseEvent *event);
221 
222     void keyPressEvent(QKeyEvent *);
223     void keyReleaseEvent(QKeyEvent *);
224 
225     virtual void initRenderer() = 0;
226     virtual void drawBackground(QPainter &p);
227 
228     /**
229      * Inserts a region consisting of gaps only before the selection. The inserted region width
230      * is specified by @countOfGaps parameter if 0 < @countOfGaps, its height is equal to the
231      * current selection's height.
232      *
233      * If there is no selection in MSA then the method does nothing.
234      *
235      * If -1 == @countOfGaps then the inserting region width is equal to
236      * the selection's width. If 1 > @countOfGaps and -1 != @countOfGaps then nothing happens.
237      */
238     void insertGapsBeforeSelection(int countOfGaps = -1);
239 
240     /**
241      * Reverse operation for @insertGapsBeforeSelection( ),
242      * removes the region preceding the selection if it consists of gaps only.
243      *
244      * If there is no selection in MSA then the method does nothing.
245      *
246      * @countOfGaps specifies maximum width of the removed region.
247      * If -1 == @countOfGaps then count of removed gap columns is equal to
248      * the selection width. If 1 > @countOfGaps and -1 != @countOfGaps then nothing happens.
249      */
250     void removeGapsPrecedingSelection(int countOfGaps = -1);
251 
252     /*
253      * Interrupts the tracking of MSA modifications caused by a region shifting,
254      * also stops shifting. The method is used to keep consistence of undo/redo stack.
255      */
256     void cancelShiftTracking();
257 
258     void drawAll();
259 
260     void updateColorAndHighlightSchemes();
261 
262     void initColorSchemes(MsaColorSchemeFactory *defaultColorSchemeFactory);
263 
264     void registerCommonColorSchemes();
265 
266     void initHighlightSchemes(MsaHighlightingSchemeFactory *hsf);
267 
268     MsaColorSchemeFactory *getDefaultColorSchemeFactory() const;
269     MsaHighlightingSchemeFactory *getDefaultHighlightingSchemeFactory() const;
270 
271     virtual void getColorAndHighlightingIds(QString &csid, QString &hsid);
272     void applyColorScheme(const QString &id);
273 
274     void processCharacterInEditMode(QKeyEvent *e);
275     void processCharacterInEditMode(char newCharacter);
276     void replaceChar(char newCharacter);
insertChar(char)277     virtual void insertChar(char) {
278     }
279     virtual bool isCharacterAcceptable(const QString &text) const;
280     virtual const QString &getInacceptableCharacterErrorMessage() const;
281 
282     void deleteOldCustomSchemes();
283 
284     /*
285      * Update collapse model on alignment modification.
286      * Note, that we have collapse model regardless if collapsing mode is enabled or not.
287      * In the disabled collapsing mode the collapse model is 'flat': 1 view row = 1 MA row.
288      */
289     virtual void updateCollapseModel(const MaModificationInfo &maModificationInfo);
290 
291 public:
292     enum MaMode {
293         ViewMode,
294         ReplaceCharMode,
295         InsertCharMode
296     };
297 
298     /** Returns current mode of the sequence area: viewing or editing. */
299     MaMode getMode() const;
300 
301     /** Swithes sequence area into the ViewMode. */
302     void exitFromEditCharacterMode();
303 
304 protected:
305     MaEditor *const editor;
306     MaEditorWgt *const ui;
307 
308     MsaColorScheme *colorScheme;
309     MsaHighlightingScheme *highlightingScheme;
310 
311     GScrollBar *shBar;
312     GScrollBar *svBar;
313     QRubberBand *rubberBand;
314     bool showRubberBandOnSelection;
315 
316     SequenceAreaRenderer *renderer;
317 
318     QPixmap *cachedView;
319     bool completeRedraw;
320 
321     MaMode maMode;
322     QTimer editModeAnimationTimer;
323     QColor selectionColor;
324 
325     bool editingEnabled;
326     bool shifting;
327     bool selecting;
328     Qt::MouseButton prevPressedButton;
329 
330     /* Last mouse press point. Global window coordinates. */
331     QPoint mousePressEventPoint;
332     /*
333      * Last mouse press point in view rows/columns coordinates.
334      * May be out of range if clicked out of the view/rows range.
335      */
336     QPoint mousePressViewPos;
337 
338     /** Selected MA row ids within the current view selection. */
339     QList<qint64> selectedMaRowIds;
340 
341     /** Selected MA row columns within the current view selection. */
342     U2Region selectedColumns;
343 
344     int maVersionBeforeShifting;
345     SelectionModificationHelper::MovableSide movableBorder;
346 
347     QList<U2MsaGap> ctrlModeGapModel;
348     qint64 lengthOnMousePress;
349 
350     QAction *replaceCharacterAction;
351     QAction *fillWithGapsinsSymAction;
352 
353 public:
354     QAction *useDotsAction;
355 
356     QList<QAction *> colorSchemeMenuActions;
357     QList<QAction *> customColorSchemeMenuActions;
358     QList<QAction *> highlightingSchemeMenuActions;
359 
360 protected:
361     // The member is intended for tracking MSA changes (handling U2UseCommonUserModStep objects)
362     // that does not fit into one method, e.g. shifting MSA region with mouse.
363     // If the changing action fits within one method it's recommended using
364     // the U2UseCommonUserModStep object explicitly.
365     MsaEditorUserModStepController changeTracker;
366 
367     static const QChar emDash;
368 };
369 
370 }  // namespace U2
371 
372 #endif  // _U2_MA_EDITOR_SEQUENCE_AREA_
373