diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index 6ae93b49856..65b496617f1 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -35,6 +35,7 @@ QLineEdit { border: 2px inset rgba(91,101,113,128); background: #49515b; color: #e0e0e0; + selection-background-color: #202020; } @@ -118,7 +119,7 @@ QMenu::indicator:selected { background-color: #747474; } -positionLine { +PositionLine { qproperty-tailGradient: false; qproperty-lineColor: rgb(255, 255, 255); } @@ -138,6 +139,17 @@ PianoRoll { qproperty-ghostNoteBorders: true; qproperty-barColor: #4afd85; qproperty-markedSemitoneColor: rgba( 0, 255, 200, 60 ); + /* Piano keys */ + qproperty-whiteKeyWidth: 64; + qproperty-whiteKeyActiveTextColor: #000; + qproperty-whiteKeyActiveTextShadow: rgb( 240, 240, 240 ); + qproperty-whiteKeyActiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #43e97b, stop:1 #3bcd6c); + qproperty-whiteKeyInactiveTextColor: rgb( 128, 128, 128); + qproperty-whiteKeyInactiveTextShadow: rgb( 240, 240, 240 ); + qproperty-whiteKeyInactiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #eeeeee, stop:1 #ffffff); + qproperty-blackKeyWidth: 48; + qproperty-blackKeyActiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #43e97b, stop:1 #3bcd6c); + qproperty-blackKeyInactiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #333, stop:1 #000); /* Grid colors */ qproperty-lineColor: rgba( 128, 128, 128, 80 ); qproperty-beatLineColor: rgba( 128, 128, 128, 160 ); diff --git a/data/themes/default/style.css b/data/themes/default/style.css index fc6495921a9..832da176f28 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -62,6 +62,7 @@ QLineEdit { border: 1px; background: #101213; color: #d1d8e4; + selection-background-color: #17793b; } QLineEdit:read-only { @@ -150,7 +151,7 @@ QMenu::indicator:selected { background-color: #101213; } -positionLine { +PositionLine { qproperty-tailGradient: true; qproperty-lineColor: rgb(255, 255, 255); } @@ -170,6 +171,17 @@ PianoRoll { qproperty-ghostNoteBorders: false; qproperty-barColor: #078f3a; qproperty-markedSemitoneColor: rgba(255, 255, 255, 30); + /* Piano keys */ + qproperty-whiteKeyWidth: 64; + qproperty-whiteKeyActiveTextColor: #000; + qproperty-whiteKeyActiveTextShadow: #fff; + qproperty-whiteKeyActiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #43e97b, stop:1 #3bcd6c); + qproperty-whiteKeyInactiveTextColor: #000; + qproperty-whiteKeyInactiveTextShadow: #fff; + qproperty-whiteKeyInactiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #eeeeee, stop:1 #ffffff); + qproperty-blackKeyWidth: 48; + qproperty-blackKeyActiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #43e97b, stop:1 #3bcd6c); + qproperty-blackKeyInactiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #333, stop:1 #000); /* Grid colors */ qproperty-lineColor: #292929; qproperty-beatLineColor: #2d6b45; diff --git a/include/AudioAlsa.h b/include/AudioAlsa.h index b1aa9647a61..4bfd0217e31 100644 --- a/include/AudioAlsa.h +++ b/include/AudioAlsa.h @@ -71,7 +71,7 @@ class AudioAlsa : public QThread, public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "ALSA (Advanced Linux Sound Architecture)" ); } diff --git a/include/AudioDummy.h b/include/AudioDummy.h index 0772c69eb36..cf77491bc3f 100644 --- a/include/AudioDummy.h +++ b/include/AudioDummy.h @@ -48,7 +48,7 @@ class AudioDummy : public QThread, public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", "Dummy (no sound output)" ); + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "Dummy (no sound output)" ); } diff --git a/include/AudioJack.h b/include/AudioJack.h index a80e8855201..844aa886d87 100644 --- a/include/AudioJack.h +++ b/include/AudioJack.h @@ -63,7 +63,7 @@ class AudioJack : public QObject, public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "JACK (JACK Audio Connection Kit)" ); } diff --git a/include/AudioOss.h b/include/AudioOss.h index 9e4787ff202..c631bcedddc 100644 --- a/include/AudioOss.h +++ b/include/AudioOss.h @@ -48,7 +48,7 @@ class AudioOss : public QThread, public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", "OSS (Open Sound System)" ); + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "OSS (Open Sound System)" ); } static QString probeDevice(); diff --git a/include/AudioPortAudio.h b/include/AudioPortAudio.h index e1288c3a45d..5279492cee7 100644 --- a/include/AudioPortAudio.h +++ b/include/AudioPortAudio.h @@ -72,7 +72,7 @@ class AudioPortAudio : public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", "PortAudio" ); + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "PortAudio" ); } diff --git a/include/AudioPulseAudio.h b/include/AudioPulseAudio.h index e65180a74a3..b92a386b99f 100644 --- a/include/AudioPulseAudio.h +++ b/include/AudioPulseAudio.h @@ -50,7 +50,7 @@ class AudioPulseAudio : public QThread, public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", "PulseAudio" ); + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "PulseAudio" ); } static QString probeDevice(); diff --git a/include/AudioSdl.h b/include/AudioSdl.h index 93f23abed22..1bda446b290 100644 --- a/include/AudioSdl.h +++ b/include/AudioSdl.h @@ -51,7 +51,7 @@ class AudioSdl : public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "SDL (Simple DirectMedia Layer)" ); } diff --git a/include/AudioSndio.h b/include/AudioSndio.h index f8cf56848a6..0cc88facfa4 100644 --- a/include/AudioSndio.h +++ b/include/AudioSndio.h @@ -49,7 +49,7 @@ class AudioSndio : public QThread, public AudioDevice inline static QString name( void ) { - return QT_TRANSLATE_NOOP( "setupWidget", "sndio" ); + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "sndio" ); } class setupWidget : public AudioDeviceSetupWidget diff --git a/include/AudioSoundIo.h b/include/AudioSoundIo.h index f743ad67629..79b586f0645 100644 --- a/include/AudioSoundIo.h +++ b/include/AudioSoundIo.h @@ -61,7 +61,7 @@ class AudioSoundIo : public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", "soundio" ); + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "soundio" ); } class setupWidget : public AudioDeviceSetupWidget diff --git a/include/AutomationEditor.h b/include/AutomationEditor.h index 0a288f8afcf..4813759697f 100644 --- a/include/AutomationEditor.h +++ b/include/AutomationEditor.h @@ -209,6 +209,7 @@ protected slots: float m_bottomLevel; float m_topLevel; + void centerTopBottomScroll(); void updateTopBottomLevels(); QScrollBar * m_leftRightScroll; diff --git a/include/ColorChooser.h b/include/ColorChooser.h index fe5b7a22a4e..ac2a1b62d4a 100644 --- a/include/ColorChooser.h +++ b/include/ColorChooser.h @@ -21,21 +21,39 @@ * */ -#include #include +#include +#include #include +#include class ColorChooser: public QColorDialog { public: ColorChooser(const QColor &initial, QWidget *parent): QColorDialog(initial, parent) {}; ColorChooser(QWidget *parent): QColorDialog(parent) {}; + //! For getting a color without having to initialise a color dialog + ColorChooser() {}; + enum class Palette {Default, Track, Mixer}; + //! Set global palette via array, checking bounds + void setPalette (QVector); + //! Set global paletter via enum + void setPalette (Palette); + //! Set palette via enum, return self pointer for chaining + ColorChooser* withPalette (Palette); + //! Return a certain palette + static QVector getPalette (Palette); protected: - // Forward key events to the parent to prevent stuck notes when the dialog gets focus + //! Forward key events to the parent to prevent stuck notes when the dialog gets focus void keyReleaseEvent(QKeyEvent *event) override { QKeyEvent ke(*event); QApplication::sendEvent(parentWidget(), &ke); } +private: + //! Copy the current QColorDialog palette into an array + static QVector defaultPalette(); + //! Generate a nice palette, with adjustable value + static QVector nicePalette (int); }; diff --git a/include/ComboBox.h b/include/ComboBox.h index d530c9d92f7..1ab1c240d80 100644 --- a/include/ComboBox.h +++ b/include/ComboBox.h @@ -32,8 +32,6 @@ #include "ComboBoxModel.h" #include "AutomatableModelView.h" - - class LMMS_EXPORT ComboBox : public QWidget, public IntModelView { Q_OBJECT @@ -51,6 +49,8 @@ class LMMS_EXPORT ComboBox : public QWidget, public IntModelView return castModel(); } + static constexpr int DEFAULT_HEIGHT = 22; + public slots: void selectNext(); void selectPrevious(); diff --git a/include/FxLine.h b/include/FxLine.h index c16dcd5f598..e9ee248b811 100644 --- a/include/FxLine.h +++ b/include/FxLine.h @@ -26,10 +26,12 @@ #ifndef FX_LINE_H #define FX_LINE_H +#include #include #include #include +#include "ColorChooser.h" #include "Knob.h" #include "LcdWidget.h" #include "SendButtonIndicator.h" @@ -101,6 +103,9 @@ class FxLine : public QWidget public slots: void renameChannel(); + void resetColor(); + void changeColor(); + void randomColor(); private slots: void renameFinished(); diff --git a/include/FxMixer.h b/include/FxMixer.h index 68b69d9bc88..920d4e27bc3 100644 --- a/include/FxMixer.h +++ b/include/FxMixer.h @@ -32,6 +32,8 @@ #include +#include + class FxRoute; typedef QVector FxRouteVector; @@ -70,6 +72,11 @@ class FxChannel : public ThreadableJob bool requiresProcessing() const override { return true; } void unmuteForSolo(); + + // TODO C++17 and above: use std::optional insteads + QColor m_color; + bool m_hasColor; + std::atomic_int m_dependenciesMet; void incrementDeps(); diff --git a/include/InstrumentSoundShaping.h b/include/InstrumentSoundShaping.h index 1b8df38d3f1..b037f615a77 100644 --- a/include/InstrumentSoundShaping.h +++ b/include/InstrumentSoundShaping.h @@ -74,7 +74,7 @@ class InstrumentSoundShaping : public Model, public JournallingObject FloatModel m_filterCutModel; FloatModel m_filterResModel; - static const QString targetNames[InstrumentSoundShaping::NumTargets][3]; + static const char *const targetNames[InstrumentSoundShaping::NumTargets][3]; friend class InstrumentSoundShapingView; diff --git a/include/Lv2Features.h b/include/Lv2Features.h new file mode 100644 index 00000000000..f036c6d1f67 --- /dev/null +++ b/include/Lv2Features.h @@ -0,0 +1,81 @@ +/* + * Lv2Features.h - Lv2Features class + * + * Copyright (c) 2020-2020 Johannes Lorenz + * + * This file is part of LMMS - https://lmms.io + * + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LV2FEATURES_H +#define LV2FEATURES_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include +#include +#include "Lv2Manager.h" + +/** + Feature container + + References all available features for a plugin and maps them to their URIs. + + The public member functions should be called in descending order: + + 1. initCommon: map plugin-common features + 2. operator[]: map plugin-specific features + 3. createFeatureVectors: create the feature vectors required for + lilv_plugin_instantiate + 4. access the latter +*/ +class Lv2Features +{ +public: + //! Return if a feature is supported by LMMS + static bool isFeatureSupported(const char *featName); + + Lv2Features(); + + //! Register only plugin-common features + void initCommon(); + //! Return reference to feature data with given URI featName + void*& operator[](const char* featName); + //! Fill m_features and m_featurePointers with all features + void createFeatureVectors(); + //! Return LV2_Feature pointer vector, suited for lilv_plugin_instantiate + const LV2_Feature* const* featurePointers() const + { + return m_featurePointers.data(); + } + +private: + //! feature storage + std::vector m_features; + //! pointers to m_features, required for lilv_plugin_instantiate + std::vector m_featurePointers; + //! features + data, ordered by URI + std::map m_featureByUri; +}; + +#endif // LMMS_HAVE_LV2 + +#endif // LV2FEATURES_H diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h index 8437715619e..0b5fc692354 100644 --- a/include/Lv2Manager.h +++ b/include/Lv2Manager.h @@ -30,9 +30,11 @@ #ifdef LMMS_HAVE_LV2 #include +#include #include #include "Lv2Basics.h" +#include "Lv2UridMap.h" #include "Plugin.h" @@ -114,10 +116,31 @@ class Lv2Manager Iterator begin() { return m_lv2InfoMap.begin(); } Iterator end() { return m_lv2InfoMap.end(); } + //! strcmp based key comparator for std::set and std::map + struct CmpStr + { + bool operator()(char const *a, char const *b) const; + }; + + UridMap& uridMap() { return m_uridMap; } + //! Return all + const std::set& supportedFeatureURIs() const + { + return m_supportedFeatureURIs; + } + bool isFeatureSupported(const char* featName) const; + private: + // general data bool m_debug; //!< if set, debug output will be printed LilvWorld* m_world; Lv2InfoMap m_lv2InfoMap; + std::set m_supportedFeatureURIs; + + // feature data that are common for all Lv2Proc + UridMap m_uridMap; + + // functions bool isSubclassOf(const LilvPluginClass *clvss, const char *uriStr); }; diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h index fb1f3466634..1c1cc11d8ac 100644 --- a/include/Lv2Proc.h +++ b/include/Lv2Proc.h @@ -34,6 +34,7 @@ #include #include "Lv2Basics.h" +#include "Lv2Features.h" #include "LinkedModelGroups.h" #include "Plugin.h" #include "PluginIssue.h" @@ -156,6 +157,7 @@ class Lv2Proc : public LinkedModelGroup const LilvPlugin* m_plugin; LilvInstance* m_instance; + Lv2Features m_features; std::vector> m_ports; StereoPortRef m_inPorts, m_outPorts; @@ -163,6 +165,8 @@ class Lv2Proc : public LinkedModelGroup //! models for the controls, sorted by port symbols std::map m_connectedModels; + void initPluginSpecificFeatures(); + //! load a file in the plugin, but don't do anything in LMMS void loadFileInternal(const QString &file); //! allocate m_ports, fill all with metadata, and assign meaning of ports diff --git a/include/Lv2UridMap.h b/include/Lv2UridMap.h new file mode 100644 index 00000000000..f1ddb6253b7 --- /dev/null +++ b/include/Lv2UridMap.h @@ -0,0 +1,69 @@ +/* + * Lv2UridMap.cpp - Lv2UridMap class + * + * Copyright (c) 2019 Johannes Lorenz + * + * This file is part of LMMS - https://lmms.io + * + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LV2URIDMAP_H +#define LV2URIDMAP_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include // TODO: use semaphore, even though this is not realtime critical +#include +#include + +/** + * Complete implementation of the Lv2 Urid Map extension + */ +class UridMap +{ + std::unordered_map m_map; + std::vector m_unMap; + + //! mutex for both m_map and m_unMap + //! the URID map is global, which is why a mutex is required here + std::mutex m_MapMutex; + + LV2_URID_Map m_mapFeature; + LV2_URID_Unmap m_unmapFeature; + + LV2_URID m_lastUrid = 0; + +public: + //! constructor; will set up the features + UridMap(); + + //! map feature function + LV2_URID map(const char* uri); + //! unmap feature function + const char* unmap(LV2_URID urid); + + // access the features + LV2_URID_Map* mapFeature() { return &m_mapFeature; } + LV2_URID_Unmap* unmapFeature() { return &m_unmapFeature; } +}; + +#endif // LMMS_HAVE_LV2 +#endif // LV2URIDMAP_H diff --git a/include/PianoRoll.h b/include/PianoRoll.h index 86a65050c1d..7a99e7b1f80 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -40,6 +40,7 @@ #include "ToolTip.h" #include "StepRecorder.h" #include "StepRecorderWidget.h" +#include "PositionLine.h" class QPainter; class QPixmap; @@ -55,25 +56,38 @@ class TimeLineWidget; class PianoRoll : public QWidget { Q_OBJECT - Q_PROPERTY( QColor barLineColor READ barLineColor WRITE setBarLineColor ) - Q_PROPERTY( QColor beatLineColor READ beatLineColor WRITE setBeatLineColor ) - Q_PROPERTY( QColor lineColor READ lineColor WRITE setLineColor ) - Q_PROPERTY( QColor noteModeColor READ noteModeColor WRITE setNoteModeColor ) - Q_PROPERTY( QColor noteColor READ noteColor WRITE setNoteColor ) - Q_PROPERTY( QColor ghostNoteColor READ ghostNoteColor WRITE setGhostNoteColor ) - Q_PROPERTY( QColor noteTextColor READ noteTextColor WRITE setNoteTextColor ) - Q_PROPERTY( QColor ghostNoteTextColor READ ghostNoteTextColor WRITE setGhostNoteTextColor ) - Q_PROPERTY( QColor barColor READ barColor WRITE setBarColor ) - Q_PROPERTY( QColor selectedNoteColor READ selectedNoteColor WRITE setSelectedNoteColor ) - Q_PROPERTY( QColor textColor READ textColor WRITE setTextColor ) - Q_PROPERTY( QColor textColorLight READ textColorLight WRITE setTextColorLight ) - Q_PROPERTY( QColor textShadow READ textShadow WRITE setTextShadow ) - Q_PROPERTY( QColor markedSemitoneColor READ markedSemitoneColor WRITE setMarkedSemitoneColor ) - Q_PROPERTY( int noteOpacity READ noteOpacity WRITE setNoteOpacity ) - Q_PROPERTY( bool noteBorders READ noteBorders WRITE setNoteBorders ) - Q_PROPERTY( int ghostNoteOpacity READ ghostNoteOpacity WRITE setGhostNoteOpacity ) - Q_PROPERTY( bool ghostNoteBorders READ ghostNoteBorders WRITE setGhostNoteBorders ) - Q_PROPERTY( QColor backgroundShade READ backgroundShade WRITE setBackgroundShade ) + Q_PROPERTY(QColor barLineColor MEMBER m_barLineColor) + Q_PROPERTY(QColor beatLineColor MEMBER m_beatLineColor) + Q_PROPERTY(QColor lineColor MEMBER m_lineColor) + Q_PROPERTY(QColor noteModeColor MEMBER m_noteModeColor) + Q_PROPERTY(QColor noteColor MEMBER m_noteColor) + Q_PROPERTY(QColor ghostNoteColor MEMBER m_ghostNoteColor) + Q_PROPERTY(QColor noteTextColor MEMBER m_noteTextColor) + Q_PROPERTY(QColor ghostNoteTextColor MEMBER m_ghostNoteTextColor) + Q_PROPERTY(QColor barColor MEMBER m_barColor) + Q_PROPERTY(QColor selectedNoteColor MEMBER m_selectedNoteColor) + Q_PROPERTY(QColor textColor MEMBER m_textColor) + Q_PROPERTY(QColor textColorLight MEMBER m_textColorLight) + Q_PROPERTY(QColor textShadow MEMBER m_textShadow) + Q_PROPERTY(QColor markedSemitoneColor MEMBER m_markedSemitoneColor) + Q_PROPERTY(int noteOpacity MEMBER m_noteOpacity) + Q_PROPERTY(bool noteBorders MEMBER m_noteBorders) + Q_PROPERTY(int ghostNoteOpacity MEMBER m_ghostNoteOpacity) + Q_PROPERTY(bool ghostNoteBorders MEMBER m_ghostNoteBorders) + Q_PROPERTY(QColor backgroundShade MEMBER m_backgroundShade) + + /* white key properties */ + Q_PROPERTY(int whiteKeyWidth MEMBER m_whiteKeyWidth) + Q_PROPERTY(QColor whiteKeyInactiveTextColor MEMBER m_whiteKeyInactiveTextColor) + Q_PROPERTY(QColor whiteKeyInactiveTextShadow MEMBER m_whiteKeyInactiveTextShadow) + Q_PROPERTY(QBrush whiteKeyInactiveBackground MEMBER m_whiteKeyInactiveBackground) + Q_PROPERTY(QColor whiteKeyActiveTextColor MEMBER m_whiteKeyActiveTextColor) + Q_PROPERTY(QColor whiteKeyActiveTextShadow MEMBER m_whiteKeyActiveTextShadow) + Q_PROPERTY(QBrush whiteKeyActiveBackground MEMBER m_whiteKeyActiveBackground) + /* black key properties */ + Q_PROPERTY(int blackKeyWidth MEMBER m_blackKeyWidth) + Q_PROPERTY(QBrush blackKeyInactiveBackground MEMBER m_blackKeyInactiveBackground) + Q_PROPERTY(QBrush blackKeyActiveBackground MEMBER m_blackKeyActiveBackground) public: enum EditModes { @@ -125,47 +139,6 @@ class PianoRoll : public QWidget int quantization() const; - // qproperty access functions - QColor barLineColor() const; - void setBarLineColor( const QColor & c ); - QColor beatLineColor() const; - void setBeatLineColor( const QColor & c ); - QColor lineColor() const; - void setLineColor( const QColor & c ); - QColor noteModeColor() const; - void setNoteModeColor( const QColor & c ); - QColor noteColor() const; - void setNoteColor( const QColor & c ); - QColor noteTextColor() const; - void setNoteTextColor( const QColor & c ); - QColor barColor() const; - void setBarColor( const QColor & c ); - QColor selectedNoteColor() const; - void setSelectedNoteColor( const QColor & c ); - QColor textColor() const; - void setTextColor( const QColor & c ); - QColor textColorLight() const; - void setTextColorLight( const QColor & c ); - QColor textShadow() const; - void setTextShadow( const QColor & c ); - QColor markedSemitoneColor() const; - void setMarkedSemitoneColor( const QColor & c ); - int noteOpacity() const; - void setNoteOpacity( const int i ); - bool noteBorders() const; - void setNoteBorders( const bool b ); - QColor ghostNoteColor() const; - void setGhostNoteColor( const QColor & c ); - QColor ghostNoteTextColor() const; - void setGhostNoteTextColor( const QColor & c ); - int ghostNoteOpacity() const; - void setGhostNoteOpacity( const int i ); - bool ghostNoteBorders() const; - void setGhostNoteBorders( const bool b ); - QColor backgroundShade() const; - void setBackgroundShade( const QColor & c ); - - protected: void keyPressEvent( QKeyEvent * ke ) override; void keyReleaseEvent( QKeyEvent * ke ) override; @@ -188,7 +161,6 @@ class PianoRoll : public QWidget void selectAll(); NoteVector getSelectedNotes() const; void selectNotesOnKey(); - int xCoordOfTick( int tick ); // for entering values with dblclick in the vol/pan bars void enterValue( NoteVector* nv ); @@ -279,6 +251,8 @@ protected slots: PR_BLACK_KEY }; + PositionLine * m_positionLine; + QVector m_nemStr; // gui names of each edit mode QMenu * m_noteEditMenu; // when you right click below the key area @@ -306,6 +280,9 @@ protected slots: void playChordNotes(int key, int velocity=-1); void pauseChordNotes(int key); + void updateScrollbars(); + void updatePositionLineHeight(); + QList getAllOctavesForKey( int keyToMirror ) const; int noteEditTop() const; @@ -320,12 +297,6 @@ protected slots: static const int cm_scrollAmtHoriz = 10; static const int cm_scrollAmtVert = 1; - static QPixmap * s_whiteKeyBigPm; - static QPixmap * s_whiteKeyBigPressedPm; - static QPixmap * s_whiteKeySmallPm; - static QPixmap * s_whiteKeySmallPressedPm; - static QPixmap * s_blackKeyPm; - static QPixmap * s_blackKeyPressedPm; static QPixmap * s_toolDraw; static QPixmap * s_toolErase; static QPixmap * s_toolSelect; @@ -389,10 +360,11 @@ protected slots: int m_moveStartX; int m_moveStartY; - int m_oldNotesEditHeight; int m_notesEditHeight; + int m_userSetNotesEditHeight; int m_ppb; // pixels per bar int m_totalKeysToScroll; + int m_pianoKeysVisible; int m_keyLineHeight; int m_octaveHeight; @@ -458,6 +430,18 @@ protected slots: bool m_noteBorders; bool m_ghostNoteBorders; QColor m_backgroundShade; + /* white key properties */ + int m_whiteKeyWidth; + QColor m_whiteKeyActiveTextColor; + QColor m_whiteKeyActiveTextShadow; + QBrush m_whiteKeyActiveBackground; + QColor m_whiteKeyInactiveTextColor; + QColor m_whiteKeyInactiveTextShadow; + QBrush m_whiteKeyInactiveBackground; + /* black key properties */ + int m_blackKeyWidth; + QBrush m_blackKeyActiveBackground; + QBrush m_blackKeyInactiveBackground; signals: void positionChanged( const MidiTime & ); @@ -501,6 +485,7 @@ class PianoRollWindow : public Editor, SerializingObject } QSize sizeHint() const override; + bool hasFocus() const; signals: void currentPatternChanged(); diff --git a/include/PositionLine.h b/include/PositionLine.h new file mode 100644 index 00000000000..d48fd3df1cb --- /dev/null +++ b/include/PositionLine.h @@ -0,0 +1,49 @@ +/* + * PositionLine.h - declaration of class PositionLine, a simple widget that + * draws a line, mainly works with TimeLineWidget + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * This file is part of LMMS - https://lmms.io + * + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef POSITION_LINE_H +#define POSITION_LINE_H + +#include + +class PositionLine : public QWidget +{ + Q_OBJECT + Q_PROPERTY(bool tailGradient MEMBER m_hasTailGradient) + Q_PROPERTY(QColor lineColor MEMBER m_lineColor) +public: + PositionLine(QWidget* parent); + +public slots: + void zoomChange(double zoom); + +private: + void paintEvent(QPaintEvent* pe) override; + + bool m_hasTailGradient; + QColor m_lineColor; +}; + +#endif \ No newline at end of file diff --git a/include/SongEditor.h b/include/SongEditor.h index c4c25d7d0c9..7e0fe986a8c 100644 --- a/include/SongEditor.h +++ b/include/SongEditor.h @@ -33,6 +33,7 @@ #include "ActionGroup.h" #include "Editor.h" #include "TrackContainerView.h" +#include "PositionLine.h" class QLabel; class QScrollBar; @@ -46,31 +47,6 @@ class Song; class TextFloat; class TimeLineWidget; -class positionLine : public QWidget -{ - Q_OBJECT - Q_PROPERTY ( bool tailGradient READ hasTailGradient WRITE setHasTailGradient ) - Q_PROPERTY ( QColor lineColor READ lineColor WRITE setLineColor ) -public: - positionLine ( QWidget* parent ); - - // qproperty access functions - bool hasTailGradient () const; - void setHasTailGradient ( const bool g ); - QColor lineColor () const; - void setLineColor ( const QColor & c ); - -public slots: - void zoomChange (double zoom); - -private: - void paintEvent( QPaintEvent* pe ) override; - - bool m_hasTailGradient; - QColor m_lineColor; - -}; - class SongEditor : public TrackContainerView { @@ -156,7 +132,7 @@ private slots: TextFloat * m_mvsStatus; TextFloat * m_mpsStatus; - positionLine * m_positionLine; + PositionLine * m_positionLine; ComboBoxModel* m_zoomingModel; ComboBoxModel* m_snappingModel; diff --git a/include/StepRecorderWidget.h b/include/StepRecorderWidget.h index 14cfc2eedce..67f2a4b6547 100644 --- a/include/StepRecorderWidget.h +++ b/include/StepRecorderWidget.h @@ -45,7 +45,9 @@ class StepRecorderWidget : public QWidget //API used by PianoRoll void setPixelsPerBar(int ppb); void setCurrentPosition(MidiTime currentPosition); + void setMargins(const QMargins &qm); void setBottomMargin(const int marginBottom); + QMargins margins(); //API used by StepRecorder void setStepsLength(MidiTime stepsLength); diff --git a/include/TimeLineWidget.h b/include/TimeLineWidget.h index 5b33bd98987..8a9dc5044ea 100644 --- a/include/TimeLineWidget.h +++ b/include/TimeLineWidget.h @@ -150,6 +150,8 @@ class TimeLineWidget : public QWidget, public JournallingObject update(); } + void setXOffset(const int x); + void addToolButtons(QToolBar* _tool_bar ); diff --git a/include/Track.h b/include/Track.h index ff0e3ef88bf..9362a838019 100644 --- a/include/Track.h +++ b/include/Track.h @@ -250,6 +250,20 @@ class TrackContentObjectView : public selectableObject, public ModelView bool needsUpdate(); void setNeedsUpdate( bool b ); + // Method to get a QVector of TCOs to be affected by a context menu action + QVector getClickedTCOs(); + + // Methods to remove, copy, cut, paste and mute a QVector of TCO views + void copy( QVector tcovs ); + void cut( QVector tcovs ); + void paste(); + // remove and toggleMute are static because they don't depend + // being called from a particular TCO view, but can be called anywhere as long + // as a valid TCO view list is given, while copy/cut require an instance for + // some metadata to be written to the clipboard. + static void remove( QVector tcovs ); + static void toggleMute( QVector tcovs ); + public slots: virtual bool close(); void cut(); @@ -257,11 +271,21 @@ public slots: void update() override; protected: + enum ContextMenuAction + { + Remove, + Cut, + Copy, + Paste, + Mute + }; + virtual void constructContextMenu( QMenu * ) { } void contextMenuEvent( QContextMenuEvent * cme ) override; + void contextMenuAction( ContextMenuAction action ); void dragEnterEvent( QDragEnterEvent * dee ) override; void dropEvent( QDropEvent * de ) override; void leaveEvent( QEvent * e ) override; @@ -370,7 +394,9 @@ class TrackContentWidget : public QWidget, public JournallingObject } bool canPasteSelection( MidiTime tcoPos, const QDropEvent *de ); + bool canPasteSelection( MidiTime tcoPos, const QMimeData *md, bool allowSameBar = false ); bool pasteSelection( MidiTime tcoPos, QDropEvent * de ); + bool pasteSelection( MidiTime tcoPos, const QMimeData * md, bool skipSafetyCheck = false ); MidiTime endPosition( const MidiTime & posStart ); @@ -391,6 +417,13 @@ public slots: void changePosition( const MidiTime & newPos = MidiTime( -1 ) ); protected: + enum ContextMenuAction + { + Paste + }; + + void contextMenuEvent( QContextMenuEvent * cme ) override; + void contextMenuAction( QContextMenuEvent * cme, ContextMenuAction action ); void dragEnterEvent( QDragEnterEvent * dee ) override; void dropEvent( QDropEvent * de ) override; void mousePressEvent( QMouseEvent * me ) override; diff --git a/plugins/Amplifier/Amplifier.cpp b/plugins/Amplifier/Amplifier.cpp index cc2a63304ff..e7250872f53 100644 --- a/plugins/Amplifier/Amplifier.cpp +++ b/plugins/Amplifier/Amplifier.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT amplifier_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Amplifier", - QT_TRANSLATE_NOOP( "pluginBrowser", "A native amplifier plugin" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A native amplifier plugin" ), "Vesa Kivimäki ", 0x0100, Plugin::Effect, diff --git a/plugins/BassBooster/BassBooster.cpp b/plugins/BassBooster/BassBooster.cpp index 535834aae77..1ed1f246712 100644 --- a/plugins/BassBooster/BassBooster.cpp +++ b/plugins/BassBooster/BassBooster.cpp @@ -34,7 +34,7 @@ Plugin::Descriptor PLUGIN_EXPORT bassbooster_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "BassBooster", - QT_TRANSLATE_NOOP( "pluginBrowser", "Boost your bass the fast and simple way" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "Boost your bass the fast and simple way" ), "Tobias Doerffel ", 0x0100, Plugin::Effect, diff --git a/plugins/Bitcrush/Bitcrush.cpp b/plugins/Bitcrush/Bitcrush.cpp index 0580d47cd5e..fa19b195fc4 100644 --- a/plugins/Bitcrush/Bitcrush.cpp +++ b/plugins/Bitcrush/Bitcrush.cpp @@ -41,7 +41,7 @@ Plugin::Descriptor PLUGIN_EXPORT bitcrush_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Bitcrush", - QT_TRANSLATE_NOOP( "pluginBrowser", "An oversampling bitcrusher" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "An oversampling bitcrusher" ), "Vesa Kivimäki ", 0x0100, Plugin::Effect, diff --git a/plugins/CrossoverEQ/CrossoverEQ.cpp b/plugins/CrossoverEQ/CrossoverEQ.cpp index e2090980a06..78029134ebc 100644 --- a/plugins/CrossoverEQ/CrossoverEQ.cpp +++ b/plugins/CrossoverEQ/CrossoverEQ.cpp @@ -36,7 +36,7 @@ Plugin::Descriptor PLUGIN_EXPORT crossovereq_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Crossover Equalizer", - QT_TRANSLATE_NOOP( "pluginBrowser", "A 4-band Crossover Equalizer" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A 4-band Crossover Equalizer" ), "Vesa Kivimäki ", 0x0100, Plugin::Effect, diff --git a/plugins/Delay/DelayEffect.cpp b/plugins/Delay/DelayEffect.cpp index 0daad0d9d8c..69ee924e8b0 100644 --- a/plugins/Delay/DelayEffect.cpp +++ b/plugins/Delay/DelayEffect.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT delay_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Delay", - QT_TRANSLATE_NOOP( "pluginBrowser", "A native delay plugin" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A native delay plugin" ), "Dave French ", 0x0100, Plugin::Effect, diff --git a/plugins/DualFilter/DualFilter.cpp b/plugins/DualFilter/DualFilter.cpp index b4e11ccb396..d4e5d4b5ef0 100644 --- a/plugins/DualFilter/DualFilter.cpp +++ b/plugins/DualFilter/DualFilter.cpp @@ -36,7 +36,7 @@ Plugin::Descriptor PLUGIN_EXPORT dualfilter_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Dual Filter", - QT_TRANSLATE_NOOP( "pluginBrowser", "A Dual filter plugin" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A Dual filter plugin" ), "Vesa Kivimäki ", 0x0100, Plugin::Effect, diff --git a/plugins/DualFilter/DualFilterControlDialog.cpp b/plugins/DualFilter/DualFilterControlDialog.cpp index 73755bef38f..0c7732bc6c2 100755 --- a/plugins/DualFilter/DualFilterControlDialog.cpp +++ b/plugins/DualFilter/DualFilterControlDialog.cpp @@ -75,12 +75,12 @@ DualFilterControlDialog::DualFilterControlDialog( DualFilterControls* controls ) ToolTip::add( enabled2Toggle, tr( "Enable/disable filter 2" ) ); ComboBox * m_filter1ComboBox = new ComboBox( this ); - m_filter1ComboBox->setGeometry( 19, 70, 137, 22 ); + m_filter1ComboBox->setGeometry( 19, 70, 137, ComboBox::DEFAULT_HEIGHT ); m_filter1ComboBox->setFont( pointSize<8>( m_filter1ComboBox->font() ) ); m_filter1ComboBox->setModel( &controls->m_filter1Model ); ComboBox * m_filter2ComboBox = new ComboBox( this ); - m_filter2ComboBox->setGeometry( 217, 70, 137, 22 ); + m_filter2ComboBox->setGeometry( 217, 70, 137, ComboBox::DEFAULT_HEIGHT ); m_filter2ComboBox->setFont( pointSize<8>( m_filter2ComboBox->font() ) ); m_filter2ComboBox->setModel( &controls->m_filter2Model ); } diff --git a/plugins/Eq/EqEffect.cpp b/plugins/Eq/EqEffect.cpp index 6b7c61bcc49..a295374931c 100644 --- a/plugins/Eq/EqEffect.cpp +++ b/plugins/Eq/EqEffect.cpp @@ -39,7 +39,7 @@ Plugin::Descriptor PLUGIN_EXPORT eq_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Equalizer", - QT_TRANSLATE_NOOP( "pluginBrowser", "A native eq plugin" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A native eq plugin" ), "Dave French ", 0x0100, Plugin::Effect, diff --git a/plugins/Flanger/FlangerEffect.cpp b/plugins/Flanger/FlangerEffect.cpp index 7e441cf62a3..65b58ce49ed 100644 --- a/plugins/Flanger/FlangerEffect.cpp +++ b/plugins/Flanger/FlangerEffect.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT flanger_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Flanger", - QT_TRANSLATE_NOOP( "pluginBrowser", "A native flanger plugin" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A native flanger plugin" ), "Dave French ", 0x0100, Plugin::Effect, diff --git a/plugins/FreeBoy/FreeBoy.cpp b/plugins/FreeBoy/FreeBoy.cpp index 2f787044f25..95ba5a215b5 100644 --- a/plugins/FreeBoy/FreeBoy.cpp +++ b/plugins/FreeBoy/FreeBoy.cpp @@ -53,7 +53,7 @@ Plugin::Descriptor PLUGIN_EXPORT freeboy_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "FreeBoy", - QT_TRANSLATE_NOOP( "pluginBrowser", "Emulation of GameBoy (TM) APU" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "Emulation of GameBoy (TM) APU" ), "Attila Herman " "Csaba Hruska ", diff --git a/plugins/GigPlayer/GigPlayer.cpp b/plugins/GigPlayer/GigPlayer.cpp index 334e2bd7737..1e077f5d4e3 100644 --- a/plugins/GigPlayer/GigPlayer.cpp +++ b/plugins/GigPlayer/GigPlayer.cpp @@ -63,7 +63,7 @@ Plugin::Descriptor PLUGIN_EXPORT gigplayer_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "GIG Player", - QT_TRANSLATE_NOOP( "pluginBrowser", "Player for GIG files" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "Player for GIG files" ), "Garrett Wilson ", 0x0100, Plugin::Instrument, diff --git a/plugins/HydrogenImport/HydrogenImport.cpp b/plugins/HydrogenImport/HydrogenImport.cpp index 4ed0a89a3f1..4a69bc4511c 100644 --- a/plugins/HydrogenImport/HydrogenImport.cpp +++ b/plugins/HydrogenImport/HydrogenImport.cpp @@ -29,7 +29,7 @@ Plugin::Descriptor PLUGIN_EXPORT hydrogenimport_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Hydrogen Import", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Filter for importing Hydrogen files into LMMS" ), "frank mather", 0x0100, diff --git a/plugins/LadspaEffect/LadspaEffect.cpp b/plugins/LadspaEffect/LadspaEffect.cpp index d7e66bf47aa..117e1e0e10e 100644 --- a/plugins/LadspaEffect/LadspaEffect.cpp +++ b/plugins/LadspaEffect/LadspaEffect.cpp @@ -53,7 +53,7 @@ Plugin::Descriptor PLUGIN_EXPORT ladspaeffect_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "LADSPA", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "plugin for using arbitrary LADSPA-effects " "inside LMMS." ), "Danny McRae ", diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index 2ee5ceabbe5..36e9df46ba7 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -39,7 +39,7 @@ Plugin::Descriptor PLUGIN_EXPORT lv2effect_plugin_descriptor = { STRINGIFY(PLUGIN_NAME), "LV2", - QT_TRANSLATE_NOOP("pluginBrowser", + QT_TRANSLATE_NOOP("PluginBrowser", "plugin for using arbitrary LV2-effects inside LMMS."), "Johannes Lorenz ", 0x0100, diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index 974aaf416b4..0f7534ac8c9 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -44,7 +44,7 @@ Plugin::Descriptor PLUGIN_EXPORT lv2instrument_plugin_descriptor = { STRINGIFY(PLUGIN_NAME), "LV2", - QT_TRANSLATE_NOOP("pluginBrowser", + QT_TRANSLATE_NOOP("PluginBrowser", "plugin for using arbitrary LV2 instruments inside LMMS."), "Johannes Lorenz ", 0x0100, diff --git a/plugins/MidiExport/MidiExport.cpp b/plugins/MidiExport/MidiExport.cpp index 1860527c10e..466664b167d 100644 --- a/plugins/MidiExport/MidiExport.cpp +++ b/plugins/MidiExport/MidiExport.cpp @@ -47,7 +47,7 @@ Plugin::Descriptor PLUGIN_EXPORT midiexport_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "MIDI Export", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Filter for exporting MIDI-files from LMMS" ), "Mohamed Abdel Maksoud and " "Hyunjin Song ", diff --git a/plugins/MidiImport/MidiImport.cpp b/plugins/MidiImport/MidiImport.cpp index 3646975a29a..f183fb418f2 100644 --- a/plugins/MidiImport/MidiImport.cpp +++ b/plugins/MidiImport/MidiImport.cpp @@ -64,7 +64,7 @@ Plugin::Descriptor PLUGIN_EXPORT midiimport_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "MIDI Import", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Filter for importing MIDI-files into LMMS" ), "Tobias Doerffel ", 0x0100, diff --git a/plugins/MultitapEcho/MultitapEcho.cpp b/plugins/MultitapEcho/MultitapEcho.cpp index bff032deed9..eb6c51cba82 100644 --- a/plugins/MultitapEcho/MultitapEcho.cpp +++ b/plugins/MultitapEcho/MultitapEcho.cpp @@ -34,7 +34,7 @@ Plugin::Descriptor PLUGIN_EXPORT multitapecho_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Multitap Echo", - QT_TRANSLATE_NOOP( "pluginBrowser", "A multitap echo delay plugin" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A multitap echo delay plugin" ), "Vesa Kivimäki ", 0x0100, Plugin::Effect, diff --git a/plugins/OpulenZ/OpulenZ.cpp b/plugins/OpulenZ/OpulenZ.cpp index 4dde05b0f3b..8ab635b4f92 100644 --- a/plugins/OpulenZ/OpulenZ.cpp +++ b/plugins/OpulenZ/OpulenZ.cpp @@ -68,7 +68,7 @@ Plugin::Descriptor PLUGIN_EXPORT opulenz_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "OpulenZ", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "2-operator FM Synth" ), "Raine M. Ekman ", 0x0100, diff --git a/plugins/ReverbSC/ReverbSC.cpp b/plugins/ReverbSC/ReverbSC.cpp index 3d56fd0d0ae..f54002199d5 100644 --- a/plugins/ReverbSC/ReverbSC.cpp +++ b/plugins/ReverbSC/ReverbSC.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT reverbsc_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "ReverbSC", - QT_TRANSLATE_NOOP( "pluginBrowser", "Reverb algorithm by Sean Costello" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "Reverb algorithm by Sean Costello" ), "Paul Batchelor", 0x0123, Plugin::Effect, diff --git a/plugins/SpectrumAnalyzer/Analyzer.cpp b/plugins/SpectrumAnalyzer/Analyzer.cpp index 656d18bd4d6..87a8542e913 100644 --- a/plugins/SpectrumAnalyzer/Analyzer.cpp +++ b/plugins/SpectrumAnalyzer/Analyzer.cpp @@ -42,7 +42,7 @@ extern "C" { { "spectrumanalyzer", "Spectrum Analyzer", - QT_TRANSLATE_NOOP("pluginBrowser", "A graphical spectrum analyzer."), + QT_TRANSLATE_NOOP("PluginBrowser", "A graphical spectrum analyzer."), "Martin Pavelek ", 0x0112, Plugin::Effect, diff --git a/plugins/SpectrumAnalyzer/SaControlsDialog.cpp b/plugins/SpectrumAnalyzer/SaControlsDialog.cpp index f1aad2a01b5..ddd1489e85d 100644 --- a/plugins/SpectrumAnalyzer/SaControlsDialog.cpp +++ b/plugins/SpectrumAnalyzer/SaControlsDialog.cpp @@ -151,8 +151,8 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) ComboBox *freqRangeCombo = new ComboBox(this, tr("Frequency range")); freqRangeCombo->setToolTip(tr("Frequency range")); - freqRangeCombo->setMinimumSize(100, 22); - freqRangeCombo->setMaximumSize(200, 22); + freqRangeCombo->setMinimumSize(100, ComboBox::DEFAULT_HEIGHT); + freqRangeCombo->setMaximumSize(200, ComboBox::DEFAULT_HEIGHT); freqRangeCombo->setModel(&controls->m_freqRangeModel); config_layout->addWidget(freqRangeCombo, 0, 3, 2, 1); @@ -171,8 +171,8 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) ComboBox *ampRangeCombo = new ComboBox(this, tr("Amplitude range")); ampRangeCombo->setToolTip(tr("Amplitude range")); - ampRangeCombo->setMinimumSize(100, 22); - ampRangeCombo->setMaximumSize(200, 22); + ampRangeCombo->setMinimumSize(100, ComboBox::DEFAULT_HEIGHT); + ampRangeCombo->setMaximumSize(200, ComboBox::DEFAULT_HEIGHT); ampRangeCombo->setModel(&controls->m_ampRangeModel); config_layout->addWidget(ampRangeCombo, 2, 3, 2, 1); @@ -201,8 +201,8 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) ComboBox *windowCombo = new ComboBox(this, tr("FFT window type")); windowCombo->setToolTip(tr("FFT window type")); - windowCombo->setMinimumSize(100, 22); - windowCombo->setMaximumSize(200, 22); + windowCombo->setMinimumSize(100, ComboBox::DEFAULT_HEIGHT); + windowCombo->setMaximumSize(200, ComboBox::DEFAULT_HEIGHT); windowCombo->setModel(&controls->m_windowModel); config_layout->addWidget(windowCombo, 2, 5, 2, 1); processor->rebuildWindow(); diff --git a/plugins/Vectorscope/Vectorscope.cpp b/plugins/Vectorscope/Vectorscope.cpp index f8bc30c40df..30d33aa2d8f 100644 --- a/plugins/Vectorscope/Vectorscope.cpp +++ b/plugins/Vectorscope/Vectorscope.cpp @@ -33,7 +33,7 @@ extern "C" { { STRINGIFY(PLUGIN_NAME), "Vectorscope", - QT_TRANSLATE_NOOP("pluginBrowser", "A stereo field visualizer."), + QT_TRANSLATE_NOOP("PluginBrowser", "A stereo field visualizer."), "Martin Pavelek ", 0x0100, Plugin::Effect, diff --git a/plugins/VstEffect/VstEffect.cpp b/plugins/VstEffect/VstEffect.cpp index 80f209a7b1c..fb805aed3c3 100644 --- a/plugins/VstEffect/VstEffect.cpp +++ b/plugins/VstEffect/VstEffect.cpp @@ -41,7 +41,7 @@ Plugin::Descriptor PLUGIN_EXPORT vsteffect_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "VST", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "plugin for using arbitrary VST effects inside LMMS." ), "Tobias Doerffel ", 0x0200, diff --git a/plugins/VstEffect/VstEffectControls.cpp b/plugins/VstEffect/VstEffectControls.cpp index ab740838855..e9133ff325e 100644 --- a/plugins/VstEffect/VstEffectControls.cpp +++ b/plugins/VstEffect/VstEffectControls.cpp @@ -374,7 +374,7 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * { sprintf( paramStr, "%d", i); m_vi->knobFModel[ i ] = new FloatModel( LocaleHelper::toFloat(s_dumpValues.at(2)), - 0.0f, 1.0f, 0.01f, _eff, tr( paramStr ) ); + 0.0f, 1.0f, 0.01f, _eff, paramStr ); } FloatModel * model = m_vi->knobFModel[i]; diff --git a/plugins/Xpressive/Xpressive.cpp b/plugins/Xpressive/Xpressive.cpp index 6e648edd481..e4eff06bdc8 100644 --- a/plugins/Xpressive/Xpressive.cpp +++ b/plugins/Xpressive/Xpressive.cpp @@ -53,7 +53,7 @@ extern "C" { Plugin::Descriptor PLUGIN_EXPORT xpressive_plugin_descriptor = { STRINGIFY( - PLUGIN_NAME), "Xpressive", QT_TRANSLATE_NOOP("pluginBrowser", + PLUGIN_NAME), "Xpressive", QT_TRANSLATE_NOOP("PluginBrowser", "Mathematical expression parser"), "Orr Dvori", 0x0100, Plugin::Instrument, new PluginPixmapLoader("logo"), NULL, NULL }; diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index 51af71f55eb..dbde6e8c45c 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -55,7 +55,7 @@ Plugin::Descriptor PLUGIN_EXPORT audiofileprocessor_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "AudioFileProcessor", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Simple sampler with various settings for " "using samples (e.g. drums) in an " "instrument-track" ), @@ -541,7 +541,7 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, // interpolation selector m_interpBox = new ComboBox( this ); - m_interpBox->setGeometry( 142, 62, 82, 22 ); + m_interpBox->setGeometry( 142, 62, 82, ComboBox::DEFAULT_HEIGHT ); m_interpBox->setFont( pointSize<8>( m_interpBox->font() ) ); // wavegraph diff --git a/plugins/bit_invader/bit_invader.cpp b/plugins/bit_invader/bit_invader.cpp index caa272fa7f0..3bc5785ef64 100644 --- a/plugins/bit_invader/bit_invader.cpp +++ b/plugins/bit_invader/bit_invader.cpp @@ -51,7 +51,7 @@ Plugin::Descriptor PLUGIN_EXPORT bitinvader_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "BitInvader", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Customizable wavetable synthesizer" ), "Andreas Brandmaier ", 0x0100, diff --git a/plugins/carlapatchbay/carlapatchbay.cpp b/plugins/carlapatchbay/carlapatchbay.cpp index ad0c1f6aef5..0090b3fcccf 100644 --- a/plugins/carlapatchbay/carlapatchbay.cpp +++ b/plugins/carlapatchbay/carlapatchbay.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT carlapatchbay_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Carla Patchbay", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Carla Patchbay Instrument" ), "falkTX ", CARLA_VERSION_HEX, diff --git a/plugins/carlarack/carlarack.cpp b/plugins/carlarack/carlarack.cpp index ee2a788354f..3b3f443abb4 100644 --- a/plugins/carlarack/carlarack.cpp +++ b/plugins/carlarack/carlarack.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT carlarack_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Carla Rack", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Carla Rack Instrument" ), "falkTX ", CARLA_VERSION_HEX, diff --git a/plugins/dynamics_processor/dynamics_processor.cpp b/plugins/dynamics_processor/dynamics_processor.cpp index 9bf7d9b3c90..38c74cf19de 100644 --- a/plugins/dynamics_processor/dynamics_processor.cpp +++ b/plugins/dynamics_processor/dynamics_processor.cpp @@ -38,7 +38,7 @@ Plugin::Descriptor PLUGIN_EXPORT dynamicsprocessor_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Dynamics Processor", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "plugin for processing dynamics in a flexible way" ), "Vesa Kivimäki ", 0x0100, diff --git a/plugins/kicker/kicker.cpp b/plugins/kicker/kicker.cpp index 5f36aae93f1..fe770c1ac1f 100644 --- a/plugins/kicker/kicker.cpp +++ b/plugins/kicker/kicker.cpp @@ -45,7 +45,7 @@ Plugin::Descriptor PLUGIN_EXPORT kicker_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Kicker", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Versatile drum synthesizer" ), "Tobias Doerffel ", 0x0100, diff --git a/plugins/ladspa_browser/ladspa_browser.cpp b/plugins/ladspa_browser/ladspa_browser.cpp index d265bc0e3e2..b363336346f 100644 --- a/plugins/ladspa_browser/ladspa_browser.cpp +++ b/plugins/ladspa_browser/ladspa_browser.cpp @@ -49,7 +49,7 @@ Plugin::Descriptor PLUGIN_EXPORT ladspabrowser_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "LADSPA Plugin Browser", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "List installed LADSPA plugins" ), "Danny McRae ", 0x0100, diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index ad3d33b0a04..72bd5a4946e 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -83,7 +83,7 @@ Plugin::Descriptor PLUGIN_EXPORT lb302_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "LB302", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Incomplete monophonic imitation tb303" ), "Paul Giblock ", 0x0100, diff --git a/plugins/monstro/Monstro.cpp b/plugins/monstro/Monstro.cpp index efb351fc839..8e0342377c3 100644 --- a/plugins/monstro/Monstro.cpp +++ b/plugins/monstro/Monstro.cpp @@ -45,7 +45,7 @@ Plugin::Descriptor PLUGIN_EXPORT monstro_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Monstro", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Monstrous 3-oscillator synth with modulation matrix" ), "Vesa Kivimäki ", 0x0100, @@ -1664,7 +1664,7 @@ QWidget * MonstroView::setupOperatorsView( QWidget * _parent ) m_osc2VolKnob -> setVolumeKnob( true ); m_osc2WaveBox = new ComboBox( view ); - m_osc2WaveBox -> setGeometry( 204, O2ROW + 7, 42, 22 ); + m_osc2WaveBox -> setGeometry( 204, O2ROW + 7, 42, ComboBox::DEFAULT_HEIGHT ); m_osc2WaveBox->setFont( pointSize<8>( m_osc2WaveBox->font() ) ); maketinyled( m_osc2SyncHButton, 212, O2ROW - 3, tr( "Hard sync oscillator 2" ) ) @@ -1679,18 +1679,18 @@ QWidget * MonstroView::setupOperatorsView( QWidget * _parent ) m_osc3VolKnob -> setVolumeKnob( true ); m_osc3Wave1Box = new ComboBox( view ); - m_osc3Wave1Box -> setGeometry( 160, O3ROW + 7, 42, 22 ); + m_osc3Wave1Box -> setGeometry( 160, O3ROW + 7, 42, ComboBox::DEFAULT_HEIGHT ); m_osc3Wave1Box->setFont( pointSize<8>( m_osc3Wave1Box->font() ) ); m_osc3Wave2Box = new ComboBox( view ); - m_osc3Wave2Box -> setGeometry( 204, O3ROW + 7, 42, 22 ); + m_osc3Wave2Box -> setGeometry( 204, O3ROW + 7, 42, ComboBox::DEFAULT_HEIGHT ); m_osc3Wave2Box->setFont( pointSize<8>( m_osc3Wave2Box->font() ) ); maketinyled( m_osc3SyncHButton, 212, O3ROW - 3, tr( "Hard sync oscillator 3" ) ) maketinyled( m_osc3SyncRButton, 191, O3ROW - 3, tr( "Reverse sync oscillator 3" ) ) m_lfo1WaveBox = new ComboBox( view ); - m_lfo1WaveBox -> setGeometry( 2, LFOROW + 7, 42, 22 ); + m_lfo1WaveBox -> setGeometry( 2, LFOROW + 7, 42, ComboBox::DEFAULT_HEIGHT ); m_lfo1WaveBox->setFont( pointSize<8>( m_lfo1WaveBox->font() ) ); maketsknob( m_lfo1AttKnob, LFOCOL1, LFOROW, tr( "Attack" ), " ms", "lfoKnob" ) @@ -1698,7 +1698,7 @@ QWidget * MonstroView::setupOperatorsView( QWidget * _parent ) makeknob( m_lfo1PhsKnob, LFOCOL3, LFOROW, tr( "Phase" ), tr( " deg" ), "lfoKnob" ) m_lfo2WaveBox = new ComboBox( view ); - m_lfo2WaveBox -> setGeometry( 127, LFOROW + 7, 42, 22 ); + m_lfo2WaveBox -> setGeometry( 127, LFOROW + 7, 42, ComboBox::DEFAULT_HEIGHT ); m_lfo2WaveBox->setFont( pointSize<8>( m_lfo2WaveBox->font() ) ); maketsknob( m_lfo2AttKnob, LFOCOL4, LFOROW, tr( "Attack" ), " ms", "lfoKnob" ) diff --git a/plugins/nes/Nes.cpp b/plugins/nes/Nes.cpp index d4bcc6881d1..d7c97fd160e 100644 --- a/plugins/nes/Nes.cpp +++ b/plugins/nes/Nes.cpp @@ -44,7 +44,7 @@ Plugin::Descriptor PLUGIN_EXPORT nes_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Nescaline", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "A NES-like synthesizer" ), "Vesa Kivimäki ", 0x0100, diff --git a/plugins/organic/organic.cpp b/plugins/organic/organic.cpp index 6eb933afad3..c78d5c056b1 100644 --- a/plugins/organic/organic.cpp +++ b/plugins/organic/organic.cpp @@ -51,7 +51,7 @@ Plugin::Descriptor PLUGIN_EXPORT organic_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Organic", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Additive Synthesizer for organ-like sounds" ), "Andreas Brandmaier ", 0x0100, diff --git a/plugins/patman/patman.cpp b/plugins/patman/patman.cpp index e5170383438..d65850edcd0 100644 --- a/plugins/patman/patman.cpp +++ b/plugins/patman/patman.cpp @@ -54,7 +54,7 @@ Plugin::Descriptor PLUGIN_EXPORT patman_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "PatMan", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "GUS-compatible patch instrument" ), "Javier Serrano Polo ", 0x0100, diff --git a/plugins/peak_controller_effect/peak_controller_effect.cpp b/plugins/peak_controller_effect/peak_controller_effect.cpp index 9d1e6ccf465..01d24600e4e 100644 --- a/plugins/peak_controller_effect/peak_controller_effect.cpp +++ b/plugins/peak_controller_effect/peak_controller_effect.cpp @@ -41,7 +41,7 @@ Plugin::Descriptor PLUGIN_EXPORT peakcontrollereffect_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Peak Controller", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Plugin for controlling knobs with sound peaks" ), "Paul Giblock ", 0x0100, diff --git a/plugins/sf2_player/sf2_player.cpp b/plugins/sf2_player/sf2_player.cpp index 7065a08002c..7a07b0ec697 100644 --- a/plugins/sf2_player/sf2_player.cpp +++ b/plugins/sf2_player/sf2_player.cpp @@ -57,7 +57,7 @@ Plugin::Descriptor PLUGIN_EXPORT sf2player_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Sf2 Player", - QT_TRANSLATE_NOOP( "pluginBrowser", "Player for SoundFont files" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "Player for SoundFont files" ), "Paul Giblock ", 0x0100, Plugin::Instrument, diff --git a/plugins/sfxr/sfxr.cpp b/plugins/sfxr/sfxr.cpp index 1aacdc12c3b..08d418836c2 100644 --- a/plugins/sfxr/sfxr.cpp +++ b/plugins/sfxr/sfxr.cpp @@ -62,7 +62,7 @@ Plugin::Descriptor PLUGIN_EXPORT sfxr_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "sfxr", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "LMMS port of sfxr" ), "Wong Cho Ching", 0x0100, diff --git a/plugins/sid/sid_instrument.cpp b/plugins/sid/sid_instrument.cpp index 27f874e12dc..485fdfffb58 100644 --- a/plugins/sid/sid_instrument.cpp +++ b/plugins/sid/sid_instrument.cpp @@ -75,7 +75,7 @@ Plugin::Descriptor PLUGIN_EXPORT sid_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "SID", - QT_TRANSLATE_NOOP( "pluginBrowser", "Emulation of the MOS6581 and MOS8580 " + QT_TRANSLATE_NOOP( "PluginBrowser", "Emulation of the MOS6581 and MOS8580 " "SID.\nThis chip was used in the Commodore 64 computer." ), "Csaba Hruska " diff --git a/plugins/stereo_enhancer/stereo_enhancer.cpp b/plugins/stereo_enhancer/stereo_enhancer.cpp index 2faa5846d2f..3f5a9a38c29 100644 --- a/plugins/stereo_enhancer/stereo_enhancer.cpp +++ b/plugins/stereo_enhancer/stereo_enhancer.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT stereoenhancer_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "StereoEnhancer Effect", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Plugin for enhancing stereo separation of a stereo input file" ), "Lou Herard ", 0x0100, diff --git a/plugins/stereo_matrix/stereo_matrix.cpp b/plugins/stereo_matrix/stereo_matrix.cpp index a03a615ba37..2ec9b49509e 100644 --- a/plugins/stereo_matrix/stereo_matrix.cpp +++ b/plugins/stereo_matrix/stereo_matrix.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT stereomatrix_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Stereo Matrix", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Plugin for freely manipulating stereo output" ), "Paul Giblock ", 0x0100, diff --git a/plugins/stk/mallets/mallets.cpp b/plugins/stk/mallets/mallets.cpp index f9e2e7ede56..6f968985ced 100644 --- a/plugins/stk/mallets/mallets.cpp +++ b/plugins/stk/mallets/mallets.cpp @@ -50,7 +50,7 @@ Plugin::Descriptor PLUGIN_EXPORT malletsstk_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Mallets", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Tuneful things to bang on" ), "Danny McRae ", 0x0100, @@ -403,7 +403,7 @@ malletsInstrumentView::malletsInstrumentView( malletsInstrument * _instrument, changePreset(); // Show widget m_presetsCombo = new ComboBox( this, tr( "Instrument" ) ); - m_presetsCombo->setGeometry( 140, 50, 99, 22 ); + m_presetsCombo->setGeometry( 140, 50, 99, ComboBox::DEFAULT_HEIGHT ); m_presetsCombo->setFont( pointSize<8>( m_presetsCombo->font() ) ); connect( &_instrument->m_presetsModel, SIGNAL( dataChanged() ), diff --git a/plugins/triple_oscillator/TripleOscillator.cpp b/plugins/triple_oscillator/TripleOscillator.cpp index 8093d218c04..9fdae48337f 100644 --- a/plugins/triple_oscillator/TripleOscillator.cpp +++ b/plugins/triple_oscillator/TripleOscillator.cpp @@ -49,7 +49,7 @@ Plugin::Descriptor PLUGIN_EXPORT tripleoscillator_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "TripleOscillator", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Three powerful oscillators you can modulate " "in several ways" ), "Tobias Doerffel ", diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index b17a1684541..dc1723db472 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -71,7 +71,7 @@ Plugin::Descriptor Q_DECL_EXPORT vestige_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "VeSTige", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "VST-host for using VST(i)-plugins within LMMS" ), "Tobias Doerffel ", 0x0100, @@ -991,7 +991,7 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume { sprintf( paramStr, "%d", i); m_vi->knobFModel[ i ] = new FloatModel( LocaleHelper::toFloat(s_dumpValues.at(2)), - 0.0f, 1.0f, 0.01f, castModel(), tr( paramStr ) ); + 0.0f, 1.0f, 0.01f, castModel(), paramStr ); } FloatModel * model = m_vi->knobFModel[i]; diff --git a/plugins/vibed/vibed.cpp b/plugins/vibed/vibed.cpp index c663660826e..6610bab0e43 100644 --- a/plugins/vibed/vibed.cpp +++ b/plugins/vibed/vibed.cpp @@ -48,7 +48,7 @@ Plugin::Descriptor PLUGIN_EXPORT vibedstrings_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Vibed", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Vibrating string modeler" ), "Danny McRae ", 0x0100, diff --git a/plugins/vst_base/RemoteVstPlugin32.cmake b/plugins/vst_base/RemoteVstPlugin32.cmake index cba9a26c8ab..466752aa5da 100644 --- a/plugins/vst_base/RemoteVstPlugin32.cmake +++ b/plugins/vst_base/RemoteVstPlugin32.cmake @@ -20,7 +20,7 @@ ELSEIF(LMMS_BUILD_WIN64 AND MSVC) IF(NOT QT_32_PREFIX) SET(LMMS_MSVC_YEAR_FOR_QT ${LMMS_MSVC_YEAR}) - if(LMMS_MSVC_YEAR_FOR_QT EQUAL 2019) + if(LMMS_MSVC_YEAR_FOR_QT EQUAL 2019 AND Qt5_VERSION VERSION_LESS "5.15") SET(LMMS_MSVC_YEAR_FOR_QT 2017) # Qt only provides binaries for MSVC 2017, but 2019 is binary compatible endif() diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 7d6a45940d2..278ab8b2d71 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -560,7 +560,7 @@ void VstPlugin::loadParameterDisplays() void VstPlugin::savePreset( ) { QString presName = currentProgramName().isEmpty() ? tr(": default") : currentProgramName(); - presName.replace(tr("\""), tr("'")); // QFileDialog unable to handle double quotes properly + presName.replace("\"", "'"); // QFileDialog unable to handle double quotes properly FileDialog sfd( NULL, tr( "Save Preset" ), presName.section(": ", 1, 1) + tr(".fxp"), tr( "Vst Plugin Preset (*.fxp *.fxb)" ) ); diff --git a/plugins/watsyn/Watsyn.cpp b/plugins/watsyn/Watsyn.cpp index a5af401ccf7..b60dd5a99c2 100644 --- a/plugins/watsyn/Watsyn.cpp +++ b/plugins/watsyn/Watsyn.cpp @@ -44,7 +44,7 @@ Plugin::Descriptor PLUGIN_EXPORT watsyn_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Watsyn", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "4-oscillator modulatable wavetable synth" ), "Vesa Kivimäki ", 0x0100, diff --git a/plugins/waveshaper/waveshaper.cpp b/plugins/waveshaper/waveshaper.cpp index a3bf2ddfb12..5327d931d9c 100644 --- a/plugins/waveshaper/waveshaper.cpp +++ b/plugins/waveshaper/waveshaper.cpp @@ -38,7 +38,7 @@ Plugin::Descriptor PLUGIN_EXPORT waveshaper_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Waveshaper Effect", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "plugin for waveshaping" ), "Vesa Kivimäki ", 0x0100, diff --git a/plugins/zynaddsubfx/ZynAddSubFx.cpp b/plugins/zynaddsubfx/ZynAddSubFx.cpp index 429948e7508..04f7bda0ff3 100644 --- a/plugins/zynaddsubfx/ZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/ZynAddSubFx.cpp @@ -58,7 +58,7 @@ Plugin::Descriptor PLUGIN_EXPORT zynaddsubfx_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "ZynAddSubFX", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Embedded ZynAddSubFX" ), "Tobias Doerffel ", 0x0100, diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 730791bf770..b7685522c24 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -93,10 +93,12 @@ set(LMMS_SRCS core/lv2/Lv2Basics.cpp core/lv2/Lv2ControlBase.cpp + core/lv2/Lv2Features.cpp core/lv2/Lv2Ports.cpp core/lv2/Lv2Proc.cpp core/lv2/Lv2Manager.cpp core/lv2/Lv2SubPluginFeatures.cpp + core/lv2/Lv2UridMap.cpp core/midi/MidiAlsaRaw.cpp core/midi/MidiAlsaSeq.cpp diff --git a/src/core/Clipboard.cpp b/src/core/Clipboard.cpp index 0c4b972865b..9b1191cdc0d 100644 --- a/src/core/Clipboard.cpp +++ b/src/core/Clipboard.cpp @@ -22,6 +22,9 @@ * */ +#include +#include + #include "Clipboard.h" #include "JournallingObject.h" @@ -35,6 +38,10 @@ void Clipboard::copy( JournallingObject * _obj ) QDomElement parent = doc.createElement( "Clipboard" ); _obj->saveState( doc, parent ); content[_obj->nodeName()] = parent.firstChild().toElement(); + + // Clear the QApplication clipboard, so we don't have any conflicts when LMMS has to + // decide between the QApplication clipboard and the internal clipboard data + QApplication::clipboard()->clear( QClipboard::Clipboard ); } diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 4a68c3f3b05..7ddcbea623a 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -73,6 +73,7 @@ FxChannel::FxChannel( int idx, Model * _parent ) : m_lock(), m_channelIndex( idx ), m_queued( false ), + m_hasColor( false ), m_dependenciesMet(0) { BufferManager::clear( m_buffer, Engine::mixer()->framesPerPeriod() ); @@ -741,6 +742,7 @@ void FxMixer::saveSettings( QDomDocument & _doc, QDomElement & _this ) ch->m_soloModel.saveSettings( _doc, fxch, "soloed" ); fxch.setAttribute( "num", i ); fxch.setAttribute( "name", ch->m_name ); + if( ch->m_hasColor ) fxch.setAttribute( "color", ch->m_color.name() ); // add the channel sends for( int si = 0; si < ch->m_sends.size(); ++si ) @@ -786,6 +788,11 @@ void FxMixer::loadSettings( const QDomElement & _this ) m_fxChannels[num]->m_muteModel.loadSettings( fxch, "muted" ); m_fxChannels[num]->m_soloModel.loadSettings( fxch, "soloed" ); m_fxChannels[num]->m_name = fxch.attribute( "name" ); + if( fxch.hasAttribute( "color" ) ) + { + m_fxChannels[num]->m_hasColor = true; + m_fxChannels[num]->m_color.setNamedColor( fxch.attribute( "color" ) ); + } m_fxChannels[num]->m_fxChain.restoreState( fxch.firstChildElement( m_fxChannels[num]->m_fxChain.nodeName() ) ); diff --git a/src/core/InstrumentSoundShaping.cpp b/src/core/InstrumentSoundShaping.cpp index 46026b96f8c..2c221cdcc01 100644 --- a/src/core/InstrumentSoundShaping.cpp +++ b/src/core/InstrumentSoundShaping.cpp @@ -43,16 +43,14 @@ const float RES_PRECISION = 1000.0f; // names for env- and lfo-targets - first is name being displayed to user // and second one is used internally, e.g. for saving/restoring settings -const QString InstrumentSoundShaping::targetNames[InstrumentSoundShaping::NumTargets][3] = +const char *const InstrumentSoundShaping::targetNames[InstrumentSoundShaping::NumTargets][3] = { - { InstrumentSoundShaping::tr( "VOLUME" ), "vol", - InstrumentSoundShaping::tr( "Volume" ) }, -/* InstrumentSoundShaping::tr( "Pan" ), - InstrumentSoundShaping::tr( "Pitch" ),*/ - { InstrumentSoundShaping::tr( "CUTOFF" ), "cut", - InstrumentSoundShaping::tr( "Cutoff frequency" ) }, - { InstrumentSoundShaping::tr( "RESO" ), "res", - InstrumentSoundShaping::tr( "Resonance" ) } + { QT_TRANSLATE_NOOP("InstrumentSoundShaping", "VOLUME"), "vol", + QT_TRANSLATE_NOOP("InstrumentSoundShaping", "Volume") }, + { QT_TRANSLATE_NOOP("InstrumentSoundShaping", "CUTOFF"), "cut", + QT_TRANSLATE_NOOP("InstrumentSoundShaping", "Cutoff frequency") }, + { QT_TRANSLATE_NOOP("InstrumentSoundShaping", "RESO"), "res", + QT_TRANSLATE_NOOP("InstrumentSoundShaping", "Resonance") } } ; @@ -77,7 +75,7 @@ InstrumentSoundShaping::InstrumentSoundShaping( value_for_zero_amount, this ); m_envLfoParameters[i]->setDisplayName( - tr( targetNames[i][2].toUtf8().constData() ) ); + tr( targetNames[i][2] ) ); } m_filterModel.addItem( tr( "Low-pass" ), make_unique( "filter_lp" ) ); diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 476d54fef25..29bc725b6b6 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -357,7 +357,7 @@ const surroundSampleFrame * Mixer::renderNextBuffer() currentPlayMode == Song::Mode_PlayBB; if( playModeSupportsMetronome && m_metronomeActive && !song->isExporting() && - p != last_metro_pos && + !song->isPaused() && p != last_metro_pos && // Stop crash with metronome if empty project Engine::getSong()->countTracks() ) { diff --git a/src/core/Plugin.cpp b/src/core/Plugin.cpp index 411f6fe5899..f1256654970 100644 --- a/src/core/Plugin.cpp +++ b/src/core/Plugin.cpp @@ -44,7 +44,7 @@ static Plugin::Descriptor dummyPluginDescriptor = { "dummy", "dummy", - QT_TRANSLATE_NOOP( "pluginBrowser", "no description" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "no description" ), "Tobias Doerffel ", 0x0100, Plugin::Undefined, diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 6505ffabda9..e52270fdd83 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include "AutomationPattern.h" @@ -734,6 +735,12 @@ void TrackContentObjectView::paintTextLabel(QString const & text, QPainter & pai */ void TrackContentObjectView::mousePressEvent( QMouseEvent * me ) { + // Right now, active is only used on right/mid clicks actions, so we use a ternary operator + // to avoid the overhead of calling getClickedTCOs when it's not used + auto active = me->button() == Qt::LeftButton + ? QVector() + : getClickedTCOs(); + setInitialPos( me->pos() ); setInitialOffsets(); if( !fixedTCOs() && me->button() == Qt::LeftButton ) @@ -826,22 +833,22 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me ) { if( me->modifiers() & Qt::ControlModifier ) { - m_tco->toggleMute(); + toggleMute( active ); } else if( me->modifiers() & Qt::ShiftModifier && !fixedTCOs() ) { - remove(); + remove( active ); } } else if( me->button() == Qt::MidButton ) { if( me->modifiers() & Qt::ControlModifier ) { - m_tco->toggleMute(); + toggleMute( active ); } else if( !fixedTCOs() ) { - remove(); + remove( active ); } } } @@ -1116,34 +1123,203 @@ void TrackContentObjectView::mouseReleaseEvent( QMouseEvent * me ) */ void TrackContentObjectView::contextMenuEvent( QContextMenuEvent * cme ) { + // Depending on whether we right-clicked a selection or an individual TCO we will have + // different labels for the actions. + bool individualTCO = getClickedTCOs().size() <= 1; + if( cme->modifiers() ) { return; } QMenu contextMenu( this ); + if( fixedTCOs() == false ) { - contextMenu.addAction( embed::getIconPixmap( "cancel" ), - tr( "Delete (middle mousebutton)" ), - this, SLOT( remove() ) ); + contextMenu.addAction( + embed::getIconPixmap( "cancel" ), + individualTCO + ? tr("Delete (middle mousebutton)") + : tr("Delete selection (middle mousebutton)"), + [this](){ contextMenuAction( Remove ); } ); + contextMenu.addSeparator(); - contextMenu.addAction( embed::getIconPixmap( "edit_cut" ), - tr( "Cut" ), this, SLOT( cut() ) ); + + contextMenu.addAction( + embed::getIconPixmap( "edit_cut" ), + individualTCO + ? tr("Cut") + : tr("Cut selection"), + [this](){ contextMenuAction( Cut ); } ); } - contextMenu.addAction( embed::getIconPixmap( "edit_copy" ), - tr( "Copy" ), m_tco, SLOT( copy() ) ); - contextMenu.addAction( embed::getIconPixmap( "edit_paste" ), - tr( "Paste" ), m_tco, SLOT( paste() ) ); + + contextMenu.addAction( + embed::getIconPixmap( "edit_copy" ), + individualTCO + ? tr("Copy") + : tr("Copy selection"), + [this](){ contextMenuAction( Copy ); } ); + + contextMenu.addAction( + embed::getIconPixmap( "edit_paste" ), + tr( "Paste" ), + [this](){ contextMenuAction( Paste ); } ); + contextMenu.addSeparator(); - contextMenu.addAction( embed::getIconPixmap( "muted" ), - tr( "Mute/unmute (<%1> + middle click)" ).arg(UI_CTRL_KEY), - m_tco, SLOT( toggleMute() ) ); + + contextMenu.addAction( + embed::getIconPixmap( "muted" ), + (individualTCO + ? tr("Mute/unmute (<%1> + middle click)") + : tr("Mute/unmute selection (<%1> + middle click)")).arg(UI_CTRL_KEY), + [this](){ contextMenuAction( Mute ); } ); + constructContextMenu( &contextMenu ); contextMenu.exec( QCursor::pos() ); } +// This method processes the actions from the context menu of the TCO View. +void TrackContentObjectView::contextMenuAction( ContextMenuAction action ) +{ + QVector active = getClickedTCOs(); + // active will be later used for the remove, copy, cut or toggleMute methods + + switch( action ) + { + case Remove: + remove( active ); + break; + case Cut: + cut( active ); + break; + case Copy: + copy( active ); + break; + case Paste: + paste(); + break; + case Mute: + toggleMute( active ); + break; + } +} + +QVector TrackContentObjectView::getClickedTCOs() +{ + // Get a list of selected selectableObjects + QVector sos = gui->songEditor()->m_editor->selectedObjects(); + + // Convert to a list of selected TCOVs + QVector selection; + selection.reserve( sos.size() ); + for( auto so: sos ) + { + TrackContentObjectView *tcov = dynamic_cast ( so ); + if( tcov != nullptr ) + { + selection.append( tcov ); + } + } + + // If we clicked part of the selection, affect all selected clips. Otherwise affect the clip we clicked + return selection.contains(this) + ? selection + : QVector( 1, this ); +} + +void TrackContentObjectView::remove( QVector tcovs ) +{ + for( auto tcov: tcovs ) + { + // No need to check if it's nullptr because we check when building the QVector + tcov->remove(); + } +} + +void TrackContentObjectView::copy( QVector tcovs ) +{ + // Checks if there are other selected TCOs and if so copy them as well + if( tcovs.size() > 1 ) + { + // Write the TCOs to a DataFile for copying + DataFile dataFile = createTCODataFiles( tcovs ); + + // Add the TCO type as a key to the final string + QString finalString = QString( "tco_%1:%2" ).arg( m_tco->getTrack()->type() ).arg( dataFile.toString() ); + + // Copy it to the clipboard + QMimeData *tco_content = new QMimeData; + tco_content->setData( StringPairDrag::mimeType(), finalString.toUtf8() ); + QApplication::clipboard()->setMimeData( tco_content, QClipboard::Clipboard ); + } + else + { + tcovs.at(0)->getTrackContentObject()->copy(); + } +} + +void TrackContentObjectView::cut( QVector tcovs ) +{ + // Checks if there are other selected TCOs and if so cut them as well + if( tcovs.size() > 1 ) + { + // Write the TCOs to a DataFile for copying + DataFile dataFile = createTCODataFiles( tcovs ); + + // Now that the dataFile is created we can delete the tracks, since we are cutting + // TODO: Is it safe to call tcov->remove(); on the current TCOV instance? + remove( tcovs ); + + // Add the TCO type as a key to the final string + QString finalString = QString( "tco_%1:%2" ).arg( m_tco->getTrack()->type() ).arg( dataFile.toString() ); + + // Copy it to the clipboard + QMimeData *tco_content = new QMimeData; + tco_content->setData( StringPairDrag::mimeType(), finalString.toUtf8() ); + QApplication::clipboard()->setMimeData( tco_content, QClipboard::Clipboard ); + } + else + { + tcovs.at(0)->cut(); + } +} + +void TrackContentObjectView::paste() +{ + // NOTE: Because we give preference to the QApplication clipboard over the LMMS Clipboard class, we need to + // clear the QApplication Clipboard during the LMMS Clipboard copy operations (Clipboard::copy does that) + + // If we have TCO data on the clipboard paste it. If not, do our regular TCO paste. + if( QApplication::clipboard()->mimeData( QClipboard::Clipboard )->hasFormat( StringPairDrag::mimeType() ) ) + { + // Paste the selection on the MidiTime of the selected Track + const QMimeData *md = QApplication::clipboard()->mimeData( QClipboard::Clipboard ); + MidiTime tcoPos = MidiTime( m_tco->startPosition() ); + + TrackContentWidget *tcw = getTrackView()->getTrackContentWidget(); + + if( tcw->pasteSelection( tcoPos, md ) == true ) + { + // If we succeed on the paste we delete the TCO we pasted on + remove(); + } + } + else + { + getTrackContentObject()->paste(); + } +} + +void TrackContentObjectView::toggleMute( QVector tcovs ) +{ + for( auto tcov: tcovs ) + { + // No need to check for nullptr because we check while building the tcovs QVector + tcov->getTrackContentObject()->toggleMute(); + } +} + @@ -1519,9 +1695,19 @@ bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QDropEvent* d { const QMimeData * mimeData = de->mimeData(); + // If the source of the DropEvent is the current instance of LMMS we don't allow pasting in the same bar + // if it's another instance of LMMS we allow it + return de->source() + ? canPasteSelection( tcoPos, mimeData ) + : canPasteSelection( tcoPos, mimeData, true ); +} + +// Overloaded method to make it possible to call this method without a Drag&Drop event +bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QMimeData* md , bool allowSameBar ) +{ Track * t = getTrack(); - QString type = StringPairDrag::decodeMimeKey( mimeData ); - QString value = StringPairDrag::decodeMimeValue( mimeData ); + QString type = StringPairDrag::decodeMimeKey( md ); + QString value = StringPairDrag::decodeMimeValue( md ); // We can only paste into tracks of the same type if( type != ( "tco_" + QString::number( t->type() ) ) || @@ -1547,9 +1733,9 @@ bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QDropEvent* d const TrackContainer::TrackList tracks = t->trackContainer()->tracks(); const int currentTrackIndex = tracks.indexOf( t ); - // Don't paste if we're on the same bar + // Don't paste if we're on the same bar and allowSameBar is false auto sourceTrackContainerId = metadata.attributeNode( "trackContainerId" ).value().toUInt(); - if( de->source() && sourceTrackContainerId == t->trackContainer()->id() && + if( !allowSameBar && sourceTrackContainerId == t->trackContainer()->id() && tcoPos == grabbedTCOBar && currentTrackIndex == initialTrackIndex ) { return false; @@ -1591,13 +1777,28 @@ bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QDropEvent* d */ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, QDropEvent * de ) { + const QMimeData * mimeData = de->mimeData(); + if( canPasteSelection( tcoPos, de ) == false ) { return false; } - QString type = StringPairDrag::decodeKey( de ); - QString value = StringPairDrag::decodeValue( de ); + // We set skipSafetyCheck to true because we already called canPasteSelection + return pasteSelection( tcoPos, mimeData, true ); +} + +// Overloaded method so we can call it without a Drag&Drop event +bool TrackContentWidget::pasteSelection( MidiTime tcoPos, const QMimeData * md, bool skipSafetyCheck ) +{ + // When canPasteSelection was already called before, skipSafetyCheck will skip this + if( !skipSafetyCheck && canPasteSelection( tcoPos, md ) == false ) + { + return false; + } + + QString type = StringPairDrag::decodeMimeKey( md ); + QString value = StringPairDrag::decodeMimeValue( md ); getTrack()->addJournalCheckPoint(); @@ -1789,6 +1990,43 @@ MidiTime TrackContentWidget::endPosition( const MidiTime & posStart ) return posStart + static_cast( w * MidiTime::ticksPerBar() / ppb ); } +void TrackContentWidget::contextMenuEvent( QContextMenuEvent * cme ) +{ + if( cme->modifiers() ) + { + return; + } + + // If we don't have TCO data in the clipboard there's no need to create this menu + // since "paste" is the only action at the moment. + const QMimeData *md = QApplication::clipboard()->mimeData( QClipboard::Clipboard ); + if( !md->hasFormat( StringPairDrag::mimeType() ) ) + { + return; + } + + QMenu contextMenu( this ); + QAction *pasteA = contextMenu.addAction( embed::getIconPixmap( "edit_paste" ), + tr( "Paste" ), [this, cme](){ contextMenuAction( cme, Paste ); } ); + // If we can't paste in the current TCW for some reason, disable the action so the user knows + pasteA->setEnabled( canPasteSelection( getPosition( cme->x() ), md ) ? true : false ); + + contextMenu.exec( QCursor::pos() ); +} + +void TrackContentWidget::contextMenuAction( QContextMenuEvent * cme, ContextMenuAction action ) +{ + switch( action ) + { + case Paste: + // Paste the selection on the MidiTime of the context menu event + const QMimeData *md = QApplication::clipboard()->mimeData( QClipboard::Clipboard ); + MidiTime tcoPos = getPosition( cme->x() ); + + pasteSelection( tcoPos, md ); + break; + } +} diff --git a/src/core/audio/AudioPortAudio.cpp b/src/core/audio/AudioPortAudio.cpp index ad67277ab35..865eeca8d5a 100644 --- a/src/core/audio/AudioPortAudio.cpp +++ b/src/core/audio/AudioPortAudio.cpp @@ -410,14 +410,14 @@ AudioPortAudio::setupWidget::setupWidget( QWidget * _parent ) : AudioDeviceSetupWidget( AudioPortAudio::name(), _parent ) { m_backend = new ComboBox( this, "BACKEND" ); - m_backend->setGeometry( 64, 15, 260, 20 ); + m_backend->setGeometry( 64, 15, 260, ComboBox::DEFAULT_HEIGHT ); QLabel * backend_lbl = new QLabel( tr( "Backend" ), this ); backend_lbl->setFont( pointSize<7>( backend_lbl->font() ) ); backend_lbl->move( 8, 18 ); m_device = new ComboBox( this, "DEVICE" ); - m_device->setGeometry( 64, 35, 260, 20 ); + m_device->setGeometry( 64, 35, 260, ComboBox::DEFAULT_HEIGHT ); QLabel * dev_lbl = new QLabel( tr( "Device" ), this ); dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) ); diff --git a/src/core/lv2/Lv2Features.cpp b/src/core/lv2/Lv2Features.cpp new file mode 100644 index 00000000000..fe668807e06 --- /dev/null +++ b/src/core/lv2/Lv2Features.cpp @@ -0,0 +1,97 @@ +/* + * Lv2Features.cpp - Lv2Features implementation + * + * Copyright (c) 2020-2020 Johannes Lorenz + * + * This file is part of LMMS - https://lmms.io + * + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "Lv2Features.h" + +#ifdef LMMS_HAVE_LV2 + +#include + +#include "Engine.h" +#include "Lv2Manager.h" + + +bool Lv2Features::isFeatureSupported(const char* featName) +{ + return Engine::getLv2Manager()->isFeatureSupported(featName); +} + + + + +Lv2Features::Lv2Features() +{ + const Lv2Manager* man = Engine::getLv2Manager(); + // create (yet empty) map feature URI -> feature + for(const char* uri : man->supportedFeatureURIs()) + { + m_featureByUri.emplace(uri, nullptr); + } +} + + + + +void Lv2Features::initCommon() +{ + Lv2Manager* man = Engine::getLv2Manager(); + // init m_featureByUri with the plugin-common features + operator[](LV2_URID__map) = man->uridMap().mapFeature(); + operator[](LV2_URID__unmap) = man->uridMap().unmapFeature(); +} + + + + +void Lv2Features::createFeatureVectors() +{ + // create vector of features + for(std::pair& pr : m_featureByUri) + { + Q_ASSERT(pr.second != nullptr); + m_features.push_back(LV2_Feature { pr.first, pr.second }); + } + + // create pointer vector (for lilv_plugin_instantiate) + m_featurePointers.reserve(m_features.size() + 1); + for(std::size_t i = 0; i < m_features.size(); ++i) + { + m_featurePointers.push_back(&m_features[i]); + } + m_featurePointers.push_back(nullptr); +} + + + + +void *&Lv2Features::operator[](const char *featName) +{ + auto itr = m_featureByUri.find(featName); + Q_ASSERT(itr != m_featureByUri.end()); + return itr->second; +} + + +#endif // LMMS_HAVE_LV2 + diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index bce3bf372ca..69fbd0137c5 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -27,6 +27,7 @@ #ifdef LMMS_HAVE_LV2 #include +#include #include #include #include @@ -50,6 +51,9 @@ Lv2Manager::Lv2Manager() m_world = lilv_world_new(); lilv_world_load_all(m_world); + + m_supportedFeatureURIs.insert(LV2_URID__map); + m_supportedFeatureURIs.insert(LV2_URID__unmap); } @@ -133,6 +137,22 @@ void Lv2Manager::initPlugins() +bool Lv2Manager::CmpStr::operator()(const char *a, const char *b) const +{ + return std::strcmp(a, b) < 0; +} + + + + +bool Lv2Manager::isFeatureSupported(const char *featName) const +{ + return m_supportedFeatureURIs.find(featName) != m_supportedFeatureURIs.end(); +} + + + + // unused + untested yet bool Lv2Manager::isSubclassOf(const LilvPluginClass* clvss, const char* uriStr) { diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 2d77f3f9835..86235f145b2 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -31,6 +31,7 @@ #include "AutomatableModel.h" #include "ComboBoxModel.h" #include "Engine.h" +#include "Lv2Features.h" #include "Lv2Manager.h" #include "Lv2Ports.h" #include "Mixer.h" @@ -74,8 +75,12 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, AutoLilvNodes reqFeats(lilv_plugin_get_required_features(plugin)); LILV_FOREACH (nodes, itr, reqFeats.get()) { - issues.emplace_back(featureNotSupported, - lilv_node_as_string(lilv_nodes_get(reqFeats.get(), itr))); + const char* reqFeatName = lilv_node_as_string( + lilv_nodes_get(reqFeats.get(), itr)); + if(!Lv2Features::isFeatureSupported(reqFeatName)) + { + issues.emplace_back(featureNotSupported, reqFeatName); + } } if (printIssues && issues.size()) @@ -240,11 +245,15 @@ AutomatableModel *Lv2Proc::modelAtPort(const QString &uri) void Lv2Proc::initPlugin() { + m_features.initCommon(); + initPluginSpecificFeatures(); + m_features.createFeatureVectors(); + createPorts(); m_instance = lilv_plugin_instantiate(m_plugin, Engine::mixer()->processingSampleRate(), - nullptr); + m_features.featurePointers()); if (m_instance) { @@ -276,6 +285,16 @@ void Lv2Proc::shutdownPlugin() +void Lv2Proc::initPluginSpecificFeatures() +{ + // nothing yet + // it would look like this: + // m_features[LV2_URID__map] = m_uridMapFeature +} + + + + void Lv2Proc::loadFileInternal(const QString &file) { (void)file; diff --git a/src/core/lv2/Lv2UridMap.cpp b/src/core/lv2/Lv2UridMap.cpp new file mode 100644 index 00000000000..7e4fa864f1e --- /dev/null +++ b/src/core/lv2/Lv2UridMap.cpp @@ -0,0 +1,99 @@ +/* + * Lv2UridMap.cpp - Lv2UridMap implementation + * + * Copyright (c) 2019 Johannes Lorenz + * + * This file is part of LMMS - https://lmms.io + * + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#include "Lv2UridMap.h" + +#ifdef LMMS_HAVE_LV2 + +static LV2_URID staticMap(LV2_URID_Map_Handle handle, const char* uri) +{ + UridMap* map = static_cast(handle); + return map->map(uri); +} + +static const char* staticUnmap(LV2_URID_Unmap_Handle handle, LV2_URID urid) +{ + UridMap* map = static_cast(handle); + return map->unmap(urid); +} + +UridMap::UridMap() +{ + m_mapFeature.handle = static_cast(this); + m_mapFeature.map = staticMap; + m_unmapFeature.handle = static_cast(this); + m_unmapFeature.unmap = staticUnmap; +} + +LV2_URID UridMap::map(const char *uri) +{ + LV2_URID result = 0u; + + // the Lv2 docs say that 0 should be returned in any case + // where creating an ID for the given URI fails + try + { + // TODO: + // when using C++14, we can get around any string allocation + // in the case the URI is already inside the map: + // * use `m_map.find(uri)` instead of `m_map.find(uriStr)` + // * to avoid temporary string construction in the `find` call, create + // m_map like this: + // std::unordered_map, std::equal<>> m_map; + // * move the try block inside the case where the URI is not in the map + const std::string uriStr = uri; + + std::lock_guard guard (m_MapMutex); + + auto itr = m_map.find(uriStr); + if (itr == m_map.end()) + { + // 1 is the first free URID + std::size_t index = 1u + m_unMap.size(); + auto pr = m_map.emplace(std::move(uriStr), index); + if (pr.second) + { + m_unMap.emplace_back(pr.first->first.c_str()); + result = static_cast(index); + } + } + else { result = itr->second; } + } + catch(...) { /* result variable is already 0 */ } + + return result; +} + +const char *UridMap::unmap(LV2_URID urid) +{ + std::size_t idx = static_cast(urid) - 1; + + std::lock_guard guard (m_MapMutex); + return (idx < m_unMap.size()) ? m_unMap[idx] : nullptr; +} + +#endif // LMMS_HAVE_LV2 + diff --git a/src/gui/AudioDeviceSetupWidget.cpp b/src/gui/AudioDeviceSetupWidget.cpp index fbec38c7682..feba3f1b0fb 100644 --- a/src/gui/AudioDeviceSetupWidget.cpp +++ b/src/gui/AudioDeviceSetupWidget.cpp @@ -26,7 +26,7 @@ AudioDeviceSetupWidget::AudioDeviceSetupWidget(const QString & caption, QWidget * parent) : - TabWidget(TabWidget::tr("Settings for %1").arg(TabWidget::tr(caption.toLatin1())), parent) + TabWidget(TabWidget::tr("Settings for %1").arg(tr(caption.toUtf8())), parent) { } diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index fb81c4b2a59..80eb716031e 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -37,6 +37,7 @@ SET(LMMS_SRCS gui/dialogs/FileDialog.cpp gui/dialogs/VersionedSaveDialog.cpp + gui/dialogs/ColorChooser.cpp gui/editors/AutomationEditor.cpp gui/editors/BBEditor.cpp @@ -82,6 +83,7 @@ SET(LMMS_SRCS gui/widgets/NStateButton.cpp gui/widgets/Oscilloscope.cpp gui/widgets/PixmapButton.cpp + gui/widgets/PositionLine.cpp gui/widgets/ProjectNotes.cpp gui/widgets/RenameDialog.cpp gui/widgets/Rubberband.cpp diff --git a/src/gui/ControllerConnectionDialog.cpp b/src/gui/ControllerConnectionDialog.cpp index f0d3d10e9c2..6b6527b3342 100644 --- a/src/gui/ControllerConnectionDialog.cpp +++ b/src/gui/ControllerConnectionDialog.cpp @@ -187,7 +187,7 @@ ControllerConnectionDialog::ControllerConnectionDialog( QWidget * _parent, this, SLOT( userToggled() ) ); m_userController = new ComboBox( m_userGroupBox, "Controller" ); - m_userController->setGeometry( 10, 24, 200, 22 ); + m_userController->setGeometry( 10, 24, 200, ComboBox::DEFAULT_HEIGHT ); for (Controller * c : Engine::getSong()->controllers()) { m_userController->model()->addItem( c->name() ); diff --git a/src/gui/EffectSelectDialog.cpp b/src/gui/EffectSelectDialog.cpp index 79e40427b28..4423ea707d7 100644 --- a/src/gui/EffectSelectDialog.cpp +++ b/src/gui/EffectSelectDialog.cpp @@ -234,7 +234,7 @@ void EffectSelectDialog::rowChanged( const QModelIndex & _idx, { QLabel *label = new QLabel(m_descriptionWidget); QString labelText = "

" + tr("Name") + ": " + QString::fromUtf8(descriptor.displayName) + "

"; - labelText += "

" + tr("Description") + ": " + qApp->translate( "pluginBrowser", descriptor.description ) + "

"; + labelText += "

" + tr("Description") + ": " + qApp->translate( "PluginBrowser", descriptor.description ) + "

"; labelText += "

" + tr("Author") + ": " + QString::fromUtf8(descriptor.author) + "

"; label->setText(labelText); diff --git a/src/gui/MidiSetupWidget.cpp b/src/gui/MidiSetupWidget.cpp index 0c34544d6bf..8342070571b 100644 --- a/src/gui/MidiSetupWidget.cpp +++ b/src/gui/MidiSetupWidget.cpp @@ -31,7 +31,7 @@ MidiSetupWidget::MidiSetupWidget(const QString & caption, const QString & configSection, const QString & devName, QWidget * parent) : - TabWidget(TabWidget::tr("Settings for %1").arg(tr(caption.toLatin1())), parent), + TabWidget(TabWidget::tr("Settings for %1").arg(tr(caption.toUtf8())), parent), m_configSection(configSection), m_device(nullptr) { diff --git a/src/gui/PluginBrowser.cpp b/src/gui/PluginBrowser.cpp index 671b58381ea..7fd1a6301a0 100644 --- a/src/gui/PluginBrowser.cpp +++ b/src/gui/PluginBrowser.cpp @@ -205,7 +205,9 @@ PluginDescWidget::PluginDescWidget(const PluginKey &_pk, setFixedHeight( DEFAULT_HEIGHT ); setMouseTracking( true ); setCursor( Qt::PointingHandCursor ); - setToolTip(_pk.description()); + setToolTip(_pk.desc->subPluginFeatures + ? _pk.description() + : tr(_pk.desc->description)); } diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index e542039c50e..f06eea9e05c 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -31,12 +31,14 @@ #include #include +#include "AudioDeviceSetupWidget.h" #include "debug.h" #include "embed.h" #include "Engine.h" #include "FileDialog.h" #include "gui_templates.h" #include "MainWindow.h" +#include "MidiSetupWidget.h" #include "Mixer.h" #include "ProjectJournal.h" #include "SetupDialog.h" @@ -195,14 +197,14 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : auto addLedCheckBox = [&XDelta, &YDelta, this]( - const char* ledText, + const QString &ledText, TabWidget* tw, int& counter, bool initialState, const char* toggledSlot, bool showRestartWarning ){ - LedCheckBox * checkBox = new LedCheckBox(tr(ledText), tw); + LedCheckBox * checkBox = new LedCheckBox(ledText, tw); counter++; checkBox->move(XDelta, YDelta * counter); checkBox->setChecked(initialState); @@ -221,21 +223,21 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : tr("Graphical user interface (GUI)"), general_w); - addLedCheckBox("Display volume as dBFS ", gui_tw, counter, + addLedCheckBox(tr("Display volume as dBFS "), gui_tw, counter, m_displaydBFS, SLOT(toggleDisplaydBFS(bool)), true); - addLedCheckBox("Enable tooltips", gui_tw, counter, + addLedCheckBox(tr("Enable tooltips"), gui_tw, counter, m_tooltips, SLOT(toggleTooltips(bool)), true); - addLedCheckBox("Enable master oscilloscope by default", gui_tw, counter, + addLedCheckBox(tr("Enable master oscilloscope by default"), gui_tw, counter, m_displayWaveform, SLOT(toggleDisplayWaveform(bool)), true); - addLedCheckBox("Enable all note labels in piano roll", gui_tw, counter, + addLedCheckBox(tr("Enable all note labels in piano roll"), gui_tw, counter, m_printNoteLabels, SLOT(toggleNoteLabels(bool)), false); - addLedCheckBox("Enable compact track buttons", gui_tw, counter, + addLedCheckBox(tr("Enable compact track buttons"), gui_tw, counter, m_compactTrackButtons, SLOT(toggleCompactTrackButtons(bool)), true); - addLedCheckBox("Enable one instrument-track-window mode", gui_tw, counter, + addLedCheckBox(tr("Enable one instrument-track-window mode"), gui_tw, counter, m_oneInstrumentTrackWindow, SLOT(toggleOneInstrumentTrackWindow(bool)), true); - addLedCheckBox("Show sidebar on the right-hand side", gui_tw, counter, + addLedCheckBox(tr("Show sidebar on the right-hand side"), gui_tw, counter, m_sideBarOnRight, SLOT(toggleSideBarOnRight(bool)), true); - addLedCheckBox("Mute automation tracks during solo", gui_tw, counter, + addLedCheckBox(tr("Mute automation tracks during solo"), gui_tw, counter, m_soloLegacyBehavior, SLOT(toggleSoloLegacyBehavior(bool)), false); gui_tw->setFixedHeight(YDelta + YDelta * counter); @@ -248,11 +250,11 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : tr("Projects"), general_w); - addLedCheckBox("Compress project files by default", projects_tw, counter, + addLedCheckBox(tr("Compress project files by default"), projects_tw, counter, m_MMPZ, SLOT(toggleMMPZ(bool)), true); - addLedCheckBox("Create a backup file when saving a project", projects_tw, counter, + addLedCheckBox(tr("Create a backup file when saving a project"), projects_tw, counter, m_disableBackup, SLOT(toggleDisableBackup(bool)), false); - addLedCheckBox("Reopen last project on startup", projects_tw, counter, + addLedCheckBox(tr("Reopen last project on startup"), projects_tw, counter, m_openLastProject, SLOT(toggleOpenLastProject(bool)), false); projects_tw->setFixedHeight(YDelta + YDelta * counter); @@ -372,9 +374,9 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : TabWidget * ui_fx_tw = new TabWidget( tr("User interface (UI) effects vs. performance"), performance_w); - addLedCheckBox("Smooth scroll in song editor", ui_fx_tw, counter, + addLedCheckBox(tr("Smooth scroll in song editor"), ui_fx_tw, counter, m_smoothScroll, SLOT(toggleSmoothScroll(bool)), false); - addLedCheckBox("Display playback cursor in AudioFileProcessor", ui_fx_tw, counter, + addLedCheckBox(tr("Display playback cursor in AudioFileProcessor"), ui_fx_tw, counter, m_animateAFP, SLOT(toggleAnimateAFP(bool)), false); ui_fx_tw->setFixedHeight(YDelta + YDelta * counter); @@ -421,10 +423,10 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : connect(m_vstAlwaysOnTopCheckBox, SIGNAL(toggled(bool)), this, SLOT(toggleVSTAlwaysOnTop(bool))); - addLedCheckBox("Sync VST plugins to host playback", plugins_tw, counter, + addLedCheckBox(tr("Sync VST plugins to host playback"), plugins_tw, counter, m_syncVSTPlugins, SLOT(toggleSyncVSTPlugins(bool)), false); - addLedCheckBox("Keep effects running even without input", plugins_tw, counter, + addLedCheckBox(tr("Keep effects running even without input"), plugins_tw, counter, m_disableAutoQuit, SLOT(toggleDisableAutoQuit(bool)), false); plugins_tw->setFixedHeight(YDelta + YDelta * counter); @@ -511,7 +513,7 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : it != m_audioIfaceSetupWidgets.end(); ++it) { m_audioIfaceNames[ - tr(it.key().toLatin1())] = it.key(); + AudioDeviceSetupWidget::tr(it.key().toUtf8())] = it.key(); } for(trMap::iterator it = m_audioIfaceNames.begin(); it != m_audioIfaceNames.end(); ++it) @@ -657,7 +659,7 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : it != m_midiIfaceSetupWidgets.end(); ++it) { m_midiIfaceNames[ - tr(it.key().toLatin1())] = it.key(); + MidiSetupWidget::tr(it.key().toUtf8())] = it.key(); } for(trMap::iterator it = m_midiIfaceNames.begin(); it != m_midiIfaceNames.end(); ++it) @@ -736,14 +738,14 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : QVBoxLayout * pathSelectorsLayout = new QVBoxLayout; pathSelectorsLayout->setSpacing(10); - auto addPathEntry = [&](const char* caption, + auto addPathEntry = [&](const QString &caption, const QString& content, const char* setSlot, const char* openSlot, QLineEdit*& lineEdit, const char* pixmap = "project_open") { - TabWidget * newTw = new TabWidget(tr(caption), + TabWidget * newTw = new TabWidget(caption, pathSelectors); newTw->setFixedHeight(48); @@ -763,37 +765,37 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : pathSelectorsLayout->addSpacing(10); }; - addPathEntry("LMMS working directory", m_workingDir, + addPathEntry(tr("LMMS working directory"), m_workingDir, SLOT(setWorkingDir(const QString &)), SLOT(openWorkingDir()), m_workingDirLineEdit); - addPathEntry("VST plugins directory", m_vstDir, + addPathEntry(tr("VST plugins directory"), m_vstDir, SLOT(setVSTDir(const QString &)), SLOT(openVSTDir()), m_vstDirLineEdit); - addPathEntry("LADSPA plugins directories", m_ladspaDir, + addPathEntry(tr("LADSPA plugins directories"), m_ladspaDir, SLOT(setLADSPADir(const QString &)), SLOT(openLADSPADir()), m_ladspaDirLineEdit, "add_folder"); - addPathEntry("SF2 directory", m_sf2Dir, + addPathEntry(tr("SF2 directory"), m_sf2Dir, SLOT(setSF2Dir(const QString &)), SLOT(openSF2Dir()), m_sf2DirLineEdit); #ifdef LMMS_HAVE_FLUIDSYNTH - addPathEntry("Default SF2", m_sf2File, + addPathEntry(tr("Default SF2"), m_sf2File, SLOT(setSF2File(const QString &)), SLOT(openSF2File()), m_sf2FileLineEdit); #endif - addPathEntry("GIG directory", m_gigDir, + addPathEntry(tr("GIG directory"), m_gigDir, SLOT(setGIGDir(const QString &)), SLOT(openGIGDir()), m_gigDirLineEdit); - addPathEntry("Theme directory", m_themeDir, + addPathEntry(tr("Theme directory"), m_themeDir, SLOT(setThemeDir(const QString &)), SLOT(openThemeDir()), m_themeDirLineEdit); - addPathEntry("Background artwork", m_backgroundPicFile, + addPathEntry(tr("Background artwork"), m_backgroundPicFile, SLOT(setBackgroundPicFile(const QString &)), SLOT(openBackgroundPicFile()), m_backgroundPicFileLineEdit); diff --git a/src/gui/TimeLineWidget.cpp b/src/gui/TimeLineWidget.cpp index 8e79410b853..070ac0a40d1 100644 --- a/src/gui/TimeLineWidget.cpp +++ b/src/gui/TimeLineWidget.cpp @@ -109,6 +109,14 @@ TimeLineWidget::~TimeLineWidget() +void TimeLineWidget::setXOffset(const int x) +{ + m_xOffset = x; + if (s_posMarkerPixmap != nullptr) { m_xOffset -= s_posMarkerPixmap->width() / 2; } +} + + + void TimeLineWidget::addToolButtons( QToolBar * _tool_bar ) { diff --git a/src/gui/dialogs/ColorChooser.cpp b/src/gui/dialogs/ColorChooser.cpp new file mode 100644 index 00000000000..b25aa97be6c --- /dev/null +++ b/src/gui/dialogs/ColorChooser.cpp @@ -0,0 +1,93 @@ +/* ColorChooser.cpp - definition of ColorChooser class. + * + * Copyright (c) 2020 russiankumar + * + * This file is part of LMMS - https://lmms.io + * + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include + + + + +//! Set global palette via array, checking bounds +void ColorChooser::setPalette (QVector colors) +{ + const int max = qMin (colors.size(), 48); + for (int i = 0; i < max; i++) + { + ColorChooser::setStandardColor (i, colors[i]); + } +} + + +//! Set global paletter via enum +void ColorChooser::setPalette (Palette palette) +{ + setPalette (getPalette (palette)); +} + + +//! Set palette via enum, return self pointer for chaining +ColorChooser* ColorChooser::withPalette (Palette palette) +{ + setPalette (palette); + return this; +} + + +//! Return a certain palette +QVector ColorChooser::getPalette (Palette palette) +{ + switch (palette) + { + case Palette::Mixer: return nicePalette(140); + case Palette::Track: return nicePalette(150); + default: return defaultPalette(); + } +} + + + + +//! Copy the current QColorDialog palette into an array +QVector ColorChooser::defaultPalette() +{ + QVector result (48); + for (int i = 0; i < 48; i++) + { + result[i] = (QColorDialog::standardColor(i)); + } + return result; +} + + +//! Generate a nice palette, with adjustable value +QVector ColorChooser::nicePalette (int base) +{ + QVector result (48); + for (int x = 0; x < 8; x++) + { + for (int y = 0; y < 6; y++) + { + result[6 * x + y].setHsl (qMax(0, 44 * x - 1), 150 - 20 * y, base - 10 * y); + } + } + return result; +} diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index 8c281bf3e1f..af9ea3b08a4 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -320,7 +320,7 @@ void AutomationEditor::updateAfterPatternChange() m_minLevel = m_pattern->firstObject()->minValue(); m_maxLevel = m_pattern->firstObject()->maxValue(); m_step = m_pattern->firstObject()->step(); - m_scrollLevel = ( m_minLevel + m_maxLevel ) / 2; + centerTopBottomScroll(); m_tensionModel->setValue( m_pattern->getTension() ); @@ -1595,12 +1595,36 @@ void AutomationEditor::drawLevelTick(QPainter & p, int tick, float value) p.fillRect( x, y_start, rect_width, rect_height, currentColor ); } - +#ifdef LMMS_DEBUG else { printf("not in range\n"); } +#endif +} + + + +// center the vertical scroll position on the first object's value +void AutomationEditor::centerTopBottomScroll() +{ + // default to the m_scrollLevel position + int pos = static_cast(m_scrollLevel); + // If a pattern exists... + if (m_pattern) + { + // get time map of current pattern + timeMap & time_map = m_pattern->getTimeMap(); + // If time_map is not empty... + if (!time_map.empty()) + { + // set the position to the inverted value ((max + min) - value) + // If we set just (max - value), we're off by m_pattern's minimum + pos = m_pattern->getMax() + m_pattern->getMin() - static_cast(time_map.begin().value()); + } + } + m_topBottomScroll->setValue(pos); } @@ -1632,8 +1656,7 @@ void AutomationEditor::resizeEvent(QResizeEvent * re) m_topBottomScroll->setRange( (int) m_scrollLevel, (int) m_scrollLevel ); } - - m_topBottomScroll->setValue( (int) m_scrollLevel ); + centerTopBottomScroll(); if( Engine::getSong() ) { @@ -2326,7 +2349,7 @@ AutomationEditorWindow::AutomationEditorWindow() : zoom_x_label->setPixmap( embed::getIconPixmap( "zoom_x" ) ); m_zoomingXComboBox = new ComboBox( zoomToolBar ); - m_zoomingXComboBox->setFixedSize( 80, 22 ); + m_zoomingXComboBox->setFixedSize( 80, ComboBox::DEFAULT_HEIGHT ); m_zoomingXComboBox->setToolTip( tr( "Horizontal zooming" ) ); for( float const & zoomLevel : m_editor->m_zoomXLevels ) @@ -2345,7 +2368,7 @@ AutomationEditorWindow::AutomationEditorWindow() : zoom_y_label->setPixmap( embed::getIconPixmap( "zoom_y" ) ); m_zoomingYComboBox = new ComboBox( zoomToolBar ); - m_zoomingYComboBox->setFixedSize( 80, 22 ); + m_zoomingYComboBox->setFixedSize( 80, ComboBox::DEFAULT_HEIGHT ); m_zoomingYComboBox->setToolTip( tr( "Vertical zooming" ) ); m_editor->m_zoomingYModel.addItem( "Auto" ); @@ -2375,7 +2398,7 @@ AutomationEditorWindow::AutomationEditorWindow() : quantize_lbl->setPixmap( embed::getIconPixmap( "quantize" ) ); m_quantizeComboBox = new ComboBox( m_toolBar ); - m_quantizeComboBox->setFixedSize( 60, 22 ); + m_quantizeComboBox->setFixedSize( 60, ComboBox::DEFAULT_HEIGHT ); m_quantizeComboBox->setToolTip( tr( "Quantization" ) ); m_quantizeComboBox->setModel( &m_editor->m_quantizeModel ); diff --git a/src/gui/editors/BBEditor.cpp b/src/gui/editors/BBEditor.cpp index bfc16df5be5..ffafc4577d8 100644 --- a/src/gui/editors/BBEditor.cpp +++ b/src/gui/editors/BBEditor.cpp @@ -75,7 +75,7 @@ BBEditor::BBEditor( BBTrackContainer* tc ) : DropToolBar *beatSelectionToolBar = addDropToolBarToTop(tr("Beat selector")); m_bbComboBox = new ComboBox( m_toolBar ); - m_bbComboBox->setFixedSize( 200, 22 ); + m_bbComboBox->setFixedSize( 200, ComboBox::DEFAULT_HEIGHT ); m_bbComboBox->setModel( &tc->m_bbComboBoxModel ); beatSelectionToolBar->addWidget( m_bbComboBox ); diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index b519fb9e5e1..0dc659dceb8 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #ifndef __USE_XOPEN #define __USE_XOPEN @@ -72,7 +73,7 @@ typedef AutomationPattern::timeMap timeMap; // some constants... const int INITIAL_PIANOROLL_WIDTH = 860; -const int INITIAL_PIANOROLL_HEIGHT = 480; +const int INITIAL_PIANOROLL_HEIGHT = 485; const int SCROLLBAR_SIZE = 12; const int PIANO_X = 0; @@ -86,9 +87,9 @@ const int DEFAULT_CELL_WIDTH = 12; const int NOTE_EDIT_RESIZE_BAR = 6; const int NOTE_EDIT_MIN_HEIGHT = 50; -const int KEY_AREA_MIN_HEIGHT = 100; +const int KEY_AREA_MIN_HEIGHT = DEFAULT_KEY_LINE_HEIGHT * 10; const int PR_BOTTOM_MARGIN = SCROLLBAR_SIZE; -const int PR_TOP_MARGIN = 16; +const int PR_TOP_MARGIN = 18; const int PR_RIGHT_MARGIN = SCROLLBAR_SIZE; @@ -107,12 +108,6 @@ const int NUM_TRIPLET_LENGTHS = 5; -QPixmap * PianoRoll::s_whiteKeySmallPm = NULL; -QPixmap * PianoRoll::s_whiteKeySmallPressedPm = NULL; -QPixmap * PianoRoll::s_whiteKeyBigPm = NULL; -QPixmap * PianoRoll::s_whiteKeyBigPressedPm = NULL; -QPixmap * PianoRoll::s_blackKeyPm = NULL; -QPixmap * PianoRoll::s_blackKeyPressedPm = NULL; QPixmap * PianoRoll::s_toolDraw = NULL; QPixmap * PianoRoll::s_toolErase = NULL; QPixmap * PianoRoll::s_toolSelect = NULL; @@ -170,14 +165,14 @@ PianoRoll::PianoRoll() : m_mouseDownTick( 0 ), m_lastMouseX( 0 ), m_lastMouseY( 0 ), - m_oldNotesEditHeight( 100 ), m_notesEditHeight( 100 ), + m_userSetNotesEditHeight(100), m_ppb( DEFAULT_PR_PPB ), m_keyLineHeight(DEFAULT_KEY_LINE_HEIGHT), m_octaveHeight(m_keyLineHeight * KeysPerOctave), - m_whiteKeySmallHeight(round(m_keyLineHeight * 1.5)), + m_whiteKeySmallHeight(qFloor(m_keyLineHeight * 1.5)), m_whiteKeyBigHeight(m_keyLineHeight * 2), - m_blackKeyHeight(round(m_keyLineHeight * 1.3333)), + m_blackKeyHeight(m_keyLineHeight), m_lenOfNewNotes( MidiTime( 0, DefaultTicksPerBar/4 ) ), m_lastNoteVolume( DefaultVolume ), m_lastNotePanning( DefaultPanning ), @@ -207,7 +202,9 @@ PianoRoll::PianoRoll() : m_ghostNoteOpacity( 255 ), m_noteBorders( true ), m_ghostNoteBorders( true ), - m_backgroundShade( 0, 0, 0 ) + m_backgroundShade( 0, 0, 0 ), + m_whiteKeyWidth(WHITE_KEY_WIDTH), + m_blackKeyWidth(BLACK_KEY_WIDTH) { // gui names of edit modes m_nemStr.push_back( tr( "Note Velocity" ) ); @@ -252,36 +249,6 @@ PianoRoll::PianoRoll() : m_semiToneMarkerMenu->addAction( copyAllNotesAction ); // init pixmaps - if( s_whiteKeySmallPm == NULL ) - { - s_whiteKeySmallPm = new QPixmap( embed::getIconPixmap( - "pr_white_key_small" ) ); - } - if( s_whiteKeySmallPressedPm == NULL ) - { - s_whiteKeySmallPressedPm = new QPixmap( embed::getIconPixmap( - "pr_white_key_small_pressed" ) ); - } - if( s_whiteKeyBigPm == NULL ) - { - s_whiteKeyBigPm = new QPixmap( embed::getIconPixmap( - "pr_white_key_big" ) ); - } - if( s_whiteKeyBigPressedPm == NULL ) - { - s_whiteKeyBigPressedPm = new QPixmap( embed::getIconPixmap( - "pr_white_key_big_pressed" ) ); - } - if( s_blackKeyPm == NULL ) - { - s_blackKeyPm = new QPixmap( embed::getIconPixmap( - "pr_black_key" ) ); - } - if( s_blackKeyPressedPm == NULL ) - { - s_blackKeyPressedPm = new QPixmap( embed::getIconPixmap( - "pr_black_key_pressed" ) ); - } if( s_toolDraw == NULL ) { s_toolDraw = new QPixmap( embed::getIconPixmap( "edit_draw" ) ); @@ -312,7 +279,7 @@ PianoRoll::PianoRoll() : setAttribute( Qt::WA_OpaquePaintEvent, true ); // add time-line - m_timeLine = new TimeLineWidget( WHITE_KEY_WIDTH, 0, m_ppb, + m_timeLine = new TimeLineWidget(m_whiteKeyWidth, 0, m_ppb, Engine::getSong()->getPlayPos( Song::Mode_PlayPattern ), m_currentPosition, @@ -322,6 +289,9 @@ PianoRoll::PianoRoll() : connect( m_timeLine, SIGNAL( positionChanged( const MidiTime & ) ), this, SLOT( updatePosition( const MidiTime & ) ) ); + // white position line follows timeline marker + m_positionLine = new PositionLine(this); + //update timeline when in step-recording mode connect( &m_stepRecorderWidget, SIGNAL( positionChanged( const MidiTime & ) ), this, SLOT( updatePositionStepRecording( const MidiTime & ) ) ); @@ -768,8 +738,8 @@ void PianoRoll::hidePattern( Pattern* pattern ) void PianoRoll::selectRegionFromPixels( int xStart, int xEnd ) { - xStart -= WHITE_KEY_WIDTH; - xEnd -= WHITE_KEY_WIDTH; + xStart -= m_whiteKeyWidth; + xEnd -= m_whiteKeyWidth; // select an area of notes int posTicks = xStart * MidiTime::ticksPerBar() / m_ppb + @@ -804,127 +774,6 @@ void PianoRoll::selectRegionFromPixels( int xStart, int xEnd ) - - -/** \brief qproperty access implementation */ - -QColor PianoRoll::barLineColor() const -{ return m_barLineColor; } - -void PianoRoll::setBarLineColor( const QColor & c ) -{ m_barLineColor = c; } - -QColor PianoRoll::beatLineColor() const -{ return m_beatLineColor; } - -void PianoRoll::setBeatLineColor( const QColor & c ) -{ m_beatLineColor = c; } - -QColor PianoRoll::lineColor() const -{ return m_lineColor; } - -void PianoRoll::setLineColor( const QColor & c ) -{ m_lineColor = c; } - -QColor PianoRoll::noteModeColor() const -{ return m_noteModeColor; } - -void PianoRoll::setNoteModeColor( const QColor & c ) -{ m_noteModeColor = c; } - -QColor PianoRoll::noteColor() const -{ return m_noteColor; } - -void PianoRoll::setNoteColor( const QColor & c ) -{ m_noteColor = c; } - -QColor PianoRoll::noteTextColor() const -{ return m_noteTextColor; } - -void PianoRoll::setNoteTextColor( const QColor & c ) -{ m_noteTextColor = c; } - -QColor PianoRoll::barColor() const -{ return m_barColor; } - -void PianoRoll::setBarColor( const QColor & c ) -{ m_barColor = c; } - -QColor PianoRoll::selectedNoteColor() const -{ return m_selectedNoteColor; } - -void PianoRoll::setSelectedNoteColor( const QColor & c ) -{ m_selectedNoteColor = c; } - -QColor PianoRoll::textColor() const -{ return m_textColor; } - -void PianoRoll::setTextColor( const QColor & c ) -{ m_textColor = c; } - -QColor PianoRoll::textColorLight() const -{ return m_textColorLight; } - -void PianoRoll::setTextColorLight( const QColor & c ) -{ m_textColorLight = c; } - -QColor PianoRoll::textShadow() const -{ return m_textShadow; } - -void PianoRoll::setTextShadow( const QColor & c ) -{ m_textShadow = c; } - -QColor PianoRoll::markedSemitoneColor() const -{ return m_markedSemitoneColor; } - -void PianoRoll::setMarkedSemitoneColor( const QColor & c ) -{ m_markedSemitoneColor = c; } - -int PianoRoll::noteOpacity() const -{ return m_noteOpacity; } - -void PianoRoll::setNoteOpacity( const int i ) -{ m_noteOpacity = i; } - -bool PianoRoll::noteBorders() const -{ return m_noteBorders; } - -void PianoRoll::setNoteBorders( const bool b ) -{ m_noteBorders = b; } - -QColor PianoRoll::ghostNoteColor() const -{ return m_ghostNoteColor; } - -void PianoRoll::setGhostNoteColor( const QColor & c ) -{ m_ghostNoteColor = c; } - -QColor PianoRoll::ghostNoteTextColor() const -{ return m_ghostNoteTextColor; } - -void PianoRoll::setGhostNoteTextColor( const QColor & c ) -{ m_ghostNoteTextColor = c; } - -int PianoRoll::ghostNoteOpacity() const -{ return m_ghostNoteOpacity; } - -void PianoRoll::setGhostNoteOpacity( const int i ) -{ m_ghostNoteOpacity = i; } - -bool PianoRoll::ghostNoteBorders() const -{ return m_ghostNoteBorders; } - -void PianoRoll::setGhostNoteBorders( const bool b ) -{ m_ghostNoteBorders = b; } - -QColor PianoRoll::backgroundShade() const -{ return m_backgroundShade; } - -void PianoRoll::setBackgroundShade( const QColor & c ) -{ m_backgroundShade = c; } - - - - void PianoRoll::drawNoteRect( QPainter & p, int x, int y, int width, const Note * n, const QColor & noteCol, const QColor & noteTextColor, const QColor & selCol, const int noteOpc, const bool borders, bool drawNoteName ) @@ -1039,9 +888,11 @@ void PianoRoll::drawDetuningInfo( QPainter & _p, const Note * _n, int _x, int _y ) const { int middle_y = _y + m_keyLineHeight / 2; - _p.setPen( noteColor() ); - _p.setClipRect(WHITE_KEY_WIDTH, PR_TOP_MARGIN, - width() - WHITE_KEY_WIDTH, + _p.setPen(m_noteColor); + _p.setClipRect( + m_whiteKeyWidth, + PR_TOP_MARGIN, + width() - m_whiteKeyWidth, keyAreaBottom() - PR_TOP_MARGIN); int old_x = 0; @@ -1449,8 +1300,7 @@ void PianoRoll::leaveEvent(QEvent * e ) int PianoRoll::noteEditTop() const { - return height() - PR_BOTTOM_MARGIN - - m_notesEditHeight + NOTE_EDIT_RESIZE_BAR; + return keyAreaBottom() + NOTE_EDIT_RESIZE_BAR; } @@ -1474,7 +1324,7 @@ int PianoRoll::noteEditRight() const int PianoRoll::noteEditLeft() const { - return WHITE_KEY_WIDTH; + return m_whiteKeyWidth; } @@ -1539,11 +1389,11 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) m_moveStartY = me->y(); } - if( me->y() > keyAreaBottom() && me->y() < noteEditTop() ) + if(me->button() == Qt::LeftButton && + me->y() > keyAreaBottom() && me->y() < noteEditTop()) { // resizing the note edit area m_action = ActionResizeNoteEditArea; - m_oldNotesEditHeight = m_notesEditHeight; return; } @@ -1556,11 +1406,11 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) int x = me->x(); - if( x > WHITE_KEY_WIDTH ) + if (x > m_whiteKeyWidth) { // set, move or resize note - x -= WHITE_KEY_WIDTH; + x -= m_whiteKeyWidth; // get tick in which the user clicked int pos_ticks = x * MidiTime::ticksPerBar() / m_ppb + @@ -1827,7 +1677,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) else if( me->buttons() == Qt::LeftButton ) { // left click - play the note - int v = ( (float) x ) / ( (float) WHITE_KEY_WIDTH ) * MidiDefaultVelocity; + int v = ((float) x) / ((float) m_whiteKeyWidth) * MidiDefaultVelocity; m_pattern->instrumentTrack()->pianoModel()->handleKeyPress(key_num, v); // if a chord is set, play the chords notes as well: playChordNotes(key_num, v); @@ -1870,7 +1720,7 @@ void PianoRoll::mouseDoubleClickEvent(QMouseEvent * me ) { // get values for going through notes int pixel_range = 4; - int x = me->x() - WHITE_KEY_WIDTH; + int x = me->x() - m_whiteKeyWidth; const int ticks_start = ( x-pixel_range/2 ) * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; const int ticks_end = ( x+pixel_range/2 ) * @@ -2203,14 +2053,23 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) } else if( m_action == ActionResizeNoteEditArea ) { + // Don't try to show more keys than the full keyboard, bail if trying to + if (m_pianoKeysVisible == NumKeys && me->y() > m_moveStartY) + { + return; + } + int newHeight = height() - me->y(); + if (me->y() < KEY_AREA_MIN_HEIGHT) + { + newHeight = height() - KEY_AREA_MIN_HEIGHT - + PR_TOP_MARGIN - PR_BOTTOM_MARGIN; // - NOTE_EDIT_RESIZE_BAR + } // change m_notesEditHeight and then repaint - m_notesEditHeight = qBound( - NOTE_EDIT_MIN_HEIGHT, - m_oldNotesEditHeight - ( me->y() - m_moveStartY ), - height() - PR_TOP_MARGIN - NOTE_EDIT_RESIZE_BAR - - PR_BOTTOM_MARGIN - KEY_AREA_MIN_HEIGHT ); - + m_notesEditHeight = qMax(NOTE_EDIT_MIN_HEIGHT, newHeight); + m_userSetNotesEditHeight = m_notesEditHeight; m_stepRecorderWidget.setBottomMargin(PR_BOTTOM_MARGIN + m_notesEditHeight); + updateScrollbars(); + updatePositionLineHeight(); repaint(); return; } @@ -2225,17 +2084,17 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) int x = me->x(); // see if they clicked on the keyboard on the left - if( x < WHITE_KEY_WIDTH && m_action == ActionNone + if (x < m_whiteKeyWidth && m_action == ActionNone && ! edit_note && key_num != m_lastKey && me->buttons() & Qt::LeftButton ) { // clicked on a key, play the note - testPlayKey( key_num, ( (float) x ) / ( (float) WHITE_KEY_WIDTH ) * MidiDefaultVelocity, 0 ); + testPlayKey(key_num, ((float) x) / ((float) m_whiteKeyWidth) * MidiDefaultVelocity, 0); update(); return; } - x -= WHITE_KEY_WIDTH; + x -= m_whiteKeyWidth; if( me->buttons() & Qt::LeftButton && m_editMode == ModeDraw @@ -2508,12 +2367,12 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) m_action == ActionSelectNotes ) { - int x = me->x() - WHITE_KEY_WIDTH; + int x = me->x() - m_whiteKeyWidth; if( x < 0 && m_currentPosition > 0 ) { x = 0; QCursor::setPos( mapToGlobal( QPoint( - WHITE_KEY_WIDTH, + m_whiteKeyWidth, me->y() ) ) ); if( m_currentPosition >= 4 ) { @@ -2525,9 +2384,9 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) m_leftRightScroll->setValue( 0 ); } } - else if( x > width() - WHITE_KEY_WIDTH ) + else if (x > width() - m_whiteKeyWidth) { - x = width() - WHITE_KEY_WIDTH; + x = width() - m_whiteKeyWidth; QCursor::setPos( mapToGlobal( QPoint( width(), me->y() ) ) ); m_leftRightScroll->setValue( m_currentPosition + @@ -2774,11 +2633,8 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) Engine::getSong()->setModified(); } -int PianoRoll::xCoordOfTick( int tick ) -{ - return WHITE_KEY_WIDTH + ( ( tick - m_currentPosition ) - * m_ppb / MidiTime::ticksPerBar() ); -} + + void PianoRoll::paintEvent(QPaintEvent * pe ) { @@ -2794,353 +2650,337 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) // fill with bg color p.fillRect( 0, 0, width(), height(), bgColor ); - // set font-size to 8 - p.setFont( pointSize<8>( p.font() ) ); + // set font-size to 80% of key line height + QFont f = p.font(); + f.setPixelSize(m_keyLineHeight * 0.8); + p.setFont(f); // font size doesn't change without this for some reason QFontMetrics fontMetrics(p.font()); - QRect const boundingRect = fontMetrics.boundingRect(QChar::fromLatin1('H')); - // This is two times of the y coordinate of the center of the bounding rectangle - // (-(top+bottom)=-2(center)) but labelHeight is more intuitive/describing name - int const labelHeight = - boundingRect.top() - boundingRect.bottom(); + // G4 is one of the widest + QRect const boundingRect = fontMetrics.boundingRect(QString("G4")); + + // Order of drawing + // - vertical quantization lines + // - piano roll + horizontal key lines + // - alternating bar colors + // - vertical beat lines + // - vertical bar lines + // - marked semitones + // - note editing + // - notes + // - selection frame + // - highlight hovered note + // - note edit area resize bar + // - cursor mode icon - // y_offset is used to align the piano-keys on the key-lines - int y_offset = 0; - - // calculate y_offset according to first key - switch( prKeyOrder[m_startKey % KeysPerOctave] ) + if (hasValidPattern()) { - case PR_BLACK_KEY: y_offset = m_keyLineHeight / 4; break; - case PR_WHITE_KEY_BIG: y_offset = m_keyLineHeight / 2; break; - case PR_WHITE_KEY_SMALL: - if( prKeyOrder[( ( m_startKey + 1 ) % - KeysPerOctave)] != PR_BLACK_KEY ) + int pianoAreaHeight, partialKeyVisible, topKey, topNote; + pianoAreaHeight = keyAreaBottom() - keyAreaTop(); + m_pianoKeysVisible = pianoAreaHeight / m_keyLineHeight; + partialKeyVisible = pianoAreaHeight % m_keyLineHeight; + // check if we're below the minimum key area size + if (m_pianoKeysVisible * m_keyLineHeight < KEY_AREA_MIN_HEIGHT) + { + m_pianoKeysVisible = KEY_AREA_MIN_HEIGHT / m_keyLineHeight; + partialKeyVisible = KEY_AREA_MIN_HEIGHT % m_keyLineHeight; + // if we have a partial key, just show it + if (partialKeyVisible > 0) { - y_offset = m_keyLineHeight / 2; + m_pianoKeysVisible += 1; + partialKeyVisible = 0; } - break; - } - // start drawing at the bottom - int key_line_y = qMin(keyAreaBottom() - 1, m_keyLineHeight * NumKeys); - // we need to set m_notesEditHeight here because it needs to fill in the - // rest of the window if key_line_y is bound to m_keyLineHeight * NumKeys - if (key_line_y == m_keyLineHeight * NumKeys) { - m_notesEditHeight = height() - (PR_TOP_MARGIN + m_keyLineHeight * NumKeys); - } - // used for aligning black-keys later - int first_white_key_height = m_whiteKeySmallHeight; - // key-counter - only needed for finding out whether the processed - // key is the first one - int keys_processed = 0; - - int key = m_startKey; - - // draw all white keys... - for( int y = key_line_y + 1 + y_offset; y > PR_TOP_MARGIN; - key_line_y -= m_keyLineHeight, ++keys_processed ) - { - // check for white key that is only half visible on the - // bottom of piano-roll - if( keys_processed == 0 && - prKeyOrder[m_startKey % KeysPerOctave] == - PR_BLACK_KEY ) + // have to modifiy the notes edit area height instead + m_notesEditHeight = height() - (m_pianoKeysVisible * m_keyLineHeight) + - PR_TOP_MARGIN - PR_BOTTOM_MARGIN; + } + // check if we're trying to show more keys than available + else if (m_pianoKeysVisible >= NumKeys) { - // draw it! - p.drawPixmap( PIANO_X, y - m_whiteKeySmallHeight, WHITE_KEY_WIDTH, m_whiteKeySmallHeight, - *s_whiteKeySmallPm ); - // update y-pos - y -= m_whiteKeySmallHeight / 2; - // move first black key down (we didn't draw whole - // white key so black key needs to be lifted down) - // (default for first_white_key_height = - // m_whiteKeySmallHeight, so m_whiteKeySmallHeight/2 - // is smaller) - first_white_key_height = m_whiteKeySmallHeight / 2; + m_pianoKeysVisible = NumKeys; + // have to modify the notes edit area height instead + m_notesEditHeight = height() - (NumKeys * m_keyLineHeight) - + PR_TOP_MARGIN - PR_BOTTOM_MARGIN; + partialKeyVisible = 0; } - // check whether to draw a big or a small white key - if( prKeyOrder[key % KeysPerOctave] == PR_WHITE_KEY_SMALL ) + topKey = qBound(0, m_startKey + m_pianoKeysVisible - 1, NumKeys - 1); + topNote = topKey % KeysPerOctave; + // if not resizing the note edit area, we can change m_notesEditHeight + if (m_action != ActionResizeNoteEditArea && partialKeyVisible != 0) { - // draw a small one while checking if it is pressed or not - if( hasValidPattern() && m_pattern->instrumentTrack()->pianoModel()->isKeyPressed( key ) ) + // calculate the height change adding and subtracting the partial key + int noteAreaPlus = (m_notesEditHeight + partialKeyVisible) - m_userSetNotesEditHeight; + int noteAreaMinus = m_userSetNotesEditHeight - (m_notesEditHeight - partialKeyVisible); + // if adding the partial key to height is more distant from the set height + // we want to subtract the partial key + if (noteAreaPlus > noteAreaMinus) { - p.drawPixmap(PIANO_X, y - m_whiteKeySmallHeight, WHITE_KEY_WIDTH, m_whiteKeySmallHeight, - *s_whiteKeySmallPressedPm); + m_notesEditHeight -= partialKeyVisible; + // since we're adding a partial key, we add one to the number visible + m_pianoKeysVisible += 1; } - else - { - p.drawPixmap(PIANO_X, y - m_whiteKeySmallHeight, WHITE_KEY_WIDTH, m_whiteKeySmallHeight, - *s_whiteKeySmallPm); - } - // update y-pos - y -= m_whiteKeySmallHeight; + // otherwise we add height + else { m_notesEditHeight += partialKeyVisible; } + } + updatePositionLineHeight(); + int x, q = quantization(), tick; + // draw vertical quantization lines + // If we're over 100% zoom, we allow all quantization level grids + if (m_zoomingModel.value() <= 3) + { + // we're under 100% zoom + // allow quantization grid up to 1/24 for triplets + if (q % 3 != 0 && q < 8) { q = 8; } + // allow quantization grid up to 1/32 for normal notes + else if (q < 6) { q = 6; } } - else if( prKeyOrder[key % KeysPerOctave] == PR_WHITE_KEY_BIG ) + auto xCoordOfTick = [=](int tick) { + return m_whiteKeyWidth + ( + (tick - m_currentPosition) * m_ppb / MidiTime::ticksPerBar() + ); + }; + p.setPen(m_lineColor); + for (tick = m_currentPosition - m_currentPosition % q, + x = xCoordOfTick(tick); + x <= width(); + tick += q, x = xCoordOfTick(tick)) { - // draw a big one while checking if it is pressed or not - if( hasValidPattern() && m_pattern->instrumentTrack()->pianoModel()->isKeyPressed( key ) ) - { - p.drawPixmap(PIANO_X, y - m_whiteKeyBigHeight, WHITE_KEY_WIDTH, m_whiteKeyBigHeight, - *s_whiteKeyBigPressedPm); - } - else - { - p.drawPixmap(PIANO_X, y-m_whiteKeyBigHeight, WHITE_KEY_WIDTH, m_whiteKeyBigHeight, - *s_whiteKeyBigPm); - } - // if a big white key has been the first key, - // black keys needs to be lifted up - if( keys_processed == 0 ) - { - first_white_key_height = m_whiteKeyBigHeight; - } - // update y-pos - y -= m_whiteKeyBigHeight; + p.drawLine(x, keyAreaTop(), x, noteEditBottom()); } - // Compute the corrections for the note names - int yCorrectionForNoteLabels = 0; + // draw horizontal grid lines and piano notes + p.setClipRect(0, keyAreaTop(), width(), keyAreaBottom() - keyAreaTop()); + // the first grid line from the top Y position + int grid_line_y = keyAreaTop() + m_keyLineHeight - 1; - int keyCode = key % KeysPerOctave; - switch (keyCode) + // lambda function for returning the height of a key + auto keyHeight = [&]( + const int key + ) -> int { - case 0: // C - case 5: // F - yCorrectionForNoteLabels = (m_whiteKeySmallHeight - labelHeight + 1) / -2; - break; - case 2: // D - case 7: // G - case 9: // A - yCorrectionForNoteLabels = (m_whiteKeyBigHeight / 2 - labelHeight + 1) / -2; - break; - case 4: // E - case 11: // B - // calculate center point of key and move half of text - yCorrectionForNoteLabels = -(((m_whiteKeySmallHeight - (m_whiteKeySmallHeight * 2 + 3) / 6) / 4) - - labelHeight / 2); - break; - } - - if( Piano::isWhiteKey( key ) ) + switch (prKeyOrder[key % KeysPerOctave]) + { + case PR_WHITE_KEY_BIG: + return m_whiteKeyBigHeight; + case PR_WHITE_KEY_SMALL: + return m_whiteKeySmallHeight; + case PR_BLACK_KEY: + return m_blackKeyHeight; + } + return 0; // should never happen + }; + // lambda function for returning the distance to the top of a key + auto gridCorrection = [&]( + const int key + ) -> int { - // Draw note names if activated in the preferences, C notes are always drawn - if ( (key % 12 == 0 || drawNoteNames) && m_keyLineHeight > 10 ) + const int keyCode = key % KeysPerOctave; + switch (prKeyOrder[keyCode]) { - QString noteString = getNoteString( key ); - - QPoint textStart( WHITE_KEY_WIDTH - 18, key_line_y ); - textStart += QPoint( 0, yCorrectionForNoteLabels ); - - p.setPen( textShadow() ); - p.drawText( textStart + QPoint( 1, 1 ), noteString ); - // The C key is painted darker than the other ones - if ( key % 12 == 0 ) + case PR_WHITE_KEY_BIG: + return m_whiteKeySmallHeight; + case PR_WHITE_KEY_SMALL: + // These two keys need to adjust up small height instead of only key line height + if (keyCode == Key_C || keyCode == Key_F) { - p.setPen( textColor() ); + return m_whiteKeySmallHeight; } - else - { - p.setPen( textColorLight() ); - } - p.drawText( textStart, noteString ); + case PR_BLACK_KEY: + return m_blackKeyHeight; } - } - ++key; - } - - // reset all values, because now we're going to draw all black keys - key = m_startKey; - keys_processed = 0; - int white_cnt = 0; - key_line_y = qMin(keyAreaBottom(), m_keyLineHeight * NumKeys); - - // and go! - for( int y = key_line_y + y_offset; - y > PR_TOP_MARGIN; ++keys_processed ) - { - // check for black key that is only half visible on the bottom - // of piano-roll - if( keys_processed == 0 - // current key may not be a black one - && prKeyOrder[key % KeysPerOctave] != PR_BLACK_KEY - // but the previous one must be black (we must check this - // because there might be two white keys (E-F) - && prKeyOrder[( key - 1 ) % KeysPerOctave] == - PR_BLACK_KEY ) + return 0; // should never happen + }; + auto keyWidth = [&]( + const int key + ) -> int { - // draw the black key! - p.drawPixmap( PIANO_X, y - m_blackKeyHeight / 2, BLACK_KEY_WIDTH, m_blackKeyHeight, - *s_blackKeyPm ); - // is the one after the start-note a black key?? - if( prKeyOrder[( key + 1 ) % KeysPerOctave] != - PR_BLACK_KEY ) + switch (prKeyOrder[key % KeysPerOctave]) { - // no, then move it up! - y -= m_keyLineHeight / 2; + case PR_WHITE_KEY_SMALL: + case PR_WHITE_KEY_BIG: + return m_whiteKeyWidth; + case PR_BLACK_KEY: + return m_blackKeyWidth; } - } - // current key black? - if( prKeyOrder[key % KeysPerOctave] == PR_BLACK_KEY) + return 0; // should never happen + }; + // lambda function to draw a key + auto drawKey = [&]( + const int key, + const int yb) { - // then draw it (calculation of y very complicated, - // but that's the only working solution, sorry...) - // check if the key is pressed or not - if( hasValidPattern() && m_pattern->instrumentTrack()->pianoModel()->isKeyPressed( key ) ) + const bool pressed = m_pattern->instrumentTrack()->pianoModel()->isKeyPressed(key); + const int keyCode = key % KeysPerOctave; + const int yt = yb - gridCorrection(key); + const int kh = keyHeight(key); + const int kw = keyWidth(key); + // set key colors + p.setPen(QColor(0, 0, 0)); + switch (prKeyOrder[keyCode]) { - p.drawPixmap( PIANO_X, y - ( first_white_key_height - - m_whiteKeySmallHeight ) - - m_whiteKeySmallHeight/2 - 1 - - m_blackKeyHeight, BLACK_KEY_WIDTH, m_blackKeyHeight, *s_blackKeyPressedPm ); + case PR_WHITE_KEY_SMALL: + case PR_WHITE_KEY_BIG: + p.setBrush(pressed ? m_whiteKeyActiveBackground : m_whiteKeyInactiveBackground); + break; + case PR_BLACK_KEY: + p.setBrush(pressed ? m_blackKeyActiveBackground : m_blackKeyInactiveBackground); } - else + // draw key + p.drawRect(PIANO_X, yt, kw, kh); + // draw note name + if (keyCode == Key_C || (drawNoteNames && Piano::isWhiteKey(key))) { - p.drawPixmap( PIANO_X, y - ( first_white_key_height - - m_whiteKeySmallHeight ) - - m_whiteKeySmallHeight/2 - 1 - - m_blackKeyHeight, BLACK_KEY_WIDTH, m_blackKeyHeight, *s_blackKeyPm ); + // small font sizes have 1 pixel offset instead of 2 + auto zoomOffset = m_zoomYLevels[m_zoomingYModel.value()] > 1.0f ? 2 : 1; + QString noteString = getNoteString(key); + QRect textRect( + m_whiteKeyWidth - boundingRect.width() - 2, + yb - m_keyLineHeight + zoomOffset, + boundingRect.width(), + boundingRect.height() + ); + p.setPen(pressed ? m_whiteKeyActiveTextShadow : m_whiteKeyInactiveTextShadow); + p.drawText(textRect.adjusted(0, 1, 1, 0), Qt::AlignRight | Qt::AlignHCenter, noteString); + p.setPen(pressed ? m_whiteKeyActiveTextColor : m_whiteKeyInactiveTextColor); + // if (keyCode == Key_C) { p.setPen(textColor()); } + // else { p.setPen(textColorLight()); } + p.drawText(textRect, Qt::AlignRight | Qt::AlignHCenter, noteString); } - // update y-pos - y -= m_whiteKeyBigHeight; - // reset white-counter - white_cnt = 0; - } - else + }; + // lambda for drawing the horizontal grid line + auto drawHorizontalLine = [&]( + const int key, + const int y + ) { - // simple workaround for increasing x if there were - // two white keys (e.g. between E and F) - ++white_cnt; - if( white_cnt > 1 ) - { - y -= m_whiteKeyBigHeight/2; - } - } - - ++key; - } - - - // erase the area below the piano, because there might be keys that - // should be only half-visible - p.fillRect( QRect( 0, key_line_y, - WHITE_KEY_WIDTH, noteEditBottom() - key_line_y ), bgColor ); - - // display note editing info - QFont f = p.font(); - f.setBold( false ); - p.setFont( pointSize<10>( f ) ); - p.setPen( noteModeColor() ); - p.drawText( QRect( 0, key_line_y, - WHITE_KEY_WIDTH, noteEditBottom() - key_line_y ), - Qt::AlignCenter | Qt::TextWordWrap, - m_nemStr.at( m_noteEditMode ) + ":" ); - - // set clipping area, because we are not allowed to paint over - // keyboard... - p.setClipRect( WHITE_KEY_WIDTH, PR_TOP_MARGIN, - width() - WHITE_KEY_WIDTH, - height() - PR_TOP_MARGIN - PR_BOTTOM_MARGIN ); - - // draw the grid - if( hasValidPattern() ) - { - int q, x, tick; - - if( m_zoomingModel.value() > 3 ) + if (key % KeysPerOctave == Key_C) { p.setPen(m_beatLineColor); } + else { p.setPen(m_lineColor); } + p.drawLine(m_whiteKeyWidth, y, width(), y); + }; + // correct y offset of the top key + switch (prKeyOrder[topNote]) { - // If we're over 100% zoom, we allow all quantization level grids - q = quantization(); - } - else if( quantization() % 3 != 0 ) - { - // If we're under 100% zoom, we allow quantization grid up to 1/24 for triplets - // to ensure a dense doesn't fill out the background - q = quantization() < 8 ? 8 : quantization(); - } - else { - // If we're under 100% zoom, we allow quantization grid up to 1/32 for normal notes - q = quantization() < 6 ? 6 : quantization(); - } - - // First we draw the vertical quantization lines - for( tick = m_currentPosition - m_currentPosition % q, x = xCoordOfTick( tick ); - x <= width(); tick += q, x = xCoordOfTick( tick ) ) - { - p.setPen( lineColor() ); - p.drawLine( x, PR_TOP_MARGIN, x, height() - PR_BOTTOM_MARGIN ); + case PR_WHITE_KEY_SMALL: + case PR_WHITE_KEY_BIG: + break; + case PR_BLACK_KEY: + // draw extra white key + drawKey(topKey + 1, grid_line_y - m_keyLineHeight); } - - // Draw horizontal lines - key = m_startKey; - for( int y = key_line_y - 1; y > PR_TOP_MARGIN; - y -= m_keyLineHeight ) + // loop through visible keys + const int lastKey = qMax(0, topKey - m_pianoKeysVisible); + for (int key = topKey; key > lastKey; --key) { - if( static_cast( key % KeysPerOctave ) == Key_C ) + bool whiteKey = Piano::isWhiteKey(key); + if (whiteKey) { - // C note gets accented - p.setPen( beatLineColor() ); + drawKey(key, grid_line_y); + drawHorizontalLine(key, grid_line_y); + grid_line_y += m_keyLineHeight; } else { - p.setPen( lineColor() ); + // draw next white key + drawKey(key - 1, grid_line_y + m_keyLineHeight); + drawHorizontalLine(key - 1, grid_line_y + m_keyLineHeight); + // draw black key over previous and next white key + drawKey(key, grid_line_y); + drawHorizontalLine(key, grid_line_y); + // drew two grid keys so skip ahead properly + grid_line_y += m_keyLineHeight + m_keyLineHeight; + // capture double key draw + --key; } - p.drawLine( WHITE_KEY_WIDTH, y, width(), y ); - ++key; } + // don't draw over keys + p.setClipRect(m_whiteKeyWidth, keyAreaTop(), width(), noteEditBottom() - keyAreaTop()); - // Draw alternating shades on bars - float timeSignature = static_cast( Engine::getSong()->getTimeSigModel().getNumerator() ) - / static_cast( Engine::getSong()->getTimeSigModel().getDenominator() ); + // draw alternating shading on bars + float timeSignature = + static_cast(Engine::getSong()->getTimeSigModel().getNumerator()) / + static_cast(Engine::getSong()->getTimeSigModel().getDenominator()); float zoomFactor = m_zoomLevels[m_zoomingModel.value()]; //the bars which disappears at the left side by scrolling int leftBars = m_currentPosition * zoomFactor / MidiTime::ticksPerBar(); - //iterates the visible bars and draw the shading on uneven bars - for( int x = WHITE_KEY_WIDTH, barCount = leftBars; x < width() + m_currentPosition * zoomFactor / timeSignature; x += m_ppb, ++barCount ) + for (int x = m_whiteKeyWidth, barCount = leftBars; + x < width() + m_currentPosition * zoomFactor / timeSignature; + x += m_ppb, ++barCount) { - if( ( barCount + leftBars ) % 2 != 0 ) + if ((barCount + leftBars) % 2 != 0) { - p.fillRect( x - m_currentPosition * zoomFactor / timeSignature, PR_TOP_MARGIN, m_ppb, - height() - ( PR_BOTTOM_MARGIN + PR_TOP_MARGIN ), backgroundShade() ); + p.fillRect(x - m_currentPosition * zoomFactor / timeSignature, + PR_TOP_MARGIN, + m_ppb, + height() - (PR_BOTTOM_MARGIN + PR_TOP_MARGIN), + m_backgroundShade); } } - // Draw the vertical beat lines + // draw vertical beat lines int ticksPerBeat = DefaultTicksPerBar / Engine::getSong()->getTimeSigModel().getDenominator(); - - for( tick = m_currentPosition - m_currentPosition % ticksPerBeat, - x = xCoordOfTick( tick ); x <= width(); - tick += ticksPerBeat, x = xCoordOfTick( tick ) ) + p.setPen(m_beatLineColor); + for(tick = m_currentPosition - m_currentPosition % ticksPerBeat, + x = xCoordOfTick( tick ); + x <= width(); + tick += ticksPerBeat, x = xCoordOfTick(tick)) { - p.setPen( beatLineColor() ); - p.drawLine( x, PR_TOP_MARGIN, x, height() - PR_BOTTOM_MARGIN ); + p.drawLine(x, PR_TOP_MARGIN, x, noteEditBottom()); } - // Draw the vertical bar lines - for( tick = m_currentPosition - m_currentPosition % MidiTime::ticksPerBar(), - x = xCoordOfTick( tick ); x <= width(); - tick += MidiTime::ticksPerBar(), x = xCoordOfTick( tick ) ) + // draw vertical bar lines + p.setPen(m_barLineColor); + for(tick = m_currentPosition - m_currentPosition % MidiTime::ticksPerBar(), + x = xCoordOfTick( tick ); + x <= width(); + tick += MidiTime::ticksPerBar(), x = xCoordOfTick(tick)) { - p.setPen( barLineColor() ); - p.drawLine( x, PR_TOP_MARGIN, x, height() - PR_BOTTOM_MARGIN ); + p.drawLine(x, PR_TOP_MARGIN, x, noteEditBottom()); } // draw marked semitones after the grid - for( int i = 0; i < m_markedSemiTones.size(); i++ ) + for(x = 0; x < m_markedSemiTones.size(); ++x) { - const int key_num = m_markedSemiTones.at( i ); - const int y = key_line_y + 5 - - m_keyLineHeight * ( key_num - m_startKey + 1 ); - - if( y > key_line_y ) - { - break; - } - - p.fillRect( WHITE_KEY_WIDTH + 1, y - m_keyLineHeight / 2, width() - 10, m_keyLineHeight + 1, - markedSemitoneColor() ); + const int key_num = m_markedSemiTones.at(x); + const int y = keyAreaBottom() + 5 - m_keyLineHeight * + (key_num - m_startKey + 1); + if(y > keyAreaBottom()) { break; } + p.fillRect(m_whiteKeyWidth + 1, + y - m_keyLineHeight / 2, + width() - 10, + m_keyLineHeight + 1, + m_markedSemitoneColor); } } + // reset clip + p.setClipRect(0, 0, width(), height()); + + // erase the area below the piano, because there might be keys that + // should be only half-visible + p.fillRect( QRect( 0, keyAreaBottom(), + m_whiteKeyWidth, noteEditBottom() - keyAreaBottom()), bgColor); + + // display note editing info + //QFont f = p.font(); + f.setBold( false ); + p.setFont( pointSize<10>( f ) ); + p.setPen(m_noteModeColor); + p.drawText( QRect( 0, keyAreaBottom(), + m_whiteKeyWidth, noteEditBottom() - keyAreaBottom()), + Qt::AlignCenter | Qt::TextWordWrap, + m_nemStr.at( m_noteEditMode ) + ":" ); + + // set clipping area, because we are not allowed to paint over + // keyboard... + p.setClipRect( + m_whiteKeyWidth, + PR_TOP_MARGIN, + width() - m_whiteKeyWidth, + height() - PR_TOP_MARGIN - PR_BOTTOM_MARGIN); + // following code draws all notes in visible area // and the note editing stuff (volume, panning, etc) @@ -3159,15 +2999,17 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) qSwap( sel_key_start, sel_key_end ); } - int y_base = key_line_y - 1; + int y_base = keyAreaBottom() - 1; if( hasValidPattern() ) { - p.setClipRect( WHITE_KEY_WIDTH, PR_TOP_MARGIN, - width() - WHITE_KEY_WIDTH, - height() - PR_TOP_MARGIN ); + p.setClipRect( + m_whiteKeyWidth, + PR_TOP_MARGIN, + width() - m_whiteKeyWidth, + height() - PR_TOP_MARGIN); - const int visible_keys = ( key_line_y-keyAreaTop() ) / - m_keyLineHeight + 2; + const int topKey = qBound(0, m_startKey + m_pianoKeysVisible - 1, NumKeys - 1); + const int bottomKey = topKey - m_pianoKeysVisible; QPolygonF editHandles; @@ -3194,21 +3036,20 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) const int x = ( pos_ticks - m_currentPosition ) * m_ppb / MidiTime::ticksPerBar(); // skip this note if not in visible area at all - if( !( x + note_width >= 0 && x <= width() - WHITE_KEY_WIDTH ) ) + if (!(x + note_width >= 0 && x <= width() - m_whiteKeyWidth)) { continue; } // is the note in visible area? - if( key > 0 && key <= visible_keys ) + if (note->key() > bottomKey && note->key() <= topKey) { - // we've done and checked all, let's draw the - // note - drawNoteRect( p, x + WHITE_KEY_WIDTH, - y_base - key * m_keyLineHeight, - note_width, note, ghostNoteColor(), ghostNoteTextColor(), selectedNoteColor(), - ghostNoteOpacity(), ghostNoteBorders(), drawNoteNames ); + // we've done and checked all, let's draw the note + drawNoteRect( + p, x + m_whiteKeyWidth, y_base - key * m_keyLineHeight, note_width, + note, m_ghostNoteColor, m_ghostNoteTextColor, m_selectedNoteColor, + m_ghostNoteOpacity, m_ghostNoteBorders, drawNoteNames); } } @@ -3236,31 +3077,30 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) const int x = ( pos_ticks - m_currentPosition ) * m_ppb / MidiTime::ticksPerBar(); // skip this note if not in visible area at all - if( !( x + note_width >= 0 && x <= width() - WHITE_KEY_WIDTH ) ) + if (!(x + note_width >= 0 && x <= width() - m_whiteKeyWidth)) { continue; } // is the note in visible area? - if( key > 0 && key <= visible_keys ) + if (note->key() > bottomKey && note->key() <= topKey) { - // we've done and checked all, let's draw the - // note - drawNoteRect( p, x + WHITE_KEY_WIDTH, - y_base - key * m_keyLineHeight, - note_width, note, noteColor(), noteTextColor(), selectedNoteColor(), - noteOpacity(), noteBorders(), drawNoteNames ); + // we've done and checked all, let's draw the note + drawNoteRect( + p, x + m_whiteKeyWidth, y_base - key * m_keyLineHeight, note_width, + note, m_noteColor, m_noteTextColor, m_selectedNoteColor, + m_noteOpacity, m_noteBorders, drawNoteNames); } // draw note editing stuff int editHandleTop = 0; if( m_noteEditMode == NoteEditVolume ) { - QColor color = barColor().lighter( 30 + ( note->getVolume() * 90 / MaxVolume ) ); + QColor color = m_barColor.lighter(30 + (note->getVolume() * 90 / MaxVolume)); if( note->selected() ) { - color = selectedNoteColor(); + color = m_selectedNoteColor; } p.setPen( QPen( color, NOTE_EDIT_LINE_WIDTH ) ); @@ -3275,10 +3115,10 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) } else if( m_noteEditMode == NoteEditPanning ) { - QColor color = noteColor(); + QColor color = m_noteColor; if( note->selected() ) { - color = selectedNoteColor(); + color = m_selectedNoteColor; } p.setPen( QPen( color, NOTE_EDIT_LINE_WIDTH ) ); @@ -3297,11 +3137,11 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) if( note->hasDetuningInfo() ) { - drawDetuningInfo( p, note, - x + WHITE_KEY_WIDTH, - y_base - key * m_keyLineHeight ); - p.setClipRect(WHITE_KEY_WIDTH, PR_TOP_MARGIN, - width() - WHITE_KEY_WIDTH, + drawDetuningInfo(p, note, x + m_whiteKeyWidth, y_base - key * m_keyLineHeight); + p.setClipRect( + m_whiteKeyWidth, + PR_TOP_MARGIN, + width() - m_whiteKeyWidth, height() - PR_TOP_MARGIN); } } @@ -3324,24 +3164,24 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) const int x = ( pos_ticks - m_currentPosition ) * m_ppb / MidiTime::ticksPerBar(); // skip this note if not in visible area at all - if( !( x + note_width >= 0 && x <= width() - WHITE_KEY_WIDTH ) ) + if (!(x + note_width >= 0 && x <= width() - m_whiteKeyWidth)) { continue; } // is the note in visible area? - if( key > 0 && key <= visible_keys ) + if (note->key() > bottomKey && note->key() <= topKey) { // we've done and checked all, let's draw the note - drawNoteRect( p, x + WHITE_KEY_WIDTH, - y_base - key * m_keyLineHeight, - note_width, note, m_stepRecorder.curStepNoteColor(), noteTextColor(), selectedNoteColor(), - noteOpacity(), noteBorders(), drawNoteNames ); + drawNoteRect( + p, x + m_whiteKeyWidth, y_base - key * m_keyLineHeight, note_width, + note, m_stepRecorder.curStepNoteColor(), m_noteTextColor, m_selectedNoteColor, + m_noteOpacity, m_noteBorders, drawNoteNames); } } - p.setPen( QPen( noteColor(), NOTE_EDIT_LINE_WIDTH + 2 ) ); + p.setPen(QPen(m_noteColor, NOTE_EDIT_LINE_WIDTH + 2)); p.drawPoints( editHandles ); } @@ -3352,14 +3192,16 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) p.setFont( pointSize<14>( f ) ); p.setPen( QApplication::palette().color( QPalette::Active, QPalette::BrightText ) ); - p.drawText( WHITE_KEY_WIDTH + 20, PR_TOP_MARGIN + 40, + p.drawText(m_whiteKeyWidth + 20, PR_TOP_MARGIN + 40, tr( "Please open a pattern by double-clicking " "on it!" ) ); } - p.setClipRect( WHITE_KEY_WIDTH, PR_TOP_MARGIN, width() - - WHITE_KEY_WIDTH, height() - PR_TOP_MARGIN - - m_notesEditHeight - PR_BOTTOM_MARGIN ); + p.setClipRect( + m_whiteKeyWidth, + PR_TOP_MARGIN, + width() - m_whiteKeyWidth, + height() - PR_TOP_MARGIN - m_notesEditHeight - PR_BOTTOM_MARGIN); // now draw selection-frame int x = ( ( sel_pos_start - m_currentPosition ) * m_ppb ) / @@ -3368,9 +3210,9 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) MidiTime::ticksPerBar() ) - x; int y = (int) y_base - sel_key_start * m_keyLineHeight; int h = (int) y_base - sel_key_end * m_keyLineHeight - y; - p.setPen( selectedNoteColor() ); + p.setPen(m_selectedNoteColor); p.setBrush( Qt::NoBrush ); - p.drawRect( x + WHITE_KEY_WIDTH, y, w, h ); + p.drawRect(x + m_whiteKeyWidth, y, w, h); // TODO: Get this out of paint event int l = ( hasValidPattern() )? (int) m_pattern->length() : 0; @@ -3383,70 +3225,92 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) } // set line colors - QColor editAreaCol = QColor( lineColor() ); - QColor currentKeyCol = QColor( beatLineColor() ); + QColor editAreaCol = QColor(m_lineColor); + QColor currentKeyCol = QColor(m_beatLineColor); editAreaCol.setAlpha( 64 ); currentKeyCol.setAlpha( 64 ); // horizontal line for the key under the cursor - if( hasValidPattern() ) + if(hasValidPattern() && gui->pianoRoll()->hasFocus()) { int key_num = getKey( mapFromGlobal( QCursor::pos() ).y() ); - p.fillRect( 10, key_line_y + 3 - m_keyLineHeight * + p.fillRect( 10, keyAreaBottom() + 3 - m_keyLineHeight * ( key_num - m_startKey + 1 ), width() - 10, m_keyLineHeight - 7, currentKeyCol ); } // bar to resize note edit area p.setClipRect( 0, 0, width(), height() ); - p.fillRect( QRect( 0, key_line_y, + p.fillRect( QRect( 0, keyAreaBottom(), width()-PR_RIGHT_MARGIN, NOTE_EDIT_RESIZE_BAR ), editAreaCol ); - const QPixmap * cursor = NULL; - // draw current edit-mode-icon below the cursor - switch( m_editMode ) - { - case ModeDraw: - if( m_mouseDownRight ) - { - cursor = s_toolErase; - } - else if( m_action == ActionMoveNote ) - { - cursor = s_toolMove; - } - else - { - cursor = s_toolDraw; - } - break; - case ModeErase: cursor = s_toolErase; break; - case ModeSelect: cursor = s_toolSelect; break; - case ModeEditDetuning: cursor = s_toolOpen; break; - } - QPoint mousePosition = mapFromGlobal( QCursor::pos() ); - if( cursor != NULL && mousePosition.y() > keyAreaTop() && mousePosition.x() > noteEditLeft()) + if (gui->pianoRoll()->hasFocus()) { - p.drawPixmap( mousePosition + QPoint( 8, 8 ), *cursor ); + const QPixmap * cursor = NULL; + // draw current edit-mode-icon below the cursor + switch( m_editMode ) + { + case ModeDraw: + if( m_mouseDownRight ) + { + cursor = s_toolErase; + } + else if( m_action == ActionMoveNote ) + { + cursor = s_toolMove; + } + else + { + cursor = s_toolDraw; + } + break; + case ModeErase: cursor = s_toolErase; break; + case ModeSelect: cursor = s_toolSelect; break; + case ModeEditDetuning: cursor = s_toolOpen; break; + } + QPoint mousePosition = mapFromGlobal( QCursor::pos() ); + if( cursor != NULL && mousePosition.y() > keyAreaTop() && mousePosition.x() > noteEditLeft()) + { + p.drawPixmap( mousePosition + QPoint( 8, 8 ), *cursor ); + } } } +void PianoRoll::updateScrollbars() +{ + m_leftRightScroll->setGeometry( + m_whiteKeyWidth, + height() - SCROLLBAR_SIZE, + width() - m_whiteKeyWidth, + SCROLLBAR_SIZE + ); + m_topBottomScroll->setGeometry( + width() - SCROLLBAR_SIZE, + PR_TOP_MARGIN, + SCROLLBAR_SIZE, + height() - PR_TOP_MARGIN - SCROLLBAR_SIZE + ); + int pianoAreaHeight = keyAreaBottom() - PR_TOP_MARGIN; + int numKeysVisible = pianoAreaHeight / m_keyLineHeight; + m_totalKeysToScroll = qMax(0, NumKeys - numKeysVisible); + m_topBottomScroll->setRange(0, m_totalKeysToScroll); + if (m_startKey > m_totalKeysToScroll) + { + m_startKey = qMax(0, m_totalKeysToScroll); + } + m_topBottomScroll->setValue(m_totalKeysToScroll - m_startKey); +} + // responsible for moving/resizing scrollbars after window-resizing void PianoRoll::resizeEvent(QResizeEvent * re) { - m_leftRightScroll->setGeometry( WHITE_KEY_WIDTH, - height() - - SCROLLBAR_SIZE, - width()-WHITE_KEY_WIDTH, - SCROLLBAR_SIZE ); - updateYScroll(); - - Engine::getSong()->getPlayPos( Song::Mode_PlayPattern - ).m_timeLine->setFixedWidth( width() ); - + updatePositionLineHeight(); + updateScrollbars(); + Engine::getSong()->getPlayPos(Song::Mode_PlayPattern) + .m_timeLine->setFixedWidth(width()); update(); } @@ -3463,7 +3327,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) if (!hasValidPattern()) {return;} // get values for going through notes int pixel_range = 8; - int x = we->x() - WHITE_KEY_WIDTH; + int x = we->x() - m_whiteKeyWidth; int ticks_start = ( x - pixel_range / 2 ) * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; int ticks_end = ( x + pixel_range / 2 ) * @@ -3568,7 +3432,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) } z = qBound( 0, z, m_zoomingModel.size() - 1 ); - int x = (we->x() - WHITE_KEY_WIDTH)* MidiTime::ticksPerBar(); + int x = (we->x() - m_whiteKeyWidth)* MidiTime::ticksPerBar(); // ticks based on the mouse x-position where the scroll wheel was used int ticks = x / m_ppb; // what would be the ticks in the new zoom level on the very same mouse x @@ -3619,24 +3483,16 @@ void PianoRoll::focusInEvent( QFocusEvent * ) -int PianoRoll::getKey(int y ) const +int PianoRoll::getKey(int y) const { - int key_line_y = keyAreaBottom() - 1; - // pressed key on piano - int key_num = ( key_line_y - y ) / m_keyLineHeight; - key_num += m_startKey; - - // some range-checking-stuff - if( key_num < 0 ) - { - key_num = 0; - } - - if( key_num >= KeysPerOctave * NumOctaves ) - { - key_num = KeysPerOctave * NumOctaves - 1; - } - + // handle case that very top pixel maps to next key above + if (y - keyAreaTop() <= 1) { y = keyAreaTop() + 2; } + int key_num = qBound( + 0, + // add + 1 to stay within the grid lines + ((keyAreaBottom() - y + 1) / m_keyLineHeight) + m_startKey, + NumKeys - 1 + ); return key_num; } @@ -3854,7 +3710,7 @@ void PianoRoll::horScrolled(int new_pos ) void PianoRoll::verScrolled( int new_pos ) { // revert value - m_startKey = m_totalKeysToScroll - new_pos; + m_startKey = qMax(0, m_totalKeysToScroll - new_pos); update(); } @@ -4008,13 +3864,13 @@ void PianoRoll::updateYScroll() int total_pixels = m_octaveHeight * NumOctaves - (height() - PR_TOP_MARGIN - PR_BOTTOM_MARGIN - m_notesEditHeight); - m_totalKeysToScroll = total_pixels * KeysPerOctave / m_octaveHeight; + m_totalKeysToScroll = qMax(0, total_pixels * KeysPerOctave / m_octaveHeight); m_topBottomScroll->setRange(0, m_totalKeysToScroll); if(m_startKey > m_totalKeysToScroll) { - m_startKey = m_totalKeysToScroll; + m_startKey = qMax(0, m_totalKeysToScroll); } m_topBottomScroll->setValue(m_totalKeysToScroll - m_startKey); } @@ -4161,7 +4017,7 @@ bool PianoRoll::deleteSelectedNotes() void PianoRoll::autoScroll( const MidiTime & t ) { - const int w = width() - WHITE_KEY_WIDTH; + const int w = width() - m_whiteKeyWidth; if( t > m_currentPosition + w * MidiTime::ticksPerBar() / m_ppb ) { m_leftRightScroll->setValue( t.getBar() * MidiTime::ticksPerBar() ); @@ -4187,6 +4043,22 @@ void PianoRoll::updatePosition( const MidiTime & t ) { autoScroll( t ); } + const int pos = m_timeLine->pos() * m_ppb / MidiTime::ticksPerBar(); + if (pos >= m_currentPosition && pos <= m_currentPosition + width() - m_whiteKeyWidth) + { + m_positionLine->show(); + m_positionLine->move(pos - (m_positionLine->width() - 1) - m_currentPosition + m_whiteKeyWidth, keyAreaTop()); + } + else + { + m_positionLine->hide(); + } +} + + +void PianoRoll::updatePositionLineHeight() +{ + m_positionLine->setFixedHeight(keyAreaBottom() - keyAreaTop()); } @@ -4230,6 +4102,7 @@ void PianoRoll::zoomingChanged() m_timeLine->setPixelsPerBar( m_ppb ); m_stepRecorderWidget.setPixelsPerBar( m_ppb ); + m_positionLine->zoomChange(m_zoomLevels[m_zoomingModel.value()]); update(); } @@ -4239,9 +4112,9 @@ void PianoRoll::zoomingYChanged() { m_keyLineHeight = m_zoomYLevels[m_zoomingYModel.value()] * DEFAULT_KEY_LINE_HEIGHT; m_octaveHeight = m_keyLineHeight * KeysPerOctave; - m_whiteKeySmallHeight = round(m_keyLineHeight * 1.5); + m_whiteKeySmallHeight = qFloor(m_keyLineHeight * 1.5); m_whiteKeyBigHeight = m_keyLineHeight * 2; - m_blackKeyHeight = round(m_keyLineHeight * 1.3333); + m_blackKeyHeight = m_keyLineHeight; //round(m_keyLineHeight * 1.3333); updateYScroll(); update(); @@ -4360,7 +4233,7 @@ Note * PianoRoll::noteUnderMouse() { QPoint pos = mapFromGlobal( QCursor::pos() ); - if( pos.x() <= WHITE_KEY_WIDTH + if (pos.x() <= m_whiteKeyWidth || pos.x() > width() - SCROLLBAR_SIZE || pos.y() < PR_TOP_MARGIN || pos.y() > keyAreaBottom() ) @@ -4369,7 +4242,7 @@ Note * PianoRoll::noteUnderMouse() } int key_num = getKey( pos.y() ); - int pos_ticks = ( pos.x() - WHITE_KEY_WIDTH ) * + int pos_ticks = (pos.x() - m_whiteKeyWidth) * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; // loop through whole note-vector... @@ -4471,7 +4344,7 @@ PianoRollWindow::PianoRollWindow() : m_zoomingComboBox = new ComboBox( m_toolBar ); m_zoomingComboBox->setModel( &m_editor->m_zoomingModel ); - m_zoomingComboBox->setFixedSize( 64, 22 ); + m_zoomingComboBox->setFixedSize( 64, ComboBox::DEFAULT_HEIGHT ); m_zoomingComboBox->setToolTip( tr( "Horizontal zooming") ); QLabel * zoom_y_lbl = new QLabel(m_toolBar); @@ -4479,7 +4352,7 @@ PianoRollWindow::PianoRollWindow() : m_zoomingYComboBox = new ComboBox(m_toolBar); m_zoomingYComboBox->setModel(&m_editor->m_zoomingYModel); - m_zoomingYComboBox->setFixedSize(64, 22); + m_zoomingYComboBox->setFixedSize(64, ComboBox::DEFAULT_HEIGHT); m_zoomingYComboBox->setToolTip(tr("Vertical zooming")); // setup quantize-stuff @@ -4488,7 +4361,7 @@ PianoRollWindow::PianoRollWindow() : m_quantizeComboBox = new ComboBox( m_toolBar ); m_quantizeComboBox->setModel( &m_editor->m_quantizeModel ); - m_quantizeComboBox->setFixedSize( 64, 22 ); + m_quantizeComboBox->setFixedSize( 64, ComboBox::DEFAULT_HEIGHT ); m_quantizeComboBox->setToolTip( tr( "Quantization") ); // setup note-len-stuff @@ -4497,7 +4370,7 @@ PianoRollWindow::PianoRollWindow() : m_noteLenComboBox = new ComboBox( m_toolBar ); m_noteLenComboBox->setModel( &m_editor->m_noteLenModel ); - m_noteLenComboBox->setFixedSize( 105, 22 ); + m_noteLenComboBox->setFixedSize( 105, ComboBox::DEFAULT_HEIGHT ); m_noteLenComboBox->setToolTip( tr( "Note length") ); // setup scale-stuff @@ -4506,7 +4379,7 @@ PianoRollWindow::PianoRollWindow() : m_scaleComboBox = new ComboBox( m_toolBar ); m_scaleComboBox->setModel( &m_editor->m_scaleModel ); - m_scaleComboBox->setFixedSize( 105, 22 ); + m_scaleComboBox->setFixedSize( 105, ComboBox::DEFAULT_HEIGHT ); m_scaleComboBox->setToolTip( tr( "Scale") ); // setup chord-stuff @@ -4515,7 +4388,7 @@ PianoRollWindow::PianoRollWindow() : m_chordComboBox = new ComboBox( m_toolBar ); m_chordComboBox->setModel( &m_editor->m_chordModel ); - m_chordComboBox->setFixedSize( 105, 22 ); + m_chordComboBox->setFixedSize( 105, ComboBox::DEFAULT_HEIGHT ); m_chordComboBox->setToolTip( tr( "Chord" ) ); // -- Clear ghost pattern button @@ -4721,6 +4594,14 @@ void PianoRollWindow::loadSettings( const QDomElement & de ) m_editor->loadMarkedSemiTones(de.firstChildElement("markedSemiTones")); MainWindow::restoreWidgetState( this, de ); + + // update margins here because we're later in the startup process + // We can't earlier because everything is still starting with the + // WHITE_KEY_WIDTH default + QMargins qm = m_editor->m_stepRecorderWidget.margins(); + qm.setLeft(m_editor->m_whiteKeyWidth); + m_editor->m_stepRecorderWidget.setMargins(qm); + m_editor->m_timeLine->setXOffset(m_editor->m_whiteKeyWidth); } @@ -4733,6 +4614,13 @@ QSize PianoRollWindow::sizeHint() const +bool PianoRollWindow::hasFocus() const +{ + return m_editor->hasFocus(); +} + + + void PianoRollWindow::updateAfterPatternChange() { patternRenamed(); diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index b6647d44a3b..e738708ac09 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -52,86 +52,6 @@ #include "PianoRoll.h" #include "Track.h" -positionLine::positionLine( QWidget* parent ) : - QWidget( parent ), - m_hasTailGradient ( false ), - m_lineColor (0, 0, 0, 0) -{ - resize( 8, height() ); - - setAttribute( Qt::WA_NoSystemBackground, true ); - setAttribute( Qt::WA_TransparentForMouseEvents ); -} - -void positionLine::paintEvent( QPaintEvent* pe ) -{ - QPainter p( this ); - - // If width is 1, we don't need a gradient - if (width() == 1) - { - p.fillRect( rect(), - QColor( m_lineColor.red(), m_lineColor.green(), m_lineColor.blue(), 153) ); - } - - // If width > 1, we need the gradient - else - { - // Create the gradient trail behind the line - QLinearGradient gradient( rect().bottomLeft(), rect().bottomRight() ); - - // If gradient is enabled, we're in focus and we're playing, enable gradient - if (Engine::getSong()->isPlaying() && m_hasTailGradient && - Engine::getSong()->playMode() == Song::Mode_PlaySong) - { - gradient.setColorAt(( ( width() - 1.0 )/width() ), - QColor( m_lineColor.red(), m_lineColor.green(), m_lineColor.blue(), 60) ); - } - else - { - gradient.setColorAt(( ( width() - 1.0 )/width() ), - QColor( m_lineColor.red(), m_lineColor.green(), m_lineColor.blue(), 0) ); - } - - // Fill in the remaining parts - gradient.setColorAt(0, - QColor( m_lineColor.red(), m_lineColor.green(), m_lineColor.blue(), 0) ); - gradient.setColorAt(1, - QColor( m_lineColor.red(), m_lineColor.green(), m_lineColor.blue(), 153) ); - - // Fill line - p.fillRect( rect(), gradient ); - } -} - -// QProperty handles -bool positionLine::hasTailGradient() const -{ return m_hasTailGradient; } - -void positionLine::setHasTailGradient( const bool g ) -{ m_hasTailGradient = g; } - -QColor positionLine::lineColor() const -{ return m_lineColor; } - -void positionLine::setLineColor( const QColor & c ) -{ m_lineColor = c; } - -// NOTE: the move() implementation fixes a bug where the position line would appear -// in an unexpected location when positioned at the start of the track -void positionLine::zoomChange( double zoom ) -{ - int playHeadPos = x() + width() - 1; - - resize( 8.0 * zoom, height() ); - move( playHeadPos - width() + 1, y() ); - - update(); -} - - - - const QVector SongEditor::m_zoomLevels = { 0.125f, 0.25f, 0.5f, 1.0f, 2.0f, 4.0f, 8.0f, 16.0f }; @@ -172,7 +92,7 @@ SongEditor::SongEditor( Song * song ) : connect( m_timeLine, SIGNAL( selectionFinished() ), this, SLOT( stopRubberBand() ) ); - m_positionLine = new positionLine( this ); + m_positionLine = new PositionLine(this); static_cast( layout() )->insertWidget( 1, m_timeLine ); connect( m_song, SIGNAL( playbackStateChanged() ), @@ -1035,7 +955,7 @@ SongEditorWindow::SongEditorWindow(Song* song) : //Set up zooming-stuff m_zoomingComboBox = new ComboBox( m_toolBar ); - m_zoomingComboBox->setFixedSize( 80, 22 ); + m_zoomingComboBox->setFixedSize( 80, ComboBox::DEFAULT_HEIGHT ); m_zoomingComboBox->move( 580, 4 ); m_zoomingComboBox->setModel(m_editor->m_zoomingModel); m_zoomingComboBox->setToolTip(tr("Horizontal zooming")); @@ -1050,7 +970,7 @@ SongEditorWindow::SongEditorWindow(Song* song) : //Set up quantization/snapping selector m_snappingComboBox = new ComboBox( m_toolBar ); - m_snappingComboBox->setFixedSize( 80, 22 ); + m_snappingComboBox->setFixedSize( 80, ComboBox::DEFAULT_HEIGHT ); m_snappingComboBox->setModel(m_editor->m_snappingModel); m_snappingComboBox->setToolTip(tr("Clip snapping size")); connect(m_editor->snappingModel(), SIGNAL(dataChanged()), this, SLOT(updateSnapLabel())); diff --git a/src/gui/widgets/ComboBox.cpp b/src/gui/widgets/ComboBox.cpp index c796bfa74c8..69955501d61 100644 --- a/src/gui/widgets/ComboBox.cpp +++ b/src/gui/widgets/ComboBox.cpp @@ -51,6 +51,8 @@ ComboBox::ComboBox( QWidget * _parent, const QString & _name ) : m_menu( this ), m_pressed( false ) { + setFixedHeight( ComboBox::DEFAULT_HEIGHT ); + if( s_background == NULL ) { s_background = new QPixmap( embed::getIconPixmap( "combobox_bg" ) ); diff --git a/src/gui/widgets/Controls.cpp b/src/gui/widgets/Controls.cpp index 15b4e0d282a..98aaf0e0eee 100644 --- a/src/gui/widgets/Controls.cpp +++ b/src/gui/widgets/Controls.cpp @@ -78,7 +78,7 @@ ComboControl::ComboControl(QWidget *parent) : m_combo(new ComboBox(nullptr)), m_label(new QLabel(m_widget)) { - m_combo->setFixedSize(64, 22); + m_combo->setFixedSize(64, ComboBox::DEFAULT_HEIGHT); QVBoxLayout* vbox = new QVBoxLayout(m_widget); vbox->addWidget(m_combo); vbox->addWidget(m_label); diff --git a/src/gui/widgets/FxLine.cpp b/src/gui/widgets/FxLine.cpp index 3314301fd77..476d8773b2b 100644 --- a/src/gui/widgets/FxLine.cpp +++ b/src/gui/widgets/FxLine.cpp @@ -25,6 +25,8 @@ #include "FxLine.h" +#include + #include #include "CaptionMenu.h" @@ -120,6 +122,8 @@ FxLine::FxLine( QWidget * _parent, FxMixerView * _mv, int _channelIndex ) : proxyWidget->setPos( 8, 145 ); connect( m_renameLineEdit, SIGNAL( editingFinished() ), this, SLOT( renameFinished() ) ); + connect( &Engine::fxMixer()->effectChannel( m_channelIndex )->m_muteModel, SIGNAL( dataChanged() ), this, SLOT( update() ) ); + } @@ -144,10 +148,11 @@ void FxLine::setChannelIndex( int index ) - void FxLine::drawFxLine( QPainter* p, const FxLine *fxLine, bool isActive, bool sendToThis, bool receiveFromThis ) { - QString name = Engine::fxMixer()->effectChannel( m_channelIndex )->m_name; + auto channel = Engine::fxMixer()->effectChannel( m_channelIndex ); + bool muted = channel->m_muteModel.value(); + QString name = channel->m_name; QString elidedName = elideName( name ); if( !m_inRename && m_renameLineEdit->text() != elidedName ) { @@ -156,8 +161,16 @@ void FxLine::drawFxLine( QPainter* p, const FxLine *fxLine, bool isActive, bool int width = fxLine->rect().width(); int height = fxLine->rect().height(); - - p->fillRect( fxLine->rect(), isActive ? fxLine->backgroundActive() : p->background() ); + + if( channel->m_hasColor && !muted ) + { + p->fillRect( fxLine->rect(), channel->m_color.darker( isActive ? 120 : 150 ) ); + } + else + { + p->fillRect( fxLine->rect(), + isActive ? fxLine->backgroundActive().color() : p->background().color() ); + } // inner border p->setPen( isActive ? fxLine->strokeInnerActive() : fxLine->strokeInnerInactive() ); @@ -224,7 +237,7 @@ void FxLine::mouseDoubleClickEvent( QMouseEvent * ) void FxLine::contextMenuEvent( QContextMenuEvent * ) { QPointer contextMenu = new CaptionMenu( Engine::fxMixer()->effectChannel( m_channelIndex )->m_name, this ); - if( m_channelIndex != 0 ) // no move-options in master + if( m_channelIndex != 0 ) // no move-options in master { contextMenu->addAction( tr( "Move &left" ), this, SLOT( moveChannelLeft() ) ); contextMenu->addAction( tr( "Move &right" ), this, SLOT( moveChannelRight() ) ); @@ -238,6 +251,10 @@ void FxLine::contextMenuEvent( QContextMenuEvent * ) contextMenu->addSeparator(); } contextMenu->addAction( embed::getIconPixmap( "cancel" ), tr( "Remove &unused channels" ), this, SLOT( removeUnusedChannels() ) ); + contextMenu->addSeparator(); + contextMenu->addAction( embed::getIconPixmap( "colorize" ), tr( "Set channel color" ), this, SLOT( changeColor() ) ); + contextMenu->addAction( embed::getIconPixmap( "colorize" ), tr( "Remove channel color" ), this, SLOT( resetColor() ) ); + contextMenu->addAction( embed::getIconPixmap( "colorize" ), tr( "Pick random channel color" ), this, SLOT( randomColor() ) ); contextMenu->exec( QCursor::pos() ); delete contextMenu; } @@ -395,3 +412,34 @@ void FxLine::setStrokeInnerInactive( const QColor & c ) { m_strokeInnerInactive = c; } + + +// Ask user for a color, and set it as the mixer line color +void FxLine::changeColor() +{ + auto channel = Engine::fxMixer()->effectChannel( m_channelIndex ); + auto new_color = ColorChooser( this ).withPalette( ColorChooser::Palette::Mixer )->getColor( channel->m_color ); + if( ! new_color.isValid() ) + { return; } + channel->m_color = new_color; + channel->m_hasColor = true; + update(); +} + + +// Disable the usage of color on this mixer line +void FxLine::resetColor() +{ + Engine::fxMixer()->effectChannel( m_channelIndex )->m_hasColor = false; + update(); +} + + +// Pick a random color from the mixer palette and set it as our color +void FxLine::randomColor() +{ + auto channel = Engine::fxMixer()->effectChannel( m_channelIndex ); + channel->m_color = ColorChooser::getPalette( ColorChooser::Palette::Mixer )[ rand() % 48 ]; + channel->m_hasColor = true; + update(); +} diff --git a/src/gui/widgets/InstrumentSoundShapingView.cpp b/src/gui/widgets/InstrumentSoundShapingView.cpp index a6c6fbd56cf..24a52fad235 100644 --- a/src/gui/widgets/InstrumentSoundShapingView.cpp +++ b/src/gui/widgets/InstrumentSoundShapingView.cpp @@ -62,7 +62,7 @@ InstrumentSoundShapingView::InstrumentSoundShapingView( QWidget * _parent ) : { m_envLfoViews[i] = new EnvelopeAndLfoView( m_targetsTabWidget ); m_targetsTabWidget->addTab( m_envLfoViews[i], - tr( InstrumentSoundShaping::targetNames[i][0].toUtf8().constData() ), + tr( InstrumentSoundShaping::targetNames[i][0] ), NULL ); } @@ -74,7 +74,7 @@ InstrumentSoundShapingView::InstrumentSoundShapingView( QWidget * _parent ) : m_filterComboBox = new ComboBox( m_filterGroupBox ); - m_filterComboBox->setGeometry( 14, 22, 120, 22 ); + m_filterComboBox->setGeometry( 14, 22, 120, ComboBox::DEFAULT_HEIGHT ); m_filterComboBox->setFont( pointSize<8>( m_filterComboBox->font() ) ); diff --git a/src/gui/widgets/PositionLine.cpp b/src/gui/widgets/PositionLine.cpp new file mode 100644 index 00000000000..ef003c5ebad --- /dev/null +++ b/src/gui/widgets/PositionLine.cpp @@ -0,0 +1,98 @@ +/* + * PositionLine.cpp + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * This file is part of LMMS - https://lmms.io + * + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "PositionLine.h" + +#include + +#include "GuiApplication.h" +#include "Song.h" + + +PositionLine::PositionLine(QWidget* parent) : + QWidget(parent), + m_hasTailGradient(false), + m_lineColor(0, 0, 0, 0) +{ + resize(8, height()); + + setAttribute(Qt::WA_NoSystemBackground, true); + setAttribute(Qt::WA_TransparentForMouseEvents); +} + +void PositionLine::paintEvent(QPaintEvent* pe) +{ + QPainter p(this); + QColor c = QColor(m_lineColor); + + // If width is 1, we don't need a gradient + if (width() == 1) + { + c.setAlpha(153); + p.fillRect(rect(), c); + } + // If width > 1, we need the gradient + else + { + // Create the gradient trail behind the line + QLinearGradient gradient(rect().bottomLeft(), rect().bottomRight()); + qreal w = (width() - 1.0) / width(); + + // If gradient is enabled, we're in focus and we're playing, enable gradient + if (m_hasTailGradient && + Engine::getSong()->isPlaying() && + (Engine::getSong()->playMode() == Song::Mode_PlaySong || + Engine::getSong()->playMode() == Song::Mode_PlayPattern)) + { + c.setAlpha(60); + gradient.setColorAt(w, c); + } + else + { + c.setAlpha(0); + gradient.setColorAt(w, c); + } + + // Fill in the remaining parts + c.setAlpha(0); + gradient.setColorAt(0, c); + c.setAlpha(153); + gradient.setColorAt(1, c); + + // Fill line + p.fillRect(rect(), gradient); + } +} + +// NOTE: the move() implementation fixes a bug where the position line would appear +// in an unexpected location when positioned at the start of the track +void PositionLine::zoomChange(double zoom) +{ + int playHeadPos = x() + width() - 1; + + resize(8.0 * zoom, height()); + move(playHeadPos - width() + 1, y()); + + update(); +} \ No newline at end of file diff --git a/src/gui/widgets/StepRecorderWidget.cpp b/src/gui/widgets/StepRecorderWidget.cpp index a546c2a2cdc..d2157ef66bc 100644 --- a/src/gui/widgets/StepRecorderWidget.cpp +++ b/src/gui/widgets/StepRecorderWidget.cpp @@ -58,6 +58,19 @@ void StepRecorderWidget::setCurrentPosition(MidiTime currentPosition) m_currentPosition = currentPosition; } +void StepRecorderWidget::setMargins(const QMargins &qm) +{ + m_left = qm.left(); + m_right = qm.right(); + m_top = qm.top(); + m_bottom = qm.bottom(); +} + +QMargins StepRecorderWidget::margins() +{ + return QMargins(m_left, m_top, m_right, m_bottom); +} + void StepRecorderWidget::setBottomMargin(const int marginBottom) { m_marginBottom = marginBottom; diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 97e6cfa4762..396ab061210 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -77,11 +77,6 @@ #include "MidiCCRackView.h" -const char * volume_help = QT_TRANSLATE_NOOP( "InstrumentTrack", - "With this knob you can set " - "the volume of the opened " - "channel."); - const int INSTRUMENT_WIDTH = 254; const int INSTRUMENT_HEIGHT = INSTRUMENT_WIDTH; const int PIANO_HEIGHT = 80; diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 86e1861c691..d73b62cbaff 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -343,29 +343,57 @@ void SampleTCOView::updateSample() void SampleTCOView::contextMenuEvent( QContextMenuEvent * _cme ) { + // Depending on whether we right-clicked a selection or an individual TCO we will have + // different labels for the actions. + bool individualTCO = getClickedTCOs().size() <= 1; + if( _cme->modifiers() ) { return; } QMenu contextMenu( this ); + if( fixedTCOs() == false ) { - contextMenu.addAction( embed::getIconPixmap( "cancel" ), - tr( "Delete (middle mousebutton)" ), - this, SLOT( remove() ) ); + contextMenu.addAction( + embed::getIconPixmap( "cancel" ), + individualTCO + ? tr("Delete (middle mousebutton)") + : tr("Delete selection (middle mousebutton)"), + [this](){ contextMenuAction( Remove ); } ); + contextMenu.addSeparator(); - contextMenu.addAction( embed::getIconPixmap( "edit_cut" ), - tr( "Cut" ), this, SLOT( cut() ) ); + + contextMenu.addAction( + embed::getIconPixmap( "edit_cut" ), + individualTCO + ? tr("Cut") + : tr("Cut selection"), + [this](){ contextMenuAction( Cut ); } ); } - contextMenu.addAction( embed::getIconPixmap( "edit_copy" ), - tr( "Copy" ), m_tco, SLOT( copy() ) ); - contextMenu.addAction( embed::getIconPixmap( "edit_paste" ), - tr( "Paste" ), m_tco, SLOT( paste() ) ); + + contextMenu.addAction( + embed::getIconPixmap( "edit_copy" ), + individualTCO + ? tr("Copy") + : tr("Copy selection"), + [this](){ contextMenuAction( Copy ); } ); + + contextMenu.addAction( + embed::getIconPixmap( "edit_paste" ), + tr( "Paste" ), + [this](){ contextMenuAction( Paste ); } ); + contextMenu.addSeparator(); - contextMenu.addAction( embed::getIconPixmap( "muted" ), - tr( "Mute/unmute (<%1> + middle click)" ).arg(UI_CTRL_KEY), - m_tco, SLOT( toggleMute() ) ); + + contextMenu.addAction( + embed::getIconPixmap( "muted" ), + (individualTCO + ? tr("Mute/unmute (<%1> + middle click)") + : tr("Mute/unmute selection (<%1> + middle click)")).arg(UI_CTRL_KEY), + [this](){ contextMenuAction( Mute ); } ); + /*contextMenu.addAction( embed::getIconPixmap( "record" ), tr( "Set/clear record" ), m_tco, SLOT( toggleRecord() ) );*/