2 * piano_roll.cpp - implementation of piano-roll which is used for actual
5 * Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
6 * Copyright (c) 2008 Andrew Kelley <superjoe30/at/gmail/dot/com>
8 * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
20 * You should have received a copy of the GNU General Public
21 * License along with this program (see COPYING); if not, write to the
22 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301 USA.
27 #include <QtGui/QApplication>
28 #include <QtGui/QButtonGroup>
29 #include <QtGui/QClipboard>
30 #include <QtGui/QKeyEvent>
31 #include <QtGui/QLabel>
32 #include <QtGui/QLayout>
33 #include <QtGui/QMdiArea>
34 #include <QtGui/QPainter>
35 #include <QtGui/QScrollBar>
36 #include <QtGui/QStyleOption>
37 #include <QtGui/QWheelEvent>
39 #include <QSignalMapper>
47 #include "piano_roll.h"
48 #include "bb_track_container.h"
49 #include "Clipboard.h"
52 #include "DetuningHelper.h"
54 #include "gui_templates.h"
55 #include "InstrumentTrack.h"
56 #include "MainWindow.h"
61 #include "pixmap_button.h"
63 #include "song_editor.h"
64 #include "templates.h"
65 #include "text_float.h"
67 #include "tool_button.h"
71 typedef AutomationPattern::timeMap timeMap
;
74 extern Keys whiteKeys
[]; // defined in piano_widget.cpp
78 const int INITIAL_PIANOROLL_WIDTH
= 840;
79 const int INITIAL_PIANOROLL_HEIGHT
= 480;
81 const int SCROLLBAR_SIZE
= 16;
82 const int PIANO_X
= 0;
84 const int WHITE_KEY_WIDTH
= 64;
85 const int BLACK_KEY_WIDTH
= 41;
86 const int WHITE_KEY_SMALL_HEIGHT
= 18;
87 const int WHITE_KEY_BIG_HEIGHT
= 24;
88 const int BLACK_KEY_HEIGHT
= 16;
89 const int C_KEY_LABEL_X
= WHITE_KEY_WIDTH
- 19;
90 const int KEY_LINE_HEIGHT
= 12;
91 const int OCTAVE_HEIGHT
= KEY_LINE_HEIGHT
* KeysPerOctave
; // = 12 * 12;
93 const int NOTE_EDIT_RESIZE_BAR
= 6;
94 const int NOTE_EDIT_MIN_HEIGHT
= 50;
95 const int KEY_AREA_MIN_HEIGHT
= 100;
96 const int PR_BOTTOM_MARGIN
= SCROLLBAR_SIZE
;
97 const int PR_TOP_MARGIN
= 48;
98 const int PR_RIGHT_MARGIN
= SCROLLBAR_SIZE
;
101 // width of area used for resizing (the grip at the end of a note)
102 const int RESIZE_AREA_WIDTH
= 4;
104 // width of line for setting volume/panning of note
105 const int NE_LINE_WIDTH
= 3;
107 // key where to start
108 const int INITIAL_START_KEY
= Key_C
+ Octave_3
* KeysPerOctave
;
110 // number of each note to provide in quantization and note lengths
111 const int NUM_EVEN_LENGTHS
= 6;
112 const int NUM_TRIPLET_LENGTHS
= 5;
116 QPixmap
* pianoRoll::s_whiteKeySmallPm
= NULL
;
117 QPixmap
* pianoRoll::s_whiteKeyBigPm
= NULL
;
118 QPixmap
* pianoRoll::s_blackKeyPm
= NULL
;
119 QPixmap
* pianoRoll::s_toolDraw
= NULL
;
120 QPixmap
* pianoRoll::s_toolErase
= NULL
;
121 QPixmap
* pianoRoll::s_toolSelect
= NULL
;
122 QPixmap
* pianoRoll::s_toolMove
= NULL
;
123 QPixmap
* pianoRoll::s_toolOpen
= NULL
;
125 // used for drawing of piano
126 pianoRoll::pianoRollKeyTypes
pianoRoll::prKeyOrder
[] =
128 PR_WHITE_KEY_SMALL
, PR_BLACK_KEY
, PR_WHITE_KEY_BIG
, PR_BLACK_KEY
,
129 PR_WHITE_KEY_SMALL
, PR_WHITE_KEY_SMALL
, PR_BLACK_KEY
, PR_WHITE_KEY_BIG
,
130 PR_BLACK_KEY
, PR_WHITE_KEY_BIG
, PR_BLACK_KEY
, PR_WHITE_KEY_SMALL
134 const int DEFAULT_PR_PPT
= KEY_LINE_HEIGHT
* DefaultStepsPerTact
;
137 pianoRoll::pianoRoll() :
138 m_nemStr( QVector
<QString
>() ),
139 m_noteEditMenu( NULL
),
140 m_signalMapper( NULL
),
146 m_recording( false ),
147 m_currentNote( NULL
),
148 m_action( ActionNone
),
149 m_noteEditMode( NoteEditVolume
),
150 m_moveBoundaryLeft( 0 ),
151 m_moveBoundaryTop( 0 ),
152 m_moveBoundaryRight( 0 ),
153 m_moveBoundaryBottom( 0 ),
155 m_mouseDownTick( 0 ),
158 m_oldNotesEditHeight( 100 ),
159 m_notesEditHeight( 100 ),
160 m_ppt( DEFAULT_PR_PPT
),
161 m_lenOfNewNotes( midiTime( 0, DefaultTicksPerTact
/4 ) ),
162 m_lastNoteVolume( DefaultVolume
),
163 m_lastNotePanning( DefaultPanning
),
164 m_startKey( INITIAL_START_KEY
),
166 m_editMode( ModeDraw
),
167 m_mouseDownLeft( false ),
168 m_mouseDownRight( false ),
169 m_scrollBack( false )
171 // gui names of edit modes
172 m_nemStr
.push_back( tr( "Note Volume" ) );
173 m_nemStr
.push_back( tr( "Note Panning" ) );
175 m_signalMapper
= new QSignalMapper( this );
176 m_noteEditMenu
= new QMenu( this );
177 m_noteEditMenu
->clear();
178 for( int i
=0; i
<m_nemStr
.size(); ++i
)
180 QAction
* act
= new QAction( m_nemStr
.at(i
), this );
181 connect( act
, SIGNAL(triggered()), m_signalMapper
, SLOT(map()) );
182 m_signalMapper
->setMapping( act
, i
);
183 m_noteEditMenu
->addAction( act
);
185 connect( m_signalMapper
, SIGNAL(mapped(int)),
186 this, SLOT(changeNoteEditMode(int)) );
189 if( s_whiteKeySmallPm
== NULL
)
191 s_whiteKeySmallPm
= new QPixmap( embed::getIconPixmap(
192 "pr_white_key_small" ) );
194 if( s_whiteKeyBigPm
== NULL
)
196 s_whiteKeyBigPm
= new QPixmap( embed::getIconPixmap(
197 "pr_white_key_big" ) );
199 if( s_blackKeyPm
== NULL
)
201 s_blackKeyPm
= new QPixmap( embed::getIconPixmap(
204 if( s_toolDraw
== NULL
)
206 s_toolDraw
= new QPixmap( embed::getIconPixmap(
209 if( s_toolErase
== NULL
)
211 s_toolErase
= new QPixmap( embed::getIconPixmap(
214 if( s_toolSelect
== NULL
)
216 s_toolSelect
= new QPixmap( embed::getIconPixmap(
219 if( s_toolMove
== NULL
)
221 s_toolMove
= new QPixmap( embed::getIconPixmap(
224 if( s_toolOpen
== NULL
)
226 s_toolOpen
= new QPixmap( embed::getIconPixmap(
230 setAttribute( Qt::WA_OpaquePaintEvent
, true );
233 m_timeLine
= new timeLine( WHITE_KEY_WIDTH
, 32, m_ppt
,
234 engine::getSong()->getPlayPos(
235 song::Mode_PlayPattern
),
236 m_currentPosition
, this );
237 connect( this, SIGNAL( positionChanged( const midiTime
& ) ),
238 m_timeLine
, SLOT( updatePosition( const midiTime
& ) ) );
239 connect( m_timeLine
, SIGNAL( positionChanged( const midiTime
& ) ),
240 this, SLOT( updatePosition( const midiTime
& ) ) );
242 // update timeline when in record-accompany mode
243 connect( engine::getSong()->getPlayPos( song::Mode_PlaySong
).m_timeLine
,
244 SIGNAL( positionChanged( const midiTime
& ) ),
246 SLOT( updatePositionAccompany( const midiTime
& ) ) );
248 /* connect( engine::getSong()->getPlayPos( song::Mode_PlayBB ).m_timeLine,
249 SIGNAL( positionChanged( const midiTime & ) ),
251 SLOT( updatePositionAccompany( const midiTime & ) ) );*/
254 m_toolBar
= new QWidget( this );
255 m_toolBar
->setFixedHeight( 32 );
256 m_toolBar
->move( 0, 0 );
257 m_toolBar
->setAutoFillBackground( true );
259 pal
.setBrush( m_toolBar
->backgroundRole(),
260 embed::getIconPixmap( "toolbar_bg" ) );
261 m_toolBar
->setPalette( pal
);
263 QHBoxLayout
* tb_layout
= new QHBoxLayout( m_toolBar
);
264 tb_layout
->setMargin( 0 );
265 tb_layout
->setSpacing( 0 );
268 // init control-buttons at the top
270 m_playButton
= new toolButton( embed::getIconPixmap( "play" ),
271 tr( "Play/pause current pattern (Space)" ),
272 this, SLOT( play() ), m_toolBar
);
274 m_recordButton
= new toolButton( embed::getIconPixmap( "record" ),
275 tr( "Record notes from MIDI-device/channel-piano" ),
276 this, SLOT( record() ), m_toolBar
);
277 m_recordAccompanyButton
= new toolButton(
278 embed::getIconPixmap( "record_accompany" ),
279 tr( "Record notes from MIDI-device/channel-piano while playing song or BB track" ),
280 this, SLOT( recordAccompany() ), m_toolBar
);
282 m_stopButton
= new toolButton( embed::getIconPixmap( "stop" ),
283 tr( "Stop playing of current pattern (Space)" ),
284 this, SLOT( stop() ), m_toolBar
);
286 m_playButton
->setWhatsThis(
287 tr( "Click here to play the current pattern. "
288 "This is useful while editing it. The pattern is "
289 "automatically looped when its end is reached." ) );
290 m_recordButton
->setWhatsThis(
291 tr( "Click here to record notes from a MIDI-"
292 "device or the virtual test-piano of the according "
293 "channel-window to the current pattern. When recording "
294 "all notes you play will be written to this pattern "
295 "and you can play and edit them afterwards." ) );
296 m_recordAccompanyButton
->setWhatsThis(
297 tr( "Click here to record notes from a MIDI-"
298 "device or the virtual test-piano of the according "
299 "channel-window to the current pattern. When recording "
300 "all notes you play will be written to this pattern "
301 "and you will hear the song or BB track in the background." ) );
302 m_stopButton
->setWhatsThis(
303 tr( "Click here to stop playback of current pattern." ) );
309 m_leftRightScroll
= new QScrollBar( Qt::Horizontal
, this );
310 m_leftRightScroll
->setSingleStep( 1 );
311 connect( m_leftRightScroll
, SIGNAL( valueChanged( int ) ), this,
312 SLOT( horScrolled( int ) ) );
314 m_topBottomScroll
= new QScrollBar( Qt::Vertical
, this );
315 m_topBottomScroll
->setSingleStep( 1 );
316 m_topBottomScroll
->setPageStep( 20 );
317 connect( m_topBottomScroll
, SIGNAL( valueChanged( int ) ), this,
318 SLOT( verScrolled( int ) ) );
320 // init edit-buttons at the top
321 m_drawButton
= new toolButton( embed::getIconPixmap( "edit_draw" ),
322 tr( "Draw mode (Shift+D)" ),
323 this, SLOT( drawButtonToggled() ),
325 m_drawButton
->setCheckable( true );
326 m_drawButton
->setChecked( true );
328 m_eraseButton
= new toolButton( embed::getIconPixmap( "edit_erase" ),
329 tr( "Erase mode (Shift+E)" ),
330 this, SLOT( eraseButtonToggled() ),
332 m_eraseButton
->setCheckable( true );
334 m_selectButton
= new toolButton( embed::getIconPixmap(
336 tr( "Select mode (Shift+S)" ),
337 this, SLOT( selectButtonToggled() ),
339 m_selectButton
->setCheckable( true );
341 m_detuneButton
= new toolButton( embed::getIconPixmap( "automation"),
342 tr( "Detune mode (Shift+T)" ),
343 this, SLOT( detuneButtonToggled() ),
345 m_detuneButton
->setCheckable( true );
347 QButtonGroup
* tool_button_group
= new QButtonGroup( this );
348 tool_button_group
->addButton( m_drawButton
);
349 tool_button_group
->addButton( m_eraseButton
);
350 tool_button_group
->addButton( m_selectButton
);
351 tool_button_group
->addButton( m_detuneButton
);
352 tool_button_group
->setExclusive( true );
354 m_drawButton
->setWhatsThis(
355 tr( "Click here and draw mode will be activated. In this "
356 "mode you can add, resize and move notes. This "
357 "is the default mode which is used most of the time. "
358 "You can also press 'Shift+D' on your keyboard to "
359 "activate this mode. In this mode, hold Ctrl to "
360 "temporarily go into select mode." ) );
361 m_eraseButton
->setWhatsThis(
362 tr( "Click here and erase mode will be activated. In this "
363 "mode you can erase notes. You can also press "
364 "'Shift+E' on your keyboard to activate this mode." ) );
365 m_selectButton
->setWhatsThis(
366 tr( "Click here and select mode will be activated. "
367 "In this mode you can select notes. Alternatively, "
368 "you can hold Ctrl in draw mode to temporarily use "
370 m_detuneButton
->setWhatsThis(
371 tr( "Click here and detune mode will be activated. "
372 "In this mode you can click a note to open its "
373 "automation detuning. You can utilize this to slide "
374 "notes from one to another. You can also press "
375 "'Shift+T' on your keyboard to activate this mode." ) );
377 m_cutButton
= new toolButton( embed::getIconPixmap( "edit_cut" ),
378 tr( "Cut selected notes (Ctrl+X)" ),
379 this, SLOT( cutSelectedNotes() ),
382 m_copyButton
= new toolButton( embed::getIconPixmap( "edit_copy" ),
383 tr( "Copy selected notes (Ctrl+C)" ),
384 this, SLOT( copySelectedNotes() ),
387 m_pasteButton
= new toolButton( embed::getIconPixmap( "edit_paste" ),
388 tr( "Paste notes from clipboard "
390 this, SLOT( pasteNotes() ),
393 m_cutButton
->setWhatsThis(
394 tr( "Click here and the selected notes will be cut into the "
395 "clipboard. You can paste them anywhere in any pattern "
396 "by clicking on the paste button." ) );
397 m_copyButton
->setWhatsThis(
398 tr( "Click here and the selected notes will be copied into the "
399 "clipboard. You can paste them anywhere in any pattern "
400 "by clicking on the paste button." ) );
401 m_pasteButton
->setWhatsThis(
402 tr( "Click here and the notes from the clipboard will be "
403 "pasted at the first visible measure." ) );
407 QLabel
* zoom_lbl
= new QLabel( m_toolBar
);
408 zoom_lbl
->setPixmap( embed::getIconPixmap( "zoom" ) );
410 // setup zooming-stuff
411 for( int i
= 0; i
< 6; ++i
)
413 m_zoomingModel
.addItem( QString::number( 25 << i
) + "%" );
415 m_zoomingModel
.setValue( m_zoomingModel
.findText( "100%" ) );
416 connect( &m_zoomingModel
, SIGNAL( dataChanged() ),
417 this, SLOT( zoomingChanged() ) );
418 m_zoomingComboBox
= new comboBox( m_toolBar
);
419 m_zoomingComboBox
->setModel( &m_zoomingModel
);
420 m_zoomingComboBox
->setFixedSize( 80, 22 );
422 // setup quantize-stuff
423 QLabel
* quantize_lbl
= new QLabel( m_toolBar
);
424 quantize_lbl
->setPixmap( embed::getIconPixmap( "quantize" ) );
426 m_quantizeModel
.addItem( tr( "Note lock" ) );
427 for( int i
= 0; i
<= NUM_EVEN_LENGTHS
; ++i
)
429 m_quantizeModel
.addItem( "1/" + QString::number( 1 << i
) );
431 for( int i
= 0; i
< NUM_TRIPLET_LENGTHS
; ++i
)
433 m_quantizeModel
.addItem( "1/" + QString::number( (1 << i
) * 3 ) );
435 m_quantizeModel
.addItem( "1/192" );
436 m_quantizeModel
.setValue( m_quantizeModel
.findText( "1/16" ) );
437 m_quantizeComboBox
= new comboBox( m_toolBar
);
438 m_quantizeComboBox
->setModel( &m_quantizeModel
);
439 m_quantizeComboBox
->setFixedSize( 80, 22 );
440 connect( &m_quantizeModel
, SIGNAL( dataChanged() ),
441 this, SLOT( quantizeChanged() ) );
444 // setup note-len-stuff
445 QLabel
* note_len_lbl
= new QLabel( m_toolBar
);
446 note_len_lbl
->setPixmap( embed::getIconPixmap( "note" ) );
448 m_noteLenModel
.addItem( tr( "Last note" ),
449 new PixmapLoader( "edit_draw" ) );
450 const QString pixmaps
[] = { "whole", "half", "quarter", "eighth",
451 "sixteenth", "thirtysecond", "triplethalf",
452 "tripletquarter", "tripleteighth",
453 "tripletsixteenth", "tripletthirtysecond" } ;
455 for( int i
= 0; i
< NUM_EVEN_LENGTHS
; ++i
)
457 m_noteLenModel
.addItem( "1/" + QString::number( 1 << i
),
458 new PixmapLoader( "note_" + pixmaps
[i
] ) );
460 for( int i
= 0; i
< NUM_TRIPLET_LENGTHS
; ++i
)
462 m_noteLenModel
.addItem( "1/" + QString::number( (1 << i
) * 3 ),
463 new PixmapLoader( "note_" + pixmaps
[i
+NUM_EVEN_LENGTHS
] ) );
465 m_noteLenModel
.setValue( 0 );
466 m_noteLenComboBox
= new comboBox( m_toolBar
);
467 m_noteLenComboBox
->setModel( &m_noteLenModel
);
468 m_noteLenComboBox
->setFixedSize( 105, 22 );
469 // Note length change can cause a redraw if Q is set to lock
470 connect( &m_noteLenModel
, SIGNAL( dataChanged() ),
471 this, SLOT( quantizeChanged() ) );
474 tb_layout
->addSpacing( 5 );
475 tb_layout
->addWidget( m_playButton
);
476 tb_layout
->addWidget( m_recordButton
);
477 tb_layout
->addWidget( m_recordAccompanyButton
);
478 tb_layout
->addWidget( m_stopButton
);
479 tb_layout
->addSpacing( 10 );
480 tb_layout
->addWidget( m_drawButton
);
481 tb_layout
->addWidget( m_eraseButton
);
482 tb_layout
->addWidget( m_selectButton
);
483 tb_layout
->addWidget( m_detuneButton
);
484 tb_layout
->addSpacing( 10 );
485 tb_layout
->addWidget( m_cutButton
);
486 tb_layout
->addWidget( m_copyButton
);
487 tb_layout
->addWidget( m_pasteButton
);
488 tb_layout
->addSpacing( 10 );
489 m_timeLine
->addToolButtons( m_toolBar
);
490 tb_layout
->addSpacing( 15 );
491 tb_layout
->addWidget( zoom_lbl
);
492 tb_layout
->addSpacing( 4 );
493 tb_layout
->addWidget( m_zoomingComboBox
);
494 tb_layout
->addSpacing( 10 );
495 tb_layout
->addWidget( quantize_lbl
);
496 tb_layout
->addSpacing( 4 );
497 tb_layout
->addWidget( m_quantizeComboBox
);
498 tb_layout
->addSpacing( 10 );
499 tb_layout
->addWidget( note_len_lbl
);
500 tb_layout
->addSpacing( 4 );
501 tb_layout
->addWidget( m_noteLenComboBox
);
502 tb_layout
->addStretch();
504 // setup our actual window
505 setFocusPolicy( Qt::StrongFocus
);
507 setWindowIcon( embed::getIconPixmap( "piano" ) );
508 setCurrentPattern( NULL
);
510 setMouseTracking( true );
512 setMinimumSize( tb_layout
->minimumSize().width(), 160 );
514 // add us to workspace
515 if( engine::mainWindow()->workspace() )
517 engine::mainWindow()->workspace()->addSubWindow( this );
518 parentWidget()->resize( INITIAL_PIANOROLL_WIDTH
,
519 INITIAL_PIANOROLL_HEIGHT
);
520 parentWidget()->hide();
524 resize( INITIAL_PIANOROLL_WIDTH
, INITIAL_PIANOROLL_HEIGHT
);
528 connect( engine::getSong(), SIGNAL( timeSignatureChanged( int, int ) ),
529 this, SLOT( update() ) );
533 void pianoRoll::changeNoteEditMode( int i
)
535 m_noteEditMode
= (noteEditMode
) i
;
540 pianoRoll::~pianoRoll()
545 void pianoRoll::setCurrentPattern( pattern
* _new_pattern
)
549 m_pattern
->instrumentTrack()->disconnect( this );
552 m_pattern
= _new_pattern
;
553 m_currentPosition
= 0;
554 m_currentNote
= NULL
;
555 m_startKey
= INITIAL_START_KEY
;
557 if( validPattern() == false )
559 //resizeEvent( NULL );
560 setWindowTitle( tr( "Piano-Roll - no pattern" ) );
566 m_leftRightScroll
->setValue( 0 );
568 const NoteVector
& notes
= m_pattern
->notes();
570 if( notes
.empty() == false )
572 // determine the central key so that we can scroll to it
574 for( NoteVector::ConstIterator it
= notes
.begin();
575 it
!= notes
.end(); ++it
)
577 if( ( *it
)->length() > 0 )
579 central_key
+= ( *it
)->key();
584 if( total_notes
> 0 )
586 central_key
= central_key
/ total_notes
-
587 ( KeysPerOctave
* NumOctaves
-
588 m_totalKeysToScroll
) / 2;
589 m_startKey
= tLimit( central_key
, 0,
590 NumOctaves
* KeysPerOctave
);
593 // resizeEvent() does the rest for us (scrolling, range-checking
594 // of start-notes and so on...)
597 connect( m_pattern
->instrumentTrack(),
598 SIGNAL( noteOn( const note
& ) ),
599 this, SLOT( startRecordNote( const note
& ) ) );
600 connect( m_pattern
->instrumentTrack(),
601 SIGNAL( noteOff( const note
& ) ),
602 this, SLOT( finishRecordNote( const note
& ) ) );
604 setWindowTitle( tr( "Piano-Roll - %1" ).arg( m_pattern
->name() ) );
612 void pianoRoll::saveSettings( QDomDocument
& _doc
, QDomElement
& _this
)
614 MainWindow::saveWidgetState( this, _this
);
620 void pianoRoll::loadSettings( const QDomElement
& _this
)
622 MainWindow::restoreWidgetState( this, _this
);
628 inline void pianoRoll::drawNoteRect( QPainter
& _p
, int _x
, int _y
,
629 int _width
, note
* _n
)
640 int volVal
= qMin( 255, (int) (
641 ( (float)( _n
->getVolume() - MinVolume
) ) /
642 ( (float)( MaxVolume
- MinVolume
) ) * 255.0f
) );
643 float rightPercent
= qMin
<float>( 1.0f
,
644 ( (float)( _n
->getPanning() - PanningLeft
) ) /
645 ( (float)( PanningRight
- PanningLeft
) ) * 2.0f
);
647 float leftPercent
= qMin
<float>( 1.0f
,
648 ( (float)( PanningRight
- _n
->getPanning() ) ) /
649 ( (float)( PanningRight
- PanningLeft
) ) * 2.0f
);
651 const QColor
defaultNoteColor( 0x00, 0xAA, 0x00 );
652 QColor col
= defaultNoteColor
;
654 col
= QColor( 0x00, 0xAA, 0x00 );
655 if( _n
->length() < 0 )
658 col
.setRgb( 0, 255, 0 );
659 _p
.fillRect( _x
, _y
, _width
, KEY_LINE_HEIGHT
- 2, col
);
661 else if( _n
->selected() )
663 col
.setRgb( 0x00, 0x40, 0xC0 );
664 _p
.fillRect( _x
, _y
, _width
, KEY_LINE_HEIGHT
- 2, col
);
668 // adjust note to make it a bit faded if it has a lower volume
669 // in stereo using gradients
670 QColor lcol
= QColor::fromHsv( col
.hue(), col
.saturation(),
671 volVal
* leftPercent
);
672 QColor rcol
= QColor::fromHsv( col
.hue(), col
.saturation(),
673 volVal
* rightPercent
);
674 col
= QColor::fromHsv( col
.hue(), col
.saturation(), volVal
);
676 QLinearGradient
gradient( _x
, _y
, _x
+_width
,
677 _y
+KEY_LINE_HEIGHT
);
678 gradient
.setColorAt( 0, lcol
);
679 gradient
.setColorAt( 1, rcol
);
680 _p
.setBrush( gradient
);
681 _p
.setPen( Qt::NoPen
);
682 _p
.drawRect( _x
, _y
, _width
, KEY_LINE_HEIGHT
-1 );
685 // hilighting lines around the note
686 _p
.setPen( Qt::SolidLine
);
687 _p
.setBrush( Qt::NoBrush
);
689 col
= defaultNoteColor
;
690 _p
.setPen( QColor::fromHsv( col
.hue(), col
.saturation(),
691 qMin
<float>( 255, volVal
*1.7f
) ) );
692 _p
.drawLine( _x
, _y
, _x
+ _width
, _y
);
693 _p
.drawLine( _x
, _y
, _x
, _y
+ KEY_LINE_HEIGHT
- 2 );
695 col
= defaultNoteColor
;
696 _p
.setPen( QColor::fromHsv( col
.hue(), col
.saturation(), volVal
/1.7 ) );
697 _p
.drawLine( _x
+ _width
, _y
, _x
+ _width
, _y
+ KEY_LINE_HEIGHT
- 2 );
698 _p
.drawLine( _x
, _y
+ KEY_LINE_HEIGHT
- 2, _x
+ _width
,
699 _y
+ KEY_LINE_HEIGHT
- 2 );
701 // that little tab thing on the end hinting at the user
702 // to resize the note
703 _p
.setPen( defaultNoteColor
.lighter( 200 ) );
706 _p
.drawLine( _x
+ _width
- 3, _y
+ 2, _x
+ _width
- 3,
707 _y
+ KEY_LINE_HEIGHT
- 4 );
709 _p
.drawLine( _x
+ _width
- 1, _y
+ 2, _x
+ _width
- 1,
710 _y
+ KEY_LINE_HEIGHT
- 4 );
711 _p
.drawLine( _x
+ _width
- 2, _y
+ 2, _x
+ _width
- 2,
712 _y
+ KEY_LINE_HEIGHT
- 4 );
718 inline void pianoRoll::drawDetuningInfo( QPainter
& _p
, note
* _n
, int _x
,
721 int middle_y
= _y
+ KEY_LINE_HEIGHT
/ 2;
722 _p
.setPen( QColor( 0xFF, 0xDF, 0x20 ) );
724 timeMap
& map
= _n
->detuning()->automationPattern()->getTimeMap();
725 for( timeMap::ConstIterator it
= map
.begin(); it
!= map
.end(); ++it
)
727 Sint32 pos_ticks
= it
.key();
728 if( pos_ticks
> _n
->length() )
732 int pos_x
= _x
+ pos_ticks
* m_ppt
/ midiTime::ticksPerTact();
734 const float level
= it
.value();
736 int pos_y
= (int)( middle_y
- level
* KEY_LINE_HEIGHT
/ 10 );
738 _p
.drawLine( pos_x
- 1, pos_y
, pos_x
+ 1, pos_y
);
739 _p
.drawLine( pos_x
, pos_y
- 1, pos_x
, pos_y
+ 1 );
746 void pianoRoll::removeSelection()
748 m_selectStartTick
= 0;
750 m_selectStartKey
= 0;
759 void pianoRoll::clearSelectedNotes()
761 if( m_pattern
!= NULL
)
763 // get note-vector of current pattern
764 const NoteVector
& notes
= m_pattern
->notes();
766 // will be our iterator in the following loop
767 NoteVector::ConstIterator it
= notes
.begin();
768 while( it
!= notes
.end() )
770 ( *it
)->setSelected( false );
780 void pianoRoll::closeEvent( QCloseEvent
* _ce
)
782 QApplication::restoreOverrideCursor();
785 parentWidget()->hide();
797 void pianoRoll::shiftSemiTone( int amount
) // shift notes by amount semitones
799 bool useAllNotes
= ! isSelection();
800 const NoteVector
& notes
= m_pattern
->notes();
801 for( NoteVector::ConstIterator it
= notes
.begin(); it
!= notes
.end();
804 // if none are selected, move all notes, otherwise
805 // only move selected notes
806 if( useAllNotes
|| ( *it
)->selected() )
808 ( *it
)->setKey( ( *it
)->key() + amount
);
812 // we modified the song
814 engine::getSongEditor()->update();
821 void pianoRoll::shiftPos( int amount
) //shift notes pos by amount
823 bool useAllNotes
= ! isSelection();
824 const NoteVector
& notes
= m_pattern
->notes();
827 for( NoteVector::ConstIterator it
= notes
.begin(); it
!= notes
.end();
830 // if none are selected, move all notes, otherwise
831 // only move selected notes
832 if( ( *it
)->selected() || (useAllNotes
&& ( *it
)->length() > 0) )
834 // don't let notes go to out of bounds
837 m_moveBoundaryLeft
= ( *it
)->pos();
838 if( m_moveBoundaryLeft
+ amount
< 0 )
840 amount
+= 0 - (amount
+ m_moveBoundaryLeft
);
844 ( *it
)->setPos( ( *it
)->pos() + amount
);
848 // we modified the song
850 engine::getSongEditor()->update();
856 bool pianoRoll::isSelection() const // are any notes selected?
858 const NoteVector
& notes
= m_pattern
->notes();
859 for( NoteVector::ConstIterator it
= notes
.begin(); it
!= notes
.end();
862 if( ( *it
)->selected() )
873 int pianoRoll::selectionCount() const // how many notes are selected?
877 const NoteVector
& notes
= m_pattern
->notes();
878 for( NoteVector::ConstIterator it
= notes
.begin(); it
!= notes
.end();
881 if( ( *it
)->selected() )
892 void pianoRoll::keyPressEvent( QKeyEvent
* _ke
)
894 if( validPattern() && _ke
->modifiers() == Qt::NoModifier
)
896 const int key_num
= PianoView::getKeyFromKeyEvent( _ke
) +
897 ( DefaultOctave
- 1 ) * KeysPerOctave
;
899 if( _ke
->isAutoRepeat() == false && key_num
> -1 )
901 m_pattern
->instrumentTrack()->
902 getPiano()->handleKeyPress( key_num
);
909 if( ( _ke
->modifiers() & Qt::ControlModifier
)
910 && m_action
== ActionNone
)
912 // shift selection up an octave
913 // if nothing selected, shift _everything_
914 shiftSemiTone( +12 );
919 m_topBottomScroll
->setValue(
920 m_topBottomScroll
->value() -
923 // if they are moving notes around or resizing,
924 // recalculate the note/resize position
925 if( m_action
== ActionMoveNote
||
926 m_action
== ActionResizeNote
)
928 dragNotes( m_lastMouseX
, m_lastMouseY
,
935 if( _ke
->modifiers() & Qt::ControlModifier
936 && m_action
== ActionNone
)
938 // shift selection down an octave
939 // if nothing selected, shift _everything_
940 shiftSemiTone( -12 );
945 m_topBottomScroll
->setValue(
946 m_topBottomScroll
->value() +
949 // if they are moving notes around or resizing,
950 // recalculate the note/resize position
951 if( m_action
== ActionMoveNote
||
952 m_action
== ActionResizeNote
)
954 dragNotes( m_lastMouseX
, m_lastMouseY
,
963 if( _ke
->modifiers() & Qt::ControlModifier
&&
964 m_action
== ActionNone
)
967 if( ( m_timeLine
->pos() -= 16 ) < 0 )
969 m_timeLine
->pos().setTicks( 0 );
971 m_timeLine
->updatePosition();
973 else if( _ke
->modifiers() & Qt::ShiftModifier
974 && m_action
== ActionNone
)
977 bool quantized
= ! ( _ke
->modifiers() &
979 int amt
= quantized
? quantization() : 1;
985 m_leftRightScroll
->setValue(
986 m_leftRightScroll
->value() -
989 // if they are moving notes around or resizing,
990 // recalculate the note/resize position
991 if( m_action
== ActionMoveNote
||
992 m_action
== ActionResizeNote
)
994 dragNotes( m_lastMouseX
, m_lastMouseY
,
1004 if( _ke
->modifiers() & Qt::ControlModifier
1005 && m_action
== ActionNone
)
1008 m_timeLine
->pos() += 16;
1009 m_timeLine
->updatePosition();
1011 else if( _ke
->modifiers() & Qt::ShiftModifier
1012 && m_action
== ActionNone
)
1015 bool quantized
= !( _ke
->modifiers() &
1017 int amt
= quantized
? quantization() : 1;
1023 m_leftRightScroll
->setValue(
1024 m_leftRightScroll
->value() +
1025 cm_scrollAmtHoriz
);
1027 // if they are moving notes around or resizing,
1028 // recalculate the note/resize position
1029 if( m_action
== ActionMoveNote
||
1030 m_action
== ActionResizeNote
)
1032 dragNotes( m_lastMouseX
, m_lastMouseY
,
1044 if( _ke
->modifiers() & Qt::ControlModifier
)
1046 copySelectedNotes();
1055 if( _ke
->modifiers() & Qt::ControlModifier
)
1066 if( _ke
->modifiers() & Qt::ControlModifier
)
1077 if( _ke
->modifiers() & Qt::ControlModifier
)
1079 m_selectButton
->setChecked( true );
1090 if( _ke
->modifiers() & Qt::ShiftModifier
)
1092 m_drawButton
->setChecked( true );
1101 if( _ke
->modifiers() & Qt::ShiftModifier
)
1103 m_eraseButton
->setChecked( true );
1112 if( _ke
->modifiers() & Qt::ShiftModifier
)
1114 m_selectButton
->setChecked( true );
1123 if( _ke
->modifiers() & Qt::ShiftModifier
)
1125 m_detuneButton
->setChecked( true );
1133 case Qt::Key_Delete
:
1134 deleteSelectedNotes();
1138 if( engine::getSong()->isPlaying() )
1149 m_timeLine
->pos().setTicks( 0 );
1150 m_timeLine
->updatePosition();
1164 int len
= 1 + _ke
->key() - Qt::Key_0
;
1167 if( _ke
->modifiers() &
1168 ( Qt::ControlModifier
| Qt::KeypadModifier
) )
1170 m_noteLenModel
.setValue( len
);
1172 else if( _ke
->modifiers() & Qt::AltModifier
)
1174 m_quantizeModel
.setValue( len
);
1179 case Qt::Key_Control
:
1180 if( m_editMode
!= ModeSelect
)
1182 m_ctrlMode
= m_editMode
;
1183 m_editMode
= ModeSelect
;
1184 QApplication::changeOverrideCursor(
1185 QCursor( Qt::ArrowCursor
) );
1198 void pianoRoll::keyReleaseEvent( QKeyEvent
* _ke
)
1200 if( validPattern() && _ke
->modifiers() == Qt::NoModifier
)
1202 const int key_num
= PianoView::getKeyFromKeyEvent( _ke
) +
1203 ( DefaultOctave
- 1 ) * KeysPerOctave
;
1205 if( _ke
->isAutoRepeat() == false && key_num
> -1 )
1207 m_pattern
->instrumentTrack()->
1208 getPiano()->handleKeyRelease( key_num
);
1211 switch( _ke
->key() )
1213 case Qt::Key_Control
:
1214 computeSelectedNotes( _ke
->modifiers() &
1216 m_editMode
= m_ctrlMode
;
1226 void pianoRoll::leaveEvent( QEvent
* _e
)
1228 while( QApplication::overrideCursor() != NULL
)
1230 QApplication::restoreOverrideCursor();
1233 QWidget::leaveEvent( _e
);
1239 inline int pianoRoll::noteEditTop() const
1241 return height() - PR_BOTTOM_MARGIN
-
1242 m_notesEditHeight
+ NOTE_EDIT_RESIZE_BAR
;
1248 inline int pianoRoll::noteEditBottom() const
1250 return height() - PR_BOTTOM_MARGIN
;
1256 inline int pianoRoll::noteEditRight() const
1258 return width() - PR_RIGHT_MARGIN
;
1264 inline int pianoRoll::noteEditLeft() const
1266 return WHITE_KEY_WIDTH
;
1272 inline int pianoRoll::keyAreaTop() const
1274 return PR_TOP_MARGIN
;
1280 inline int pianoRoll::keyAreaBottom() const
1282 return height() - PR_BOTTOM_MARGIN
- m_notesEditHeight
;
1288 void pianoRoll::mousePressEvent( QMouseEvent
* _me
)
1290 if( validPattern() == false )
1295 if( m_editMode
== ModeEditDetuning
&& noteUnderMouse() )
1297 noteUnderMouse()->editDetuningPattern();
1301 // if holding control, go to selection mode
1302 if( _me
->modifiers() & Qt::ControlModifier
&& m_editMode
!= ModeSelect
)
1304 m_ctrlMode
= m_editMode
;
1305 m_editMode
= ModeSelect
;
1306 QApplication::changeOverrideCursor(
1307 QCursor( Qt::ArrowCursor
) );
1311 // keep track of the point where the user clicked down
1312 if( _me
->button() == Qt::LeftButton
)
1314 m_moveStartX
= _me
->x();
1315 m_moveStartY
= _me
->y();
1318 if( _me
->y() > keyAreaBottom() && _me
->y() < noteEditTop() )
1320 // resizing the note edit area
1321 m_action
= ActionResizeNoteEditArea
;
1322 m_oldNotesEditHeight
= m_notesEditHeight
;
1326 if( _me
->y() > PR_TOP_MARGIN
)
1328 bool edit_note
= ( _me
->y() > noteEditTop() );
1330 int key_num
= getKey( _me
->y() );
1335 if( x
> WHITE_KEY_WIDTH
)
1337 // set, move or resize note
1339 x
-= WHITE_KEY_WIDTH
;
1341 // get tick in which the user clicked
1342 int pos_ticks
= x
* midiTime::ticksPerTact() / m_ppt
+
1346 // get note-vector of current pattern
1347 const NoteVector
& notes
= m_pattern
->notes();
1349 // will be our iterator in the following loop
1350 NoteVector::ConstIterator it
= notes
.begin();
1352 // loop through whole note-vector...
1353 while( it
!= notes
.end() )
1355 midiTime len
= ( *it
)->length();
1360 // and check whether the user clicked on an
1361 // existing note or an edit-line
1362 if( pos_ticks
>= ( *it
)->pos() &&
1365 ( edit_note
== false &&
1366 pos_ticks
<= ( *it
)->pos() + len
&&
1367 ( *it
)->key() == key_num
)
1369 ( edit_note
== true &&
1370 pos_ticks
<= ( *it
)->pos() +
1372 midiTime::ticksPerTact() /
1382 // first check whether the user clicked in note-edit-
1384 if( edit_note
== true )
1386 // scribble note edit changes
1387 mouseMoveEvent( _me
);
1391 else if( _me
->button() == Qt::LeftButton
&&
1392 m_editMode
== ModeDraw
)
1394 note
* created_new_note
= NULL
;
1395 // did it reach end of vector because
1396 // there's no note??
1397 if( it
== notes
.end() )
1400 pattern::MelodyPattern
);
1402 // then set new note
1404 // clear selection and select this new note
1405 clearSelectedNotes();
1407 // +32 to quanitize the note correctly when placing notes with
1408 // the mouse. We do this here instead of in note.quantized
1409 // because live notes should still be quantized at the half.
1410 midiTime
note_pos( pos_ticks
- ( quantization() / 2 ) );
1411 midiTime
note_len( newNoteLen() );
1413 note
new_note( note_len
, note_pos
, key_num
);
1414 new_note
.setSelected( true );
1415 new_note
.setPanning( m_lastNotePanning
);
1416 new_note
.setVolume( m_lastNoteVolume
);
1417 created_new_note
= m_pattern
->addNote( new_note
);
1419 // reset it so that it can be used for
1420 // ops (move, resize) after this
1423 while( it
!= notes
.end() &&
1424 *it
!= created_new_note
)
1432 m_currentNote
= *it
;
1433 m_lastNotePanning
= ( *it
)->getPanning();
1434 m_lastNoteVolume
= ( *it
)->getVolume();
1435 m_lenOfNewNotes
= ( *it
)->length();
1437 // remember which key and tick we started with
1438 m_mouseDownKey
= m_startKey
;
1439 m_mouseDownTick
= m_currentPosition
;
1443 while( it
!= notes
.end() )
1446 // remember note starting positions
1447 ( *it
)->setOldKey( ( *it
)->key() );
1448 ( *it
)->setOldPos( ( *it
)->pos() );
1449 ( *it
)->setOldLength( ( *it
)->length() );
1451 if( ( *it
)->selected() )
1454 // figure out the bounding box of all the selected notes
1457 m_moveBoundaryLeft
= ( *it
)->pos().getTicks();
1458 m_moveBoundaryRight
= ( *it
)->pos() + ( *it
)->length();
1459 m_moveBoundaryBottom
= ( *it
)->key();
1460 m_moveBoundaryTop
= ( *it
)->key();
1466 m_moveBoundaryLeft
= qMin(
1467 ( *it
)->pos().getTicks(),
1468 m_moveBoundaryLeft
);
1469 m_moveBoundaryRight
= qMax( ( *it
)->pos() +
1471 m_moveBoundaryRight
);
1472 m_moveBoundaryBottom
= qMin( ( *it
)->key(),
1473 m_moveBoundaryBottom
);
1474 m_moveBoundaryTop
= qMax( ( *it
)->key(),
1475 m_moveBoundaryTop
);
1482 // if clicked on an unselected note, remove selection
1483 // and select that new note
1484 if( ! m_currentNote
->selected() )
1486 clearSelectedNotes();
1487 m_currentNote
->setSelected( true );
1488 m_moveBoundaryLeft
= m_currentNote
->pos().getTicks();
1489 m_moveBoundaryRight
= m_currentNote
->pos() + m_currentNote
->length();
1490 m_moveBoundaryBottom
= m_currentNote
->key();
1491 m_moveBoundaryTop
= m_currentNote
->key();
1495 // clicked at the "tail" of the note?
1496 if( pos_ticks
*m_ppt
/midiTime::ticksPerTact() >
1497 ( m_currentNote
->pos() +
1498 m_currentNote
->length() )*m_ppt
/
1499 midiTime::ticksPerTact() -
1500 RESIZE_AREA_WIDTH
&&
1501 m_currentNote
->length() > 0 )
1503 // then resize the note
1504 m_action
= ActionResizeNote
;
1506 // set resize-cursor
1507 QCursor
c( Qt::SizeHorCursor
);
1508 QApplication::setOverrideCursor( c
);
1512 // otherwise move it
1513 m_action
= ActionMoveNote
;
1516 QCursor
c( Qt::SizeAllCursor
);
1517 QApplication::setOverrideCursor( c
);
1519 // if they're holding shift, copy all selected notes
1520 if( //*it != created_new_note &&
1521 _me
->modifiers() & Qt::ShiftModifier
)
1523 // vector to hold new notes until we're through the loop
1524 QVector
<note
> newNotes
;
1526 while( it
!= notes
.end() )
1528 if( ( *it
)->selected() )
1531 note
noteCopy( (note
) **it
);
1532 newNotes
.push_back( noteCopy
);
1537 if( newNotes
.size() != 0 )
1539 //put notes from vector into piano roll
1540 for( int i
=0; i
<newNotes
.size(); ++i
)
1542 note
* newNote
= m_pattern
->addNote( newNotes
[i
] );
1543 newNote
->setSelected( false );
1546 // added new notes, so must update engine, song, etc
1547 engine::getSong()->setModified();
1549 engine::getSongEditor()->update();
1554 testPlayNote( m_currentNote
);
1560 engine::getSong()->setModified();
1562 else if( ( _me
->buttons() == Qt::RightButton
&&
1563 m_editMode
== ModeDraw
) ||
1564 m_editMode
== ModeErase
)
1566 // erase single note
1567 m_mouseDownRight
= true;
1568 if( it
!= notes
.end() )
1570 if( ( *it
)->length() > 0 )
1572 m_pattern
->removeNote( *it
);
1576 ( *it
)->setLength( 0 );
1577 m_pattern
->dataChanged();
1579 engine::getSong()->setModified();
1582 else if( _me
->button() == Qt::LeftButton
&&
1583 m_editMode
== ModeSelect
)
1585 // select an area of notes
1587 m_selectStartTick
= pos_ticks
;
1589 m_selectStartKey
= key_num
;
1591 m_action
= ActionSelectNotes
;
1594 // call mousemove to fix glitch where selection
1595 // appears in wrong spot on mousedown
1596 mouseMoveEvent( _me
);
1601 else if( _me
->y() < keyAreaBottom() )
1603 // clicked on keyboard on the left - play note
1604 m_lastKey
= key_num
;
1605 if( ! m_recording
&& ! engine::getSong()->isPlaying() )
1607 int v
= ( (float) x
) / ( (float) WHITE_KEY_WIDTH
) * 127;
1608 m_pattern
->instrumentTrack()->processInEvent(
1609 midiEvent( MidiNoteOn
, 0, key_num
, v
),
1615 if( _me
->buttons() == Qt::LeftButton
)
1617 // clicked in the box below the keys to the left of note edit area
1618 m_noteEditMode
= (noteEditMode
)(((int)m_noteEditMode
)+1);
1619 if( m_noteEditMode
== NoteEditCount
)
1621 m_noteEditMode
= (noteEditMode
)0;
1625 else if( _me
->buttons() == Qt::RightButton
)
1627 // pop menu asking which one they want to edit
1628 m_noteEditMenu
->popup( mapToGlobal( QPoint( _me
->x(), _me
->y() ) ) );
1637 void pianoRoll::mouseDoubleClickEvent( QMouseEvent
* _me
)
1639 if( validPattern() == false )
1644 // if they clicked in the note edit area, clear selection
1645 if( _me
->x() > noteEditLeft() && _me
->x() < noteEditRight()
1646 && _me
->y() > noteEditTop() && _me
->y() < noteEditBottom() )
1648 clearSelectedNotes();
1655 void pianoRoll::testPlayNote( note
* n
)
1657 m_lastKey
= n
->key();
1659 if( ! n
->isPlaying() && ! m_recording
&&
1660 ! engine::getSong()->isPlaying() )
1662 n
->setIsPlaying( true );
1663 m_pattern
->instrumentTrack()->processInEvent(
1664 midiEvent( MidiNoteOn
, 0, n
->key(),
1665 n
->getVolume() * 127 / 100 ), midiTime() );
1667 midiEvent
evt( MidiMetaEvent
, 0, n
->key(),
1668 panningToMidi( n
->getPanning() ) );
1670 evt
.m_metaEvent
= MidiNotePanning
;
1671 m_pattern
->instrumentTrack()->processInEvent( evt
, midiTime() );
1678 void pianoRoll::pauseTestNotes( bool _pause
)
1680 const NoteVector
& notes
= m_pattern
->notes();
1681 NoteVector::ConstIterator it
= notes
.begin();
1682 while( it
!= notes
.end() )
1684 if( ( *it
)->isPlaying() )
1689 m_pattern
->instrumentTrack()->
1691 midiEvent( MidiNoteOff
, 0,
1692 ( *it
)->key(), 0 ),
1698 ( *it
)->setIsPlaying( false );
1699 testPlayNote( *it
);
1710 void pianoRoll::testPlayKey( int _key
, int _vol
, int _pan
)
1713 m_pattern
->instrumentTrack()->processInEvent(
1714 midiEvent( MidiNoteOff
, 0, m_lastKey
, 0 ),
1717 // remember which one we're playing
1721 m_pattern
->instrumentTrack()->processInEvent(
1722 midiEvent( MidiNoteOn
, 0, _key
, _vol
),
1725 // set panning of newly played key
1726 midiEvent
evt( MidiMetaEvent
, 0, _key
, _pan
);
1727 evt
.m_metaEvent
= MidiNotePanning
;
1733 void pianoRoll::computeSelectedNotes(bool shift
)
1735 if( m_selectStartTick
== 0 &&
1736 m_selectedTick
== 0 &&
1737 m_selectStartKey
== 0 &&
1738 m_selectedKeys
== 0 )
1740 // don't bother, there's no selection
1744 // setup selection-vars
1745 int sel_pos_start
= m_selectStartTick
;
1746 int sel_pos_end
= m_selectStartTick
+m_selectedTick
;
1747 if( sel_pos_start
> sel_pos_end
)
1749 qSwap
<int>( sel_pos_start
, sel_pos_end
);
1752 int sel_key_start
= m_selectStartKey
- m_startKey
+ 1;
1753 int sel_key_end
= sel_key_start
+ m_selectedKeys
;
1754 if( sel_key_start
> sel_key_end
)
1756 qSwap
<int>( sel_key_start
, sel_key_end
);
1759 //int y_base = noteEditTop() - 1;
1760 if( validPattern() == true )
1762 const NoteVector
& notes
= m_pattern
->notes();
1764 for( NoteVector::ConstIterator it
= notes
.begin();
1765 it
!= notes
.end(); ++it
)
1767 // make a new selection unless they're holding shift
1770 ( *it
)->setSelected( false );
1773 Sint32 len_ticks
= ( *it
)->length();
1775 if( len_ticks
== 0 )
1779 else if( len_ticks
< 0 )
1784 const int key
= ( *it
)->key() - m_startKey
+ 1;
1786 Sint32 pos_ticks
= ( *it
)->pos();
1788 // if the selection even barely overlaps the note
1789 if( key
> sel_key_start
&&
1790 key
<= sel_key_end
&&
1791 pos_ticks
+ len_ticks
> sel_pos_start
&&
1792 pos_ticks
< sel_pos_end
)
1794 ( *it
)->setSelected( true );
1806 void pianoRoll::mouseReleaseEvent( QMouseEvent
* _me
)
1808 bool mustRepaint
= false;
1810 if( _me
->button() & Qt::LeftButton
)
1812 m_mouseDownLeft
= false;
1815 if( _me
->button() & Qt::RightButton
)
1817 m_mouseDownRight
= false;
1821 if( _me
->button() & Qt::LeftButton
&&
1822 m_editMode
== ModeSelect
&&
1823 m_action
== ActionSelectNotes
)
1825 // select the notes within the selection rectangle and
1826 // then destroy the selection rectangle
1828 computeSelectedNotes( _me
->modifiers() & Qt::ShiftModifier
);
1831 else if( _me
->button() & Qt::LeftButton
&&
1832 m_action
== ActionMoveNote
)
1834 // we moved one or more notes so they have to be
1835 // moved properly according to new starting-
1836 // time in the note-array of pattern
1838 m_pattern
->rearrangeAllNotes();
1841 if( _me
->button() & Qt::LeftButton
&&
1842 ( m_action
== ActionMoveNote
|| m_action
== ActionResizeNote
) )
1844 // if we only moved one note, deselect it so we can
1845 // edit the notes in the note edit area
1846 if( selectionCount() == 1 )
1848 clearSelectedNotes();
1853 if( validPattern() == true )
1855 // turn off all notes that are playing
1856 const NoteVector
& notes
= m_pattern
->notes();
1858 NoteVector::ConstIterator it
= notes
.begin();
1859 while( it
!= notes
.end() )
1861 if( ( *it
)->isPlaying() )
1863 m_pattern
->instrumentTrack()->
1865 midiEvent( MidiNoteOff
, 0,
1866 ( *it
)->key(), 0 ),
1868 ( *it
)->setIsPlaying( false );
1874 // stop playing keys that we let go of
1875 m_pattern
->instrumentTrack()->processInEvent(
1876 midiEvent( MidiNoteOff
, 0, m_lastKey
, 0 ),
1881 m_currentNote
= NULL
;
1883 m_action
= ActionNone
;
1885 if( m_editMode
== ModeDraw
)
1887 QApplication::restoreOverrideCursor();
1899 void pianoRoll::mouseMoveEvent( QMouseEvent
* _me
)
1901 if( validPattern() == false )
1907 if( m_action
== ActionNone
&& _me
->buttons() == 0 )
1909 if( _me
->y() > keyAreaBottom() && _me
->y() < noteEditTop() )
1911 QApplication::setOverrideCursor(
1912 QCursor( Qt::SizeVerCursor
) );
1916 else if( m_action
== ActionResizeNoteEditArea
)
1918 // change m_notesEditHeight and then repaint
1919 m_notesEditHeight
= tLimit
<int>(
1920 m_oldNotesEditHeight
- ( _me
->y() - m_moveStartY
),
1921 NOTE_EDIT_MIN_HEIGHT
,
1922 height() - PR_TOP_MARGIN
- NOTE_EDIT_RESIZE_BAR
-
1923 PR_BOTTOM_MARGIN
- KEY_AREA_MIN_HEIGHT
);
1928 if( _me
->y() > PR_TOP_MARGIN
|| m_action
!= ActionNone
)
1930 bool edit_note
= ( _me
->y() > noteEditTop() )
1931 && m_action
!= ActionSelectNotes
;
1934 int key_num
= getKey( _me
->y() );
1937 // see if they clicked on the keyboard on the left
1938 if( x
< WHITE_KEY_WIDTH
&& m_action
== ActionNone
1939 && ! edit_note
&& key_num
!= m_lastKey
1940 && _me
->buttons() & Qt::LeftButton
)
1942 // clicked on a key, play the note
1943 testPlayKey( key_num
,
1944 ( (float) x
) / ( (float) WHITE_KEY_WIDTH
) * 127,
1950 x
-= WHITE_KEY_WIDTH
;
1952 if( _me
->buttons() & Qt::LeftButton
1953 && m_editMode
== ModeDraw
1954 && (m_action
== ActionMoveNote
|| m_action
== ActionResizeNote
) )
1956 // handle moving notes and resizing them
1957 bool replay_note
= key_num
!= m_lastKey
1958 && m_action
== ActionMoveNote
;
1965 dragNotes(_me
->x(), _me
->y(), _me
->modifiers() & Qt::AltModifier
);
1967 if( replay_note
&& m_action
== ActionMoveNote
)
1969 pauseTestNotes( false );
1972 else if( ( edit_note
== true || m_action
== ActionChangeNoteProperty
) &&
1973 _me
->buttons() & Qt::LeftButton
)
1975 // editing note properties
1977 // Change notes within a certain pixel range of where
1978 // the mouse cursor is
1979 int pixel_range
= 14;
1981 // convert to ticks so that we can check which notes
1983 int ticks_start
= (x
-pixel_range
/2) *
1984 midiTime::ticksPerTact() / m_ppt
+ m_currentPosition
;
1985 int ticks_end
= (x
+pixel_range
/2) *
1986 midiTime::ticksPerTact() / m_ppt
+ m_currentPosition
;
1988 // get note-vector of current pattern
1989 const NoteVector
& notes
= m_pattern
->notes();
1991 // determine what volume/panning to set note to
1992 volume_t vol
= tLimit
<int>( MinVolume
+
1993 ( ( (float)noteEditBottom() ) - ( (float)_me
->y() ) ) /
1994 ( (float)( noteEditBottom() - noteEditTop() ) ) *
1995 ( MaxVolume
- MinVolume
),
1996 MinVolume
, MaxVolume
);
1997 panning_t pan
= tLimit
<int>( PanningLeft
+
1998 ( (float)( noteEditBottom() - _me
->y() ) ) /
1999 ( (float)( noteEditBottom() - noteEditTop() ) ) *
2000 ( (float)( PanningRight
- PanningLeft
) ),
2001 PanningLeft
, PanningRight
);
2003 if( m_noteEditMode
== NoteEditVolume
)
2005 m_lastNoteVolume
= vol
;
2007 else if( m_noteEditMode
== NoteEditPanning
)
2009 m_lastNotePanning
= pan
;
2014 // loop through vector
2015 bool use_selection
= isSelection();
2016 NoteVector::ConstIterator it
= notes
.begin();
2017 while( it
!= notes
.end() )
2019 if( ( *it
)->pos().getTicks() >= ticks_start
2020 && ( *it
)->pos().getTicks() <= ticks_end
2021 && ( *it
)->length().getTicks() > 0
2022 && ( ( *it
)->selected() || ! use_selection
) )
2024 m_pattern
->dataChanged();
2026 // play the note so that the user can tell how loud it is
2027 // and where it is panned
2028 testPlayNote( *it
);
2030 if( m_noteEditMode
== NoteEditVolume
)
2032 ( *it
)->setVolume( vol
);
2033 m_pattern
->instrumentTrack()->processInEvent(
2041 else if( m_noteEditMode
== NoteEditPanning
)
2043 ( *it
)->setPanning( pan
);
2044 midiEvent
evt( MidiMetaEvent
, 0,
2045 ( *it
)->key(), panningToMidi( pan
) );
2046 evt
.m_metaEvent
= MidiNotePanning
;
2047 m_pattern
->instrumentTrack()->processInEvent(
2053 if( ( *it
)->isPlaying() )
2055 // mouse not over this note, stop playing it.
2056 m_pattern
->instrumentTrack()->processInEvent(
2057 midiEvent( MidiNoteOff
, 0,
2058 ( *it
)->key(), 0 ), midiTime() );
2060 ( *it
)->setIsPlaying( false );
2068 else if( _me
->buttons() == Qt::NoButton
&& m_editMode
== ModeDraw
)
2070 // set move- or resize-cursor
2072 // get tick in which the cursor is posated
2073 int pos_ticks
= ( x
* midiTime::ticksPerTact() ) /
2074 m_ppt
+ m_currentPosition
;
2076 // get note-vector of current pattern
2077 const NoteVector
& notes
= m_pattern
->notes();
2079 // will be our iterator in the following loop
2080 NoteVector::ConstIterator it
= notes
.begin();
2082 // loop through whole note-vector...
2083 while( it
!= notes
.end() )
2085 // and check whether the cursor is over an
2087 if( pos_ticks
>= ( *it
)->pos() &&
2088 pos_ticks
<= ( *it
)->pos() +
2089 ( *it
)->length() &&
2090 ( *it
)->key() == key_num
&&
2091 ( *it
)->length() > 0 )
2098 // did it reach end of vector because there's
2100 if( it
!= notes
.end() )
2102 // cursor at the "tail" of the note?
2103 if( ( *it
)->length() > 0 &&
2105 midiTime::ticksPerTact() >
2107 ( *it
)->length() )*m_ppt
/
2108 midiTime::ticksPerTact()-
2111 if( QApplication::overrideCursor() )
2113 if( QApplication::overrideCursor()->shape() != Qt::SizeHorCursor
)
2115 while( QApplication::overrideCursor() != NULL
)
2117 QApplication::restoreOverrideCursor();
2120 QCursor
c( Qt::SizeHorCursor
);
2121 QApplication::setOverrideCursor( c
);
2126 QCursor
c( Qt::SizeHorCursor
);
2127 QApplication::setOverrideCursor(
2133 if( QApplication::overrideCursor() )
2135 if( QApplication::overrideCursor()->shape() != Qt::SizeAllCursor
)
2137 while( QApplication::overrideCursor() != NULL
)
2139 QApplication::restoreOverrideCursor();
2142 QCursor
c( Qt::SizeAllCursor
);
2143 QApplication::setOverrideCursor(
2149 QCursor
c( Qt::SizeAllCursor
);
2150 QApplication::setOverrideCursor(
2157 // the cursor is over no note, so restore cursor
2158 while( QApplication::overrideCursor() != NULL
)
2160 QApplication::restoreOverrideCursor();
2164 else if( _me
->buttons() & Qt::LeftButton
&&
2165 m_editMode
== ModeSelect
&&
2166 m_action
== ActionSelectNotes
)
2169 // change size of selection
2171 // get tick in which the cursor is posated
2172 int pos_ticks
= x
* midiTime::ticksPerTact() / m_ppt
+
2175 m_selectedTick
= pos_ticks
- m_selectStartTick
;
2176 if( (int) m_selectStartTick
+ m_selectedTick
< 0 )
2178 m_selectedTick
= -static_cast<int>(
2179 m_selectStartTick
);
2181 m_selectedKeys
= key_num
- m_selectStartKey
;
2182 if( key_num
<= m_selectStartKey
)
2187 else if( m_editMode
== ModeDraw
&& _me
->buttons() & Qt::RightButton
)
2189 // holding down right-click to delete notes
2191 // get tick in which the user clicked
2192 int pos_ticks
= x
* midiTime::ticksPerTact() / m_ppt
+
2196 // get note-vector of current pattern
2197 const NoteVector
& notes
= m_pattern
->notes();
2199 // will be our iterator in the following loop
2200 NoteVector::ConstIterator it
= notes
.begin();
2202 // loop through whole note-vector...
2203 while( it
!= notes
.end() )
2205 midiTime len
= ( *it
)->length();
2210 // and check whether the user clicked on an
2211 // existing note or an edit-line
2212 if( pos_ticks
>= ( *it
)->pos() &&
2215 ( edit_note
== false &&
2216 pos_ticks
<= ( *it
)->pos() + len
&&
2217 ( *it
)->key() == key_num
)
2219 ( edit_note
== true &&
2220 pos_ticks
<= ( *it
)->pos() +
2222 midiTime::ticksPerTact() /
2228 if( it
!= notes
.end() )
2230 if( ( *it
)->length() > 0 )
2232 m_pattern
->removeNote( *it
);
2236 ( *it
)->setLength( 0 );
2237 m_pattern
->dataChanged();
2239 engine::getSong()->setModified();
2251 if( _me
->buttons() & Qt::LeftButton
&&
2252 m_editMode
== ModeSelect
&&
2253 m_action
== ActionSelectNotes
)
2256 int x
= _me
->x() - WHITE_KEY_WIDTH
;
2257 if( x
< 0 && m_currentPosition
> 0 )
2260 QCursor::setPos( mapToGlobal( QPoint(
2263 if( m_currentPosition
>= 4 )
2265 m_leftRightScroll
->setValue(
2266 m_currentPosition
- 4 );
2270 m_leftRightScroll
->setValue( 0 );
2273 else if( x
> width() - WHITE_KEY_WIDTH
)
2275 x
= width() - WHITE_KEY_WIDTH
;
2276 QCursor::setPos( mapToGlobal( QPoint( width(),
2278 m_leftRightScroll
->setValue( m_currentPosition
+
2282 // get tick in which the cursor is posated
2283 int pos_ticks
= x
* midiTime::ticksPerTact()/ m_ppt
+
2286 m_selectedTick
= pos_ticks
-
2288 if( (int) m_selectStartTick
+ m_selectedTick
<
2291 m_selectedTick
= -static_cast<int>(
2292 m_selectStartTick
);
2296 int key_num
= getKey( _me
->y() );
2297 int visible_keys
= ( height() - PR_TOP_MARGIN
-
2299 m_notesEditHeight
) /
2300 KEY_LINE_HEIGHT
+ 2;
2301 const int s_key
= m_startKey
- 1;
2303 if( key_num
<= s_key
)
2305 QCursor::setPos( mapToGlobal( QPoint( _me
->x(),
2306 keyAreaBottom() ) ) );
2307 m_topBottomScroll
->setValue(
2308 m_topBottomScroll
->value() + 1 );
2311 else if( key_num
>= s_key
+ visible_keys
)
2313 QCursor::setPos( mapToGlobal( QPoint( _me
->x(),
2314 PR_TOP_MARGIN
) ) );
2315 m_topBottomScroll
->setValue(
2316 m_topBottomScroll
->value() - 1 );
2317 key_num
= s_key
+ visible_keys
;
2320 m_selectedKeys
= key_num
- m_selectStartKey
;
2321 if( key_num
<= m_selectStartKey
)
2326 QApplication::restoreOverrideCursor();
2329 m_lastMouseX
= _me
->x();
2330 m_lastMouseY
= _me
->y();
2338 void pianoRoll::dragNotes( int x
, int y
, bool alt
)
2340 // dragging one or more notes around
2342 // convert pixels to ticks and keys
2343 int off_x
= x
- m_moveStartX
;
2344 int off_ticks
= off_x
* midiTime::ticksPerTact() / m_ppt
;
2345 int off_key
= getKey( y
) - getKey( m_moveStartY
);
2347 // handle scroll changes while dragging
2348 off_ticks
-= m_mouseDownTick
- m_currentPosition
;
2349 off_key
-= m_mouseDownKey
- m_startKey
;
2352 // if they're not holding alt, quantize the offset
2355 off_ticks
= floor( off_ticks
/ quantization() )
2359 // make sure notes won't go outside boundary conditions
2360 if( m_action
== ActionMoveNote
)
2362 if( m_moveBoundaryLeft
+ off_ticks
< 0 )
2364 off_ticks
+= 0 - (off_ticks
+ m_moveBoundaryLeft
);
2366 if( m_moveBoundaryTop
+ off_key
> NumKeys
)
2368 off_key
-= NumKeys
- (m_moveBoundaryTop
+ off_key
);
2370 if( m_moveBoundaryBottom
+ off_key
< 0 )
2372 off_key
+= 0 - (m_moveBoundaryBottom
+ off_key
);
2376 // get note-vector of current pattern
2377 const NoteVector
& notes
= m_pattern
->notes();
2379 // will be our iterator in the following loop
2380 NoteVector::ConstIterator it
= notes
.begin();
2381 while( it
!= notes
.end() )
2383 if( ( *it
)->selected() )
2386 if( m_action
== ActionMoveNote
)
2389 int pos_ticks
= ( *it
)->oldPos().getTicks()
2391 int key_num
= ( *it
)->oldKey() + off_key
;
2397 // upper/lower bound checks on key_num
2402 else if( key_num
> NumKeys
)
2407 ( *it
)->setPos( midiTime( pos_ticks
) );
2408 ( *it
)->setKey( key_num
);
2410 else if( m_action
== ActionResizeNote
)
2413 int ticks_new
= ( *it
)->oldLength().getTicks()
2415 if( ticks_new
<= 0 )
2419 ( *it
)->setLength( midiTime( ticks_new
) );
2421 m_lenOfNewNotes
= ( *it
)->length();
2427 m_pattern
->dataChanged();
2428 engine::getSong()->setModified();
2433 void pianoRoll::paintEvent( QPaintEvent
* _pe
)
2436 opt
.initFrom( this );
2438 style()->drawPrimitive( QStyle::PE_Widget
, &opt
, &p
, this );
2440 // set font-size to 8
2441 p
.setFont( pointSize
<8>( p
.font() ) );
2443 // y_offset is used to align the piano-keys on the key-lines
2446 // calculate y_offset according to first key
2447 switch( prKeyOrder
[m_startKey
% KeysPerOctave
] )
2449 case PR_BLACK_KEY
: y_offset
= KEY_LINE_HEIGHT
/4; break;
2450 case PR_WHITE_KEY_BIG
: y_offset
= KEY_LINE_HEIGHT
/2; break;
2451 case PR_WHITE_KEY_SMALL
:
2452 if( prKeyOrder
[( ( m_startKey
+ 1 ) %
2453 KeysPerOctave
)] != PR_BLACK_KEY
)
2455 y_offset
= KEY_LINE_HEIGHT
/ 2;
2460 // start drawing at the bottom
2461 int key_line_y
= keyAreaBottom() - 1;
2462 // used for aligning black-keys later
2463 int first_white_key_height
= WHITE_KEY_SMALL_HEIGHT
;
2464 // key-counter - only needed for finding out whether the processed
2465 // key is the first one
2466 int keys_processed
= 0;
2468 int key
= m_startKey
;
2470 // draw all white keys...
2471 for( int y
= key_line_y
+ 1 + y_offset
; y
> PR_TOP_MARGIN
;
2472 key_line_y
-= KEY_LINE_HEIGHT
, ++keys_processed
)
2474 // check for white key that is only half visible on the
2475 // bottom of piano-roll
2476 if( keys_processed
== 0 &&
2477 prKeyOrder
[m_startKey
% KeysPerOctave
] ==
2481 p
.drawPixmap( PIANO_X
, y
- WHITE_KEY_SMALL_HEIGHT
,
2482 *s_whiteKeySmallPm
);
2484 y
-= WHITE_KEY_SMALL_HEIGHT
/ 2;
2485 // move first black key down (we didn't draw whole
2486 // white key so black key needs to be lifted down)
2487 // (default for first_white_key_height =
2488 // WHITE_KEY_SMALL_HEIGHT, so WHITE_KEY_SMALL_HEIGHT/2
2490 first_white_key_height
= WHITE_KEY_SMALL_HEIGHT
/ 2;
2492 // check whether to draw a big or a small white key
2493 if( prKeyOrder
[key
% KeysPerOctave
] == PR_WHITE_KEY_SMALL
)
2495 // draw a small one...
2496 p
.drawPixmap( PIANO_X
, y
- WHITE_KEY_SMALL_HEIGHT
,
2497 *s_whiteKeySmallPm
);
2499 y
-= WHITE_KEY_SMALL_HEIGHT
;
2502 else if( prKeyOrder
[key
% KeysPerOctave
] ==
2505 // draw a big one...
2506 p
.drawPixmap( PIANO_X
, y
-WHITE_KEY_BIG_HEIGHT
,
2508 // if a big white key has been the first key,
2509 // black keys needs to be lifted up
2510 if( keys_processed
== 0 )
2512 first_white_key_height
= WHITE_KEY_BIG_HEIGHT
;
2515 y
-= WHITE_KEY_BIG_HEIGHT
;
2518 if( static_cast<Keys
>( key
% KeysPerOctave
) == Key_C
)
2520 p
.setPen( QColor( 240, 240, 240 ) );
2521 p
.drawText( C_KEY_LABEL_X
+ 1, y
+14, "C" +
2522 QString::number( static_cast<int>( key
/
2523 KeysPerOctave
) ) );
2524 p
.setPen( QColor( 0, 0, 0 ) );
2525 p
.drawText( C_KEY_LABEL_X
, y
+ 13, "C" +
2526 QString::number( static_cast<int>( key
/
2527 KeysPerOctave
) ) );
2528 p
.setPen( QColor( 0x4F, 0x4F, 0x4F ) );
2532 p
.setPen( QColor( 0x3F, 0x3F, 0x3F ) );
2535 p
.drawLine( WHITE_KEY_WIDTH
, key_line_y
, width(), key_line_y
);
2539 // reset all values, because now we're going to draw all black keys
2545 for( int y
= keyAreaBottom() + y_offset
;
2546 y
> PR_TOP_MARGIN
; ++keys_processed
)
2548 // check for black key that is only half visible on the bottom
2550 if( keys_processed
== 0
2551 // current key may not be a black one
2552 && prKeyOrder
[key
% KeysPerOctave
] != PR_BLACK_KEY
2553 // but the previous one must be black (we must check this
2554 // because there might be two white keys (E-F)
2555 && prKeyOrder
[( key
- 1 ) % KeysPerOctave
] ==
2558 // draw the black key!
2559 p
.drawPixmap( PIANO_X
, y
- BLACK_KEY_HEIGHT
/ 2,
2561 // is the one after the start-note a black key??
2562 if( prKeyOrder
[( key
+ 1 ) % KeysPerOctave
] !=
2565 // no, then move it up!
2566 y
-= KEY_LINE_HEIGHT
/ 2;
2569 // current key black?
2570 if( prKeyOrder
[key
% KeysPerOctave
] == PR_BLACK_KEY
)
2572 // then draw it (calculation of y very complicated,
2573 // but that's the only working solution, sorry...)
2574 p
.drawPixmap( PIANO_X
, y
- ( first_white_key_height
-
2575 WHITE_KEY_SMALL_HEIGHT
) -
2576 WHITE_KEY_SMALL_HEIGHT
/2 - 1 -
2577 BLACK_KEY_HEIGHT
, *s_blackKeyPm
);
2580 y
-= WHITE_KEY_BIG_HEIGHT
;
2581 // reset white-counter
2586 // simple workaround for increasing x if there were
2587 // two white keys (e.g. between E and F)
2591 y
-= WHITE_KEY_BIG_HEIGHT
/2;
2599 // erase the area below the piano, because there might be keys that
2600 // should be only half-visible
2601 p
.fillRect( QRect( 0, keyAreaBottom(),
2602 WHITE_KEY_WIDTH
, noteEditBottom()-keyAreaBottom() ),
2603 QColor( 0, 0, 0 ) );
2605 // display note editing info
2608 p
.setFont( pointSize
<10>( f
) );
2609 p
.setPen( QColor( 255, 255, 0 ) );
2610 p
.drawText( QRect( 0, keyAreaBottom(),
2611 WHITE_KEY_WIDTH
, noteEditBottom() - keyAreaBottom() ),
2612 Qt::AlignCenter
| Qt::TextWordWrap
,
2613 m_nemStr
.at( m_noteEditMode
) + ":" );
2615 // set clipping area, because we are not allowed to paint over
2617 p
.setClipRect( WHITE_KEY_WIDTH
, PR_TOP_MARGIN
,
2618 width() - WHITE_KEY_WIDTH
,
2619 height() - PR_TOP_MARGIN
- PR_BOTTOM_MARGIN
);
2621 // draw vertical raster
2623 // triplet mode occurs if the note duration isn't a multiple of 3
2624 bool triplets
= ( quantization() % 3 != 0 );
2626 int spt
= midiTime::stepsPerTact();
2627 float pp16th
= m_ppt
/ spt
;
2628 int bpt
= DefaultBeatsPerTact
;
2630 spt
= static_cast<int>(1.5 * spt
);
2631 bpt
= static_cast<int>(bpt
* 2.0/3.0);
2635 int tact_16th
= m_currentPosition
/ bpt
;
2637 const int offset
= ( m_currentPosition
% bpt
) *
2638 m_ppt
/ midiTime::ticksPerTact();
2640 bool show32nds
= ( m_zoomingModel
.value() > 3 );
2642 // we need float here as odd time signatures might produce rounding
2643 // errors else and thus an unusable grid
2644 for( float x
= WHITE_KEY_WIDTH
- offset
; x
< width();
2645 x
+= pp16th
, ++tact_16th
)
2647 if( x
>= WHITE_KEY_WIDTH
)
2649 // every tact-start needs to be a bright line
2650 if( tact_16th
% spt
== 0 )
2652 p
.setPen( QColor( 0x7F, 0x7F, 0x7F ) );
2655 else if( tact_16th
% 4 == 0 )
2657 p
.setPen( QColor( 0x5F, 0x5F, 0x5F ) );
2662 p
.setPen( QColor( 0x3F, 0x3F, 0x3F ) );
2665 p
.drawLine( (int)x
, PR_TOP_MARGIN
, (int)x
, height() -
2668 // extra 32nd's line
2671 p
.setPen( QColor( 0x22, 0x22, 0x22 ) );
2672 p
.drawLine( (int)(x
+ pp16th
/2) , PR_TOP_MARGIN
,
2673 (int)(x
+ pp16th
/2), height() -
2681 // following code draws all notes in visible area
2682 // and the note editing stuff (volume, panning, etc)
2684 // setup selection-vars
2685 int sel_pos_start
= m_selectStartTick
;
2686 int sel_pos_end
= m_selectStartTick
+m_selectedTick
;
2687 if( sel_pos_start
> sel_pos_end
)
2689 qSwap
<int>( sel_pos_start
, sel_pos_end
);
2692 int sel_key_start
= m_selectStartKey
- m_startKey
+ 1;
2693 int sel_key_end
= sel_key_start
+ m_selectedKeys
;
2694 if( sel_key_start
> sel_key_end
)
2696 qSwap
<int>( sel_key_start
, sel_key_end
);
2699 int y_base
= keyAreaBottom() - 1;
2700 if( validPattern() == true )
2702 p
.setClipRect( WHITE_KEY_WIDTH
, PR_TOP_MARGIN
,
2703 width() - WHITE_KEY_WIDTH
,
2704 height() - PR_TOP_MARGIN
);
2706 const NoteVector
& notes
= m_pattern
->notes();
2708 const int visible_keys
= ( keyAreaBottom()-keyAreaTop() ) /
2709 KEY_LINE_HEIGHT
+ 2;
2711 QPolygon editHandles
;
2713 for( NoteVector::ConstIterator it
= notes
.begin();
2714 it
!= notes
.end(); ++it
)
2716 Sint32 len_ticks
= ( *it
)->length();
2718 if( len_ticks
== 0 )
2722 else if( len_ticks
< 0 )
2727 const int key
= ( *it
)->key() - m_startKey
+ 1;
2729 Sint32 pos_ticks
= ( *it
)->pos();
2731 int note_width
= len_ticks
* m_ppt
/
2732 midiTime::ticksPerTact();
2733 const int x
= ( pos_ticks
- m_currentPosition
) *
2734 m_ppt
/ midiTime::ticksPerTact();
2735 // skip this note if not in visible area at all
2736 if( !( x
+ note_width
>= 0 &&
2737 x
<= width() - WHITE_KEY_WIDTH
) )
2742 // is the note in visible area?
2743 if( key
> 0 && key
<= visible_keys
)
2746 // we've done and checked all, let's draw the
2748 drawNoteRect( p
, x
+ WHITE_KEY_WIDTH
,
2749 y_base
- key
* KEY_LINE_HEIGHT
,
2753 // draw note editing stuff
2754 int editHandleTop
= 0;
2755 if( m_noteEditMode
== NoteEditVolume
)
2757 QColor color
= QColor::fromHsv( 120, 221,
2758 qMin(255, 60 + ( *it
)->getVolume() ) );
2759 if( ( *it
)->selected() )
2761 color
.setRgb( 0x00, 0x40, 0xC0 );
2763 p
.setPen( QPen( color
, NE_LINE_WIDTH
) );
2765 editHandleTop
= noteEditBottom() -
2766 ( (float)( ( *it
)->getVolume() - MinVolume
) ) /
2767 ( (float)( MaxVolume
- MinVolume
) ) *
2768 ( (float)( noteEditBottom() - noteEditTop() ) );
2770 p
.drawLine( noteEditLeft() + x
, editHandleTop
,
2771 noteEditLeft() + x
, noteEditBottom() );
2774 else if( m_noteEditMode
== NoteEditPanning
)
2776 QColor
color( 0xFF, 0xB0, 0x00 );
2777 if( ( *it
)->selected() )
2779 color
.setRgb( 0x00, 0x40, 0xC0 );
2782 p
.setPen( QPen( color
, NE_LINE_WIDTH
) );
2784 editHandleTop
= noteEditBottom() -
2785 ( (float)( ( *it
)->getPanning() - PanningLeft
) ) /
2786 ( (float)( (PanningRight
- PanningLeft
) ) ) *
2787 ( (float)( noteEditBottom() - noteEditTop() ) );
2789 p
.drawLine( noteEditLeft() + x
, noteEditTop() +
2790 ( (float)( noteEditBottom() - noteEditTop() ) ) / 2.0f
,
2791 noteEditLeft() + x
, editHandleTop
);
2793 editHandles
<< QPoint( x
+ noteEditLeft(),
2796 if( ( *it
)->hasDetuningInfo() )
2798 drawDetuningInfo( p
, *it
,
2799 x
+ WHITE_KEY_WIDTH
,
2800 y_base
- key
* KEY_LINE_HEIGHT
);
2804 p
.setPen( QPen( QColor( 0xEA, 0xA1, 0x00 ),
2805 NE_LINE_WIDTH
+2 ) );
2806 p
.drawPoints( editHandles
);
2813 p
.setFont( pointSize
<14>( f
) );
2814 p
.setPen( QColor( 0, 255, 0 ) );
2815 p
.drawText( WHITE_KEY_WIDTH
+ 20, PR_TOP_MARGIN
+ 40,
2816 tr( "Please open a pattern by double-clicking "
2820 p
.setClipRect( WHITE_KEY_WIDTH
, PR_TOP_MARGIN
, width() -
2821 WHITE_KEY_WIDTH
, height() - PR_TOP_MARGIN
-
2822 m_notesEditHeight
- PR_BOTTOM_MARGIN
);
2824 // now draw selection-frame
2825 int x
= ( ( sel_pos_start
- m_currentPosition
) * m_ppt
) /
2826 midiTime::ticksPerTact();
2827 int w
= ( ( ( sel_pos_end
- m_currentPosition
) * m_ppt
) /
2828 midiTime::ticksPerTact() ) - x
;
2829 int y
= (int) y_base
- sel_key_start
* KEY_LINE_HEIGHT
;
2830 int h
= (int) y_base
- sel_key_end
* KEY_LINE_HEIGHT
- y
;
2831 p
.setPen( QColor( 0, 64, 192 ) );
2832 p
.drawRect( x
+ WHITE_KEY_WIDTH
, y
, w
, h
);
2834 // TODO: Get this out of paint event
2835 int l
= ( validPattern() == true )? (int) m_pattern
->length() : 0;
2837 // reset scroll-range
2838 if( m_leftRightScroll
->maximum() != l
)
2840 m_leftRightScroll
->setRange( 0, l
);
2841 m_leftRightScroll
->setPageStep( l
);
2844 // horizontal line for the key under the cursor
2845 if( validPattern() == true )
2847 int key_num
= getKey( mapFromGlobal( QCursor::pos() ).y() );
2848 p
.fillRect( 10, keyAreaBottom() + 3 - KEY_LINE_HEIGHT
*
2849 ( key_num
- m_startKey
+ 1 ),
2850 width() - 10, KEY_LINE_HEIGHT
- 7,
2851 QColor( 64, 64, 64 ) );
2854 // bar to resize note edit area
2855 p
.setClipRect( 0, 0, width(), height() );
2856 p
.fillRect( QRect( 0, keyAreaBottom(),
2857 width()-PR_RIGHT_MARGIN
, NOTE_EDIT_RESIZE_BAR
),
2858 QColor( 64, 64, 64 ) );
2860 const QPixmap
* cursor
= NULL
;
2861 // draw current edit-mode-icon below the cursor
2862 switch( m_editMode
)
2865 if( m_mouseDownRight
)
2867 cursor
= s_toolErase
;
2869 else if( m_action
== ActionMoveNote
)
2871 cursor
= s_toolMove
;
2875 cursor
= s_toolDraw
;
2878 case ModeErase
: cursor
= s_toolErase
; break;
2879 case ModeSelect
: cursor
= s_toolSelect
; break;
2880 case ModeEditDetuning
: cursor
= s_toolOpen
; break;
2882 if( cursor
!= NULL
)
2884 p
.drawPixmap( mapFromGlobal( QCursor::pos() ) + QPoint( 8, 8 ),
2892 // responsible for moving/resizing scrollbars after window-resizing
2893 void pianoRoll::resizeEvent( QResizeEvent
* )
2895 m_leftRightScroll
->setGeometry( WHITE_KEY_WIDTH
, height() -
2897 width()-WHITE_KEY_WIDTH
,
2899 m_topBottomScroll
->setGeometry( width() - SCROLLBAR_SIZE
, PR_TOP_MARGIN
,
2901 height() - PR_TOP_MARGIN
-
2904 int total_pixels
= OCTAVE_HEIGHT
* NumOctaves
- ( height() -
2905 PR_TOP_MARGIN
- PR_BOTTOM_MARGIN
-
2906 m_notesEditHeight
);
2907 m_totalKeysToScroll
= total_pixels
* KeysPerOctave
/ OCTAVE_HEIGHT
;
2909 m_topBottomScroll
->setRange( 0, m_totalKeysToScroll
);
2911 if( m_startKey
> m_totalKeysToScroll
)
2913 m_startKey
= m_totalKeysToScroll
;
2915 m_topBottomScroll
->setValue( m_totalKeysToScroll
- m_startKey
);
2917 engine::getSong()->getPlayPos( song::Mode_PlayPattern
2918 ).m_timeLine
->setFixedWidth( width() );
2919 m_toolBar
->setFixedWidth( width() );
2926 void pianoRoll::wheelEvent( QWheelEvent
* _we
)
2929 if( _we
->modifiers() & Qt::ControlModifier
)
2931 if( _we
->delta() > 0 )
2933 m_ppt
= qMin( m_ppt
* 2, KEY_LINE_HEIGHT
*
2934 DefaultStepsPerTact
* 8 );
2936 else if( m_ppt
>= 72 )
2940 // update combobox with zooming-factor
2941 m_zoomingModel
.setValue(
2942 m_zoomingModel
.findText( QString::number(
2943 static_cast<int>( m_ppt
* 100 /
2944 DEFAULT_PR_PPT
) ) +"%" ) );
2946 m_timeLine
->setPixelsPerTact( m_ppt
);
2949 else if( _we
->modifiers() & Qt::ShiftModifier
2950 || _we
->orientation() == Qt::Horizontal
)
2952 m_leftRightScroll
->setValue( m_leftRightScroll
->value() -
2953 _we
->delta() * 2 / 15 );
2957 m_topBottomScroll
->setValue( m_topBottomScroll
->value() -
2958 _we
->delta() / 30 );
2965 int pianoRoll::getKey( int _y
) const
2967 int key_line_y
= keyAreaBottom() - 1;
2968 // pressed key on piano
2969 int key_num
= ( key_line_y
- _y
) / KEY_LINE_HEIGHT
;
2970 key_num
+= m_startKey
;
2972 // some range-checking-stuff
2978 if( key_num
>= KeysPerOctave
* NumOctaves
)
2980 key_num
= KeysPerOctave
* NumOctaves
- 1;
2989 song::PlayModes
pianoRoll::desiredPlayModeForAccompany() const
2991 if( m_pattern
->getTrack()->getTrackContainer() ==
2992 engine::getBBTrackContainer() )
2994 return song::Mode_PlayBB
;
2996 return song::Mode_PlaySong
;
3002 void pianoRoll::play()
3004 if( validPattern() == false )
3009 if( engine::getSong()->isPlaying() )
3011 if( engine::getSong()->playMode() != song::Mode_PlayPattern
)
3013 engine::getSong()->stop();
3014 engine::getSong()->playPattern( m_pattern
);
3015 m_playButton
->setIcon( embed::getIconPixmap(
3020 engine::getSong()->pause();
3021 m_playButton
->setIcon( embed::getIconPixmap( "play" ) );
3024 else if( engine::getSong()->isPaused() )
3026 engine::getSong()->resumeFromPause();
3027 m_playButton
->setIcon( embed::getIconPixmap( "pause" ) );
3031 m_playButton
->setIcon( embed::getIconPixmap( "pause" ) );
3032 engine::getSong()->playPattern( m_pattern
);
3039 void pianoRoll::record()
3041 if( engine::getSong()->isPlaying() )
3045 if( m_recording
== true || validPattern() == false )
3052 engine::getSong()->playPattern( m_pattern
, false );
3058 void pianoRoll::recordAccompany()
3060 if( engine::getSong()->isPlaying() )
3064 if( m_recording
== true || validPattern() == false )
3071 if( m_pattern
->getTrack()->getTrackContainer() == engine::getSong() )
3073 engine::getSong()->play();
3077 engine::getSong()->playBB();
3085 void pianoRoll::stop()
3087 engine::getSong()->stop();
3088 m_playButton
->setIcon( embed::getIconPixmap( "play" ) );
3089 m_playButton
->update();
3090 m_recording
= false;
3091 m_scrollBack
= true;
3097 void pianoRoll::startRecordNote( const note
& _n
)
3099 if( m_recording
== true && validPattern() == true &&
3100 engine::getSong()->isPlaying() &&
3101 ( engine::getSong()->playMode() ==
3102 desiredPlayModeForAccompany() ||
3103 engine::getSong()->playMode() ==
3104 song::Mode_PlayPattern
) )
3107 if( engine::getSong()->playMode() == song::Mode_PlaySong
)
3109 sub
= m_pattern
->startPosition();
3111 note
n( 1, engine::getSong()->getPlayPos(
3112 engine::getSong()->playMode() ) - sub
,
3113 _n
.key(), _n
.getVolume(), _n
.getPanning() );
3116 m_recordingNotes
<< n
;
3124 void pianoRoll::finishRecordNote( const note
& _n
)
3126 if( m_recording
== true && validPattern() == true &&
3127 engine::getSong()->isPlaying() &&
3128 ( engine::getSong()->playMode() ==
3129 desiredPlayModeForAccompany() ||
3130 engine::getSong()->playMode() ==
3131 song::Mode_PlayPattern
) )
3133 for( QList
<note
>::Iterator it
= m_recordingNotes
.begin();
3134 it
!= m_recordingNotes
.end(); ++it
)
3136 if( it
->key() == _n
.key() )
3138 note
n( _n
.length(), it
->pos(),
3139 it
->key(), it
->getVolume(),
3141 n
.quantizeLength( quantization() );
3142 m_pattern
->addNote( n
);
3144 m_recordingNotes
.erase( it
);
3154 void pianoRoll::horScrolled( int _new_pos
)
3156 m_currentPosition
= _new_pos
;
3157 emit
positionChanged( m_currentPosition
);
3164 void pianoRoll::verScrolled( int _new_pos
)
3167 m_startKey
= m_totalKeysToScroll
- _new_pos
;
3175 void pianoRoll::drawButtonToggled()
3177 m_editMode
= ModeDraw
;
3184 void pianoRoll::eraseButtonToggled()
3186 m_editMode
= ModeErase
;
3193 void pianoRoll::selectButtonToggled()
3195 m_editMode
= ModeSelect
;
3201 void pianoRoll::detuneButtonToggled()
3203 m_editMode
= ModeEditDetuning
;
3209 void pianoRoll::selectAll()
3211 if( validPattern() == false )
3216 const NoteVector
& notes
= m_pattern
->notes();
3218 // if first_time = true, we HAVE to set the vars for select
3219 bool first_time
= true;
3221 for( NoteVector::ConstIterator it
= notes
.begin(); it
!= notes
.end();
3224 Uint32 len_ticks
= ( *it
)->length();
3228 const int key
= ( *it
)->key();
3230 Uint32 pos_ticks
= ( *it
)->pos();
3231 if( key
<= m_selectStartKey
|| first_time
)
3233 // if we move start-key down, we have to add
3234 // the difference between old and new start-key
3235 // to m_selectedKeys, otherwise the selection
3236 // is just moved down...
3237 m_selectedKeys
+= m_selectStartKey
3239 m_selectStartKey
= key
- 1;
3241 if( key
>= m_selectedKeys
+m_selectStartKey
||
3244 m_selectedKeys
= key
- m_selectStartKey
;
3246 if( pos_ticks
< m_selectStartTick
||
3249 m_selectStartTick
= pos_ticks
;
3251 if( pos_ticks
+ len_ticks
>
3252 m_selectStartTick
+ m_selectedTick
||
3255 m_selectedTick
= pos_ticks
+
3267 // returns vector with pointers to all selected notes
3268 void pianoRoll::getSelectedNotes( NoteVector
& _selected_notes
)
3270 if( validPattern() == false )
3275 const NoteVector
& notes
= m_pattern
->notes();
3277 for( NoteVector::ConstIterator it
= notes
.begin(); it
!= notes
.end();
3280 if( ( *it
)->selected() )
3282 _selected_notes
.push_back( *it
);
3290 void pianoRoll::copy_to_clipboard( const NoteVector
& _notes
) const
3292 multimediaProject
mmp( multimediaProject::ClipboardData
);
3293 QDomElement note_list
= mmp
.createElement( "note-list" );
3294 mmp
.content().appendChild( note_list
);
3296 midiTime
start_pos( _notes
.front()->pos().getTact(), 0 );
3297 for( NoteVector::ConstIterator it
= _notes
.begin(); it
!= _notes
.end();
3300 note
clip_note( **it
);
3301 clip_note
.setPos( clip_note
.pos( start_pos
) );
3302 clip_note
.saveState( mmp
, note_list
);
3305 QMimeData
* clip_content
= new QMimeData
;
3306 clip_content
->setData( Clipboard::mimeType(), mmp
.toString().toUtf8() );
3307 QApplication::clipboard()->setMimeData( clip_content
,
3308 QClipboard::Clipboard
);
3314 void pianoRoll::copySelectedNotes()
3316 NoteVector selected_notes
;
3317 getSelectedNotes( selected_notes
);
3319 if( selected_notes
.empty() == false )
3321 copy_to_clipboard( selected_notes
);
3328 void pianoRoll::cutSelectedNotes()
3330 if( validPattern() == false )
3335 NoteVector selected_notes
;
3336 getSelectedNotes( selected_notes
);
3338 if( selected_notes
.empty() == false )
3340 copy_to_clipboard( selected_notes
);
3342 engine::getSong()->setModified();
3344 for( NoteVector::Iterator it
= selected_notes
.begin();
3345 it
!= selected_notes
.end(); ++it
)
3347 // note (the memory of it) is also deleted by
3348 // pattern::removeNote(...) so we don't have to do that
3349 m_pattern
->removeNote( *it
);
3354 engine::getSongEditor()->update();
3360 void pianoRoll::pasteNotes()
3362 if( validPattern() == false )
3367 QString value
= QApplication::clipboard()
3368 ->mimeData( QClipboard::Clipboard
)
3369 ->data( Clipboard::mimeType() );
3371 if( !value
.isEmpty() )
3373 multimediaProject
mmp( value
.toUtf8() );
3375 QDomNodeList list
= mmp
.elementsByTagName(
3376 note::classNodeName() );
3378 // remove selection and select the newly pasted notes
3379 clearSelectedNotes();
3381 for( int i
= 0; !list
.item( i
).isNull(); ++i
)
3385 cur_note
.restoreState( list
.item( i
).toElement() );
3386 cur_note
.setPos( cur_note
.pos() + m_timeLine
->pos() );
3389 cur_note
.setSelected( true );
3392 m_pattern
->addNote( cur_note
);
3395 // we only have to do the following lines if we pasted at
3396 // least one note...
3397 engine::getSong()->setModified();
3398 m_ctrlMode
= ModeDraw
;
3399 m_drawButton
->setChecked( true );
3401 engine::getSongEditor()->update();
3408 void pianoRoll::deleteSelectedNotes()
3410 if( validPattern() == false )
3415 bool update_after_delete
= false;
3418 // get note-vector of current pattern
3419 const NoteVector
& notes
= m_pattern
->notes();
3421 // will be our iterator in the following loop
3422 NoteVector::ConstIterator it
= notes
.begin();
3423 while( it
!= notes
.end() )
3425 if( ( *it
)->selected() )
3428 m_pattern
->removeNote( ( *it
) );
3429 update_after_delete
= true;
3431 // start over, make sure we get all the notes
3440 if( update_after_delete
== true )
3442 engine::getSong()->setModified();
3444 engine::getSongEditor()->update();
3452 void pianoRoll::autoScroll( const midiTime
& _t
)
3454 const int w
= width() - WHITE_KEY_WIDTH
;
3455 if( _t
> m_currentPosition
+ w
* midiTime::ticksPerTact() / m_ppt
)
3457 m_leftRightScroll
->setValue( _t
.getTact() *
3458 midiTime::ticksPerTact() );
3460 else if( _t
< m_currentPosition
)
3462 midiTime t
= qMax( _t
- w
* midiTime::ticksPerTact() *
3463 midiTime::ticksPerTact() / m_ppt
, 0 );
3464 m_leftRightScroll
->setValue( t
.getTact() *
3465 midiTime::ticksPerTact() );
3467 m_scrollBack
= false;
3473 void pianoRoll::updatePosition( const midiTime
& _t
)
3475 if( ( engine::getSong()->isPlaying() &&
3476 engine::getSong()->playMode() ==
3477 song::Mode_PlayPattern
&&
3478 m_timeLine
->autoScroll() == timeLine::AutoScrollEnabled
) ||
3479 m_scrollBack
== true )
3488 void pianoRoll::updatePositionAccompany( const midiTime
& _t
)
3490 song
* s
= engine::getSong();
3492 if( m_recording
&& validPattern() &&
3493 s
->playMode() != song::Mode_PlayPattern
)
3496 if( s
->playMode() != song::Mode_PlayBB
)
3498 pos
-= m_pattern
->startPosition();
3502 s
->getPlayPos( song::Mode_PlayPattern
).setTicks( pos
);
3511 void pianoRoll::zoomingChanged()
3513 const QString
& zfac
= m_zoomingModel
.currentText();
3514 m_ppt
= zfac
.left( zfac
.length() - 1 ).toInt() * DEFAULT_PR_PPT
/ 100;
3516 assert( m_ppt
> 0 );
3518 m_timeLine
->setPixelsPerTact( m_ppt
);
3526 void pianoRoll::quantizeChanged()
3528 if( m_quantizeModel
.value() == 0 &&
3529 m_noteLenModel
.value() == 0 )
3531 m_quantizeModel
.setValue( m_quantizeModel
.findText( "1/16" ) );
3539 int pianoRoll::quantization() const
3541 if( m_quantizeModel
.value() == 0 )
3543 return newNoteLen();
3545 return DefaultTicksPerTact
/ m_quantizeModel
.currentText().right(
3546 m_quantizeModel
.currentText().length() -
3553 midiTime
pianoRoll::newNoteLen() const
3555 if( m_noteLenModel
.value() == 0 )
3557 return m_lenOfNewNotes
;
3559 return midiTime::ticksPerTact() / m_noteLenModel
.currentText().right(
3560 m_noteLenModel
.currentText().length() -
3567 bool pianoRoll::mouseOverNote()
3569 return validPattern() && noteUnderMouse() != NULL
;
3575 note
* pianoRoll::noteUnderMouse()
3577 QPoint pos
= mapFromGlobal( QCursor::pos() );
3579 // get note-vector of current pattern
3580 const NoteVector
& notes
= m_pattern
->notes();
3582 if( pos
.x() <= WHITE_KEY_WIDTH
|| pos
.x() > width() - SCROLLBAR_SIZE
3583 || pos
.y() < PR_TOP_MARGIN
3584 || pos
.y() > keyAreaBottom() )
3589 int key_num
= getKey( pos
.y() );
3590 int pos_ticks
= ( pos
.x() - WHITE_KEY_WIDTH
) *
3591 midiTime::ticksPerTact() / m_ppt
+ m_currentPosition
;
3593 // will be our iterator in the following loop
3594 NoteVector::ConstIterator it
= notes
.begin();
3596 // loop through whole note-vector...
3597 while( it
!= notes
.end() )
3599 // and check whether the cursor is over an
3601 if( pos_ticks
>= ( *it
)->pos() &&
3602 pos_ticks
<= ( *it
)->pos() + ( *it
)->length() &&
3603 ( *it
)->key() == key_num
&& ( *it
)->length() > 0 )
3616 #include "moc_piano_roll.cxx"