From fdeef1e0d0973f3ae294f82ab1fb64e4aed83cc5 Mon Sep 17 00:00:00 2001 From: Javier Serrano Polo Date: Thu, 29 Jun 2006 01:01:13 +0000 Subject: [PATCH] renamed time* classes to automation* ones git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@183 0778d3d1-df1d-0410-868b-ea421aaaa00d --- Makefile.am | 12 +- include/automation_editor.h | 258 ++++ include/automation_pattern.h | 111 ++ include/engine.h | 8 +- include/main_window.h | 2 +- include/song_editor.h | 2 +- include/track.h | 8 +- src/core/automation_editor.cpp | 2082 +++++++++++++++++++++++++++++ src/core/engine.cpp | 10 +- src/core/main_window.cpp | 40 +- src/core/track.cpp | 12 +- src/lmms_single_source.cpp | 4 +- src/tracks/automation_pattern.cpp | 249 ++++ src/tracks/instrument_track.cpp | 2 +- src/tracks/sample_track.cpp | 3 +- src/widgets/knob.cpp | 8 +- 16 files changed, 2757 insertions(+), 54 deletions(-) create mode 100644 include/automation_editor.h create mode 100644 include/automation_pattern.h create mode 100644 src/core/automation_editor.cpp create mode 100644 src/tracks/automation_pattern.cpp diff --git a/Makefile.am b/Makefile.am index 6538344ae0..7c4c23af76 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,6 +48,8 @@ lmms_MOC = \ ./about_dialog.moc \ ./arp_and_chords_tab_widget.moc \ ./automatable_button.moc \ + ./automation_editor.moc \ + ./automation_pattern.moc \ ./bb_editor.moc \ ./bb_track.moc \ ./instrument_track.moc \ @@ -92,8 +94,6 @@ lmms_MOC = \ ./tab_button.moc \ ./tab_widget.moc \ ./tempo_sync_knob.moc \ - ./time_pattern.moc \ - ./time_roll.moc \ ./timeline.moc \ ./tool_button.moc \ ./track_container.moc \ @@ -125,6 +125,7 @@ lmms_SOURCES = \ $(srcdir)/src/audio/audio_sdl.cpp \ $(srcdir)/src/core/about_dialog.cpp \ $(srcdir)/src/core/arp_and_chords_tab_widget.cpp \ + $(srcdir)/src/core/automation_editor.cpp \ $(srcdir)/src/core/bb_editor.cpp \ $(srcdir)/src/core/config_mgr.cpp \ $(srcdir)/src/core/engine.cpp \ @@ -152,7 +153,6 @@ lmms_SOURCES = \ $(srcdir)/src/core/track.cpp \ $(srcdir)/src/core/track_container.cpp \ $(srcdir)/src/core/surround_area.cpp \ - $(srcdir)/src/core/time_roll.cpp \ $(srcdir)/src/core/timeline.cpp \ $(srcdir)/src/lib/base64.cpp \ $(srcdir)/src/lib/buffer_allocator.cpp \ @@ -171,11 +171,11 @@ lmms_SOURCES = \ $(srcdir)/src/midi/midi_mapper.cpp \ $(srcdir)/src/midi/midi_oss.cpp \ $(srcdir)/src/midi/midi_port.cpp \ + $(srcdir)/src/tracks/automation_pattern.cpp \ $(srcdir)/src/tracks/bb_track.cpp \ $(srcdir)/src/tracks/instrument_track.cpp \ $(srcdir)/src/tracks/pattern.cpp \ $(srcdir)/src/tracks/sample_track.cpp \ - $(srcdir)/src/tracks/time_pattern.cpp \ $(srcdir)/src/widgets/automatable_button.cpp \ $(srcdir)/src/widgets/combobox.cpp \ $(srcdir)/src/widgets/cpuload_widget.cpp \ @@ -214,6 +214,8 @@ lmms_SOURCES = \ $(srcdir)/include/audio_oss.h \ $(srcdir)/include/audio_sample_recorder.h \ $(srcdir)/include/audio_sdl.h \ + $(srcdir)/include/automation_editor.h \ + $(srcdir)/include/automation_pattern.h \ $(srcdir)/include/interpolation.h \ $(srcdir)/include/lmms_constants.h \ $(srcdir)/include/lmms_math.h \ @@ -272,8 +274,6 @@ lmms_SOURCES = \ $(srcdir)/include/types.h \ $(srcdir)/include/qt3support.h \ $(srcdir)/include/embed.h \ - $(srcdir)/include/time_pattern.h \ - $(srcdir)/include/time_roll.h \ $(srcdir)/include/timeline.h \ $(srcdir)/include/config_mgr.h \ $(srcdir)/include/spc_bg_hndl_widget.h \ diff --git a/include/automation_editor.h b/include/automation_editor.h new file mode 100644 index 0000000000..fc884e0d02 --- /dev/null +++ b/include/automation_editor.h @@ -0,0 +1,258 @@ +/* + * automation_editor.h - declaration of class automationEditor which is a window + * where you can edit dynamic values in an easy way + * + * Copyright (c) 2006 Javier Serrano Polo + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _AUTOMATION_EDITOR_H +#define _AUTOMATION_EDITOR_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#include + +#endif + +#include "types.h" +#include "note.h" +#include "engine.h" +#include "journalling_object.h" +#include "automation_pattern.h" + + +class QPainter; +class QPixmap; +class QScrollBar; + +class comboBox; +class notePlayHandle; +class timeLine; +class toolButton; + + +class automationEditor : public QWidget, public journallingObject +{ + Q_OBJECT +public: + void FASTCALL setCurrentPattern( automationPattern * _new_pattern ); + + inline const automationPattern * currentPattern( void ) const + { + return( m_pattern ); + } + + inline bool validPattern( void ) const + { + return( m_pattern != NULL ); + } + + int quantization( void ) const; + + + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + inline virtual QString nodeName( void ) const + { + return( "automation-editor" ); + } + + +public slots: + virtual void update( void ); + + +protected: + typedef automationPattern::timeMap timeMap; + + virtual void closeEvent( QCloseEvent * _ce ); + virtual void enterEvent( QEvent * _e ); + virtual void keyPressEvent( QKeyEvent * _ke ); + virtual void leaveEvent( QEvent * _e ); + virtual void mousePressEvent( QMouseEvent * _me ); + virtual void mouseReleaseEvent( QMouseEvent * _me ); + virtual void mouseMoveEvent( QMouseEvent * _me ); + virtual void paintEvent( QPaintEvent * _pe ); + virtual void resizeEvent( QResizeEvent * _re ); + virtual void wheelEvent( QWheelEvent * _we ); + + int FASTCALL getValue( int _y ); + static inline void drawValueRect( QPainter & _p, Uint16 _x, Uint16 _y, + Sint16 _width, Sint16 _height, + const bool _is_selected ); + void removeSelection( void ); + void selectAll( void ); + void FASTCALL getSelectedValues( timeMap & _selected_values ); + + +protected slots: + void play( void ); + void stop( void ); + + void horScrolled( int _new_pos ); + void verScrolled( int _new_pos ); + + void drawButtonToggled( void ); + void eraseButtonToggled( void ); + void selectButtonToggled( void ); + void moveButtonToggled( void ); + + void copySelectedValues( void ); + void cutSelectedValues( void ); + void pasteValues( void ); + void deleteSelectedValues( void ); + + void updatePosition( const midiTime & _t ); + + void zoomingXChanged( const QString & _zfac ); + void zoomingYChanged( const QString & _zfac ); + + +private: + + enum editModes + { + DRAW, + ERASE, + SELECT, + MOVE + } ; + + enum actions + { + NONE, + MOVE_VALUE, + SELECT_VALUES, + MOVE_SELECTION + } ; + + // some constants... + static const int INITIAL_WIDTH = 640; + static const int INITIAL_HEIGHT = 480; + + static const int SCROLLBAR_SIZE = 16; + static const int TOP_MARGIN = 48; + + static const int DEFAULT_Y_DELTA = 12; + static const int DEFAULT_STEPS_PER_TACT = 16; + static const int DEFAULT_PPT = 12 * DEFAULT_STEPS_PER_TACT; + + static const int VALUES_WIDTH = 64; + + automationEditor( engine * _engine ); + automationEditor( const automationEditor & ); + virtual ~automationEditor(); + + + static QPixmap * s_toolDraw; + static QPixmap * s_toolErase; + static QPixmap * s_toolSelect; + static QPixmap * s_toolMove; + + + QWidget * m_toolBar; + + toolButton * m_playButton; + toolButton * m_stopButton; + + toolButton * m_drawButton; + toolButton * m_eraseButton; + toolButton * m_selectButton; + toolButton * m_moveButton; + + toolButton * m_cutButton; + toolButton * m_copyButton; + toolButton * m_pasteButton; + + comboBox * m_zoomingXComboBox; + comboBox * m_zoomingYComboBox; + comboBox * m_quantizeComboBox; + + QPixmap m_paintPixmap; + bool m_cursorInside; + + + automationPattern * m_pattern; + int m_min_level; + int m_max_level; + int m_scroll_level; + int m_bottom_level; + int m_top_level; + + void updateTopBottomLevels( void ); + + QScrollBar * m_leftRightScroll; + QScrollBar * m_topBottomScroll; + + midiTime m_currentPosition; + + actions m_action; + + Uint32 m_selectStartTact64th; + int m_selectedTact64th; + int m_selectStartKey; + int m_selectedKeys; + + int m_moveStartKey; + int m_moveStartTact64th; + int m_moveXOffset; + + int m_ppt; + int m_y_delta; + + timeMap m_valuesToCopy; + timeMap m_selValuesForMove; + + + editModes m_editMode; + + + timeLine * m_timeLine; + bool m_scrollBack; + + bool xVisible( int _x ); + bool inBBEditor( void ); + + + + friend class engine; + + +signals: + void positionChanged( const midiTime & ); + +} ; + + +#endif + diff --git a/include/automation_pattern.h b/include/automation_pattern.h new file mode 100644 index 0000000000..cd6fe08922 --- /dev/null +++ b/include/automation_pattern.h @@ -0,0 +1,111 @@ +/* + * automation_pattern.h - declaration of class automationPattern, which contains + * all information about an automation pattern + * + * Copyright (c) 2006 Javier Serrano Polo + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _AUTOMATION_PATTERN_H +#define _AUTOMATION_PATTERN_H + +#include "qt3support.h" +#include "track.h" +#include "level_object.h" + + + + +class automationPattern : public QObject, public journallingObject +{ + Q_OBJECT +public: + typedef QMap timeMap; + + automationPattern( track * _channel_track, levelObject * _object ); + automationPattern( const automationPattern & _pat_to_copy ); + virtual ~automationPattern(); + + + virtual midiTime length( void ) const; + + midiTime FASTCALL putValue( const midiTime & _time, const int _value, + const bool _quant_pos = TRUE ); + + void FASTCALL removeValue( const midiTime & _time ); + + void clearValues( void ); + + inline timeMap & getTimeMap( void ) + { + return( m_time_map ); + } + + int FASTCALL valueAt( const midiTime & _time ); + + const QString name( void ); + + // settings-management + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + + static inline const QString classNodeName( void ) + { + return( "automation-pattern" ); + } + + inline virtual QString nodeName( void ) const + { + return( classNodeName() ); + } + + inline track * getTrack( void ) + { + return( m_track ); + } + + inline levelObject * object( void ) + { + return( m_object ); + } + + void processMidiTime( const midiTime & _time ); + + +protected slots: + void openInAutomationEditor( void ); + void clear( void ); + + +private: + track * m_track; + levelObject * m_object; + timeMap m_time_map; + + void init( void ); + +} ; + + + + +#endif diff --git a/include/engine.h b/include/engine.h index 30a876aa20..79ee713e35 100644 --- a/include/engine.h +++ b/include/engine.h @@ -26,6 +26,7 @@ #ifndef _ENGINE_H #define _ENGINE_H +class automationEditor; class bbEditor; class projectJournal; class mainWindow; @@ -33,7 +34,6 @@ class mixer; class pianoRoll; class projectNotes; class songEditor; -class timeRoll; class engine @@ -98,9 +98,9 @@ public: return( m_projectJournal ); } - inline timeRoll * getTimeRoll( void ) + inline automationEditor * getAutomationEditor( void ) { - return( m_timeRoll ); + return( m_automationEditor ); } @@ -110,11 +110,11 @@ private: mixer * m_mixer; mainWindow * m_mainWindow; songEditor * m_songEditor; + automationEditor * m_automationEditor; bbEditor * m_bbEditor; pianoRoll * m_pianoRoll; projectNotes * m_projectNotes; projectJournal * m_projectJournal; - timeRoll * m_timeRoll; } ; diff --git a/include/main_window.h b/include/main_window.h index e3945dea4c..04cef9987f 100644 --- a/include/main_window.h +++ b/include/main_window.h @@ -118,11 +118,11 @@ public slots: void showSettingsDialog( void ); void aboutLMMS( void ); void help( void ); + void toggleAutomationEditorWin( void ); void toggleBBEditorWin( void ); void toggleSongEditorWin( void ); void toggleProjectNotesWin( void ); void togglePianoRollWin( void ); - void toggleTimeRollWin( void ); void undo( void ); void redo( void ); diff --git a/include/song_editor.h b/include/song_editor.h index 18e2b4073a..033c9bd64a 100644 --- a/include/song_editor.h +++ b/include/song_editor.h @@ -59,7 +59,7 @@ public: PLAY_TRACK, PLAY_BB, PLAY_PATTERN, - PLAY_TIME_PATTERN, + PLAY_AUTOMATION_PATTERN, PLAY_MODE_CNT } ; diff --git a/include/track.h b/include/track.h index 93b06f12a3..04610beb73 100644 --- a/include/track.h +++ b/include/track.h @@ -62,9 +62,9 @@ class QMenu; class QPushButton; +class automationPattern; class pixmapButton; class textFloat; -class timePattern; class track; class trackContainer; class trackContentWidget; @@ -485,8 +485,8 @@ public: return( &m_trackWidget->getTrackContentWidget() ); } - void addTimePattern( timePattern * _pattern ); - void removeTimePattern( timePattern * _pattern ); + void addAutomationPattern( automationPattern * _pattern ); + void removeAutomationPattern( automationPattern * _pattern ); // name-stuff inline virtual const QString & name( void ) const @@ -507,7 +507,7 @@ protected: private: trackContainer * m_trackContainer; trackWidget * m_trackWidget; - QPtrList m_time_patterns; + QPtrList m_automation_patterns; } ; diff --git a/src/core/automation_editor.cpp b/src/core/automation_editor.cpp new file mode 100644 index 0000000000..a5a69e3c70 --- /dev/null +++ b/src/core/automation_editor.cpp @@ -0,0 +1,2082 @@ +#ifndef SINGLE_SOURCE_COMPILE + +/* + * automation_editor.cpp - implementation of automationEditor which is used for + * actual setting of dynamic values + * + * Copyright (c) 2006 Javier Serrano Polo + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include +#include +#include +#include +#include + +#else + +#include +#include +#include +#include +#include +#include + +#define addButton insert +#define setCheckable setToggleButton + +#endif + + +#ifndef __USE_XOPEN +#define __USE_XOPEN +#endif + +#include + + +#include "automation_editor.h" +#include "song_editor.h" +#include "main_window.h" +#include "embed.h" +#include "pixmap_button.h" +#include "templates.h" +#include "gui_templates.h" +#include "timeline.h" +#include "tooltip.h" +#include "midi.h" +#include "tool_button.h" +#include "text_float.h" +#include "combobox.h" +#include "bb_editor.h" + + +QPixmap * automationEditor::s_toolDraw = NULL; +QPixmap * automationEditor::s_toolErase = NULL; +QPixmap * automationEditor::s_toolSelect = NULL; +QPixmap * automationEditor::s_toolMove = NULL; + + +automationEditor::automationEditor( engine * _engine ) : + QWidget( _engine->getMainWindow()->workspace() ), + journallingObject( _engine ), + m_paintPixmap(), + m_cursorInside( FALSE ), + m_pattern( NULL ), + m_min_level( 0 ), + m_max_level( 0 ), + m_scroll_level( 0 ), + m_bottom_level( 0 ), + m_top_level( 0 ), + m_currentPosition(), + m_action( NONE ), + m_moveStartKey( 0 ), + m_moveStartTact64th( 0 ), + m_ppt( DEFAULT_PPT ), + m_y_delta( DEFAULT_Y_DELTA ), + m_editMode( DRAW ), + m_scrollBack( FALSE ) +{ + // init pixmaps + if( s_toolDraw == NULL ) + { + s_toolDraw = new QPixmap( embed::getIconPixmap( + "edit_draw" ) ); + } + if( s_toolErase == NULL ) + { + s_toolErase= new QPixmap( embed::getIconPixmap( + "edit_erase" ) ); + } + if( s_toolSelect == NULL ) + { + s_toolSelect = new QPixmap( embed::getIconPixmap( + "edit_select" ) ); + } + if( s_toolMove == NULL ) + { + s_toolMove = new QPixmap( embed::getIconPixmap( + "edit_move" ) ); + } + +#ifdef QT4 + // add us to workspace + eng()->getMainWindow()->workspace()->addWindow( this ); +#endif + + // add time-line + m_timeLine = new timeLine( VALUES_WIDTH, 32, m_ppt, + eng()->getSongEditor()->getPlayPos( + songEditor::PLAY_AUTOMATION_PATTERN ), + m_currentPosition, this, eng() ); + connect( this, SIGNAL( positionChanged( const midiTime & ) ), + m_timeLine, SLOT( updatePosition( const midiTime & ) ) ); + connect( m_timeLine, SIGNAL( positionChanged( const midiTime & ) ), + this, SLOT( updatePosition( const midiTime & ) ) ); + + + m_toolBar = new QWidget( this ); + m_toolBar->setFixedHeight( 32 ); + m_toolBar->move( 0, 0 ); +#ifdef QT4 + m_toolBar->setAutoFillBackground( TRUE ); + QPalette pal; + pal.setBrush( m_toolBar->backgroundRole(), + embed::getIconPixmap( "toolbar_bg" ) ); + m_toolBar->setPalette( pal ); +#else + m_toolBar->setPaletteBackgroundPixmap( embed::getIconPixmap( + "toolbar_bg" ) ); +#endif + + QHBoxLayout * tb_layout = new QHBoxLayout( m_toolBar ); + tb_layout->setMargin( 0 ); + tb_layout->setSpacing( 0 ); + + + // init control-buttons at the top + + m_playButton = new toolButton( embed::getIconPixmap( "play" ), + tr( "Play/pause current pattern (Space)" ), + this, SLOT( play() ), m_toolBar ); + + m_stopButton = new toolButton( embed::getIconPixmap( "stop" ), + tr( "Stop playing of current pattern (Space)" ), + this, SLOT( stop() ), m_toolBar ); + +#ifdef QT4 + m_playButton->setWhatsThis( +#else + QWhatsThis::add( m_playButton, +#endif + tr( "Click here, if you want to play the current pattern. " + "This is useful while editing it. The pattern is " + "automatically looped when its end is reached." ) ); +#ifdef QT4 + m_stopButton->setWhatsThis( +#else + QWhatsThis::add( m_stopButton, +#endif + tr( "Click here, if you want to stop playing of current " + "pattern." ) ); + + + + removeSelection(); + + // init scrollbars + m_leftRightScroll = new QScrollBar( Qt::Horizontal, this ); + m_topBottomScroll = new QScrollBar( Qt::Vertical, this ); + connect( m_leftRightScroll, SIGNAL( valueChanged( int ) ), this, + SLOT( horScrolled( int ) ) ); + connect( m_topBottomScroll, SIGNAL( valueChanged( int ) ), this, + SLOT( verScrolled( int ) ) ); + + // init edit-buttons at the top + m_drawButton = new toolButton( embed::getIconPixmap( "edit_draw" ), + tr( "Draw mode (D)" ), + this, SLOT( drawButtonToggled() ), + m_toolBar ); + m_drawButton->setCheckable( TRUE ); + m_drawButton->setChecked( TRUE ); + + m_eraseButton = new toolButton( embed::getIconPixmap( "edit_erase" ), + tr( "Erase mode (E)" ), + this, SLOT( eraseButtonToggled() ), + m_toolBar ); + m_eraseButton->setCheckable( TRUE ); + + m_selectButton = new toolButton( embed::getIconPixmap( + "edit_select" ), + tr( "Select mode (S)" ), + this, SLOT( selectButtonToggled() ), + m_toolBar ); + m_selectButton->setCheckable( TRUE ); + + m_moveButton = new toolButton( embed::getIconPixmap( "edit_move" ), + tr( "Move selection mode (M)" ), + this, SLOT( moveButtonToggled() ), + m_toolBar ); + m_moveButton->setCheckable( TRUE ); + + QButtonGroup * tool_button_group = new QButtonGroup( this ); + tool_button_group->addButton( m_drawButton ); + tool_button_group->addButton( m_eraseButton ); + tool_button_group->addButton( m_selectButton ); + tool_button_group->addButton( m_moveButton ); + tool_button_group->setExclusive( TRUE ); +#ifndef QT4 + tool_button_group->hide(); +#endif + +#ifdef QT4 + m_drawButton->setWhatsThis( +#else + QWhatsThis::add( m_drawButton, +#endif + tr( "If you click here, draw-mode will be activated. In this " + "mode you can add, resize and move single values. This " + "is the default-mode which is used most of the time. " + "You can also press 'D' on your keyboard to activate " + "this mode." ) ); +#ifdef QT4 + m_eraseButton->setWhatsThis( +#else + QWhatsThis::add( m_eraseButton, +#endif + tr( "If you click here, erase-mode will be activated. In this " + "mode you can erase single values. You can also press " + "'E' on your keyboard to activate this mode." ) ); +#ifdef QT4 + m_selectButton->setWhatsThis( +#else + QWhatsThis::add( m_selectButton, +#endif + tr( "If you click here, select-mode will be activated. In this " + "mode you can select values. This is neccessary " + "if you want to cut, copy, paste, delete or move " + "values. You can also press 'S' on your keyboard to " + "activate this mode." ) ); +#ifdef QT4 + m_moveButton->setWhatsThis( +#else + QWhatsThis::add( m_moveButton, +#endif + tr( "If you click here, move-mode will be activated. In this " + "mode you can move the values you selected in select-" + "mode. You can also press 'M' on your keyboard to " + "activate this mode." ) ); + + m_cutButton = new toolButton( embed::getIconPixmap( "edit_cut" ), + tr( "Cut selected values (Ctrl+X)" ), + this, SLOT( cutSelectedValues() ), + m_toolBar ); + + m_copyButton = new toolButton( embed::getIconPixmap( "edit_copy" ), + tr( "Copy selected values (Ctrl+C)" ), + this, SLOT( copySelectedValues() ), + m_toolBar ); + + m_pasteButton = new toolButton( embed::getIconPixmap( "edit_paste" ), + tr( "Paste values from clipboard " + "(Ctrl+V)" ), + this, SLOT( pasteValues() ), + m_toolBar ); + +#ifdef QT4 + m_cutButton->setWhatsThis( +#else + QWhatsThis::add( m_cutButton, +#endif + tr( "If you click here, selected values will be cut into the " + "clipboard. You can paste them anywhere in any pattern " + "by clicking on the paste-button." ) ); +#ifdef QT4 + m_copyButton->setWhatsThis( +#else + QWhatsThis::add( m_copyButton, +#endif + tr( "If you click here, selected values will be copied into " + "the clipboard. You can paste them anywhere in any " + "pattern by clicking on the paste-button." ) ); +#ifdef QT4 + m_pasteButton->setWhatsThis( +#else + QWhatsThis::add( m_pasteButton, +#endif + tr( "If you click here, the values from the clipboard will be " + "pasted at the first visible tact." ) ); + + + + // setup zooming-stuff + QLabel * zoom_x_lbl = new QLabel( m_toolBar ); + zoom_x_lbl->setPixmap( embed::getIconPixmap( "zoom_x" ) ); + + m_zoomingXComboBox = new comboBox( m_toolBar, eng() ); + m_zoomingXComboBox->setFixedSize( 80, 22 ); + for( int i = 0; i < 6; ++i ) + { + m_zoomingXComboBox->addItem( QString::number( 25 << i ) + "%" ); + } + m_zoomingXComboBox->setValue( m_zoomingXComboBox->findText( "100%" ) ); + connect( m_zoomingXComboBox, SIGNAL( activated( const QString & ) ), + this, SLOT( zoomingXChanged( const QString & ) ) ); + + + QLabel * zoom_y_lbl = new QLabel( m_toolBar ); + zoom_y_lbl->setPixmap( embed::getIconPixmap( "zoom_y" ) ); + + m_zoomingYComboBox = new comboBox( m_toolBar, eng() ); + m_zoomingYComboBox->setFixedSize( 80, 22 ); + for( int i = 0; i < 6; ++i ) + { + m_zoomingYComboBox->addItem( QString::number( 25 << i ) + "%" ); + } + m_zoomingYComboBox->setValue( m_zoomingYComboBox->findText( "100%" ) ); + connect( m_zoomingYComboBox, SIGNAL( activated( const QString & ) ), + this, SLOT( zoomingYChanged( const QString & ) ) ); + + + // setup quantize-stuff + QLabel * quantize_lbl = new QLabel( m_toolBar ); + quantize_lbl->setPixmap( embed::getIconPixmap( "quantize" ) ); + + m_quantizeComboBox = new comboBox( m_toolBar, eng() ); + m_quantizeComboBox->setFixedSize( 60, 22 ); + for( int i = 0; i < 7; ++i ) + { + m_quantizeComboBox->addItem( "1/" + QString::number( 1 << i ) ); + } + m_quantizeComboBox->setValue( m_quantizeComboBox->findText( + "1/16" ) ); + + tb_layout->addSpacing( 5 ); + tb_layout->addWidget( m_playButton ); + tb_layout->addWidget( m_stopButton ); + tb_layout->addSpacing( 10 ); + tb_layout->addWidget( m_drawButton ); + tb_layout->addWidget( m_eraseButton ); + tb_layout->addWidget( m_selectButton ); + tb_layout->addWidget( m_moveButton ); + tb_layout->addSpacing( 10 ); + tb_layout->addWidget( m_cutButton ); + tb_layout->addWidget( m_copyButton ); + tb_layout->addWidget( m_pasteButton ); + tb_layout->addSpacing( 10 ); + m_timeLine->addToolButtons( m_toolBar ); + tb_layout->addSpacing( 15 ); + tb_layout->addWidget( zoom_x_lbl ); + tb_layout->addSpacing( 4 ); + tb_layout->addWidget( m_zoomingXComboBox ); + tb_layout->addSpacing( 10 ); + tb_layout->addWidget( zoom_y_lbl ); + tb_layout->addSpacing( 4 ); + tb_layout->addWidget( m_zoomingYComboBox ); + tb_layout->addSpacing( 10 ); + tb_layout->addWidget( quantize_lbl ); + tb_layout->addSpacing( 4 ); + tb_layout->addWidget( m_quantizeComboBox ); + tb_layout->addStretch(); + + // setup our actual window +//TODO: change icon + setWindowIcon( embed::getIconPixmap( "piano" ) ); + resize( INITIAL_WIDTH, INITIAL_HEIGHT ); + setCurrentPattern( NULL ); + +#ifndef QT4 + setBackgroundMode( Qt::NoBackground ); +#endif + setMouseTracking( TRUE ); + + hide(); + +} + + + + +automationEditor::~automationEditor() +{ +} + + + + +void automationEditor::setCurrentPattern( automationPattern * _new_pattern ) +{ + m_pattern = _new_pattern; + m_currentPosition = 0; + + if( validPattern() == FALSE ) + { + //resizeEvent( NULL ); + setWindowTitle( tr( "Automation Editor - no pattern" ) ); + + update(); + return; + } + + m_min_level = m_pattern->object()->minLevel(); + m_max_level = m_pattern->object()->maxLevel(); + m_scroll_level = ( m_min_level + m_max_level ) / 2; + + timeMap & time_map = m_pattern->getTimeMap(); + int central_key = 0; + if( !time_map.isEmpty() ) + { + // determine the central key so that we can scroll to it + int total_values = 0; + for( timeMap::iterator it = time_map.begin(); + it != time_map.end(); ++it ) + { + central_key += it.data(); + ++total_values; + } + + } + // resizeEvent() does the rest for us (scrolling, range-checking + // of levels and so on...) + resizeEvent( NULL ); + + setWindowTitle( tr( "Automation Editor - %1" ).arg( + m_pattern->name() ) ); + + update(); +} + + + + +void automationEditor::saveSettings( QDomDocument & _doc, QDomElement & _this ) +{ + mainWindow::saveWidgetState( this, _this ); +} + + + + +void automationEditor::loadSettings( const QDomElement & _this ) +{ + mainWindow::restoreWidgetState( this, _this ); +} + + + + +inline void automationEditor::drawValueRect( QPainter & _p, + Uint16 _x, Uint16 _y, + Sint16 _width, Sint16 _height, + const bool _is_selected ) +{ + QColor current_color( 0xFF, 0xB0, 0x00 ); + if( _is_selected == TRUE ) + { + current_color.setRgb( 0x00, 0x40, 0xC0 ); + } + _p.fillRect( _x, _y, _width, _height, current_color ); + + _p.setPen( QColor( 0xFF, 0xDF, 0x20 ) ); + _p.drawLine( _x - 1, _y, _x + 1, _y ); + _p.drawLine( _x, _y - 1, _x, _y + 1 ); + +// _p.setPen( QColor( 0xFF, 0x9F, 0x00 ) ); + +// _p.setPen( QColor( 0xFF, 0xFF, 0x40 ) ); +} + + + + +void automationEditor::update( void ) +{ + if( m_paintPixmap.isNull() == TRUE || m_paintPixmap.size() != size() ) + { + m_paintPixmap = QPixmap( size() ); + } + m_paintPixmap.fill( QColor( 0, 0, 0 ) ); + QPainter p( &m_paintPixmap ); + + // set font-size to 8 + p.setFont( pointSize<8>( p.font() ) ); + + int grid_height = height() - TOP_MARGIN - SCROLLBAR_SIZE; + + // start drawing at the bottom + int grid_bottom = height() - SCROLLBAR_SIZE - 1; + + p.fillRect( 0, TOP_MARGIN, VALUES_WIDTH, height() - TOP_MARGIN, + QColor( 0x33, 0x33, 0x33 ) ); + + // print value numbers + int font_height = p.fontMetrics().height(); + AlignmentFlags text_flags = + (AlignmentFlags)( AlignRight | AlignVCenter ); + + for( int y = grid_bottom, level = m_bottom_level; + y >= TOP_MARGIN && level <= m_top_level; + y -= m_y_delta, ++level ) + { + if( level % 5 == 0 ) + { + p.setPen( QColor( 240, 240, 240 ) ); + p.drawText( 1, y - font_height + 1, + VALUES_WIDTH - 10, 2 * font_height, + text_flags, QString::number( level ) ); + + p.setPen( QColor( 0, 0, 0 ) ); + p.drawText( 0, y - font_height, + VALUES_WIDTH - 10, 2 * font_height, + text_flags, QString::number( level ) ); + } + } + + // set clipping area, because we are not allowed to paint over + // keyboard... + p.setClipRect( VALUES_WIDTH, TOP_MARGIN, width() - VALUES_WIDTH, + grid_height ); + + // draw vertical raster + int tact_16th = m_currentPosition / 4; + const int offset = ( m_currentPosition % 4 ) * m_ppt / + DEFAULT_STEPS_PER_TACT / 4; + + int x_line_end = m_top_level < m_max_level ? + TOP_MARGIN : + grid_bottom - ( m_top_level - m_bottom_level ) * m_y_delta; + + for( int x = VALUES_WIDTH - offset; x < width(); + x += m_ppt / DEFAULT_STEPS_PER_TACT, ++tact_16th ) + { + if( x >= VALUES_WIDTH ) + { + // every tact-start needs to be a bright line + if( tact_16th % 16 == 0 ) + { + p.setPen( QColor( 0x7F, 0x7F, 0x7F ) ); + } + // normal line + else if( tact_16th % 4 == 0 ) + { + p.setPen( QColor( 0x5F, 0x5F, 0x5F ) ); + } + // weak line + else + { + p.setPen( QColor( 0x3F, 0x3F, 0x3F ) ); + } + p.drawLine( x, grid_bottom, x, x_line_end ); + } + } + + + for( int y = grid_bottom, level = m_bottom_level; + y >= TOP_MARGIN && level <= m_top_level; + y -= m_y_delta, ++level ) + { + if( level % 5 == 0 ) + { + p.setPen( QColor( 0x4F, 0x4F, 0x4F ) ); + } + else + { + p.setPen( QColor( 0x3F, 0x3F, 0x3F ) ); + } + + // draw key-line + p.drawLine( VALUES_WIDTH, y, width(), y ); + } + + + + // following code draws all visible values + + // setup selection-vars + int sel_pos_start = m_selectStartTact64th; + int sel_pos_end = m_selectStartTact64th+m_selectedTact64th; + if( sel_pos_start > sel_pos_end ) + { + qSwap( sel_pos_start, sel_pos_end ); + } + + int sel_key_start = m_selectStartKey; + int sel_key_end = sel_key_start + m_selectedKeys; + if( sel_key_start > sel_key_end ) + { + qSwap( sel_key_start, sel_key_end ); + } + + int y_base = height() - SCROLLBAR_SIZE - 1; + if( validPattern() == TRUE ) + { + timeMap & time_map = m_pattern->getTimeMap(); + + for( timeMap::iterator it = time_map.begin(), it_next; + it != time_map.end(); ++it ) + { + Sint32 len_tact_64th = 4; + + const int value = it.data(); + + Sint32 pos_tact_64th = it.key(); + + const int x = ( pos_tact_64th - m_currentPosition ) * + m_ppt / 64; + + it_next = it; + ++it_next; + + int next_x; + int rect_width; + if( it_next != time_map.end() ) + { + Sint32 next_pos_tact_64th = it_next.key(); + next_x = ( next_pos_tact_64th + - m_currentPosition ) * m_ppt / 64; + rect_width = next_x - x; + } + else + { + next_x = -1; + rect_width = width() - x; + } + + // skip this value if not in visible area at all + if( !xVisible( x ) && !xVisible( next_x ) + && it_next != time_map.end() ) + { + continue;; + } + + // is the value in visible area? + if( ( value >= m_bottom_level && value <= m_top_level ) + || ( value > m_top_level && m_top_level >= 0 ) + || ( value < m_bottom_level + && m_bottom_level <= 0 ) ) + { + bool is_selected = FALSE; + // if we're in move-mode, we may only draw + // values in selected area, that have originally + // been selected and not values that are now in + // selection because the user moved it... + if( m_editMode == MOVE ) + { + if( qFind( m_selValuesForMove.begin(), + m_selValuesForMove.end(), *it ) + != m_selValuesForMove.end() ) + { + is_selected = TRUE; + } + } + else if( value > sel_key_start && + value <= sel_key_end && + pos_tact_64th >= sel_pos_start && + pos_tact_64th + len_tact_64th <= + sel_pos_end ) + { + is_selected = TRUE; + } + + // we've done and checked all, lets draw the + // value + drawValueRect( p, x + VALUES_WIDTH, + y_base - ( value + - m_bottom_level ) + * m_y_delta, + rect_width, value * m_y_delta, + is_selected ); + } + } + } + else + { + QFont f = p.font(); + f.setBold( TRUE ); + p.setFont( pointSize<14>( f ) ); + p.setPen( QColor( 0, 255, 0 ) ); + p.drawText( VALUES_WIDTH + 20, TOP_MARGIN + 40, + tr( "Please open a time pattern with the " + "context menu of a control!" ) ); + } + + p.setClipRect( VALUES_WIDTH, TOP_MARGIN, width() - VALUES_WIDTH, + grid_height ); + + // now draw selection-frame + int x = ( ( sel_pos_start - m_currentPosition ) * m_ppt ) / 64; + int w = ( ( ( sel_pos_end - m_currentPosition ) * m_ppt ) / + 64 ) - x; + int y = (int) y_base - sel_key_start * m_y_delta; + int h = (int) y_base - sel_key_end * m_y_delta - y; + p.setPen( QColor( 0, 64, 192 ) ); + p.drawRect( x + VALUES_WIDTH, y, w, h ); + + int l = ( validPattern() == TRUE )? (int) m_pattern->length() : 0; + + // reset scroll-range + m_leftRightScroll->setRange( 0, l ); +#ifdef QT4 + m_leftRightScroll->setSingleStep( 1 ); + m_leftRightScroll->setPageStep( l ); +#else + m_leftRightScroll->setSteps( 1, l ); +#endif + + QWidget::update(); +} + + + + +void automationEditor::removeSelection( void ) +{ + m_selectStartTact64th = 0; + m_selectedTact64th = 0; + m_selectStartKey = 0; + m_selectedKeys = 0; +} + + + + +void automationEditor::closeEvent( QCloseEvent * _ce ) +{ + QApplication::restoreOverrideCursor(); + hide(); + _ce->ignore (); +} + + + + +void automationEditor::enterEvent( QEvent * _e ) +{ + m_cursorInside = TRUE; + QWidget::enterEvent( _e ); +} + + + + +void automationEditor::keyPressEvent( QKeyEvent * _ke ) +{ + switch( _ke->key() ) + { + case Qt::Key_Up: + m_topBottomScroll->setValue( + m_topBottomScroll->value() - 1 ); + break; + + case Qt::Key_Down: + m_topBottomScroll->setValue( + m_topBottomScroll->value() + 1 ); + break; + + case Qt::Key_Left: + { + if( ( m_timeLine->pos() -= 16 ) < 0 ) + { + m_timeLine->pos().setTact( 0 ); + m_timeLine->pos().setTact64th( 0 ); + } + m_timeLine->updatePosition(); + break; + } + case Qt::Key_Right: + { + m_timeLine->pos() += 16; + m_timeLine->updatePosition(); + break; + } + + case Qt::Key_C: + if( _ke->modifiers() & Qt::ControlModifier ) + { + copySelectedValues(); + } + else + { + _ke->ignore(); + } + break; + + case Qt::Key_X: + if( _ke->modifiers() & Qt::ControlModifier ) + { + cutSelectedValues(); + } + else + { + _ke->ignore(); + } + break; + + case Qt::Key_V: + if( _ke->modifiers() & Qt::ControlModifier ) + { + pasteValues(); + } + else + { + _ke->ignore(); + } + break; + + case Qt::Key_A: + if( _ke->modifiers() & Qt::ControlModifier ) + { + m_selectButton->setChecked( TRUE ); + selectAll(); + update(); + } + else + { + _ke->ignore(); + } + break; + + case Qt::Key_D: + m_drawButton->setChecked( TRUE ); + break; + + case Qt::Key_E: + m_eraseButton->setChecked( TRUE ); + break; + + case Qt::Key_S: + m_selectButton->setChecked( TRUE ); + break; + + case Qt::Key_M: + m_moveButton->setChecked( TRUE ); + break; + + case Qt::Key_Delete: + deleteSelectedValues(); + break; + + case Qt::Key_Space: + if( eng()->getSongEditor()->playing() ) + { + stop(); + } + else + { + play(); + } + break; + + case Qt::Key_Home: + m_timeLine->pos().setTact( 0 ); + m_timeLine->pos().setTact64th( 0 ); + m_timeLine->updatePosition(); + break; + + default: + _ke->ignore(); + break; + } +} + + + + +void automationEditor::leaveEvent( QEvent * _e ) +{ + while( QApplication::overrideCursor() != NULL ) + { + QApplication::restoreOverrideCursor(); + } + m_cursorInside = FALSE; + + QWidget::leaveEvent( _e ); +} + + + + +void automationEditor::mousePressEvent( QMouseEvent * _me ) +{ + if( validPattern() == FALSE ) + { + return; + } + + if( _me->y() > TOP_MARGIN ) + { + int value = getValue( _me->y() ); + + int x = _me->x(); + + if( x > VALUES_WIDTH ) + { + // set or move value + + x -= VALUES_WIDTH; + + // get tact-64th in which the user clicked + int pos_tact_64th = x * 64 / m_ppt + + m_currentPosition; + + // get time map of current pattern + timeMap & time_map = m_pattern->getTimeMap(); + + // will be our iterator in the following loop + timeMap::iterator it = time_map.begin(); + + // loop through whole time-map... + while( it != time_map.end() ) + { + midiTime len = 4; + + // and check whether the user clicked on an + // existing value + if( pos_tact_64th >= it.key() && + len > 0 && + pos_tact_64th <= it.key() + len && + it.data() == value ) + { + break; + } + ++it; + } + + // left button?? + if( _me->button() == Qt::LeftButton && + m_editMode == DRAW ) + { + // did it reach end of map because + // there's no value?? + if( it == time_map.end() ) + { + // then set new value + midiTime value_pos( pos_tact_64th ); + + midiTime new_time = + m_pattern->putValue( value_pos, + value ); + + // reset it so that it can be used for + // ops (move, resize) after this + // code-block + it = time_map.begin(); + while( it != time_map.end() && + it.key() != new_time ) + { + ++it; + } + } + + // move it + m_action = MOVE_VALUE; + int aligned_x = (int)( (float)( ( + it.key() - + m_currentPosition ) * + m_ppt ) / 64.0f ); + m_moveXOffset = x - aligned_x - 1; + // set move-cursor + QCursor c( Qt::SizeAllCursor ); + QApplication::setOverrideCursor( c ); + + eng()->getSongEditor()->setModified(); + } + else if( ( _me->button() == Qt::RightButton && + m_editMode == DRAW ) || + m_editMode == ERASE ) + { + // erase single value + + if( it != time_map.end() ) + { + m_pattern->removeValue( it.key() ); + eng()->getSongEditor()->setModified(); + } + } + else if( _me->button() == Qt::LeftButton && + m_editMode == SELECT ) + { + + // select an area of values + + m_selectStartTact64th = pos_tact_64th; + m_selectedTact64th = 0; + m_selectStartKey = value; + m_selectedKeys = 1; + m_action = SELECT_VALUES; + } + else if( _me->button() == Qt::RightButton && + m_editMode == SELECT ) + { + // when clicking right in select-move, we + // switch to move-mode + m_moveButton->setChecked( TRUE ); + } + else if( _me->button() == Qt::LeftButton && + m_editMode == MOVE ) + { + + // move selection (including selected values) + + // save position where move-process began + m_moveStartTact64th = pos_tact_64th; + m_moveStartKey = value; + + m_action = MOVE_SELECTION; + + eng()->getSongEditor()->setModified(); + } + else if( _me->button() == Qt::RightButton && + m_editMode == MOVE ) + { + // when clicking right in select-move, we + // switch to draw-mode + m_drawButton->setChecked( TRUE ); + } + + update(); + } + } +} + + + + +void automationEditor::mouseReleaseEvent( QMouseEvent * _me ) +{ + m_action = NONE; + + if( m_editMode == DRAW ) + { + QApplication::restoreOverrideCursor(); + } +} + + + + +void automationEditor::mouseMoveEvent( QMouseEvent * _me ) +{ + if( validPattern() == FALSE ) + { + update(); + return; + } + + if( _me->y() > TOP_MARGIN ) + { + int value = getValue( _me->y() ); + int x = _me->x(); + + if( _me->x() <= VALUES_WIDTH ) + { + QWidget::update(); + return; + } + x -= VALUES_WIDTH; + + if( +#ifdef QT4 + _me->buttons() & +#else + _me->state() == +#endif + Qt::LeftButton && m_editMode == DRAW ) + { + if( m_action == MOVE_VALUE ) + { + x -= m_moveXOffset; + } + int pos_tact_64th = x * 64 / m_ppt + + m_currentPosition; + if( m_action == MOVE_VALUE ) + { + // moving value + if( pos_tact_64th < 0 ) + { + pos_tact_64th = 0; + } + + // we moved the value so the value has to be + // moved properly according to new starting- + // time in the time map of pattern + m_pattern->removeValue( + midiTime( pos_tact_64th ) ); + m_pattern->putValue( midiTime( pos_tact_64th ), + value ); + } + + eng()->getSongEditor()->setModified(); + + } + else if( +#ifdef QT4 + _me->buttons() & +#else + _me->state() == +#endif + Qt::NoButton && m_editMode == DRAW ) + { + // set move- or resize-cursor + + // get tact-64th in which the cursor is posated + int pos_tact_64th = ( x * 64 ) / m_ppt + + m_currentPosition; + + // get time map of current pattern + timeMap & time_map = m_pattern->getTimeMap(); + + // will be our iterator in the following loop + timeMap::iterator it = time_map.begin(); + + // loop through whole time map... + while( it != time_map.end() ) + { + // and check whether the cursor is over an + // existing value + if( pos_tact_64th >= it.key() && + pos_tact_64th <= it.key() + + //TODO: Add constant + 4 && + it.data() == value ) + { + break; + } + ++it; + } + + // did it reach end of map because there's + // no value?? + if( it != time_map.end() ) + { + if( QApplication::overrideCursor() ) + { + if( QApplication::overrideCursor()->shape() != Qt::SizeAllCursor ) + { + while( QApplication::overrideCursor() != NULL ) + { + QApplication::restoreOverrideCursor(); + } + + QCursor c( Qt::SizeAllCursor ); + QApplication::setOverrideCursor( + c ); + } + } + else + { + QCursor c( Qt::SizeAllCursor ); + QApplication::setOverrideCursor( c ); + } + } + else + { + // the cursor is over no value, so restore + // cursor + while( QApplication::overrideCursor() != NULL ) + { + QApplication::restoreOverrideCursor(); + } + } + } + else if( +#ifdef QT4 + _me->buttons() & +#else + _me->modifiers() == +#endif + Qt::LeftButton && + m_editMode == SELECT && + m_action == SELECT_VALUES ) + { + + // change size of selection + + if( x < 0 && m_currentPosition > 0 ) + { + x = 0; + QCursor::setPos( mapToGlobal( QPoint( + VALUES_WIDTH, _me->y() ) ) ); + if( m_currentPosition >= 4 ) + { + m_leftRightScroll->setValue( + m_currentPosition - 4 ); + } + else + { + m_leftRightScroll->setValue( 0 ); + } + } + else if( x > width() - VALUES_WIDTH ) + { + x = width() - VALUES_WIDTH; + QCursor::setPos( mapToGlobal( QPoint( width(), + _me->y() ) ) ); + m_leftRightScroll->setValue( m_currentPosition + + 4 ); + } + + // get tact-64th in which the cursor is posated + int pos_tact_64th = x * 64 / m_ppt + + m_currentPosition; + + m_selectedTact64th = pos_tact_64th - + m_selectStartTact64th; + if( (int) m_selectStartTact64th + m_selectedTact64th < + 0 ) + { + m_selectedTact64th = -static_cast( + m_selectStartTact64th ); + } + m_selectedKeys = value - m_selectStartKey; + if( value <= m_selectStartKey ) + { + --m_selectedKeys; + } + } + else if( +#ifdef QT4 + _me->buttons() & +#else + _me->modifiers() == +#endif + Qt::LeftButton && + m_editMode == MOVE && + m_action == MOVE_SELECTION ) + { + // move selection + selected values + + // do horizontal move-stuff + int pos_tact_64th = x * 64 / m_ppt + + m_currentPosition; + int tact_64th_diff = pos_tact_64th - + m_moveStartTact64th; + if( m_selectedTact64th > 0 ) + { + if( (int) m_selectStartTact64th + + tact_64th_diff < 0 ) + { + tact_64th_diff = -m_selectStartTact64th; + } + } + else + { + if( (int) m_selectStartTact64th + + m_selectedTact64th + tact_64th_diff < + 0 ) + { + tact_64th_diff = -( + m_selectStartTact64th + + m_selectedTact64th ); + } + } + m_selectStartTact64th += tact_64th_diff; + + int tact_diff = tact_64th_diff / 64; + tact_64th_diff = tact_64th_diff % 64; + + + // do vertical move-stuff + int key_diff = value - m_moveStartKey; + + if( m_selectedKeys > 0 ) + { + if( m_selectStartKey + key_diff < m_min_level ) + { + key_diff = m_min_level - + m_selectStartKey; + } + else if( m_selectStartKey + m_selectedKeys + + key_diff > m_max_level ) + { + key_diff = m_max_level - + m_selectStartKey - + m_selectedKeys; + } + } + else + { + if( m_selectStartKey + m_selectedKeys + + key_diff < m_min_level ) + { + key_diff = m_min_level - + m_selectStartKey - + m_selectedKeys; + } + else if( m_selectStartKey + key_diff > + m_max_level ) + { + key_diff = m_max_level - + m_selectStartKey; + } + } + m_selectStartKey += key_diff; + + + timeMap new_selValuesForMove; + for( timeMap::iterator it = m_selValuesForMove.begin(); + it != m_selValuesForMove.end(); ++it ) + { + int value_tact = it.key().getTact() + tact_diff; + int value_tact_64th = it.key().getTact64th() + + tact_64th_diff; + while( value_tact_64th < 0 ) + { + --value_tact; + value_tact_64th += 64; + } + while( value_tact_64th > 64 ) + { + ++value_tact; + value_tact_64th -= 64; + } + m_pattern->removeValue( it.key() ); + midiTime new_value_pos( value_tact, + value_tact_64th ); + new_selValuesForMove[ + m_pattern->putValue( new_value_pos, + it.data() + key_diff, FALSE )] + = it.data() + key_diff; + } + m_selValuesForMove = new_selValuesForMove; + + m_moveStartTact64th = pos_tact_64th; + m_moveStartKey = value; + } + } + else + { + if( +#ifdef QT4 + _me->buttons() & +#else + _me->modifiers() == +#endif + Qt::LeftButton && + m_editMode == SELECT && + m_action == SELECT_VALUES ) + { + + int x = _me->x() - VALUES_WIDTH; + if( x < 0 && m_currentPosition > 0 ) + { + x = 0; + QCursor::setPos( mapToGlobal( QPoint( VALUES_WIDTH, + _me->y() ) ) ); + if( m_currentPosition >= 4 ) + { + m_leftRightScroll->setValue( + m_currentPosition - 4 ); + } + else + { + m_leftRightScroll->setValue( 0 ); + } + } + else if( x > width() - VALUES_WIDTH ) + { + x = width() - VALUES_WIDTH; + QCursor::setPos( mapToGlobal( QPoint( width(), + _me->y() ) ) ); + m_leftRightScroll->setValue( m_currentPosition + + 4 ); + } + + // get tact-64th in which the cursor is posated + int pos_tact_64th = x * 64 / m_ppt + + m_currentPosition; + + m_selectedTact64th = pos_tact_64th - + m_selectStartTact64th; + if( (int) m_selectStartTact64th + m_selectedTact64th < + 0 ) + { + m_selectedTact64th = -static_cast( + m_selectStartTact64th ); + } + + int value = getValue( _me->y() ); + + if( value <= m_bottom_level ) + { + QCursor::setPos( mapToGlobal( QPoint( _me->x(), + height() - + SCROLLBAR_SIZE ) ) ); + m_topBottomScroll->setValue( + m_topBottomScroll->value() + 1 ); + value = m_bottom_level; + } + else if( value >= m_top_level ) + { + QCursor::setPos( mapToGlobal( QPoint( _me->x(), + TOP_MARGIN ) ) ); + m_topBottomScroll->setValue( + m_topBottomScroll->value() - 1 ); + value = m_top_level; + } + + m_selectedKeys = value - m_selectStartKey; + if( value <= m_selectStartKey ) + { + --m_selectedKeys; + } + } + QApplication::restoreOverrideCursor(); + } + + if( +#ifdef QT4 + _me->buttons() & +#else + _me->state() == +#endif + Qt::NoButton ) + { + QWidget::update(); + } + else + { + update(); + } +} + + + + +void automationEditor::paintEvent( QPaintEvent * ) +{ +#ifdef QT4 + QPainter p( this ); +#else + QPixmap draw_pm( size() ); + draw_pm.fill( QColor( 0, 0, 0 ) ); + + QPainter p( &draw_pm, this ); +#endif + p.drawPixmap( 0, 0, m_paintPixmap ); + + if( m_cursorInside == TRUE ) + { + p.setClipRect( VALUES_WIDTH, TOP_MARGIN, width() - VALUES_WIDTH, + height() - TOP_MARGIN - SCROLLBAR_SIZE ); + if( validPattern() == TRUE ) + { +//TODO: What's this? + p.fillRect( 10, height() + 3 - SCROLLBAR_SIZE, + width() - 10, DEFAULT_Y_DELTA - 7, + QColor( 64, 64, 64 ) ); + } + + const QPixmap * cursor = NULL; + // draw current edit-mode-icon below the cursor + switch( m_editMode ) + { + case DRAW: cursor = s_toolDraw; break; + case ERASE: cursor = s_toolErase; break; + case SELECT: cursor = s_toolSelect; break; + case MOVE: cursor = s_toolMove; break; + } + p.drawPixmap( mapFromGlobal( QCursor::pos() ) + QPoint( 8, 8 ), + *cursor ); + } + +#ifndef QT4 + // and blit all the drawn stuff on the screen... + bitBlt( this, rect().topLeft(), &draw_pm ); +#endif +} + + + + +// responsible for moving/resizing scrollbars after window-resizing +void automationEditor::resizeEvent( QResizeEvent * ) +{ + m_leftRightScroll->setGeometry( VALUES_WIDTH, height() - SCROLLBAR_SIZE, + width() - VALUES_WIDTH, + SCROLLBAR_SIZE ); + + int grid_height = height() - TOP_MARGIN - SCROLLBAR_SIZE; + m_topBottomScroll->setGeometry( width() - SCROLLBAR_SIZE, TOP_MARGIN, + SCROLLBAR_SIZE, grid_height ); + + int half_grid = grid_height / 2; + int total_pixels = ( m_max_level - m_min_level ) * m_y_delta + 1; + if( grid_height < total_pixels ) + { + int min_scroll = m_min_level + (int)floorf( half_grid + / (float)m_y_delta ); + int max_scroll = m_max_level - (int)floorf( ( grid_height + - half_grid ) / (float)m_y_delta ); + m_topBottomScroll->setRange( min_scroll, max_scroll ); + } + else + { + m_topBottomScroll->setRange( m_scroll_level, m_scroll_level ); + } + +#ifdef QT4 + m_topBottomScroll->setSingleStep( 1 ); + m_topBottomScroll->setPageStep( 20 ); +#else + m_topBottomScroll->setSteps( 1, 20 ); +#endif + + m_topBottomScroll->setValue( m_scroll_level ); + + eng()->getSongEditor()->getPlayPos( songEditor::PLAY_AUTOMATION_PATTERN + ).m_timeLine->setFixedWidth( width() ); + m_toolBar->setFixedWidth( width() ); + + updateTopBottomLevels(); + update(); +} + + + + +void automationEditor::wheelEvent( QWheelEvent * _we ) +{ + _we->accept(); + if( eng()->getMainWindow()->isCtrlPressed() == TRUE ) + { + if( _we->delta() > 0 ) + { + m_ppt = tMin( m_ppt * 2, m_y_delta * + DEFAULT_STEPS_PER_TACT * 8 ); + } + else if( m_ppt >= 72 ) + { + m_ppt /= 2; + } + // update combobox with zooming-factor + m_zoomingXComboBox->setValue( + m_zoomingXComboBox->findText( QString::number( + static_cast( m_ppt * 100 / + DEFAULT_PPT ) ) +"%" ) ); + // update timeline + m_timeLine->setPixelsPerTact( m_ppt ); + update(); + } + else if( eng()->getMainWindow()->isShiftPressed() ) + { + m_leftRightScroll->setValue( m_leftRightScroll->value() - + _we->delta() * 2 / 15 ); + } + else + { + m_topBottomScroll->setValue( m_topBottomScroll->value() - + _we->delta() / 30 ); + } +} + + + + +int automationEditor::getValue( int _y ) +{ + int key_line_y = height() - SCROLLBAR_SIZE - 1; + // pressed value on time roll + int value = m_bottom_level + (int)roundf( ( key_line_y - _y ) + / (float)m_y_delta ); + + // some range-checking-stuff + if( value < m_bottom_level ) + { + value = m_bottom_level; + } + + if( value > m_top_level ) + { + value = m_top_level; + } + + return( value ); +} + + + + +void automationEditor::play( void ) +{ + if( validPattern() == FALSE ) + { + return; + } + + if( inBBEditor() ) + { + eng()->getBBEditor()->play(); + if( eng()->getSongEditor()->playing() ) + { + m_playButton->setIcon( embed::getIconPixmap( + "pause" ) ); + } + else + { + m_playButton->setIcon( embed::getIconPixmap( "play" ) ); + } + } + else + { + if( eng()->getSongEditor()->playing() ) + { + if( eng()->getSongEditor()->playMode() != + songEditor::PLAY_AUTOMATION_PATTERN ) + { + //TODO: stop/play? + eng()->getSongEditor()->stop(); + eng()->getSongEditor()->play(); + m_playButton->setIcon( embed::getIconPixmap( + "pause" ) ); + } + else + { + eng()->getSongEditor()->pause(); + m_playButton->setIcon( embed::getIconPixmap( + "play" ) ); + } + } + else if( eng()->getSongEditor()->paused() ) + { + eng()->getSongEditor()->resumeFromPause(); + m_playButton->setIcon( embed::getIconPixmap( + "pause" ) ); + } + else + { + m_playButton->setIcon( embed::getIconPixmap( "pause" ) ); + eng()->getSongEditor()->play(); + } + } +} + + + + +void automationEditor::stop( void ) +{ + if( inBBEditor() ) + { + eng()->getBBEditor()->stop(); + } + else + { + eng()->getSongEditor()->stop(); + } + m_playButton->setIcon( embed::getIconPixmap( "play" ) ); + m_playButton->update(); + m_scrollBack = TRUE; +} + + + + +void automationEditor::horScrolled( int _new_pos ) +{ + m_currentPosition = _new_pos; + emit positionChanged( m_currentPosition ); + update(); +} + + + + +void automationEditor::verScrolled( int _new_pos ) +{ + m_scroll_level = _new_pos; + updateTopBottomLevels(); + update(); +} + + + + +void automationEditor::drawButtonToggled( void ) +{ + m_editMode = DRAW; + removeSelection(); + update(); +} + + + + +void automationEditor::eraseButtonToggled( void ) +{ + m_editMode = ERASE; + removeSelection(); + update(); +} + + + + +void automationEditor::selectButtonToggled( void ) +{ + m_editMode = SELECT; + removeSelection(); + update(); +} + + + + +void automationEditor::moveButtonToggled( void ) +{ + m_editMode = MOVE; + m_selValuesForMove.clear(); + getSelectedValues( m_selValuesForMove ); + update(); +} + + + + +void automationEditor::selectAll( void ) +{ + if( validPattern() == FALSE ) + { + return; + } + + timeMap & time_map = m_pattern->getTimeMap(); + + // if first_time = TRUE, we HAVE to set the vars for select + bool first_time = TRUE; + + for( timeMap::iterator it = time_map.begin(); it != time_map.end(); + ++it ) + { + //TODO: Add constant + Uint32 len_tact_64th = 4; + + const int key = it.data(); + + Uint32 pos_tact_64th = it.key(); + if( key <= m_selectStartKey || first_time ) + { + // if we move start-key down, we have to add + // the difference between old and new start-key + // to m_selectedKeys, otherwise the selection + // is just moved down... + int diff = m_selectStartKey - ( key - 1 ); + m_selectStartKey = key - 1; + m_selectedKeys += diff; + } + if( key >= m_selectedKeys+m_selectStartKey || first_time ) + { + m_selectedKeys = key - m_selectStartKey; + } + if( pos_tact_64th < m_selectStartTact64th || first_time ) + { + m_selectStartTact64th = pos_tact_64th; + } + if( pos_tact_64th + len_tact_64th > + m_selectStartTact64th + m_selectedTact64th || + first_time ) + { + m_selectedTact64th = pos_tact_64th + len_tact_64th - + m_selectStartTact64th; + } + first_time = FALSE; + } +} + + + + +// returns vector with pointers to all selected values +void automationEditor::getSelectedValues( timeMap & _selected_values ) +{ + if( validPattern() == FALSE ) + { + return; + } + + int sel_pos_start = m_selectStartTact64th; + int sel_pos_end = sel_pos_start + m_selectedTact64th; + if( sel_pos_start > sel_pos_end ) + { + qSwap( sel_pos_start, sel_pos_end ); + } + + int sel_key_start = m_selectStartKey; + int sel_key_end = sel_key_start + m_selectedKeys; + if( sel_key_start > sel_key_end ) + { + qSwap( sel_key_start, sel_key_end ); + } + + timeMap & time_map = m_pattern->getTimeMap(); + + for( timeMap::iterator it = time_map.begin(); it != time_map.end(); + ++it ) + { + //TODO: Add constant + Sint32 len_tact_64th = 4; + + int key = it.data(); + Sint32 pos_tact_64th = it.key(); + + if( key > sel_key_start && key <= sel_key_end && + pos_tact_64th >= sel_pos_start && + pos_tact_64th+len_tact_64th <= sel_pos_end ) + { + _selected_values[it.key()] = it.data(); + } + } +} + + + + +void automationEditor::copySelectedValues( void ) +{ + m_valuesToCopy.clear(); + + timeMap selected_values; + getSelectedValues( selected_values ); + + if( !selected_values.isEmpty() ) + { + midiTime start_pos( selected_values.begin().key().getTact(), + 0 ); + for( timeMap::iterator it = selected_values.begin(); + it != selected_values.end(); ++it ) + { + m_valuesToCopy[it.key()] = it.data(); + } + textFloat::displayMessage( tr( "Values copied" ), + tr( "All selected values were copied to the " + "clipboard." ), + embed::getIconPixmap( "edit_copy" ), 2000 ); + } +} + + + + +void automationEditor::cutSelectedValues( void ) +{ + if( validPattern() == FALSE ) + { + return; + } + + m_valuesToCopy.clear(); + + timeMap selected_values; + getSelectedValues( selected_values ); + + if( !selected_values.isEmpty() ) + { + eng()->getSongEditor()->setModified(); + + midiTime start_pos( selected_values.begin().key().getTact(), + 0 ); + + for( timeMap::iterator it = selected_values.begin(); + it != selected_values.end(); ++it ) + { + m_valuesToCopy[it.key()] = it.data(); + m_pattern->removeValue( it.key() ); + } + } + + update(); + eng()->getSongEditor()->update(); +} + + + + +void automationEditor::pasteValues( void ) +{ + if( validPattern() == FALSE ) + { + return; + } + + if( !m_valuesToCopy.isEmpty() ) + { + for( timeMap::iterator it = m_valuesToCopy.begin(); + it != m_valuesToCopy.end(); ++it ) + { + m_pattern->putValue( it.key() + m_currentPosition, + it.data() ); + } + + // we only have to do the following lines if we pasted at + // least one value... + eng()->getSongEditor()->setModified(); + update(); + eng()->getSongEditor()->update(); + } +} + + + + +void automationEditor::deleteSelectedValues( void ) +{ + if( validPattern() == FALSE ) + { + return; + } + + timeMap selected_values; + getSelectedValues( selected_values ); + + const bool update_after_delete = !selected_values.empty(); + + for( timeMap::iterator it = selected_values.begin(); + it != selected_values.end(); ++it ) + { + m_pattern->removeValue( it.key() ); + } + + if( update_after_delete == TRUE ) + { + eng()->getSongEditor()->setModified(); + update(); + eng()->getSongEditor()->update(); + } +} + + + + +void automationEditor::updatePosition( const midiTime & _t ) +{ + if( ( eng()->getSongEditor()->playing() && + eng()->getSongEditor()->playMode() == + songEditor::PLAY_AUTOMATION_PATTERN ) || + m_scrollBack == TRUE ) + { + const int w = width() - VALUES_WIDTH; + if( _t > m_currentPosition + w * 64 / m_ppt ) + { + m_leftRightScroll->setValue( _t.getTact() * 64 ); + } + else if( _t < m_currentPosition ) + { + midiTime t = tMax( _t - w * 64 * 64 / m_ppt, 0 ); + m_leftRightScroll->setValue( t.getTact() * 64 ); + } + m_scrollBack = FALSE; + } +} + + + + +void automationEditor::zoomingXChanged( const QString & _zfac ) +{ + m_ppt = _zfac.left( _zfac.length() - 1 ).toInt() * DEFAULT_PPT / 100; +#ifdef LMMS_DEBUG + assert( m_ppt > 0 ); +#endif + m_timeLine->setPixelsPerTact( m_ppt ); + update(); +} + + + + +void automationEditor::zoomingYChanged( const QString & _zfac ) +{ + m_y_delta = _zfac.left( _zfac.length() - 1 ).toInt() * DEFAULT_Y_DELTA + / 100; +#ifdef LMMS_DEBUG + assert( m_y_delta > 0 ); +#endif + resizeEvent( NULL ); +} + + + + +int automationEditor::quantization( void ) const +{ + return( 64 / m_quantizeComboBox->currentText().right( + m_quantizeComboBox->currentText().length() - + 2 ).toInt() ); +} + + + + +void automationEditor::updateTopBottomLevels( void ) +{ + int total_pixels = ( m_max_level - m_min_level ) * m_y_delta + 1; + int grid_height = height() - TOP_MARGIN - SCROLLBAR_SIZE; + int half_grid = grid_height / 2; + + if ( total_pixels > grid_height ) + { + int central_level = m_min_level + m_max_level - m_scroll_level; + + m_bottom_level = central_level - (int)roundf( half_grid + / (float)m_y_delta ); + if( m_bottom_level < m_min_level ) + { + m_bottom_level = m_min_level; + m_top_level = m_min_level + (int)floorf( grid_height + / (float)m_y_delta ); + } + else + { + m_top_level = m_bottom_level + (int)floorf( grid_height + / (float)m_y_delta ); + if( m_top_level > m_max_level ) + { + m_top_level = m_max_level; + m_bottom_level = m_max_level - (int)floorf( + grid_height / (float)m_y_delta ); + } + } + } + else + { + m_bottom_level = m_min_level; + m_top_level = m_max_level; + } +} + + + + +inline bool automationEditor::xVisible( int _x ) +{ + return( _x >= 0 && _x <= width() - VALUES_WIDTH ); +} + + + + +inline bool automationEditor::inBBEditor( void ) +{ + return( m_pattern->getTrack()->getTrackContainer() + == eng()->getBBEditor() ); +} + + + + +#include "automation_editor.moc" + + +#ifdef QT3 +#undef addButton +#undef setCheckable +#endif + + +#endif diff --git a/src/core/engine.cpp b/src/core/engine.cpp index 1b9d1d4bf9..7bad7b7f6c 100644 --- a/src/core/engine.cpp +++ b/src/core/engine.cpp @@ -25,6 +25,7 @@ */ +#include "automation_editor.h" #include "bb_editor.h" #include "project_journal.h" #include "engine.h" @@ -34,7 +35,6 @@ #include "preset_preview_play_handle.h" #include "project_notes.h" #include "song_editor.h" -#include "time_roll.h" engine::engine( const bool _has_gui ) : @@ -42,10 +42,10 @@ engine::engine( const bool _has_gui ) : m_mixer( NULL ), m_mainWindow( NULL ), m_songEditor( NULL ), + m_automationEditor( NULL ), m_bbEditor( NULL ), m_pianoRoll( NULL ), - m_projectJournal( NULL ), - m_timeRoll( NULL ) + m_projectJournal( NULL ) { m_projectJournal = new projectJournal( this ); m_mainWindow = new mainWindow( this ); @@ -54,7 +54,7 @@ engine::engine( const bool _has_gui ) : m_projectNotes = new projectNotes( this ); m_bbEditor = new bbEditor( this ); m_pianoRoll = new pianoRoll( this ); - m_timeRoll = new timeRoll( this ); + m_automationEditor = new automationEditor( this ); m_mixer->initDevices(); @@ -74,7 +74,7 @@ engine::~engine() delete m_songEditor; delete m_bbEditor; delete m_pianoRoll; - delete m_timeRoll; + delete m_automationEditor; presetPreviewPlayHandle::cleanUp( this ); diff --git a/src/core/main_window.cpp b/src/core/main_window.cpp index 1e37aa04ee..3dd9366e6d 100644 --- a/src/core/main_window.cpp +++ b/src/core/main_window.cpp @@ -77,7 +77,7 @@ #include "audio_dummy.h" #include "tool_button.h" #include "project_journal.h" -#include "time_roll.h" +#include "automation_editor.h" #if QT_VERSION >= 0x030100 @@ -383,21 +383,23 @@ void mainWindow::finalize( void ) //TODO: Change icon - toolButton * time_roll_window = new toolButton( - embed::getIconPixmap( "piano" ), - tr( "Show/hide Time-Roll" ) + + toolButton * automation_editor_window = new toolButton( + embed::getIconPixmap( "piano" ), + tr( "Show/hide Automation Editor" ) + " (F9)", - this, SLOT( toggleTimeRollWin() ), - m_toolBar ); - time_roll_window->setShortcut( Qt::Key_F9 ); + this, + SLOT( toggleAutomationEditorWin() ), + m_toolBar ); + automation_editor_window->setShortcut( Qt::Key_F9 ); #ifdef QT4 - time_roll_window->setWhatsThis( + automation_editor_window->setWhatsThis( #else - QWhatsThis::add( time_roll_window, + QWhatsThis::add( automation_editor_window, #endif tr( "By pressing this button, you can show or hide the " - "Time-Roll. With the help of the Time-Roll " - "you can edit dynamic values in an easy way." + "Automation Editor. With the help of the " + "Automation Editor you can edit dynamic values " + "in an easy way." ) ); //TODO: Relocate effect board button @@ -435,7 +437,7 @@ void mainWindow::finalize( void ) m_toolBarLayout->addWidget( bb_editor_window, 1, 1 ); m_toolBarLayout->addWidget( piano_roll_window, 1, 2 ); m_toolBarLayout->addWidget( song_editor_window, 1, 3 ); - m_toolBarLayout->addWidget( time_roll_window, 1, 4 ); + m_toolBarLayout->addWidget( automation_editor_window, 1, 4 ); //TODO: Relocate effect board //m_toolBarLayout->addWidget( effect_board_window, 1, 4 ); m_toolBarLayout->addWidget( project_notes_window, 1, 5 ); @@ -897,18 +899,18 @@ void mainWindow::togglePianoRollWin( void ) -void mainWindow::toggleTimeRollWin( void ) +void mainWindow::toggleAutomationEditorWin( void ) { - if( eng()->getTimeRoll()->isHidden() == TRUE || - ( m_workspace != NULL && - m_workspace->activeWindow() != eng()->getTimeRoll() ) ) + if( eng()->getAutomationEditor()->isHidden() == TRUE || + ( m_workspace != NULL && m_workspace->activeWindow() + != eng()->getAutomationEditor() ) ) { - eng()->getTimeRoll()->show(); - eng()->getTimeRoll()->setFocus(); + eng()->getAutomationEditor()->show(); + eng()->getAutomationEditor()->setFocus(); } else { - eng()->getTimeRoll()->hide(); + eng()->getAutomationEditor()->hide(); } } diff --git a/src/core/track.cpp b/src/core/track.cpp index 665922e91f..322f243cdc 100644 --- a/src/core/track.cpp +++ b/src/core/track.cpp @@ -1730,17 +1730,17 @@ void track::swapPositionOfTCOs( csize _tco_num1, csize _tco_num2 ) -void track::addTimePattern( timePattern * _pattern ) +void track::addAutomationPattern( automationPattern * _pattern ) { - m_time_patterns.append( _pattern ); + m_automation_patterns.append( _pattern ); } -void track::removeTimePattern( timePattern * _pattern ) +void track::removeAutomationPattern( automationPattern * _pattern ) { - m_time_patterns.remove( _pattern ); + m_automation_patterns.remove( _pattern ); } @@ -1748,8 +1748,8 @@ void track::removeTimePattern( timePattern * _pattern ) void track::sendMidiTime( const midiTime & _time ) { - QPtrListIterator it( m_time_patterns ); - timePattern * pattern ; + QPtrListIterator it( m_automation_patterns ); + automationPattern * pattern ; while( ( pattern = it.current() ) ) { ++it; diff --git a/src/lmms_single_source.cpp b/src/lmms_single_source.cpp index d2222c7f96..68f1c41c64 100644 --- a/src/lmms_single_source.cpp +++ b/src/lmms_single_source.cpp @@ -21,6 +21,7 @@ #include "src/core/sample_play_handle.cpp" #include "src/core/piano_roll.cpp" #include "src/core/arp_and_chords_tab_widget.cpp" +#include "src/core/automation_editor.cpp" #include "src/core/about_dialog.cpp" #include "src/core/instrument.cpp" #include "src/core/main.cpp" @@ -38,7 +39,6 @@ #include "src/core/piano_widget.cpp" #include "src/core/name_label.cpp" #include "src/core/preset_preview_play_handle.cpp" -#include "src/core/time_roll.cpp" #include "src/core/track_container.cpp" #include "src/core/track.cpp" #include "src/core/file_browser.cpp" @@ -60,10 +60,10 @@ #include "src/audio/audio_sample_recorder.cpp" #include "src/audio/audio_file_wave.cpp" #include "src/lmms_single_source.cpp" +#include "src/tracks/automation_pattern.cpp" #include "src/tracks/pattern.cpp" #include "src/tracks/bb_track.cpp" #include "src/tracks/sample_track.cpp" -#include "src/tracks/time_pattern.cpp" #include "src/widgets/project_notes.cpp" #include "src/widgets/led_checkbox.cpp" #include "src/widgets/knob.cpp" diff --git a/src/tracks/automation_pattern.cpp b/src/tracks/automation_pattern.cpp new file mode 100644 index 0000000000..728504aa07 --- /dev/null +++ b/src/tracks/automation_pattern.cpp @@ -0,0 +1,249 @@ +#ifndef SINGLE_SOURCE_COMPILE + +/* + * automation_pattern.cpp - implementation of class automationPattern which + * holds dynamic values + * + * Copyright (c) 2006 Javier Serrano Polo + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +#include "automation_pattern.h" +#include "templates.h" +#include "automation_editor.h" + + + + +automationPattern::automationPattern ( track * _track, levelObject * _object ) : + journallingObject( _track->eng() ), + m_track( _track ), + m_object( _object ) +{ + init(); +} + + + + +automationPattern::automationPattern( const automationPattern & _pat_to_copy ) : + journallingObject( _pat_to_copy.m_track->eng() ), + m_track( _pat_to_copy.m_track ), + m_object( _pat_to_copy.m_object ) +{ + for( timeMap::const_iterator it = _pat_to_copy.m_time_map.begin(); + it != _pat_to_copy.m_time_map.end(); ++it ) + { + m_time_map[it.key()] = it.data(); + } + + init(); +} + + + + +automationPattern::~automationPattern() +{ + m_track->removeAutomationPattern( this ); + + if( eng()->getAutomationEditor()->currentPattern() == this ) + { + eng()->getAutomationEditor()->setCurrentPattern( NULL ); + } + + m_time_map.clear(); +} + + + + +void automationPattern::init( void ) +{ + m_track->addAutomationPattern( this ); +} + + + + +midiTime automationPattern::length( void ) const +{ + Sint32 max_length = 0; + + for( timeMap::const_iterator it = m_time_map.begin(); + it != m_time_map.end(); + ++it ) + { + max_length = tMax( max_length, it.key() ); + } + if( max_length % 64 == 0 ) + { + return( midiTime( tMax( max_length, 64 ) ) ); + } + return( midiTime( tMax( midiTime( max_length ).getTact() + 1, 1 ), + 0 ) ); +} + + + + +midiTime automationPattern::putValue( const midiTime & _time, const int _value, + const bool _quant_pos ) +{ + midiTime new_time = _quant_pos ? + note::quantized( _time, + eng()->getAutomationEditor()->quantization() ) : + _time; + + m_time_map[new_time] = _value; + + return( new_time ); +} + + + + +void automationPattern::removeValue( const midiTime & _time ) +{ + m_time_map.remove( _time ); +} + + + + +void automationPattern::clearValues( void ) +{ + m_time_map.clear(); + if( eng()->getAutomationEditor()->currentPattern() == this ) + { + eng()->getAutomationEditor()->update(); + } +} + + + + +int automationPattern::valueAt( const midiTime & _time ) +{ + if( m_time_map.contains( _time ) ) + { + return( m_time_map[_time] ); + } + //TODO: Return a better value!! + return( 0 ); +} + + + + +void automationPattern::saveSettings( QDomDocument & _doc, QDomElement & _this ) +{ + for( timeMap::iterator it = m_time_map.begin(); it != m_time_map.end(); + ++it ) + { + QDomElement element = _doc.createElement( "time" ); + element.setAttribute( "pos", static_cast( it.key() ) ); + element.setAttribute( "value", m_object->levelToLabel( + it.data() ) ); + _this.appendChild( element ); + } +} + + + + +void automationPattern::loadSettings( const QDomElement & _this ) +{ + clearValues(); + + for( QDomNode node = _this.firstChild(); !node.isNull(); + node = node.nextSibling() ) + { + QDomElement element = node.toElement(); + if( element.isNull() || element.tagName() != "time" ) + { + continue; + } + m_time_map[midiTime( element.attribute( "pos" ).toInt() )] + = m_object->labelToLevel( + element.attribute( "value" ) ); + } +} + + + + +void automationPattern::openInAutomationEditor( void ) +{ + eng()->getAutomationEditor()->setCurrentPattern( this ); + eng()->getAutomationEditor()->show(); + eng()->getAutomationEditor()->setFocus(); +} + + + + +void automationPattern::clear( void ) +{ + clearValues(); +} + + + + +const QString automationPattern::name( void ) +{ + return( m_track->name() + " - " + dynamic_cast( m_object ) + ->accessibleName() ); +} + + + + +void automationPattern::processMidiTime( const midiTime & _time ) +{ + timeMap::iterator it = m_time_map.find( _time ); + if( it != m_time_map.end() ) + { + m_object->setLevel( it.data() ); + } +} + + + + +#include "automation_pattern.moc" + + +#endif diff --git a/src/tracks/instrument_track.cpp b/src/tracks/instrument_track.cpp index 8654ba5e1b..b659b9cbb9 100644 --- a/src/tracks/instrument_track.cpp +++ b/src/tracks/instrument_track.cpp @@ -1106,7 +1106,7 @@ void instrumentTrack::loadTrackSpecificSettings( const QDomElement & _this ) { m_midiWidget->restoreState( node.toElement() ); } - else if( timePattern::classNodeName() + else if( automationPattern::classNodeName() != node.nodeName() ) { // if node-name doesn't match any known one, diff --git a/src/tracks/sample_track.cpp b/src/tracks/sample_track.cpp index ee20265bd5..2c53f1e15b 100644 --- a/src/tracks/sample_track.cpp +++ b/src/tracks/sample_track.cpp @@ -522,7 +522,8 @@ void sampleTrack::loadTrackSpecificSettings( const QDomElement & _this ) } else { - QDomNode node = _this.namedItem( timePattern::classNodeName() ); + QDomNode node = _this.namedItem( + automationPattern::classNodeName() ); if( node.isElement() && node.namedItem( "vol" ).isElement() ) { m_volumeKnob->loadSettings( _this, "vol" ); diff --git a/src/widgets/knob.cpp b/src/widgets/knob.cpp index c0b3956712..82d93a6e2c 100644 --- a/src/widgets/knob.cpp +++ b/src/widgets/knob.cpp @@ -118,7 +118,7 @@ knob::knob( int _knob_num, QWidget * _parent, const QString & _name, m_knobPixmap = new QPixmap( embed::getIconPixmap( "knob0" + QString::number( m_knobNum + 1 ) ) ); #endif - getTimePattern(); + getAutomationPattern(); setRange( 0.0f, 100.0f, 1.0f ); @@ -374,9 +374,9 @@ void knob::contextMenuEvent( QContextMenuEvent * ) contextMenu.addSeparator(); //TODO: Change icon contextMenu.addAction( embed::getIconPixmap( "piano" ), - tr( "&Open in time-roll" ), - getTimePattern(), - SLOT( openInTimeRoll() ) ); + tr( "&Open in automation editor" ), + getAutomationPattern(), + SLOT( openInAutomationEditor() ) ); contextMenu.addSeparator(); contextMenu.addAction( tr( "Connect to MIDI-device" ), this, SLOT( connectToMidiDevice() ) );