diff --git a/CMakeLists.txt b/CMakeLists.txt index dc3fd191d..3d6463805 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,8 +104,8 @@ if(PHONON_FOUND) message(STATUS "Phonon found; ensure that phonon-vlc is at least 0.4") endif() -macro_optional_find_package(LibEchonest 1.2.1) -macro_log_feature(LIBECHONEST_FOUND "Echonest" "Qt library for communicating with The Echo Nest" "http://projects.kde.org/libechonest" TRUE "" "libechonest 1.2.1 is needed for dynamic playlists and the infosystem") +macro_optional_find_package(LibEchonest 2.0.0) +macro_log_feature(LIBECHONEST_FOUND "Echonest" "Qt library for communicating with The Echo Nest" "http://projects.kde.org/libechonest" TRUE "" "libechonest 2.0.0 is needed for dynamic playlists and the infosystem") macro_optional_find_package(CLucene 0.9.23) macro_log_feature(CLucene_FOUND "CLucene" "The open-source, C++ search engine" "http://clucene.sf.net" TRUE "" "CLucene is used for indexing the collection") diff --git a/README b/README index 1c71fe586..a67094792 100644 --- a/README +++ b/README @@ -34,7 +34,7 @@ Dependencies TagLib 1.6.2 - http://developer.kde.org/~wheeler/taglib.html Boost 1.3 - http://www.boost.org/ CLucene 0.9.23 (0.9.21 will fail) - http://clucene.sourceforge.net/download.shtml - libechonest 1.2.1 - http://projects.kde.org/projects/playground/libs/libechonest/ + libechonest 2.0.0 - http://projects.kde.org/projects/playground/libs/libechonest/ Attica 0.4.0 - ftp://ftp.kde.org/pub/kde/stable/attica/ QuaZip 0.4.3 - http://quazip.sourceforge.net/ diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 548d1bd66..8d022ae17 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -1671,12 +1671,12 @@ connect and stream from you? SpotifyPlaylistUpdater - + Delete in Spotify? أحذف في سبوتيفي (Spotify)؟ - + Would you like to delete the corresponding Spotify playlist as well? هل ترغب في حذف قائمة التشغيل المطابقة على سبوتيفي (Spotify)؟ @@ -3030,7 +3030,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::SpotifyParser - + Error fetching Spotify information from the network! مشكلة في جلب معلومات "Spotify" من الشبكة! diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 41646624d..69c7fb575 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -1673,12 +1673,12 @@ Tomahaw създаде доклад относно това и изпращай SpotifyPlaylistUpdater - + Delete in Spotify? Изтривам и в Spotify? - + Would you like to delete the corresponding Spotify playlist as well? Желаеш ли да изтриеш и съответните Spotify списъци? @@ -3037,7 +3037,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::SpotifyParser - + Error fetching Spotify information from the network! Грешка при извличане на информация от Spotify diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index 39bdc19ae..955a02cd4 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -1669,12 +1669,12 @@ connect and stream from you? SpotifyPlaylistUpdater - + Delete in Spotify? Voleu esborrar-ho de Spotify? - + Would you like to delete the corresponding Spotify playlist as well? Voleu esborrar les llistes de Spotify, també? @@ -3029,7 +3029,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::SpotifyParser - + Error fetching Spotify information from the network! Error en cercar la informació de Spotify a través de la xarxa! diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index eddf0e796..cc0a37f69 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -1670,12 +1670,12 @@ erlauben sich mit dir zu verbinden? SpotifyPlaylistUpdater - + Delete in Spotify? Auf Spotify löschen - + Would you like to delete the corresponding Spotify playlist as well? Möchtest du die entsprechende Spotify Playlist auch löschen? @@ -3025,7 +3025,7 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::SpotifyParser - + Error fetching Spotify information from the network! Konnte Spotify-Daten nicht laden! diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index 730a84a58..893995482 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -1670,12 +1670,12 @@ connect and stream from you? SpotifyPlaylistUpdater - + Delete in Spotify? Delete in Spotify? - + Would you like to delete the corresponding Spotify playlist as well? Would you like to delete the corresponding Spotify playlist as well? @@ -3030,7 +3030,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::SpotifyParser - + Error fetching Spotify information from the network! Error fetching Spotify information from the network! diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 1e15f3b45..5137dfcd0 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -1668,12 +1668,12 @@ connect and stream from you? SpotifyPlaylistUpdater - + Delete in Spotify? ¿Borrar en Spotify? - + Would you like to delete the corresponding Spotify playlist as well? Quieres eliminar la lista de Spotify correspondiente, también? @@ -3028,7 +3028,7 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::SpotifyParser - + Error fetching Spotify information from the network! Error al buscar la información de Spotify en la red! diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 278c8e8e0..62e675a3c 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -1670,12 +1670,12 @@ de se connecter et streamer de vous? SpotifyPlaylistUpdater - + Delete in Spotify? Supprimer dans Spotify ? - + Would you like to delete the corresponding Spotify playlist as well? Voulez-vous aussi supprimer la liste de lecture correspondante dans Spotify ? @@ -3030,7 +3030,7 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::SpotifyParser - + Error fetching Spotify information from the network! Échec du chargement des informations Spotify depuis le réseau! diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index e662672b0..19c1685c7 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -1670,12 +1670,12 @@ connect and stream from you? SpotifyPlaylistUpdater - + Delete in Spotify? Spotifyにも削除しますか? - + Would you like to delete the corresponding Spotify playlist as well? 同期のSpotifyのプレイリストも削除してもよろしいですか? @@ -3020,7 +3020,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::SpotifyParser - + Error fetching Spotify information from the network! diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index 6184f3d42..4b7d2cfd6 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -1668,12 +1668,12 @@ connect and stream from you? SpotifyPlaylistUpdater - + Delete in Spotify? - + Would you like to delete the corresponding Spotify playlist as well? @@ -3024,7 +3024,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::SpotifyParser - + Error fetching Spotify information from the network! diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 8be3fe8aa..d33e59fa4 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -1670,12 +1670,12 @@ se conecte e faça o stream de você? SpotifyPlaylistUpdater - + Delete in Spotify? Deletar no Spotify? - + Would you like to delete the corresponding Spotify playlist as well? Você também gostaria de deletar a playlist correspondente no Spotify? @@ -3030,7 +3030,7 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::SpotifyParser - + Error fetching Spotify information from the network! Erro ao obter informações do Spotify pela rede! diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index 2bd49de0c..7d42754ce 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -473,12 +473,12 @@ connect and stream from you? &Copy to Clipboard - + &Скопировать в буфер обмена Open &Log-file - + Открыть Log файл @@ -1669,12 +1669,12 @@ connect and stream from you? SpotifyPlaylistUpdater - + Delete in Spotify? Удалить в Spotify? - + Would you like to delete the corresponding Spotify playlist as well? @@ -2034,7 +2034,7 @@ connect and stream from you? Log Out - + Выйти @@ -3024,7 +3024,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::SpotifyParser - + Error fetching Spotify information from the network! Возникла ошибка при получении информации из Spotify! diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index ad6f19cb3..7babac319 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -1669,12 +1669,12 @@ connect and stream from you? SpotifyPlaylistUpdater - + Delete in Spotify? - + Would you like to delete the corresponding Spotify playlist as well? @@ -3019,7 +3019,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::SpotifyParser - + Error fetching Spotify information from the network! diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 2243d4b64..e167967a9 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -1668,12 +1668,12 @@ connect and stream from you? SpotifyPlaylistUpdater - + Delete in Spotify? - + Would you like to delete the corresponding Spotify playlist as well? @@ -3018,7 +3018,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::SpotifyParser - + Error fetching Spotify information from the network! diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index 629cf3e86..010057964 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -1668,12 +1668,12 @@ connect and stream from you? SpotifyPlaylistUpdater - + Delete in Spotify? - + Would you like to delete the corresponding Spotify playlist as well? @@ -3018,7 +3018,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::SpotifyParser - + Error fetching Spotify information from the network! diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index 9366a8bbb..3f38ca231 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -1668,12 +1668,12 @@ connect and stream from you? SpotifyPlaylistUpdater - + Delete in Spotify? - + Would you like to delete the corresponding Spotify playlist as well? @@ -3018,7 +3018,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::SpotifyParser - + Error fetching Spotify information from the network! diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index 3059f9815..b39793890 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -121,7 +121,6 @@ CatalogManager::catalogs() const EchonestGenerator::EchonestGenerator ( QObject* parent ) : GeneratorInterface ( parent ) , m_dynPlaylist( new Echonest::DynamicPlaylist() ) - , m_steeredSinceLastTrack( false ) { m_type = "echonest"; m_mode = OnDemand; @@ -135,6 +134,13 @@ EchonestGenerator::EchonestGenerator ( QObject* parent ) EchonestGenerator::~EchonestGenerator() { + if ( !m_dynPlaylist->sessionId().isNull() ) + { + // Running session, delete it + QNetworkReply* deleteReply = m_dynPlaylist->deleteSession(); + connect( deleteReply, SIGNAL( finished() ), deleteReply, SLOT( deleteLater() ) ); + } + delete m_dynPlaylist; } @@ -196,6 +202,13 @@ EchonestGenerator::generate( int number ) void EchonestGenerator::startOnDemand() { + if ( !m_dynPlaylist->sessionId().isNull() ) + { + // Running session, delete it + QNetworkReply* deleteReply = m_dynPlaylist->deleteSession(); + connect( deleteReply, SIGNAL( finished() ), deleteReply, SLOT( deleteLater() ) ); + } + connect( this, SIGNAL( paramsGenerated( Echonest::DynamicPlaylist::PlaylistParams ) ), this, SLOT( doStartOnDemand( Echonest::DynamicPlaylist::PlaylistParams ) ) ); try { getParams(); @@ -227,7 +240,7 @@ EchonestGenerator::doStartOnDemand( const Echonest::DynamicPlaylist::PlaylistPar { disconnect( this, SIGNAL( paramsGenerated( Echonest::DynamicPlaylist::PlaylistParams ) ), this, SLOT( doStartOnDemand( Echonest::DynamicPlaylist::PlaylistParams ) ) ); - QNetworkReply* reply = m_dynPlaylist->start( params ); + QNetworkReply* reply = m_dynPlaylist->create( params ); qDebug() << "starting a dynamic playlist from echonest!" << reply->url().toString(); connect( reply, SIGNAL( finished() ), this, SLOT( dynamicStarted() ) ); } @@ -242,14 +255,15 @@ EchonestGenerator::fetchNext( int rating ) return; } - QNetworkReply* reply; - if( m_steeredSinceLastTrack ) { - qDebug() << "Steering dynamic playlist!" << m_steerData.first << m_steerData.second; - reply = m_dynPlaylist->fetchNextSong( Echonest::DynamicPlaylist::DynamicControls() << m_steerData ); - m_steeredSinceLastTrack = false; - } else { - reply = m_dynPlaylist->fetchNextSong( rating ); + if ( rating > -1 ) + { + Echonest::DynamicPlaylist::DynamicFeedback feedback; + feedback.append( Echonest::DynamicPlaylist::DynamicFeedbackParamData( Echonest::DynamicPlaylist::RateSong, QString( "last^%1").arg( rating * 2 ).toUtf8() ) ); + QNetworkReply* reply = m_dynPlaylist->feedback( feedback ); + connect( reply, SIGNAL( finished() ), reply, SLOT( deleteLater() ) ); // we don't care about the result, just send it off } + + QNetworkReply* reply = m_dynPlaylist->next( 1, 0 ); qDebug() << "getting next song from echonest" << reply->url().toString(); connect( reply, SIGNAL( finished() ), this, SLOT( dynamicFetched() ) ); } @@ -371,9 +385,8 @@ EchonestGenerator::dynamicStarted() try { - Echonest::Song song = m_dynPlaylist->parseStart( reply ); - query_ptr songQuery = queryFromSong( song ); - emit nextTrackGenerated( songQuery ); + m_dynPlaylist->parseCreate( reply ); + fetchNext(); } catch( const Echonest::ParseError& e ) { qWarning() << "libechonest threw an error parsing the start of the dynamic playlist:" << e.errorType() << e.what(); emit error( "The Echo Nest returned an error starting the station", e.what() ); @@ -388,12 +401,18 @@ EchonestGenerator::dynamicFetched() Q_ASSERT( qobject_cast< QNetworkReply* >( sender() ) ); QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() ); - resetSteering(); - try { - Echonest::Song song = m_dynPlaylist->parseNextSong( reply ); - query_ptr songQuery = queryFromSong( song ); + Echonest::DynamicPlaylist::FetchPair fetched = m_dynPlaylist->parseNext( reply ); + + if ( fetched.first.size() != 1 ) + { + qWarning() << "Did not get any track when looking up the next song from the echo nest!"; + emit error( "No more songs from The Echo Nest available in the station", "" ); + return; + } + + query_ptr songQuery = queryFromSong( fetched.first.first() ); emit nextTrackGenerated( songQuery ); } catch( const Echonest::ParseError& e ) { qWarning() << "libechonest threw an error parsing the next song of the dynamic playlist:" << e.errorType() << e.what(); @@ -402,33 +421,6 @@ EchonestGenerator::dynamicFetched() } -void -EchonestGenerator::steerDescription( const QString& desc ) -{ - m_steeredSinceLastTrack = true; - m_steerData.first = Echonest::DynamicPlaylist::SteerDescription; - m_steerData.second = desc; -} - - -void -EchonestGenerator::steerField( const QString& field ) -{ - m_steeredSinceLastTrack = true; - m_steerData.first = Echonest::DynamicPlaylist::Steer; - m_steerData.second = field; -} - - -void -EchonestGenerator::resetSteering() -{ - m_steeredSinceLastTrack = false; - m_steerData.first = Echonest::DynamicPlaylist::Steer; - m_steerData.second = QString(); -} - - QByteArray EchonestGenerator::catalogId(const QString &collectionId) { @@ -508,21 +500,6 @@ EchonestGenerator::queryFromSong( const Echonest::Song& song ) } -QWidget* -EchonestGenerator::steeringWidget() -{ - if( m_steerer.isNull() ) { - m_steerer = QWeakPointer< EchonestSteerer >( new EchonestSteerer ); - - connect( m_steerer.data(), SIGNAL( steerField( QString ) ), this, SLOT( steerField( QString ) ) ); - connect( m_steerer.data(), SIGNAL( steerDescription( QString ) ), this, SLOT( steerDescription( QString ) ) ); - connect( m_steerer.data(), SIGNAL( reset() ), this, SLOT( resetSteering() ) ); - } - - return m_steerer.data(); -} - - QString EchonestGenerator::sentenceSummary() { diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h index 231415eb7..0e79820da 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h @@ -77,8 +77,8 @@ public: virtual void startOnDemand(); virtual void fetchNext( int rating = -1 ); virtual QString sentenceSummary(); - virtual bool onDemandSteerable() const { return true; } - virtual QWidget* steeringWidget(); + virtual bool onDemandSteerable() const { return false; } + virtual QWidget* steeringWidget() { return 0; } Q_INVOKABLE static QStringList styles(); static QStringList moods(); @@ -94,11 +94,6 @@ private slots: void dynamicStarted(); void dynamicFetched(); - // steering controls - void steerField( const QString& field ); - void steerDescription( const QString& desc ); - void resetSteering(); - void doGenerate( const Echonest::DynamicPlaylist::PlaylistParams& params ); void doStartOnDemand( const Echonest::DynamicPlaylist::PlaylistParams& params ); @@ -131,10 +126,6 @@ private: // used for the intermediary song id lookup QSet< QNetworkReply* > m_waiting; Echonest::DynamicPlaylist::PlaylistParams m_storedParams; - - QWeakPointer m_steerer; - bool m_steeredSinceLastTrack; - Echonest::DynamicPlaylist::DynamicControl m_steerData; }; }; diff --git a/src/libtomahawk/thirdparty/Qocoa/qocoa_mac.h b/src/libtomahawk/thirdparty/Qocoa/qocoa_mac.h index 1b6963d11..ee620a3a5 100644 --- a/src/libtomahawk/thirdparty/Qocoa/qocoa_mac.h +++ b/src/libtomahawk/thirdparty/Qocoa/qocoa_mac.h @@ -30,7 +30,8 @@ THE SOFTWARE. static inline NSString* fromQString(const QString &string) { - char* cString = string.toUtf8().data(); + const QByteArray utf8 = string.toUtf8(); + const char* cString = utf8.constData(); return [[NSString alloc] initWithUTF8String:cString]; } @@ -41,11 +42,17 @@ static inline QString toQString(NSString *string) return QString::fromUtf8([string UTF8String]); } -static inline void zeroLayout(void *cocoaView, QWidget *parent) +static inline NSImage* fromQPixmap(const QPixmap &pixmap) { + CGImageRef cgImage = pixmap.toMacCGImageRef(); + return [[NSImage alloc] initWithCGImage:cgImage size:NSZeroSize]; +} + +static inline void setupLayout(void *cocoaView, QWidget *parent) +{ + parent->setAttribute(Qt::WA_NativeWindow); QVBoxLayout *layout = new QVBoxLayout(parent); layout->setMargin(0); - parent->setAttribute(Qt::WA_NativeWindow); layout->addWidget(new QMacCocoaViewContainer(cocoaView, parent)); } diff --git a/src/libtomahawk/thirdparty/Qocoa/qsearchfield.cpp b/src/libtomahawk/thirdparty/Qocoa/qsearchfield.cpp index ee267ad81..a1aa829e2 100644 --- a/src/libtomahawk/thirdparty/Qocoa/qsearchfield.cpp +++ b/src/libtomahawk/thirdparty/Qocoa/qsearchfield.cpp @@ -29,10 +29,13 @@ THE SOFTWARE. #include "widgets/searchlineedit/SearchLineEdit.h" #include "utils/TomahawkUtilsGui.h" -class DLLEXPORT QSearchFieldPrivate +class DLLEXPORT QSearchFieldPrivate : public QObject { + Q_OBJECT public: - QSearchFieldPrivate(SearchLineEdit *lineEdit) : lineEdit(lineEdit) {} + QSearchFieldPrivate(SearchLineEdit *lineEdit) : QObject( lineEdit ), lineEdit(lineEdit) {} + virtual ~QSearchFieldPrivate() {} + SearchLineEdit *lineEdit; }; @@ -44,7 +47,7 @@ QSearchField::QSearchField(QWidget *parent) : QWidget(parent) connect(lineEdit, SIGNAL(returnPressed()), this, SIGNAL(returnPressed())); - pimpl = new QSearchFieldPrivate(lineEdit); + pimpl = QPointer< QSearchFieldPrivate>( new QSearchFieldPrivate( lineEdit ) ); QVBoxLayout *layout = new QVBoxLayout(this); layout->addWidget(lineEdit); @@ -65,20 +68,81 @@ QSearchField::QSearchField(QWidget *parent) : QWidget(parent) void QSearchField::setText(const QString &text) { - pimpl->lineEdit->setText(text); + Q_ASSERT( !pimpl.isNull() ); + if ( pimpl.isNull() ) + return; + + pimpl.data()->lineEdit->setText(text); } void QSearchField::setPlaceholderText(const QString& text) { - pimpl->lineEdit->setInactiveText( text ); + Q_ASSERT( !pimpl.isNull() ); + if ( pimpl.isNull() ) + return; + + pimpl.data()->lineEdit->setInactiveText( text ); } void QSearchField::clear() { - pimpl->lineEdit->clear(); + Q_ASSERT( !pimpl.isNull() ); + if ( pimpl.isNull() ) + return; + + pimpl.data()->lineEdit->clear(); } QString QSearchField::text() const { - return pimpl->lineEdit->text(); + Q_ASSERT( !pimpl.isNull() ); + if ( pimpl.isNull() ) + return QString(); + + return pimpl.data()->lineEdit->text(); } + +QString QSearchField::placeholderText() const +{ + Q_ASSERT( !pimpl.isNull() ); + if ( pimpl.isNull() ) + return QString(); + + return pimpl.data()->lineEdit->placeholderText(); +} + +void QSearchField::selectAll() +{ + Q_ASSERT( !pimpl.isNull() ); + if ( pimpl.isNull() ) + return; + + pimpl.data()->lineEdit->selectAll(); +} + +void QSearchField::setFocus() +{ + Q_ASSERT( !pimpl.isNull() ); + if ( pimpl.isNull() ) + return; + + pimpl.data()->lineEdit->setFocus(); +} + +void QSearchField::setFocus(Qt::FocusReason reason) +{ + Q_ASSERT( !pimpl.isNull() ); + if ( pimpl.isNull() ) + return; + + pimpl.data()->lineEdit->setFocus(reason); +} + + +void QSearchField::resizeEvent(QResizeEvent* e) +{ + QWidget::resizeEvent(e); +} + + +#include "qsearchfield.moc" diff --git a/src/libtomahawk/thirdparty/Qocoa/qsearchfield.h b/src/libtomahawk/thirdparty/Qocoa/qsearchfield.h index e9371ba96..0b429972f 100644 --- a/src/libtomahawk/thirdparty/Qocoa/qsearchfield.h +++ b/src/libtomahawk/thirdparty/Qocoa/qsearchfield.h @@ -2,6 +2,7 @@ #define QSEARCHFIELD_H #include +#include #include "DllMacro.h" @@ -9,23 +10,34 @@ class QSearchFieldPrivate; class DLLEXPORT QSearchField : public QWidget { Q_OBJECT + + Q_PROPERTY(QString placeholderText READ placeholderText WRITE setPlaceholderText); + public: - explicit QSearchField(QWidget* parent); + explicit QSearchField(QWidget *parent); QString text() const; + QString placeholderText() const; + void setFocus(Qt::FocusReason); public slots: void setText(const QString &text); void setPlaceholderText(const QString& text); - void clear(); + void selectAll(); + void setFocus(); + signals: void textChanged(const QString &text); + void editingFinished(); void returnPressed(); +protected: + void resizeEvent(QResizeEvent*); + private: friend class QSearchFieldPrivate; - QSearchFieldPrivate *pimpl; + QPointer pimpl; }; #endif // QSEARCHFIELD_H diff --git a/src/libtomahawk/thirdparty/Qocoa/qsearchfield_mac.mm b/src/libtomahawk/thirdparty/Qocoa/qsearchfield_mac.mm index efa05f631..40b109849 100644 --- a/src/libtomahawk/thirdparty/Qocoa/qsearchfield_mac.mm +++ b/src/libtomahawk/thirdparty/Qocoa/qsearchfield_mac.mm @@ -30,42 +30,105 @@ THE SOFTWARE. #import "Foundation/NSNotification.h" #import "AppKit/NSSearchField.h" -class QSearchFieldPrivate +#include +#include + +#define KEYCODE_A 0 +#define KEYCODE_X 7 +#define KEYCODE_C 8 +#define KEYCODE_V 9 + +class QSearchFieldPrivate : public QObject { public: QSearchFieldPrivate(QSearchField *qSearchField, NSSearchField *nsSearchField) - : qSearchField(qSearchField), nsSearchField(nsSearchField) {} + : QObject(qSearchField), qSearchField(qSearchField), nsSearchField(nsSearchField) {} void textDidChange(const QString &text) { - emit qSearchField->textChanged(text); + if (qSearchField) + emit qSearchField->textChanged(text); } void textDidEndEditing() { - emit qSearchField->returnPressed(); + if (qSearchField) + emit qSearchField->editingFinished(); } - QSearchField *qSearchField; + void returnPressed() + { + if (qSearchField) + emit qSearchField->returnPressed(); + } + + QPointer qSearchField; NSSearchField *nsSearchField; }; @interface QSearchFieldDelegate : NSObject { @public - QSearchFieldPrivate* pimpl; + QPointer pimpl; } -(void)controlTextDidChange:(NSNotification*)notification; --(void)controlTextDidEndEditing:(NSNotification*)aNotification; +-(void)controlTextDidEndEditing:(NSNotification*)notification; @end @implementation QSearchFieldDelegate -(void)controlTextDidChange:(NSNotification*)notification { - pimpl->textDidChange(toQString([[notification object] stringValue])); + Q_ASSERT(pimpl); + if (pimpl) + pimpl->textDidChange(toQString([[notification object] stringValue])); } -(void)controlTextDidEndEditing:(NSNotification*)notification { - pimpl->textDidEndEditing(); + Q_UNUSED(notification); + // No Q_ASSERT here as it is called on destruction. + if (pimpl) + pimpl->textDidEndEditing(); + + if ([[[notification userInfo] objectForKey:@"NSTextMovement"] intValue] == NSReturnTextMovement) + pimpl->returnPressed(); +} +@end + +@interface QocoaSearchField : NSSearchField +-(BOOL)performKeyEquivalent:(NSEvent*)event; +@end + +@implementation QocoaSearchField +-(BOOL)performKeyEquivalent:(NSEvent*)event { + if ([event type] == NSKeyDown && [event modifierFlags] & NSCommandKeyMask) + { + const unsigned short keyCode = [event keyCode]; + if (keyCode == KEYCODE_A) + { + [self performSelector:@selector(selectText:)]; + return YES; + } + else if (keyCode == KEYCODE_C) + { + QClipboard* clipboard = QApplication::clipboard(); + clipboard->setText(toQString([self stringValue])); + return YES; + } + else if (keyCode == KEYCODE_V) + { + QClipboard* clipboard = QApplication::clipboard(); + [self setStringValue:fromQString(clipboard->text())]; + return YES; + } + else if (keyCode == KEYCODE_X) + { + QClipboard* clipboard = QApplication::clipboard(); + clipboard->setText(toQString([self stringValue])); + [self setStringValue:@""]; + return YES; + } + } + + return NO; } @end @@ -73,27 +136,30 @@ QSearchField::QSearchField(QWidget *parent) : QWidget(parent) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSSearchField *search = [[NSSearchField alloc] init]; - pimpl = new QSearchFieldPrivate(this, search); + NSSearchField *search = [[QocoaSearchField alloc] init]; QSearchFieldDelegate *delegate = [[QSearchFieldDelegate alloc] init]; - delegate->pimpl = pimpl; + pimpl = delegate->pimpl = new QSearchFieldPrivate(this, search); [search setDelegate:delegate]; - zeroLayout(search, this); + setupLayout(search, this); + + setFixedHeight(24); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); layout()->setContentsMargins(2, 0, 2, 0); setStyleSheet( "* { background: #DDE4EB; }" ); - setMinimumSize(layout()->sizeHint().width(), 20); - [search release]; [pool drain]; } void QSearchField::setText(const QString &text) { + Q_ASSERT(pimpl); + if (!pimpl) + return; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; [pimpl->nsSearchField setStringValue:fromQString(text)]; [pool drain]; @@ -101,6 +167,10 @@ void QSearchField::setText(const QString &text) void QSearchField::setPlaceholderText(const QString& text) { + Q_ASSERT(pimpl); + if (!pimpl) + return; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; [[pimpl->nsSearchField cell] setPlaceholderString:fromQString(text)]; [pool drain]; @@ -108,10 +178,57 @@ void QSearchField::setPlaceholderText(const QString& text) void QSearchField::clear() { + Q_ASSERT(pimpl); + if (!pimpl) + return; + [pimpl->nsSearchField setStringValue:@""]; + emit textChanged(QString()); +} + +void QSearchField::selectAll() +{ + Q_ASSERT(pimpl); + if (!pimpl) + return; + + [pimpl->nsSearchField performSelector:@selector(selectText:)]; } QString QSearchField::text() const { + Q_ASSERT(pimpl); + if (!pimpl) + return QString(); + return toQString([pimpl->nsSearchField stringValue]); } + +QString QSearchField::placeholderText() const +{ + Q_ASSERT(pimpl); + if (!pimpl) + return QString(); + + return toQString([[pimpl->nsSearchField cell] placeholderString]); +} + +void QSearchField::setFocus(Qt::FocusReason reason) +{ + Q_ASSERT(pimpl); + if (!pimpl) + return; + + if ([pimpl->nsSearchField acceptsFirstResponder]) + [[pimpl->nsSearchField window] makeFirstResponder: pimpl->nsSearchField]; +} + +void QSearchField::setFocus() +{ + setFocus(Qt::OtherFocusReason); +} + +void QSearchField::resizeEvent(QResizeEvent *resizeEvent) +{ + QWidget::resizeEvent(resizeEvent); +}