first re-commit.
This commit is contained in:
515
raDIYo.cpp
Normal file
515
raDIYo.cpp
Normal file
@@ -0,0 +1,515 @@
|
||||
/***************************************************************************
|
||||
|
||||
source::worx raDIYo
|
||||
Copyright © 2020-2022 c.holzheuer
|
||||
chris@sourceworx.org
|
||||
|
||||
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.
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#include <QDebug>
|
||||
#include <QStandardPaths>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QAction>
|
||||
#include <QCoreApplication>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QTime>
|
||||
#include <QUrl>
|
||||
#include <QFileInfo>
|
||||
#include <QStyle>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QFontDatabase>
|
||||
#include <QByteArray>
|
||||
#include <QFileInfo>
|
||||
|
||||
|
||||
//#include <stdio.h>
|
||||
#include <raDIYo.h>
|
||||
#include <swcontrol.h>
|
||||
#include <swdialbutton.h>
|
||||
|
||||
#include <swsendercontrol.h>
|
||||
#include <swsongscontrol.h>
|
||||
#include <swshutdowncontrol.h>
|
||||
#include <swclockcontrol.h>
|
||||
#include <swalarmcontrol.h>
|
||||
#include <swsetupcontrol.h>
|
||||
#include <swusbcontrol.h>
|
||||
#include <swbluetoothcontrol.h>
|
||||
#include <swplayercontrol.h>
|
||||
|
||||
#include <SWPiGPIO.h>
|
||||
|
||||
|
||||
RaDIYo::RaDIYo()
|
||||
: QWidget( nullptr )
|
||||
{
|
||||
|
||||
|
||||
setupUi( this );
|
||||
|
||||
// startup
|
||||
|
||||
// Das ist die Bildschirmgröße des Raspi 7 inch displays
|
||||
resize( SWScreenLargeX, SWScreenLargeY );
|
||||
|
||||
setupFonts();
|
||||
setupControls();
|
||||
setupConnections();
|
||||
setupDefaults();
|
||||
|
||||
// bei untätigkeit kommt wieder die Uhr
|
||||
//_idleTimer.setInterval( raDIYo::IdleTimeOut );
|
||||
//_idleTimer.setSingleShot( true );
|
||||
|
||||
// hier sind wir auch für die voreinstellungen zuständig: falls
|
||||
// nicht vorhanden, default setzen
|
||||
|
||||
// '_alarmControl->onShow();' stellt den Alarmtimer ein
|
||||
// Das gehört hierher ins setup, weil
|
||||
// wir den Alarm auch gesetzt haben wollen
|
||||
// _ohne_ das AlarmConmtrol aufrufen zu müssen.
|
||||
|
||||
_alarmControl->onShow();
|
||||
|
||||
|
||||
// wir starten mit der Uhr
|
||||
onChangeState( RaDIYo::Clock );
|
||||
|
||||
// Der Startwert für die Lautstärke
|
||||
// ist 20% vom Maximum und muss
|
||||
// hier als Absolutwert gesetzt werden
|
||||
// weil zur Laufzeit nur mit deltas
|
||||
// gearbeitet wird.
|
||||
|
||||
int vol = 20;
|
||||
_nowPlaying.volume = vol;
|
||||
_volumeWidget->setValue( vol );
|
||||
_playerControl->setValue( vol );
|
||||
|
||||
// not least
|
||||
_version->setText( raDIYo::Version );
|
||||
qDebug( raDIYo::Version );
|
||||
|
||||
}
|
||||
|
||||
|
||||
RaDIYo::~RaDIYo()
|
||||
{
|
||||
_playerControl->stopPlaying();
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
delete _dialLeft;
|
||||
delete _dialRight;
|
||||
#endif
|
||||
|
||||
for( SWControl* ctrl : _controls )
|
||||
delete ctrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Behandelt die 'direkten' (vom touchscreen) Buttonsclicks aus der Buttongroup
|
||||
* und den 'indirekten' Aufruf über die RotaryDials: Wenn die Buttonleiste aktiv ist,
|
||||
* wird beim Klick auf den SenderButton auch 'idActivated(newId)' gesendet.
|
||||
*
|
||||
* @param newID id des Buttons ( entspricht der id des neuen Controls )
|
||||
*/
|
||||
|
||||
void RaDIYo::onChangeState( int newState )
|
||||
{
|
||||
|
||||
//qDebug() << " ------- RaDIYo::onChangeState: " << newState << ": " << stateName( newState ) << " old: " << stateName( _curCtrlIdx );
|
||||
|
||||
bool pauseClicked = false;
|
||||
|
||||
switch( newState )
|
||||
{
|
||||
|
||||
// wir sind am abspielen, pause geklickt
|
||||
case RaDIYo::Pause :
|
||||
|
||||
// hide button
|
||||
pauseClicked = true;
|
||||
newState = RaDIYo::Play;
|
||||
// fallthrough ...
|
||||
|
||||
// play button geklickt
|
||||
case RaDIYo::Play :
|
||||
|
||||
showTitleText( pauseClicked );
|
||||
_playerControl->togglePlaying( !pauseClicked );
|
||||
_buttonPlay->setVisible( pauseClicked );
|
||||
_buttonPause->setVisible( !pauseClicked );
|
||||
break;
|
||||
|
||||
case RaDIYo::Stop :
|
||||
|
||||
showTitleText( false );
|
||||
_playerControl->stopPlaying();
|
||||
// Pause gilt nicht mehr nach 'stop'
|
||||
_buttonPlay->setVisible( true );
|
||||
_buttonPause->setVisible( false );
|
||||
// fallthrough..
|
||||
|
||||
case RaDIYo::Back :
|
||||
|
||||
//_curCtrlIdx = _lstState;
|
||||
newState = Clock; //?? oder select
|
||||
|
||||
} // switch
|
||||
|
||||
|
||||
if( _curCtrlIdx == newState )
|
||||
return;
|
||||
|
||||
// Hier wird unterschieden zwischen 'echten' Controls ('play', 'sender' ...)
|
||||
// und dummies ohne eigene Seite ('stop', 'pause', 'back' )
|
||||
|
||||
_upMode = true;
|
||||
SWControl* newControl = _controls[newState];
|
||||
|
||||
// wenn vorhanden ...
|
||||
if( nullptr != newControl )
|
||||
{
|
||||
// ... dann das alte aus-
|
||||
_controls[_curCtrlIdx]->fadeOut();
|
||||
//_curCtrlIdx = newState;
|
||||
|
||||
// und das neue Control einblenden ...
|
||||
newControl->fadeIn();
|
||||
|
||||
// Kontrolle übergeben ...
|
||||
_activeReceiver = newControl;
|
||||
|
||||
// Wird das 'neue' Control vom RotaryDial gesteuert?
|
||||
if( !newControl->acceptDial() )
|
||||
{
|
||||
_activeReceiver = static_cast<SWDialHandler*>( &_raDIYoButtons );
|
||||
_upMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
_buttonBack->setVisible( _upMode );
|
||||
_buttonShutdown->setVisible( !_upMode );
|
||||
|
||||
_curCtrlIdx = newState;
|
||||
|
||||
_raDIYoButtons.setCurrentActiveId( _curCtrlIdx );
|
||||
bool isPlaying = _playerState == QMediaPlayer::PlayingState;
|
||||
_buttonStop->setEnabled( isPlaying );
|
||||
|
||||
|
||||
}
|
||||
|
||||
void RaDIYo::showTitleText( bool pauseClicked )
|
||||
{
|
||||
QString color = pauseClicked ? "rgb(181,181,181)" : "white" ;
|
||||
_currentTitle->setStyleSheet( _titleCss.arg( color ) );
|
||||
_currentTitle->setText( _nowPlaying.title );
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Wird nicht verwendet
|
||||
void RaDIYo::onIdleTimeOut()
|
||||
{
|
||||
// swap back
|
||||
onChangeState( RaDIYo::Clock );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Slot, der bei Zustandsänderungen des Players
|
||||
* aufgerufen wird
|
||||
* @param state der neue PlayerState
|
||||
*/
|
||||
|
||||
void RaDIYo::onPlayingChanged( QMediaPlayer::State state )
|
||||
{
|
||||
bool callStop = false;
|
||||
|
||||
// State-Änderung kommt von 'innen', also vom Player weil der Song zu Ende ist.
|
||||
if( _curCtrlIdx == RaDIYo::Play && state == QMediaPlayer::StoppedState )
|
||||
callStop = true;
|
||||
|
||||
_playerState = state;
|
||||
|
||||
if( callStop )
|
||||
onChangeState( RaDIYo::Stop );
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Es wurde ein Sender oder Song aus der jeweiligen ListView ausgewählt.
|
||||
* @param item der Sender/Song
|
||||
|
||||
* @brief Event von aussen: Ein PlayListEntry wurde aktiviert
|
||||
* und wir jetzt abgespielt.
|
||||
* @param item
|
||||
*/
|
||||
|
||||
void RaDIYo::onPlayUrl( SWUrl item )
|
||||
{
|
||||
_nowPlaying = item;
|
||||
onPlayCurrentUrl();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief von aussen und von innen, spielt
|
||||
* die derzeitige defaultquelle ab, wird
|
||||
* vom Wecker benutzt.
|
||||
*/
|
||||
|
||||
void RaDIYo::onPlayCurrentUrl()
|
||||
{
|
||||
_playerControl->setUrl( _nowPlaying.urlText );
|
||||
onChangeState( RaDIYo::Play );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
|
||||
void RaDIYo::onRightButtonClicked()
|
||||
{
|
||||
onChangeState( _upMode ? RaDIYo::Back : RaDIYo::Shutdown );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Behandelt Impulse der Linken DialControls.
|
||||
* @param delta: +1 oder -1 je nach Drehrichtung
|
||||
*/
|
||||
|
||||
void RaDIYo::onRightDeltaChanged( int delta )
|
||||
{
|
||||
// 'setValue( value )' reicht hier nicht,
|
||||
// denn der neue Wert muss weiter gereicht
|
||||
// werden, also 'onDialDeltaChanged'
|
||||
|
||||
_volumeWidget->onDialDeltaChanged( delta );
|
||||
_playerControl->onDialDeltaChanged( delta );
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Der linke Button wird an das aktive Control
|
||||
* weitergeleitet.
|
||||
*/
|
||||
|
||||
void RaDIYo::onLeftButtonClicked()
|
||||
{
|
||||
// ex.left
|
||||
Q_ASSERT( _activeReceiver != nullptr );
|
||||
_activeReceiver->onDialPushed();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief KISS: der rechte Regler steuert immer die Lautstärke.
|
||||
*/
|
||||
|
||||
void RaDIYo::onLeftDeltaChanged( int delta )
|
||||
{
|
||||
|
||||
|
||||
|
||||
Q_ASSERT( _activeReceiver != nullptr );
|
||||
_activeReceiver->onDialDeltaChanged( delta );
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
///
|
||||
/// --- Setup
|
||||
///
|
||||
|
||||
|
||||
/**
|
||||
* @brief Fonts explicit laden, _vor_ setupUi
|
||||
*/
|
||||
|
||||
void RaDIYo::setupFonts()
|
||||
{
|
||||
|
||||
QStringList fontList = QDir( raDIYo::FontDir ).entryList();
|
||||
for( const QString& fontName : fontList )
|
||||
QFontDatabase::addApplicationFont( raDIYo::FontDir + fontName );
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @param id
|
||||
* @param control
|
||||
*/
|
||||
|
||||
void RaDIYo::addControl( int id, SWControl* control )
|
||||
{
|
||||
_controls[ id ] = control;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief setupControls
|
||||
*/
|
||||
|
||||
void RaDIYo::setupControls()
|
||||
{
|
||||
|
||||
// Setup Controls: 'RaDIYo' Buttons
|
||||
|
||||
_raDIYoButtons.addKeyButton( _buttonPlay, Play, "play" );
|
||||
_raDIYoButtons.addKeyButton( _buttonPause, Pause, "pause" );
|
||||
_raDIYoButtons.addKeyButton( _buttonStop, Stop, "stop" );
|
||||
_raDIYoButtons.addKeyButton( _buttonAlarm, Alarm, "alarm" );
|
||||
_raDIYoButtons.addKeyButton( _buttonClock, Clock, "clock" );
|
||||
_raDIYoButtons.addKeyButton( _buttonSender, Sender, "sender" );
|
||||
_raDIYoButtons.addKeyButton( _buttonSongs, Songs, "songs" );
|
||||
_raDIYoButtons.addKeyButton( _buttonUSB, USB, "usb" );
|
||||
_raDIYoButtons.addKeyButton( _buttonBack, Back, "down" );
|
||||
_raDIYoButtons.addKeyButton( _buttonShutdown, Shutdown, "shutdown" );
|
||||
|
||||
// Startzustand darstellen
|
||||
_buttonBack->hide();
|
||||
_buttonPause->hide();
|
||||
|
||||
// Alle Controls
|
||||
_senderControl = new SWSenderControl( this, &_mainSettings );
|
||||
_songsControl = new SWSongsControl( this, &_mainSettings );
|
||||
_playerControl = new SWPlayerControl( this, &_mainSettings );
|
||||
_alarmControl = new SWAlarmControl( this, &_mainSettings );
|
||||
_usbControl = new SWUSBControl( this, &_mainSettings );
|
||||
|
||||
// Leere Plätze mit 'nullptr' als solche kennzeichnen
|
||||
addControl( Play, _playerControl );
|
||||
addControl( Pause, nullptr );
|
||||
addControl( Stop, nullptr );
|
||||
addControl( Clock, new SWClockControl( this, &_mainSettings ) );
|
||||
addControl( Alarm, _alarmControl ) ;
|
||||
addControl( Sender, _senderControl );
|
||||
addControl( Songs, _songsControl );
|
||||
addControl( USB, _usbControl );
|
||||
addControl( Back, nullptr );
|
||||
addControl( Shutdown, new SWShutdownControl( this, &_mainSettings ) );
|
||||
|
||||
}
|
||||
|
||||
|
||||
void RaDIYo::setupConnections()
|
||||
{
|
||||
|
||||
// Linux, kompiliert auch unter Win
|
||||
_dialLeft = new PiGRotaryDial( 22, 27, 17, this );
|
||||
_dialRight = new PiGRotaryDial( 06, 13, 05, this );
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
|
||||
connect( _dialLeft, SIGNAL(clicked()), this, SLOT(onLeftButtonClicked() ) );
|
||||
connect( _dialLeft, SIGNAL(deltaChanged(int)), this, SLOT(onLeftDeltaChanged(int) ) );
|
||||
|
||||
connect( _dialRight, SIGNAL(clicked()), this, SLOT(onRightButtonClicked() ) );
|
||||
connect( _dialRight, SIGNAL(deltaChanged(int)), this, SLOT(onRightDeltaChanged(int) ) );
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
// Setup Controls: Fake Dials
|
||||
|
||||
connect( &_dialDialog.leftDial().pushButton(), &QPushButton::clicked, this, &RaDIYo::onLeftButtonClicked );
|
||||
connect( &_dialDialog, &SWDummyDialDialog::leftDeltaChanged, this, &RaDIYo::onLeftDeltaChanged );
|
||||
|
||||
connect( &_dialDialog.rightDial().pushButton(), &QPushButton::clicked, this, &RaDIYo::onRightButtonClicked );
|
||||
connect( &_dialDialog, &SWDummyDialDialog::rightDeltaChanged, this, &RaDIYo::onRightDeltaChanged );
|
||||
|
||||
_dialDialog.show();
|
||||
|
||||
#endif
|
||||
|
||||
connect( _senderControl, SIGNAL( entryActivated(SWUrl) ), this, SLOT( onPlayUrl(SWUrl) ) );
|
||||
connect( _songsControl, SIGNAL( entryActivated(SWUrl) ), this, SLOT( onPlayUrl(SWUrl) ) );
|
||||
connect( _usbControl, SIGNAL( entryActivated(SWUrl) ), this, SLOT( onPlayUrl(SWUrl) ) );
|
||||
connect( _usbControl, SIGNAL( driveMounted(int) ), this, SLOT( onChangeState(int) ) );
|
||||
|
||||
qRegisterMetaType< QMediaPlayer::State>(" QMediaPlayer::State");
|
||||
connect( _playerControl, SIGNAL( stateChanged(QMediaPlayer::State) ), this, SLOT( onPlayingChanged(QMediaPlayer::State) ) );
|
||||
|
||||
// Der Wecker sendet ggf auch ...
|
||||
connect( _alarmControl, SIGNAL( playCurrentUrl() ), this, SLOT( onPlayCurrentUrl() ) );
|
||||
connect( &_raDIYoButtons,SIGNAL( idClicked(int) ), this, SLOT( onChangeState(int) ) );
|
||||
// weiterleiten
|
||||
connect( _volumeWidget, SIGNAL( deltaChanged(int) ), this, SLOT( onRightDeltaChanged(int) ) );
|
||||
}
|
||||
|
||||
|
||||
void RaDIYo::setupDefaults()
|
||||
{
|
||||
|
||||
// default Sender belegen
|
||||
if( !_mainSettings.contains( raDIYo::KeyDefaultSender ) )
|
||||
_mainSettings.setValue( raDIYo::KeyDefaultSender, raDIYo::DefaultSender );
|
||||
|
||||
// default playlist item soll nicht leer sein
|
||||
QString defSender =_mainSettings.value( raDIYo::KeyDefaultSender ).toString();
|
||||
_nowPlaying = SWUrl( defSender, raDIYo::DefaultVolume );
|
||||
_playerControl->setUrl( _nowPlaying.urlText );
|
||||
|
||||
// wir nehmen den default-sender
|
||||
_currentTitle->setText( _nowPlaying.title );
|
||||
|
||||
// not least: Pfad setzen
|
||||
// windows: c:\users\chris\<my.Radiyo>
|
||||
// linux: /home/chris/<my.Radiyo>
|
||||
|
||||
QString songsPath = _mainSettings.value( raDIYo::KeySongsPath ).toString();
|
||||
if( songsPath.isEmpty() )
|
||||
{
|
||||
songsPath = QStandardPaths::writableLocation( QStandardPaths::DocumentsLocation );
|
||||
songsPath += _mainSettings.value( raDIYo::KeySongsDir, raDIYo::SongsDir ).toString();
|
||||
_mainSettings.setValue( raDIYo::KeySongsPath, songsPath );
|
||||
}
|
||||
|
||||
// css kram erzeugen
|
||||
_titleCss = readResource( raDIYo::ResTitleStyle );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Debugfunktion, die den Namen eines State ausgibt.
|
||||
* @param state
|
||||
*/
|
||||
|
||||
QString RaDIYo::stateName( int state )
|
||||
{
|
||||
|
||||
switch( state )
|
||||
{
|
||||
case Sender: return "Sender";
|
||||
case Songs: return "Songs";
|
||||
case USB: return "USB";
|
||||
case Clock: return "Clock";
|
||||
case Alarm: return "Alarm";
|
||||
case Play: return "Player";
|
||||
case Pause: return "Pause";
|
||||
case Stop: return "Stop";
|
||||
case Back: return "Back";
|
||||
case Shutdown: return "Shutdown";
|
||||
}
|
||||
return "42";
|
||||
}
|
||||
|
Reference in New Issue
Block a user