/*************************************************************************** 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #include #include #include #include #include #include #include #include #include #include #include #include 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( &_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\ // linux: /home/chris/ 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"; }