hydrogen 1.2.3
AutomationPathView.cpp
Go to the documentation of this file.
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 * Copyright(c) 2008-2024 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY, without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see https://www.gnu.org/licenses
20 *
21 */
22
23#include "AutomationPathView.h"
26#include "../HydrogenApp.h"
27#include "../Skin.h"
28
29using namespace H2Core;
30
32 : QWidget(parent),
34 m_nGridWidth(16),
35 m_nMarginHeight(4),
36 m_bIsHolding(false),
37 m_fTick( 0 )
38{
39 setFocusPolicy( Qt::ClickFocus );
42
44
45 _path = nullptr;
46 autoResize();
47
48 qreal pixelRatio = devicePixelRatio();
49 m_pBackgroundPixmap = new QPixmap( width() * pixelRatio,
50 height() * pixelRatio );
51 m_pBackgroundPixmap->setDevicePixelRatio( pixelRatio );
53}
54
61
68
69
71{
72 if ( path == _path ) {
73 return;
74 }
75 _path = path;
76
77 if( _path ) {
78 _selectedPoint = path->end();
79 }
80
81 if ( bUpdate ) {
83 update();
84 }
85}
86
87// Make sure we have the current automation path
89{
90 auto pSong = Hydrogen::get_instance()->getSong();
91 if ( pSong != nullptr ) {
92 setAutomationPath( pSong->getVelocityAutomationPath(), false );
93 } else {
94 setAutomationPath( nullptr, false );
95 }
96}
97
99{
100 if ( ( SONG_EDITOR_MIN_GRID_WIDTH <= width ) && ( SONG_EDITOR_MAX_GRID_WIDTH >= width ) ) {
101 m_nGridWidth = width;
102 autoResize();
104 update();
105 }
106}
107
108
112QPoint AutomationPathView::translatePoint(float x,float y) const
113{
114 return translatePoint(std::make_pair(x, y));
115}
116
117
121QPoint AutomationPathView::translatePoint(const std::pair<float,float> &p) const
122{
123 int contentHeight = height() - 2* m_nMarginHeight;
124
125 return QPoint(
127 m_nMarginHeight + contentHeight * ((_path->get_max()-p.second)/(_path->get_max()-_path->get_min()))
128 );
129}
130
131
135bool AutomationPathView::checkBounds(QMouseEvent *event) const
136{
137 return event->x() > SongEditor::nMargin
138 && event->y() > m_nMarginHeight
139 && event->y() < height()-m_nMarginHeight;
140}
141
142
146std::pair<const float, float> AutomationPathView::locate(QMouseEvent *event) const
147{
148 int contentHeight = height() - 2* m_nMarginHeight;
149
150 float x = (event->x() - SongEditor::nMargin) / (float)m_nGridWidth;
151 float y = ((contentHeight-event->y()+m_nMarginHeight)/(float)contentHeight)
152 * (_path->get_max() - _path->get_min()) + _path->get_min();
153
154 return std::pair<const float,float>(x,y);
155}
156
158 m_fTick = fTick;
159 update();
160}
161
166{
167
168 if (!isVisible()) {
169 return;
170 }
171
172 qreal pixelRatio = devicePixelRatio();
173 if ( pixelRatio != m_pBackgroundPixmap->devicePixelRatio() ||
174 width() * pixelRatio != m_pBackgroundPixmap->width() ||
175 height() * pixelRatio != m_pBackgroundPixmap->height() ) {
177 }
178
179 QPainter painter( this );
180 painter.drawPixmap( ev->rect(), *m_pBackgroundPixmap,
181 QRectF( pixelRatio * ev->rect().x(),
182 pixelRatio * ev->rect().y(),
183 pixelRatio * ev->rect().width(),
184 pixelRatio * ev->rect().height() ) );
185
186 // Draw playhead
187 //
188 // Using the grid width of the song editor over class' own one is
189 // crucial in order to keep the full-height playhead in sync.
190 auto pSongEditorPanel = HydrogenApp::get_instance()->getSongEditorPanel();
191 if ( m_fTick != -1 && pSongEditorPanel != nullptr ) {
192 int nOffset = Skin::getPlayheadShaftOffset();
193 int nX = static_cast<int>( static_cast<float>(SongEditor::nMargin) + 1 +
194 m_fTick *
195 static_cast<float>(pSongEditorPanel->getSongEditor()->
196 getGridWidth()) -
197 static_cast<float>(Skin::nPlayheadWidth) / 2 );
198 Skin::setPlayheadPen( &painter, false );
199 painter.drawLine( nX + nOffset, 0, nX + nOffset, height() );
200 }
201
202}
203
205
208
209 QColor backgroundColor =
210 pPref->getColorTheme()->m_songEditor_automationBackgroundColor;
211 QColor automationLineColor =
212 pPref->getColorTheme()->m_songEditor_automationLineColor;
213 QColor nodeColor = pPref->getColorTheme()->m_songEditor_automationNodeColor;
214 QColor textColor = pPref->getColorTheme()->m_songEditor_textColor;
215
216 // Resize pixmap if pixel ratio has changed
217 qreal pixelRatio = devicePixelRatio();
218 if ( m_pBackgroundPixmap->devicePixelRatio() != pixelRatio ||
219 width() != m_pBackgroundPixmap->width() ||
220 height() != m_pBackgroundPixmap->height() ) {
221 delete m_pBackgroundPixmap;
222 m_pBackgroundPixmap = new QPixmap( width() * pixelRatio , height() * pixelRatio );
223 m_pBackgroundPixmap->setDevicePixelRatio( pixelRatio );
224 }
225
226 m_pBackgroundPixmap->fill( backgroundColor );
227
228 QPainter painter( m_pBackgroundPixmap );
229 painter.setRenderHint(QPainter::Antialiasing);
230
231 // Border
232 painter.setPen( Qt::black );
233 painter.drawLine( 0, 0, width(), 0 );
234
235 QPen rulerPen(Qt::DotLine);
236 rulerPen.setColor( textColor );
237 painter.setPen(rulerPen);
238
239 /* Paint min, max */
240 painter.drawLine(0, m_nMarginHeight, width(), m_nMarginHeight);
241 painter.drawLine(0, height()-m_nMarginHeight, width(), height()-m_nMarginHeight);
242
243 if (!_path) {
244 return;
245 }
246
247 /* Paint default */
248 QPoint def = translatePoint(0, _path->get_default());
249 painter.drawLine(0, def.y(), width(), def.y());
250
251 QPen linePen( automationLineColor );
252 linePen.setWidth(2);
253 painter.setPen(linePen);
254
255 if (_path->empty()) {
256 QPoint p = translatePoint(0,_path->get_default());
257
258 painter.drawLine(0, p.y(), width(), p.y());
259 } else {
260 std::pair<float, float> firstPoint = *_path->begin();
261 QPoint lastPoint = translatePoint(0,firstPoint.second);
262 lastPoint.setX(0);
263
264 for (auto point : *_path) {
265 QPoint current = translatePoint(point);
266 painter.drawLine(lastPoint, current);
267 lastPoint = current;
268 }
269 QPoint last(width(), lastPoint.y());
270 painter.drawLine(lastPoint, last);
271 }
272
273
274 QPen circlePen( nodeColor );
275 circlePen.setWidth(1);
276 painter.setPen(circlePen);
277 painter.setBrush(QBrush( pPref->getColorTheme()->m_windowColor ));
278
279 for (auto point : *_path) {
280
281 QPoint center = translatePoint(point);
282 painter.drawEllipse(center, 3, 3);
283
284 }
285}
286
287
296{
298
299 if (! checkBounds(event) || !_path) {
300 return;
301 }
302
303 auto p = locate(event);
304 float x = p.first;
305 float y = p.second;
306
308 if (_selectedPoint == _path->end()) {
309 _path->add_point(x, y);
311
312 m_bPointAdded = true;
313 } else {
315 m_fOriginX = x;
316 m_fOriginY = y;
317 m_bPointAdded = false;
318 }
320
322 update();
323
324 m_bIsHolding = true;
325
326 emit valueChanged();
327}
328
329
336{
338 m_bIsHolding = false;
339
340 if (! checkBounds(event) || !_path) {
341 return;
342 }
343
344 auto p = locate(event);
345 float x = p.first;
346 float y = p.second;
347 if (m_bPointAdded) {
348 emit pointAdded(x, y);
349 } else {
350 emit pointMoved(m_fOriginX, m_fOriginY, x, y);
351 }
352
353 emit valueChanged();
354}
355
356
363{
365 if (! checkBounds(event) || !_path) {
366 return;
367 }
368
369 auto p = locate(event);
370 float x = p.first;
371 float y = p.second;
372
373 if ( m_bIsHolding && _path && _selectedPoint != _path->end() ) {
376 }
377
379 update();
380}
381
382
389{
391 if ( event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace ) {
392 if ( _path && _selectedPoint != _path->end() ) {
393 float x = _selectedPoint->first;
394 float y = _selectedPoint->second;
397
399
400 emit pointRemoved( x, y );
402 update();
403 emit valueChanged();
404
405 event->accept();
406 return;
407 }
408 }
409
410 event->ignore();
411}
412
413
static const uint SONG_EDITOR_MAX_GRID_WIDTH
Definition SongEditor.h:55
static const uint SONG_EDITOR_MIN_GRID_WIDTH
Definition SongEditor.h:54
AutomationPathView(QWidget *parent=nullptr)
float m_fTick
< Point that is being dragged
void pointMoved(float ox, float oy, float tx, float ty)
H2Core::AutomationPath * _path
bool checkBounds(QMouseEvent *event) const
Check if user clicked within area inside margins.
bool m_bPointAdded
< Whether any points are being dragged
void mouseReleaseEvent(QMouseEvent *event) override
Handler for releasing mouse button.
void pointAdded(float x, float y)
void updatePosition(float fTick)
void mouseMoveEvent(QMouseEvent *event) override
Handler for mouse moves.
void keyPressEvent(QKeyEvent *event) override
Handler for key presses.
void onPreferencesChanged(H2Core::Preferences::Changes changes)
int m_nMarginHeight
< Width of song grid cell size - in order to properly align AutomationPathView and SongEditor
void mousePressEvent(QMouseEvent *event) override
Handle mouse click.
void pointRemoved(float x, float y)
QPoint translatePoint(float x, float y) const
Locate path point on a wdiget surface.
int getGridWidth() const noexcept
H2Core::AutomationPath::iterator _selectedPoint
< Original position of selected point
void paintEvent(QPaintEvent *event) override
Repaint widget.
std::pair< const float, float > locate(QMouseEvent *) const
Locate clicked point on a path.
float m_fOriginY
< Original position of selected point
float m_fOriginX
< Whether a new point was added during mouse move
void autoResize()
Resize widget to fit everything.
void setGridWidth(int width)
void setAutomationPath(H2Core::AutomationPath *path, bool bUpdate=true)
int m_nMaxPatternSequence
< Height of top and bottom margins
float get_default() const noexcept
bool empty() const noexcept
float get_max() const noexcept
void remove_point(float x)
Remove point from path.
iterator move(iterator &in, float x, float y)
Move point to other location.
iterator find(float x)
Find point near specific location.
void add_point(float x, float y)
Add a point to path.
float get_min() const noexcept
std::shared_ptr< Song > getSong() const
Get the current song.
Definition Hydrogen.h:122
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:83
void setIsModified(bool bIsModified)
Wrapper around Song::setIsModified() that checks whether a song is set.
Manager for User Preferences File (singleton)
Definition Preferences.h:78
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
int getMaxBars() const
Changes
Bitwise or-able options showing which part of the Preferences were altered using the PreferencesDialo...
@ Colors
At least one of the colors has changed.
static HydrogenApp * get_instance()
Returns the instance of HydrogenApp class.
void preferencesChanged(H2Core::Preferences::Changes changes)
Propagates a change in the Preferences through the GUI.
SongEditorPanel * getSongEditorPanel()
static constexpr int nPlayheadWidth
Definition Skin.h:77
static void setPlayheadPen(QPainter *p, bool bHovered=false)
Definition Skin.cpp:190
static int getPlayheadShaftOffset()
Definition Skin.h:79
static constexpr int nMargin
Definition SongEditor.h:106