Renamed all automation related files and classes to match new coding style
[lmms.git] / src / gui / piano_roll.cpp
blob12b726c4b5c32f1ddc8dc6c659830fa5f414f84a
1 /*
2 * piano_roll.cpp - implementation of piano-roll which is used for actual
3 * writing of melodies
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>
38 #include <QString>
39 #include <QSignalMapper>
41 #ifndef __USE_XOPEN
42 #define __USE_XOPEN
43 #endif
45 #include <math.h>
47 #include "piano_roll.h"
48 #include "bb_track_container.h"
49 #include "Clipboard.h"
50 #include "combobox.h"
51 #include "debug.h"
52 #include "DetuningHelper.h"
53 #include "embed.h"
54 #include "gui_templates.h"
55 #include "InstrumentTrack.h"
56 #include "MainWindow.h"
57 #include "midi.h"
58 #include "mmp.h"
59 #include "pattern.h"
60 #include "Piano.h"
61 #include "pixmap_button.h"
62 #include "song.h"
63 #include "song_editor.h"
64 #include "templates.h"
65 #include "text_float.h"
66 #include "timeline.h"
67 #include "tool_button.h"
68 #include "tooltip.h"
71 typedef AutomationPattern::timeMap timeMap;
74 extern Keys whiteKeys[]; // defined in piano_widget.cpp
77 // some constants...
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 ),
141 m_zoomingModel(),
142 m_quantizeModel(),
143 m_noteLenModel(),
144 m_pattern( NULL ),
145 m_currentPosition(),
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 ),
154 m_mouseDownKey( 0 ),
155 m_mouseDownTick( 0 ),
156 m_lastMouseX( 0 ),
157 m_lastMouseY( 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 ),
165 m_lastKey( 0 ),
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)) );
188 // init pixmaps
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(
202 "pr_black_key" ) );
204 if( s_toolDraw == NULL )
206 s_toolDraw = new QPixmap( embed::getIconPixmap(
207 "edit_draw" ) );
209 if( s_toolErase == NULL )
211 s_toolErase= new QPixmap( embed::getIconPixmap(
212 "edit_erase" ) );
214 if( s_toolSelect == NULL )
216 s_toolSelect = new QPixmap( embed::getIconPixmap(
217 "edit_select" ) );
219 if( s_toolMove == NULL )
221 s_toolMove = new QPixmap( embed::getIconPixmap(
222 "edit_move" ) );
224 if( s_toolOpen == NULL )
226 s_toolOpen = new QPixmap( embed::getIconPixmap(
227 "automation" ) );
230 setAttribute( Qt::WA_OpaquePaintEvent, true );
232 // add time-line
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 & ) ),
245 this,
246 SLOT( updatePositionAccompany( const midiTime & ) ) );
247 // TODO
248 /* connect( engine::getSong()->getPlayPos( song::Mode_PlayBB ).m_timeLine,
249 SIGNAL( positionChanged( const midiTime & ) ),
250 this,
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 );
258 QPalette pal;
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." ) );
306 removeSelection();
308 // init scrollbars
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() ),
324 m_toolBar );
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() ),
331 m_toolBar );
332 m_eraseButton->setCheckable( true );
334 m_selectButton = new toolButton( embed::getIconPixmap(
335 "edit_select" ),
336 tr( "Select mode (Shift+S)" ),
337 this, SLOT( selectButtonToggled() ),
338 m_toolBar );
339 m_selectButton->setCheckable( true );
341 m_detuneButton = new toolButton( embed::getIconPixmap( "automation"),
342 tr( "Detune mode (Shift+T)" ),
343 this, SLOT( detuneButtonToggled() ),
344 m_toolBar );
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 "
369 "select mode." ) );
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() ),
380 m_toolBar );
382 m_copyButton = new toolButton( embed::getIconPixmap( "edit_copy" ),
383 tr( "Copy selected notes (Ctrl+C)" ),
384 this, SLOT( copySelectedNotes() ),
385 m_toolBar );
387 m_pasteButton = new toolButton( embed::getIconPixmap( "edit_paste" ),
388 tr( "Paste notes from clipboard "
389 "(Ctrl+V)" ),
390 this, SLOT( pasteNotes() ),
391 m_toolBar );
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 );
506 setFocus();
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();
522 else
524 resize( INITIAL_PIANOROLL_WIDTH, INITIAL_PIANOROLL_HEIGHT );
525 hide();
528 connect( engine::getSong(), SIGNAL( timeSignatureChanged( int, int ) ),
529 this, SLOT( update() ) );
533 void pianoRoll::changeNoteEditMode( int i )
535 m_noteEditMode = (noteEditMode) i;
536 repaint();
540 pianoRoll::~pianoRoll()
545 void pianoRoll::setCurrentPattern( pattern * _new_pattern )
547 if( validPattern() )
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" ) );
562 update();
563 return;
566 m_leftRightScroll->setValue( 0 );
568 const NoteVector & notes = m_pattern->notes();
569 int central_key = 0;
570 if( notes.empty() == false )
572 // determine the central key so that we can scroll to it
573 int total_notes = 0;
574 for( NoteVector::ConstIterator it = notes.begin();
575 it != notes.end(); ++it )
577 if( ( *it )->length() > 0 )
579 central_key += ( *it )->key();
580 ++total_notes;
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...)
595 resizeEvent( NULL );
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() ) );
606 update();
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 )
631 ++_x;
632 ++_y;
633 _width -= 2;
635 if( _width <= 0 )
637 _width = 2;
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 )
657 //step note
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 );
666 else
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 ) );
704 if( _width > 2 )
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,
719 int _y )
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() )
730 break;
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;
749 m_selectedTick = 0;
750 m_selectStartKey = 0;
751 m_selectedKeys = 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 );
772 ++it;
780 void pianoRoll::closeEvent( QCloseEvent * _ce )
782 QApplication::restoreOverrideCursor();
783 if( parentWidget() )
785 parentWidget()->hide();
787 else
789 hide();
791 _ce->ignore();
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();
802 ++it )
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
813 update();
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();
826 bool first = true;
827 for( NoteVector::ConstIterator it = notes.begin(); it != notes.end();
828 ++it )
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
835 if( first )
837 m_moveBoundaryLeft = ( *it )->pos();
838 if( m_moveBoundaryLeft + amount < 0 )
840 amount += 0 - (amount + m_moveBoundaryLeft);
842 first = false;
844 ( *it )->setPos( ( *it )->pos() + amount );
848 // we modified the song
849 update();
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();
860 ++it )
862 if( ( *it )->selected() )
864 return true;
868 return false;
873 int pianoRoll::selectionCount() const // how many notes are selected?
875 int sum = 0;
877 const NoteVector & notes = m_pattern->notes();
878 for( NoteVector::ConstIterator it = notes.begin(); it != notes.end();
879 ++it )
881 if( ( *it )->selected() )
883 ++sum;
887 return sum;
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 );
906 switch( _ke->key() )
908 case Qt::Key_Up:
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 );
916 else
918 // scroll
919 m_topBottomScroll->setValue(
920 m_topBottomScroll->value() -
921 cm_scrollAmtVert );
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,
929 _ke->modifiers() &
930 Qt::AltModifier );
933 break;
934 case Qt::Key_Down:
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 );
942 else
944 // scroll
945 m_topBottomScroll->setValue(
946 m_topBottomScroll->value() +
947 cm_scrollAmtVert );
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,
955 _ke->modifiers() &
956 Qt::AltModifier );
959 break;
961 case Qt::Key_Left:
963 if( _ke->modifiers() & Qt::ControlModifier &&
964 m_action == ActionNone )
966 // move time ticker
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)
976 // move notes
977 bool quantized = ! ( _ke->modifiers() &
978 Qt::AltModifier );
979 int amt = quantized ? quantization() : 1;
980 shiftPos( -amt );
982 else
984 // scroll
985 m_leftRightScroll->setValue(
986 m_leftRightScroll->value() -
987 cm_scrollAmtHoriz );
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,
995 _ke->modifiers() &
996 Qt::AltModifier );
1000 break;
1002 case Qt::Key_Right:
1004 if( _ke->modifiers() & Qt::ControlModifier
1005 && m_action == ActionNone)
1007 // move time ticker
1008 m_timeLine->pos() += 16;
1009 m_timeLine->updatePosition();
1011 else if( _ke->modifiers() & Qt::ShiftModifier
1012 && m_action == ActionNone)
1014 // move notes
1015 bool quantized = !( _ke->modifiers() &
1016 Qt::AltModifier );
1017 int amt = quantized ? quantization() : 1;
1018 shiftPos( +amt );
1020 else
1022 // scroll
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,
1033 _ke->modifiers() &
1034 Qt::AltModifier );
1040 break;
1043 case Qt::Key_C:
1044 if( _ke->modifiers() & Qt::ControlModifier )
1046 copySelectedNotes();
1048 else
1050 _ke->ignore();
1052 break;
1054 case Qt::Key_X:
1055 if( _ke->modifiers() & Qt::ControlModifier )
1057 cutSelectedNotes();
1059 else
1061 _ke->ignore();
1063 break;
1065 case Qt::Key_V:
1066 if( _ke->modifiers() & Qt::ControlModifier )
1068 pasteNotes();
1070 else
1072 _ke->ignore();
1074 break;
1076 case Qt::Key_A:
1077 if( _ke->modifiers() & Qt::ControlModifier )
1079 m_selectButton->setChecked( true );
1080 selectAll();
1081 update();
1083 else
1085 _ke->ignore();
1087 break;
1089 case Qt::Key_D:
1090 if( _ke->modifiers() & Qt::ShiftModifier )
1092 m_drawButton->setChecked( true );
1094 else
1096 _ke->ignore();
1098 break;
1100 case Qt::Key_E:
1101 if( _ke->modifiers() & Qt::ShiftModifier )
1103 m_eraseButton->setChecked( true );
1105 else
1107 _ke->ignore();
1109 break;
1111 case Qt::Key_S:
1112 if( _ke->modifiers() & Qt::ShiftModifier )
1114 m_selectButton->setChecked( true );
1116 else
1118 _ke->ignore();
1120 break;
1122 case Qt::Key_T:
1123 if( _ke->modifiers() & Qt::ShiftModifier )
1125 m_detuneButton->setChecked( true );
1127 else
1129 _ke->ignore();
1131 break;
1133 case Qt::Key_Delete:
1134 deleteSelectedNotes();
1135 break;
1137 case Qt::Key_Space:
1138 if( engine::getSong()->isPlaying() )
1140 stop();
1142 else
1144 play();
1146 break;
1148 case Qt::Key_Home:
1149 m_timeLine->pos().setTicks( 0 );
1150 m_timeLine->updatePosition();
1151 break;
1153 case Qt::Key_0:
1154 case Qt::Key_1:
1155 case Qt::Key_2:
1156 case Qt::Key_3:
1157 case Qt::Key_4:
1158 case Qt::Key_5:
1159 case Qt::Key_6:
1160 case Qt::Key_7:
1161 case Qt::Key_8:
1162 case Qt::Key_9:
1164 int len = 1 + _ke->key() - Qt::Key_0;
1165 if( len == 10 )
1166 len = 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 );
1176 break;
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 ) );
1186 update();
1188 break;
1189 default:
1190 _ke->ignore();
1191 break;
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() &
1215 Qt::ShiftModifier);
1216 m_editMode = m_ctrlMode;
1217 update();
1218 break;
1220 _ke->ignore();
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 )
1292 return;
1295 if( m_editMode == ModeEditDetuning && noteUnderMouse() )
1297 noteUnderMouse()->editDetuningPattern();
1298 return;
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 ) );
1308 update();
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;
1323 return;
1326 if( _me->y() > PR_TOP_MARGIN )
1328 bool edit_note = ( _me->y() > noteEditTop() );
1330 int key_num = getKey( _me->y() );
1332 int x = _me->x();
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 +
1343 m_currentPosition;
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();
1356 if( len < 0 )
1358 len = 4;
1360 // and check whether the user clicked on an
1361 // existing note or an edit-line
1362 if( pos_ticks >= ( *it )->pos() &&
1363 len > 0 &&
1365 ( edit_note == false &&
1366 pos_ticks <= ( *it )->pos() + len &&
1367 ( *it )->key() == key_num )
1369 ( edit_note == true &&
1370 pos_ticks <= ( *it )->pos() +
1371 NE_LINE_WIDTH *
1372 midiTime::ticksPerTact() /
1373 m_ppt )
1377 break;
1379 ++it;
1382 // first check whether the user clicked in note-edit-
1383 // area
1384 if( edit_note == true )
1386 // scribble note edit changes
1387 mouseMoveEvent( _me );
1388 return;
1390 // left button??
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() )
1399 m_pattern->setType(
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
1421 // code-block
1422 it = notes.begin();
1423 while( it != notes.end() &&
1424 *it != created_new_note )
1426 ++it;
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;
1441 bool first = true;
1442 it = notes.begin();
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
1455 if( first )
1457 m_moveBoundaryLeft = ( *it )->pos().getTicks();
1458 m_moveBoundaryRight = ( *it )->pos() + ( *it )->length();
1459 m_moveBoundaryBottom = ( *it )->key();
1460 m_moveBoundaryTop = ( *it )->key();
1462 first = false;
1464 else
1466 m_moveBoundaryLeft = qMin(
1467 ( *it )->pos().getTicks(),
1468 m_moveBoundaryLeft );
1469 m_moveBoundaryRight = qMax( ( *it )->pos() +
1470 ( *it )->length(),
1471 m_moveBoundaryRight );
1472 m_moveBoundaryBottom = qMin( ( *it )->key(),
1473 m_moveBoundaryBottom );
1474 m_moveBoundaryTop = qMax( ( *it )->key(),
1475 m_moveBoundaryTop );
1479 ++it;
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 );
1510 else
1512 // otherwise move it
1513 m_action = ActionMoveNote;
1515 // set move-cursor
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;
1525 it = notes.begin();
1526 while( it != notes.end() )
1528 if( ( *it )->selected() )
1530 // copy this note
1531 note noteCopy( (note) **it );
1532 newNotes.push_back( noteCopy );
1534 ++it;
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();
1548 update();
1549 engine::getSongEditor()->update();
1553 // play the note
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 );
1574 else
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;
1588 m_selectedTick = 0;
1589 m_selectStartKey = key_num;
1590 m_selectedKeys = 1;
1591 m_action = ActionSelectNotes;
1594 // call mousemove to fix glitch where selection
1595 // appears in wrong spot on mousedown
1596 mouseMoveEvent( _me );
1599 update();
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 ),
1610 midiTime() );
1613 else
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;
1623 repaint();
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 )
1641 return;
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() )
1686 if( _pause )
1688 // stop note
1689 m_pattern->instrumentTrack()->
1690 processInEvent(
1691 midiEvent( MidiNoteOff, 0,
1692 ( *it )->key(), 0 ),
1693 midiTime() );
1695 else
1697 // start note
1698 ( *it )->setIsPlaying( false );
1699 testPlayNote( *it );
1703 ++it;
1710 void pianoRoll::testPlayKey( int _key, int _vol, int _pan )
1712 // turn off old key
1713 m_pattern->instrumentTrack()->processInEvent(
1714 midiEvent( MidiNoteOff, 0, m_lastKey, 0 ),
1715 midiTime() );
1717 // remember which one we're playing
1718 m_lastKey = _key;
1720 // play new key
1721 m_pattern->instrumentTrack()->processInEvent(
1722 midiEvent( MidiNoteOn, 0, _key, _vol ),
1723 midiTime() );
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
1741 return;
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
1768 if( ! shift )
1770 ( *it )->setSelected( false );
1773 Sint32 len_ticks = ( *it )->length();
1775 if( len_ticks == 0 )
1777 continue;
1779 else if( len_ticks < 0 )
1781 len_ticks = 4;
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 );
1799 removeSelection();
1800 update();
1806 void pianoRoll::mouseReleaseEvent( QMouseEvent * _me )
1808 bool mustRepaint = false;
1810 if( _me->button() & Qt::LeftButton )
1812 m_mouseDownLeft = false;
1813 mustRepaint = true;
1815 if( _me->button() & Qt::RightButton )
1817 m_mouseDownRight = false;
1818 mustRepaint = true;
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()->
1864 processInEvent(
1865 midiEvent( MidiNoteOff, 0,
1866 ( *it )->key(), 0 ),
1867 midiTime() );
1868 ( *it )->setIsPlaying( false );
1871 ++it;
1874 // stop playing keys that we let go of
1875 m_pattern->instrumentTrack()->processInEvent(
1876 midiEvent( MidiNoteOff, 0, m_lastKey, 0 ),
1877 midiTime() );
1881 m_currentNote = NULL;
1883 m_action = ActionNone;
1885 if( m_editMode == ModeDraw )
1887 QApplication::restoreOverrideCursor();
1890 if( mustRepaint )
1892 repaint();
1899 void pianoRoll::mouseMoveEvent( QMouseEvent * _me )
1901 if( validPattern() == false )
1903 update();
1904 return;
1907 if( m_action == ActionNone && _me->buttons() == 0 )
1909 if( _me->y() > keyAreaBottom() && _me->y() < noteEditTop() )
1911 QApplication::setOverrideCursor(
1912 QCursor( Qt::SizeVerCursor ) );
1913 return;
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 );
1924 repaint();
1925 return;
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() );
1935 int x = _me->x();
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,
1945 0 );
1946 update();
1947 return;
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;
1960 if( replay_note )
1962 pauseTestNotes();
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
1982 // are in the range
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(
2034 midiEvent(
2035 MidiKeyPressure,
2037 ( *it )->key(),
2038 vol * 127 / 100),
2039 midiTime() );
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(
2048 evt, midiTime() );
2051 else
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 );
2064 ++it;
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
2086 // existing note
2087 if( pos_ticks >= ( *it )->pos() &&
2088 pos_ticks <= ( *it )->pos() +
2089 ( *it )->length() &&
2090 ( *it )->key() == key_num &&
2091 ( *it )->length() > 0 )
2093 break;
2095 ++it;
2098 // did it reach end of vector because there's
2099 // no note??
2100 if( it != notes.end() )
2102 // cursor at the "tail" of the note?
2103 if( ( *it )->length() > 0 &&
2104 pos_ticks*m_ppt /
2105 midiTime::ticksPerTact() >
2106 ( ( *it )->pos() +
2107 ( *it )->length() )*m_ppt/
2108 midiTime::ticksPerTact()-
2109 RESIZE_AREA_WIDTH )
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 );
2124 else
2126 QCursor c( Qt::SizeHorCursor );
2127 QApplication::setOverrideCursor(
2128 c );
2131 else
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(
2144 c );
2147 else
2149 QCursor c( Qt::SizeAllCursor );
2150 QApplication::setOverrideCursor(
2151 c );
2155 else
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 +
2173 m_currentPosition;
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 )
2184 --m_selectedKeys;
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 +
2193 m_currentPosition;
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();
2206 if( len < 0 )
2208 len = 4;
2210 // and check whether the user clicked on an
2211 // existing note or an edit-line
2212 if( pos_ticks >= ( *it )->pos() &&
2213 len > 0 &&
2215 ( edit_note == false &&
2216 pos_ticks <= ( *it )->pos() + len &&
2217 ( *it )->key() == key_num )
2219 ( edit_note == true &&
2220 pos_ticks <= ( *it )->pos() +
2221 NE_LINE_WIDTH *
2222 midiTime::ticksPerTact() /
2223 m_ppt )
2227 // delete this note
2228 if( it != notes.end() )
2230 if( ( *it )->length() > 0 )
2232 m_pattern->removeNote( *it );
2234 else
2236 ( *it )->setLength( 0 );
2237 m_pattern->dataChanged();
2239 engine::getSong()->setModified();
2242 else
2244 ++it;
2249 else
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 )
2259 x = 0;
2260 QCursor::setPos( mapToGlobal( QPoint(
2261 WHITE_KEY_WIDTH,
2262 _me->y() ) ) );
2263 if( m_currentPosition >= 4 )
2265 m_leftRightScroll->setValue(
2266 m_currentPosition - 4 );
2268 else
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(),
2277 _me->y() ) ) );
2278 m_leftRightScroll->setValue( m_currentPosition +
2279 4 );
2282 // get tick in which the cursor is posated
2283 int pos_ticks = x * midiTime::ticksPerTact()/ m_ppt +
2284 m_currentPosition;
2286 m_selectedTick = pos_ticks -
2287 m_selectStartTick;
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 -
2298 PR_BOTTOM_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 );
2309 key_num = s_key;
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 )
2323 --m_selectedKeys;
2326 QApplication::restoreOverrideCursor();
2329 m_lastMouseX = _me->x();
2330 m_lastMouseY = _me->y();
2332 update();
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
2353 if( ! alt )
2355 off_ticks = floor( off_ticks / quantization() )
2356 * 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 )
2388 // moving note
2389 int pos_ticks = ( *it )->oldPos().getTicks()
2390 + off_ticks;
2391 int key_num = ( *it )->oldKey() + off_key;
2393 if( pos_ticks < 0 )
2395 pos_ticks = 0;
2397 // upper/lower bound checks on key_num
2398 if( key_num < 0 )
2400 key_num = 0;
2402 else if( key_num > NumKeys )
2404 key_num = NumKeys;
2407 ( *it )->setPos( midiTime( pos_ticks ) );
2408 ( *it )->setKey( key_num );
2410 else if( m_action == ActionResizeNote )
2412 // resizing note
2413 int ticks_new = ( *it )->oldLength().getTicks()
2414 + off_ticks;
2415 if( ticks_new <= 0 )
2417 ticks_new = 1;
2419 ( *it )->setLength( midiTime( ticks_new ) );
2421 m_lenOfNewNotes = ( *it )->length();
2424 ++it;
2427 m_pattern->dataChanged();
2428 engine::getSong()->setModified();
2433 void pianoRoll::paintEvent( QPaintEvent * _pe )
2435 QStyleOption opt;
2436 opt.initFrom( this );
2437 QPainter p( 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
2444 int y_offset = 0;
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;
2457 break;
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] ==
2478 PR_BLACK_KEY )
2480 // draw it!
2481 p.drawPixmap( PIANO_X, y - WHITE_KEY_SMALL_HEIGHT,
2482 *s_whiteKeySmallPm );
2483 // update y-pos
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
2489 // is smaller)
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 );
2498 // update y-pos
2499 y -= WHITE_KEY_SMALL_HEIGHT;
2502 else if( prKeyOrder[key % KeysPerOctave] ==
2503 PR_WHITE_KEY_BIG )
2505 // draw a big one...
2506 p.drawPixmap( PIANO_X, y-WHITE_KEY_BIG_HEIGHT,
2507 *s_whiteKeyBigPm );
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;
2514 // update y-pos
2515 y -= WHITE_KEY_BIG_HEIGHT;
2517 // label C-keys...
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 ) );
2530 else
2532 p.setPen( QColor( 0x3F, 0x3F, 0x3F ) );
2534 // draw key-line
2535 p.drawLine( WHITE_KEY_WIDTH, key_line_y, width(), key_line_y );
2536 ++key;
2539 // reset all values, because now we're going to draw all black keys
2540 key = m_startKey;
2541 keys_processed = 0;
2542 int white_cnt = 0;
2544 // and go!
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
2549 // of piano-roll
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] ==
2556 PR_BLACK_KEY )
2558 // draw the black key!
2559 p.drawPixmap( PIANO_X, y - BLACK_KEY_HEIGHT / 2,
2560 *s_blackKeyPm );
2561 // is the one after the start-note a black key??
2562 if( prKeyOrder[( key + 1 ) % KeysPerOctave] !=
2563 PR_BLACK_KEY )
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 );
2579 // update y-pos
2580 y -= WHITE_KEY_BIG_HEIGHT;
2581 // reset white-counter
2582 white_cnt = 0;
2584 else
2586 // simple workaround for increasing x if there were
2587 // two white keys (e.g. between E and F)
2588 ++white_cnt;
2589 if( white_cnt > 1 )
2591 y -= WHITE_KEY_BIG_HEIGHT/2;
2595 ++key;
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
2606 QFont f = p.font();
2607 f.setBold( false );
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
2616 // keyboard...
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;
2629 if ( triplets ) {
2630 spt = static_cast<int>(1.5 * spt);
2631 bpt = static_cast<int>(bpt * 2.0/3.0);
2632 pp16th *= 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 ) );
2654 // normal line
2655 else if( tact_16th % 4 == 0 )
2657 p.setPen( QColor( 0x5F, 0x5F, 0x5F ) );
2659 // weak line
2660 else
2662 p.setPen( QColor( 0x3F, 0x3F, 0x3F ) );
2665 p.drawLine( (int)x, PR_TOP_MARGIN, (int)x, height() -
2666 PR_BOTTOM_MARGIN );
2668 // extra 32nd's line
2669 if( show32nds )
2671 p.setPen( QColor( 0x22, 0x22, 0x22 ) );
2672 p.drawLine( (int)(x + pp16th/2) , PR_TOP_MARGIN,
2673 (int)(x + pp16th/2), height() -
2674 PR_BOTTOM_MARGIN );
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 )
2720 continue;
2722 else if( len_ticks < 0 )
2724 len_ticks = 4;
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 ) )
2739 continue;
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
2747 // note
2748 drawNoteRect( p, x + WHITE_KEY_WIDTH,
2749 y_base - key * KEY_LINE_HEIGHT,
2750 note_width, *it );
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(),
2794 editHandleTop+1 );
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 );
2809 else
2811 QFont f = p.font();
2812 f.setBold( true );
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 "
2817 "on it!" ) );
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 )
2864 case ModeDraw:
2865 if( m_mouseDownRight )
2867 cursor = s_toolErase;
2869 else if( m_action == ActionMoveNote )
2871 cursor = s_toolMove;
2873 else
2875 cursor = s_toolDraw;
2877 break;
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 ),
2885 *cursor );
2892 // responsible for moving/resizing scrollbars after window-resizing
2893 void pianoRoll::resizeEvent( QResizeEvent * )
2895 m_leftRightScroll->setGeometry( WHITE_KEY_WIDTH, height() -
2896 SCROLLBAR_SIZE,
2897 width()-WHITE_KEY_WIDTH,
2898 SCROLLBAR_SIZE );
2899 m_topBottomScroll->setGeometry( width() - SCROLLBAR_SIZE, PR_TOP_MARGIN,
2900 SCROLLBAR_SIZE,
2901 height() - PR_TOP_MARGIN -
2902 SCROLLBAR_SIZE );
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() );
2920 update();
2926 void pianoRoll::wheelEvent( QWheelEvent * _we )
2928 _we->accept();
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 )
2938 m_ppt /= 2;
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 ) ) +"%" ) );
2945 // update timeline
2946 m_timeLine->setPixelsPerTact( m_ppt );
2947 update();
2949 else if( _we->modifiers() & Qt::ShiftModifier
2950 || _we->orientation() == Qt::Horizontal )
2952 m_leftRightScroll->setValue( m_leftRightScroll->value() -
2953 _we->delta() * 2 / 15 );
2955 else
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
2973 if( key_num < 0 )
2975 key_num = 0;
2978 if( key_num >= KeysPerOctave * NumOctaves )
2980 key_num = KeysPerOctave * NumOctaves - 1;
2983 return key_num;
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 )
3006 return;
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(
3016 "pause" ) );
3018 else
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" ) );
3029 else
3031 m_playButton->setIcon( embed::getIconPixmap( "pause" ) );
3032 engine::getSong()->playPattern( m_pattern );
3039 void pianoRoll::record()
3041 if( engine::getSong()->isPlaying() )
3043 stop();
3045 if( m_recording == true || validPattern() == false )
3047 return;
3050 m_recording = true;
3052 engine::getSong()->playPattern( m_pattern, false );
3058 void pianoRoll::recordAccompany()
3060 if( engine::getSong()->isPlaying() )
3062 stop();
3064 if( m_recording == true || validPattern() == false )
3066 return;
3069 m_recording = true;
3071 if( m_pattern->getTrack()->getTrackContainer() == engine::getSong() )
3073 engine::getSong()->play();
3075 else
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 ) )
3106 midiTime sub;
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() );
3114 if( n.pos() >= 0 )
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(),
3140 it->getPanning() );
3141 n.quantizeLength( quantization() );
3142 m_pattern->addNote( n );
3143 update();
3144 m_recordingNotes.erase( it );
3145 break;
3154 void pianoRoll::horScrolled( int _new_pos )
3156 m_currentPosition = _new_pos;
3157 emit positionChanged( m_currentPosition );
3158 update();
3164 void pianoRoll::verScrolled( int _new_pos )
3166 // revert value
3167 m_startKey = m_totalKeysToScroll - _new_pos;
3169 update();
3175 void pianoRoll::drawButtonToggled()
3177 m_editMode = ModeDraw;
3178 update();
3184 void pianoRoll::eraseButtonToggled()
3186 m_editMode = ModeErase;
3187 update();
3193 void pianoRoll::selectButtonToggled()
3195 m_editMode = ModeSelect;
3196 update();
3201 void pianoRoll::detuneButtonToggled()
3203 m_editMode = ModeEditDetuning;
3204 update();
3209 void pianoRoll::selectAll()
3211 if( validPattern() == false )
3213 return;
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();
3222 ++it )
3224 Uint32 len_ticks = ( *it )->length();
3226 if( len_ticks > 0 )
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
3238 - ( key - 1 );
3239 m_selectStartKey = key - 1;
3241 if( key >= m_selectedKeys+m_selectStartKey ||
3242 first_time )
3244 m_selectedKeys = key - m_selectStartKey;
3246 if( pos_ticks < m_selectStartTick ||
3247 first_time )
3249 m_selectStartTick = pos_ticks;
3251 if( pos_ticks + len_ticks >
3252 m_selectStartTick + m_selectedTick ||
3253 first_time )
3255 m_selectedTick = pos_ticks +
3256 len_ticks -
3257 m_selectStartTick;
3259 first_time = false;
3267 // returns vector with pointers to all selected notes
3268 void pianoRoll::getSelectedNotes( NoteVector & _selected_notes )
3270 if( validPattern() == false )
3272 return;
3275 const NoteVector & notes = m_pattern->notes();
3277 for( NoteVector::ConstIterator it = notes.begin(); it != notes.end();
3278 ++it )
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();
3298 ++it )
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 )
3332 return;
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 );
3353 update();
3354 engine::getSongEditor()->update();
3360 void pianoRoll::pasteNotes()
3362 if( validPattern() == false )
3364 return;
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 )
3383 // create the note
3384 note cur_note;
3385 cur_note.restoreState( list.item( i ).toElement() );
3386 cur_note.setPos( cur_note.pos() + m_timeLine->pos() );
3388 // select it
3389 cur_note.setSelected( true );
3391 // add to pattern
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 );
3400 update();
3401 engine::getSongEditor()->update();
3408 void pianoRoll::deleteSelectedNotes()
3410 if( validPattern() == false )
3412 return;
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() )
3427 // delete this note
3428 m_pattern->removeNote( ( *it ) );
3429 update_after_delete = true;
3431 // start over, make sure we get all the notes
3432 it = notes.begin();
3434 else
3436 ++it;
3440 if( update_after_delete == true )
3442 engine::getSong()->setModified();
3443 update();
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 )
3481 autoScroll( _t );
3488 void pianoRoll::updatePositionAccompany( const midiTime & _t )
3490 song * s = engine::getSong();
3492 if( m_recording && validPattern() &&
3493 s->playMode() != song::Mode_PlayPattern )
3495 midiTime pos = _t;
3496 if( s->playMode() != song::Mode_PlayBB )
3498 pos -= m_pattern->startPosition();
3500 if( (int) pos > 0 )
3502 s->getPlayPos( song::Mode_PlayPattern ).setTicks( pos );
3503 autoScroll( 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;
3515 #ifdef LMMS_DEBUG
3516 assert( m_ppt > 0 );
3517 #endif
3518 m_timeLine->setPixelsPerTact( m_ppt );
3519 update();
3526 void pianoRoll::quantizeChanged()
3528 if( m_quantizeModel.value() == 0 &&
3529 m_noteLenModel.value() == 0 )
3531 m_quantizeModel.setValue( m_quantizeModel.findText( "1/16" ) );
3532 return;
3534 // Could be smarter
3535 update();
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() -
3547 2 ).toInt();
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() -
3561 2 ).toInt();
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() )
3586 return NULL;
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
3600 // existing note
3601 if( pos_ticks >= ( *it )->pos() &&
3602 pos_ticks <= ( *it )->pos() + ( *it )->length() &&
3603 ( *it )->key() == key_num && ( *it )->length() > 0 )
3605 break;
3607 ++it;
3610 return *it;
3616 #include "moc_piano_roll.cxx"