diff --git a/data/images/account-settings.png b/data/images/account-settings.png new file mode 100644 index 000000000..19fddebe8 Binary files /dev/null and b/data/images/account-settings.png differ diff --git a/data/images/advanced-settings.png b/data/images/advanced-settings.png new file mode 100644 index 000000000..9d3ebb405 Binary files /dev/null and b/data/images/advanced-settings.png differ diff --git a/data/images/lastfm-settings.png b/data/images/lastfm-settings.png new file mode 100644 index 000000000..ff57ab5a3 Binary files /dev/null and b/data/images/lastfm-settings.png differ diff --git a/data/images/music-settings.png b/data/images/music-settings.png new file mode 100644 index 000000000..3dd854027 Binary files /dev/null and b/data/images/music-settings.png differ diff --git a/data/images/resolvers-settings.png b/data/images/resolvers-settings.png new file mode 100644 index 000000000..28f88d2b5 Binary files /dev/null and b/data/images/resolvers-settings.png differ diff --git a/data/images/sipplugin-offline.png b/data/images/sipplugin-offline.png new file mode 100644 index 000000000..fd50901be Binary files /dev/null and b/data/images/sipplugin-offline.png differ diff --git a/data/images/sipplugin-online.png b/data/images/sipplugin-online.png new file mode 100644 index 000000000..1221e1310 Binary files /dev/null and b/data/images/sipplugin-online.png differ diff --git a/include/tomahawk/tomahawkapp.h b/include/tomahawk/tomahawkapp.h index 1547254dd..d814ea32d 100644 --- a/include/tomahawk/tomahawkapp.h +++ b/include/tomahawk/tomahawkapp.h @@ -82,7 +82,7 @@ public: void init(); static TomahawkApp* instance(); - SipHandler* sipHandler() { return m_sipHandler; } + SipHandler* sipHandler(); XMPPBot* xmppBot() { return m_xmppBot; } #ifndef TOMAHAWK_HEADLESS @@ -119,7 +119,6 @@ private: Database* m_database; ScanManager *m_scanManager; AudioEngine* m_audioEngine; - SipHandler* m_sipHandler; Servent* m_servent; Tomahawk::InfoSystem::InfoSystem* m_infoSystem; XMPPBot* m_xmppBot; diff --git a/resources.qrc b/resources.qrc index acaac9674..6d0f9b26f 100644 --- a/resources.qrc +++ b/resources.qrc @@ -79,6 +79,13 @@ ./data/images/add.png ./data/images/recently-played.png ./data/images/supercollection.png +./data/images/sipplugin-online.png +./data/images/sipplugin-offline.png +./data/images/advanced-settings.png +./data/images/account-settings.png +./data/images/music-settings.png +./data/images/resolvers-settings.png +./data/images/lastfm-settings.png ./data/topbar-radiobuttons.css ./data/icons/tomahawk-icon-16x16.png ./data/icons/tomahawk-icon-32x32.png diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5186ddd2b..45cf481d1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -64,6 +64,8 @@ SET( tomahawkSourcesGui ${tomahawkSourcesGui} tomahawktrayicon.cpp audiocontrols.cpp settingsdialog.cpp + configdelegatebase.cpp + sipconfigdelegate.cpp resolverconfigdelegate.cpp resolversmodel.cpp tomahawkwindow.cpp @@ -104,15 +106,18 @@ SET( tomahawkHeadersGui ${tomahawkHeadersGui} tomahawktrayicon.h audiocontrols.h settingsdialog.h + configdelegatebase.h resolverconfigdelegate.h + sipconfigdelegate.h resolversmodel.h - resolverconfigwrapper.h + delegateconfigwrapper.h tomahawkwindow.h ) SET( tomahawkUI ${tomahawkUI} tomahawkwindow.ui settingsdialog.ui + stackedsettingsdialog.ui proxydialog.ui audiocontrols.ui diff --git a/src/configdelegatebase.cpp b/src/configdelegatebase.cpp new file mode 100644 index 000000000..2ddd010c6 --- /dev/null +++ b/src/configdelegatebase.cpp @@ -0,0 +1,120 @@ +/* + Copyright (C) 2011 Leo Franchi + + 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 3 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. If not, see . +*/ + + +#include "configdelegatebase.h" + +#include "utils/tomahawkutils.h" + +#include +#include +#include + +ConfigDelegateBase::ConfigDelegateBase ( QObject* parent ) + : QStyledItemDelegate ( parent ) + , m_configPressed( false ) +{ + +} + + +QSize +ConfigDelegateBase::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const +{ + int width = QStyledItemDelegate::sizeHint( option, index ).width(); + + QStyleOptionViewItemV4 opt = option; + initStyleOption( &opt, index ); + + + QFont name = opt.font; + name.setPointSize( name.pointSize() + 2 ); + name.setBold( true ); + + QFont path = opt.font; + path.setItalic( true ); + path.setPointSize( path.pointSize() - 1 ); + + + QFontMetrics bfm( name ); + QFontMetrics sfm( path ); + return QSize( width, 2 * PADDING + bfm.height() + sfm.height() ); +} + +void +ConfigDelegateBase::drawCheckBox( QStyleOptionViewItemV4& opt, QPainter* p, const QWidget* w ) const +{ + m_checkRect = opt.rect; + QStyle* style = w ? w->style() : QApplication::style(); + opt.checkState == Qt::Checked ? opt.state |= QStyle::State_On : opt.state |= QStyle::State_Off; + style->drawPrimitive( QStyle::PE_IndicatorViewItemCheck, &opt, p, w ); +} + + +void +ConfigDelegateBase::drawConfigWrench ( QPainter* painter, QStyleOptionViewItemV4& opt, QStyleOptionToolButton& topt ) const +{ + const QWidget* w = opt.widget; + QStyle* style = w ? w->style() : QApplication::style(); + + // draw it the same size as the check belox + topt.font = opt.font; + topt.icon = QIcon( RESPATH "images/configure.png" ); + topt.iconSize = QSize( 16, 16 ); + topt.subControls = QStyle::SC_ToolButton; + topt.activeSubControls = QStyle::SC_None; + topt.features = QStyleOptionToolButton::None; + topt.state = m_configPressed ? QStyle::State_On : QStyle::State_Raised; + if( opt.state & QStyle::State_MouseOver || m_configPressed ) + topt.state |= QStyle::State_HasFocus; + style->drawComplexControl( QStyle::CC_ToolButton, &topt, painter, w ); +} + +bool +ConfigDelegateBase::editorEvent ( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ) +{ + QStyleOptionViewItemV4 viewOpt( option ); + initStyleOption( &viewOpt, index ); + + if( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseButtonDblClick ) { + m_configPressed = false; + + QMouseEvent* me = static_cast< QMouseEvent* >( event ); + if( me->button() != Qt::LeftButton || !m_checkRect.contains( me->pos() ) ) + return false; + + // eat the double click events inside the check rect + if( event->type() == QEvent::MouseButtonDblClick ) { + return true; + } + + Qt::CheckState curState = static_cast< Qt::CheckState >( index.data( Qt::CheckStateRole ).toInt() ); + Qt::CheckState newState = curState == Qt::Checked ? Qt::Unchecked : Qt::Checked; + return model->setData( index, newState, Qt::CheckStateRole ); + + } else if( event->type() == QEvent::MouseButtonPress ) { + QMouseEvent* me = static_cast< QMouseEvent* >( event ); + if( me->button() == Qt::LeftButton && configRectForIndex( option, index ).contains( me->pos() ) ) { + m_configPressed = true; + + emit configPressed( index ); + return true; + } + } + + return QStyledItemDelegate::editorEvent( event, model, option, index ); +} diff --git a/src/configdelegatebase.h b/src/configdelegatebase.h new file mode 100644 index 000000000..c9f01b864 --- /dev/null +++ b/src/configdelegatebase.h @@ -0,0 +1,54 @@ +/* + Copyright (C) 2011 Leo Franchi + + 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 3 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. If not, see . +*/ + + +#ifndef CONFIGDELEGATEBASE_H +#define CONFIGDELEGATEBASE_H + +#include "dllmacro.h" + +#include + +#define PADDING 4 + +class QPainter; +class DLLEXPORT ConfigDelegateBase : public QStyledItemDelegate +{ + Q_OBJECT +public: + ConfigDelegateBase( QObject* parent = 0 ); + virtual QSize sizeHint ( const QStyleOptionViewItem& option, const QModelIndex& index ) const; + + virtual bool editorEvent ( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ); + + // if you want to use a config wrench, you need to have this say where to paint it + virtual QRect configRectForIndex( const QStyleOptionViewItem& option, const QModelIndex& idx ) const = 0; + +signals: + void configPressed( const QModelIndex& idx ); + +protected: + void drawCheckBox( QStyleOptionViewItemV4& opt, QPainter* p, const QWidget* w ) const; + void drawConfigWrench( QPainter* painter, QStyleOptionViewItemV4& option, QStyleOptionToolButton& topt ) const; + +private: + mutable QRect m_checkRect; + mutable QRect m_configRect; + bool m_configPressed; +}; + +#endif // CONFIGDELEGATEBASE_H diff --git a/src/resolverconfigwrapper.h b/src/delegateconfigwrapper.h similarity index 89% rename from src/resolverconfigwrapper.h rename to src/delegateconfigwrapper.h index e43700e38..0476ea8c2 100644 --- a/src/resolverconfigwrapper.h +++ b/src/delegateconfigwrapper.h @@ -22,14 +22,16 @@ #include #include -class ResolverConfigWrapper : public QDialog +class DelegateConfigWrapper : public QDialog { Q_OBJECT public: - ResolverConfigWrapper( QWidget* conf, const QString& title, QWidget* parent ) : QDialog( parent ), m_widget( conf ) + DelegateConfigWrapper( QWidget* conf, const QString& title, QWidget* parent ) : QDialog( parent ), m_widget( conf ) { - setWindowTitle( title ); + m_widget->setVisible( true ); + m_widget->setWindowFlags( Qt::Sheet ); + setWindowTitle( title ); QVBoxLayout* v = new QVBoxLayout( this ); v->addWidget( m_widget ); @@ -46,6 +48,7 @@ public slots: // let the config widget live to see another day layout()->removeWidget( m_widget ); m_widget->setParent( 0 ); + m_widget->setVisible( false ); QDialogButtonBox* buttons = qobject_cast< QDialogButtonBox* >( sender() ); if( buttons->standardButton( b ) == QDialogButtonBox::Ok ) @@ -59,6 +62,7 @@ public slots: { layout()->removeWidget( m_widget ); m_widget->setParent( 0 ); + m_widget->setVisible( false ); } private: diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 3e1d9e258..aab9a3e60 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -33,6 +33,7 @@ set( libSources sip/SipPlugin.cpp sip/SipHandler.cpp + sip/SipModel.cpp audio/audioengine.cpp @@ -193,6 +194,7 @@ set( libHeaders sip/SipPlugin.h sip/SipHandler.h + sip/SipModel.h audio/audioengine.h diff --git a/src/libtomahawk/sip/SipHandler.cpp b/src/libtomahawk/sip/SipHandler.cpp index a3b7b0375..bf0408707 100644 --- a/src/libtomahawk/sip/SipHandler.cpp +++ b/src/libtomahawk/sip/SipHandler.cpp @@ -42,6 +42,8 @@ SipHandler* SipHandler::s_instance = 0; SipHandler* SipHandler::instance() { + if( s_instance == 0 ) + s_instance = new SipHandler( 0 ); return s_instance; } @@ -51,7 +53,7 @@ SipHandler::SipHandler( QObject* parent ) { s_instance = this; - loadPlugins( findPlugins() ); + loadPluginFactories( findPluginFactories() ); connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( onSettingsChanged() ) ); } @@ -59,14 +61,7 @@ SipHandler::SipHandler( QObject* parent ) SipHandler::~SipHandler() { - disconnectPlugins(); -} - - -QList< SipPlugin* > -SipHandler::plugins() const -{ - return m_plugins; + disconnectAll(); } const QPixmap SipHandler::avatar( const QString& name ) const @@ -95,7 +90,7 @@ SipHandler::onSettingsChanged() QStringList -SipHandler::findPlugins() +SipHandler::findPluginFactories() { QStringList paths; QList< QDir > pluginDirs; @@ -137,7 +132,7 @@ SipHandler::findPlugins() void -SipHandler::loadPlugins( const QStringList& paths ) +SipHandler::loadPluginFactories( const QStringList& paths ) { foreach ( QString fileName, paths ) { @@ -145,13 +140,67 @@ SipHandler::loadPlugins( const QStringList& paths ) continue; qDebug() << "Trying to load plugin:" << fileName; - loadPlugin( fileName ); + loadPluginFactory( fileName ); } } +SipPlugin* +SipHandler::createPlugin( const QString& factoryId ) +{ + Q_ASSERT( m_pluginFactories.contains( factoryId ) ); + + SipPlugin* sip = m_pluginFactories[ factoryId ]->createPlugin(); + hookUpPlugin( sip ); + + emit pluginAdded( sip ); + return sip; +} + +SipPlugin* +SipHandler::loadPlugin( const QString& pluginId ) +{ + QString factoryName = factoryFromId( pluginId ); + + Q_ASSERT( m_pluginFactories.contains( factoryName ) ); + + SipPlugin* sip = m_pluginFactories[ factoryName ]->createPlugin( pluginId ); + hookUpPlugin( sip ); + + // caller responsible for calling pluginAdded() + return sip; +} + +void +SipHandler::removePlugin( SipPlugin* p ) +{ + p->disconnectPlugin(); + + m_allPlugins.removeAll( p ); + m_enabledPlugins.removeAll( p ); + + TomahawkSettings::instance()->removeSipPlugin( p->pluginId() ); + + emit pluginRemoved( p ); +} + void -SipHandler::loadPlugin( const QString& path ) +SipHandler::hookUpPlugin( SipPlugin* sip ) +{ + QObject::connect( sip, SIGNAL( peerOnline( QString ) ), SLOT( onPeerOnline( QString ) ) ); + QObject::connect( sip, SIGNAL( peerOffline( QString ) ), SLOT( onPeerOffline( QString ) ) ); + QObject::connect( sip, SIGNAL( msgReceived( QString, QString ) ), SLOT( onMessage( QString, QString ) ) ); + + QObject::connect( sip, SIGNAL( error( int, QString ) ), SLOT( onError( int, QString ) ) ); + QObject::connect( sip, SIGNAL( stateChanged( SipPlugin::ConnectionState ) ), SLOT( onStateChanged( SipPlugin::ConnectionState ) ) ); + + QObject::connect( sip, SIGNAL( avatarReceived( QString, QPixmap ) ), SLOT( onAvatarReceived( QString, QPixmap ) ) ); + QObject::connect( sip, SIGNAL( avatarReceived( QPixmap ) ), SLOT( onAvatarReceived( QPixmap ) ) ); +} + + +void +SipHandler::loadPluginFactory( const QString& path ) { QPluginLoader loader( path ); QObject* plugin = loader.instance(); @@ -160,37 +209,24 @@ SipHandler::loadPlugin( const QString& path ) qDebug() << "Error loading plugin:" << loader.errorString(); } - SipPlugin* sip = qobject_cast(plugin); - if ( sip ) + SipPluginFactory* sipfactory = qobject_cast(plugin); + if ( sipfactory ) { - if ( pluginLoaded( sip->name() ) ) - { - qDebug() << "Plugin" << sip->name() << "already loaded! Not loading:" << loader.fileName(); - return; - } - qDebug() << "Loaded plugin:" << loader.fileName(); - - QObject::connect( sip, SIGNAL( peerOnline( QString ) ), SLOT( onPeerOnline( QString ) ) ); - QObject::connect( sip, SIGNAL( peerOffline( QString ) ), SLOT( onPeerOffline( QString ) ) ); - QObject::connect( sip, SIGNAL( msgReceived( QString, QString ) ), SLOT( onMessage( QString, QString ) ) ); - - QObject::connect( sip, SIGNAL( connected() ), SIGNAL( connected() ) ); - QObject::connect( sip, SIGNAL( disconnected() ), SIGNAL( disconnected() ) ); - QObject::connect( sip, SIGNAL( error( int, QString ) ), SLOT( onError( int, QString ) ) ); - - QObject::connect( sip, SIGNAL( avatarReceived( QString, QPixmap ) ), SLOT( onAvatarReceived( QString, QPixmap ) ) ); - QObject::connect( sip, SIGNAL( avatarReceived( QPixmap ) ), SLOT( onAvatarReceived( QPixmap ) ) ); - m_plugins << sip; + qDebug() << "Loaded plugin factory:" << loader.fileName() << sipfactory->factoryId() << sipfactory->prettyName(); + m_pluginFactories[ sipfactory->factoryId() ] = sipfactory; + } else + { + qDebug() << "Loaded invalid plugin.." << loader.fileName(); } } bool -SipHandler::pluginLoaded( const QString& name ) const +SipHandler::pluginLoaded( const QString& pluginId ) const { - foreach( SipPlugin* plugin, m_plugins ) + foreach( SipPlugin* plugin, m_allPlugins ) { - if ( plugin->name() == name ) + if ( plugin->pluginId() == pluginId ) return true; } @@ -201,15 +237,111 @@ SipHandler::pluginLoaded( const QString& name ) const void SipHandler::checkSettings() { - foreach( SipPlugin* sip, m_plugins ) + foreach( SipPlugin* sip, m_allPlugins ) { sip->checkSettings(); } } +void +SipHandler::addSipPlugin( SipPlugin* p, bool enabled, bool startup ) +{ + m_allPlugins << p; + + if ( enabled ) + { + p->connectPlugin( startup ); + m_enabledPlugins << p; + } + + emit pluginAdded( p ); +} void -SipHandler::connectPlugins( bool startup, const QString &pluginName ) +SipHandler::removeSipPlugin( SipPlugin* p ) +{ + p->disconnectPlugin(); + emit pluginRemoved( p ); + // emit first so sipmodel can find the indexOf + + TomahawkSettings::instance()->removeSipPlugin( p->pluginId() ); + m_allPlugins.removeAll( p ); + m_enabledPlugins.removeAll( p ); +} + +bool +SipHandler::hasPluginType( const QString& factoryId ) const +{ + foreach( SipPlugin* p, m_allPlugins ) { + if( factoryFromId( p->pluginId() ) == factoryId ) + return true; + } + return false; +} + + +void +SipHandler::loadFromConfig( bool startup ) +{ + QStringList pluginIds = TomahawkSettings::instance()->sipPlugins(); + QStringList enabled = TomahawkSettings::instance()->enabledSipPlugins(); + foreach( const QString& pluginId, pluginIds ) + { + QString pluginFactory = factoryFromId( pluginId ); + if( m_pluginFactories.contains( pluginFactory ) ) + { + SipPlugin* p = loadPlugin( pluginId ); + addSipPlugin( p, enabled.contains( pluginId ), startup ); + } + } + m_connected = true; +} + +void +SipHandler::connectAll() +{ + foreach( SipPlugin* sip, m_enabledPlugins ) + { + sip->connectPlugin(); + } + m_connected = true; +} + + +void +SipHandler::disconnectAll() +{ + foreach( SipPlugin* p, m_connectedPlugins ) + p->disconnectPlugin(); + + SourceList::instance()->removeAllRemote(); + m_connected = false; +} + +void +SipHandler::disablePlugin( SipPlugin* p ) +{ + Q_ASSERT( m_enabledPlugins.contains( p ) ); + + TomahawkSettings::instance()->disableSipPlugin( p->pluginId() ); + p->disconnectPlugin(); + + m_enabledPlugins.removeAll( p ); +} + +void +SipHandler::enablePlugin( SipPlugin* p ) +{ + Q_ASSERT( !m_enabledPlugins.contains( p ) ); + p->connectPlugin(); + + TomahawkSettings::instance()->enableSipPlugin( p->pluginId() ); + m_enabledPlugins << p; +} + + +void +SipHandler::connectPlugin( bool startup, const QString &pluginId ) { #ifndef TOMAHAWK_HEADLESS if ( !TomahawkSettings::instance()->acceptedLegalWarning() ) @@ -226,33 +358,49 @@ SipHandler::connectPlugins( bool startup, const QString &pluginName ) TomahawkSettings::instance()->setAcceptedLegalWarning( true ); } #endif - foreach( SipPlugin* sip, m_plugins ) + foreach( SipPlugin* sip, m_allPlugins ) { - if ( pluginName.isEmpty() || ( !pluginName.isEmpty() && sip->name() == pluginName ) ) + if ( sip->pluginId() == pluginId ) + { + Q_ASSERT( m_enabledPlugins.contains( sip ) ); // make sure the plugin we're connecting is enabled. should always be the case sip->connectPlugin( startup ); - } - - if ( pluginName.isEmpty() ) - { - m_connected = true; + } } } void -SipHandler::disconnectPlugins( const QString &pluginName ) +SipHandler::disconnectPlugin( const QString &pluginName ) { - foreach( SipPlugin* sip, m_plugins ) + foreach( SipPlugin* sip, m_connectedPlugins ) { - if ( pluginName.isEmpty() || ( !pluginName.isEmpty() && sip->name() == pluginName ) ) + if ( sip->name() == pluginName ) sip->disconnectPlugin(); } +} - if ( pluginName.isEmpty() ) - { - SourceList::instance()->removeAllRemote(); - m_connected = false; - } +QList< SipPlugin* > +SipHandler::allPlugins() const +{ + return m_allPlugins; +} + +QList< SipPlugin* > +SipHandler::enabledPlugins() const +{ + return m_enabledPlugins; +} + +QList< SipPlugin* > +SipHandler::connectedPlugins() const +{ + return m_connectedPlugins; +} + +QList< SipPluginFactory* > +SipHandler::pluginFactories() const +{ + return m_pluginFactories.values(); } @@ -260,9 +408,9 @@ void SipHandler::toggleConnect() { if( m_connected ) - disconnectPlugins(); + disconnectAll(); else - connectPlugins(); + connectAll(); } @@ -368,20 +516,43 @@ SipHandler::onMessage( const QString& from, const QString& msg ) void SipHandler::onError( int code, const QString& msg ) { - qWarning() << "Failed to connect to SIP:" << code << msg; + SipPlugin* sip = qobject_cast< SipPlugin* >( sender() ); + Q_ASSERT( sip ); + + qWarning() << "Failed to connect to SIP:" << sip->accountName() << code << msg; if ( code == SipPlugin::AuthError ) { - emit authError(); + emit authError( sip ); } else { - SipPlugin* sip = qobject_cast(sender()); QTimer::singleShot( 10000, sip, SLOT( connectPlugin() ) ); } } -void SipHandler::onAvatarReceived( const QString& from, const QPixmap& avatar ) +void +SipHandler::onStateChanged( SipPlugin::ConnectionState state ) +{ + SipPlugin* sip = qobject_cast< SipPlugin* >( sender() ); + Q_ASSERT( sip ); + + if ( sip->connectionState() == SipPlugin::Disconnected ) + { + m_connectedPlugins.removeAll( sip ); + emit disconnected( sip ); + } else if ( sip->connectionState() == SipPlugin::Connected ) + { + m_connectedPlugins.removeAll( sip ); + emit disconnected( sip ); + } + + emit stateChanged( sip, state ); +} + + +void +SipHandler::onAvatarReceived( const QString& from, const QPixmap& avatar ) { qDebug() << Q_FUNC_INFO << "Set avatar on source for " << from; Q_ASSERT(!avatar.isNull()); @@ -413,8 +584,23 @@ void SipHandler::onAvatarReceived( const QString& from, const QPixmap& avatar ) } } -void SipHandler::onAvatarReceived( const QPixmap& avatar ) +void +SipHandler::onAvatarReceived( const QPixmap& avatar ) { qDebug() << Q_FUNC_INFO << "Set own avatar on MyCollection"; SourceList::instance()->getLocal()->setAvatar( avatar ); } + + +QString +SipHandler::factoryFromId( const QString& pluginId ) const +{ + return pluginId.split( "_" ).first(); +} + +SipPluginFactory* +SipHandler::factoryFromPlugin( SipPlugin* p ) const +{ + QString factoryId = factoryFromId( p->pluginId() ); + return m_pluginFactories.value( factoryId, 0 ); +} diff --git a/src/libtomahawk/sip/SipHandler.h b/src/libtomahawk/sip/SipHandler.h index 1c1c6f5bc..e108c1d28 100644 --- a/src/libtomahawk/sip/SipHandler.h +++ b/src/libtomahawk/sip/SipHandler.h @@ -38,28 +38,56 @@ public: SipHandler( QObject* parent ); ~SipHandler(); - QList< SipPlugin* > plugins() const; + QList< SipPluginFactory* > pluginFactories() const; + QList< SipPlugin* > allPlugins() const; + QList< SipPlugin* > enabledPlugins() const; + QList< SipPlugin* > connectedPlugins() const; + void loadFromConfig( bool startup = false ); + + void addSipPlugin( SipPlugin* p, bool enable = true, bool connectImmediately = true ); + void removeSipPlugin( SipPlugin* p ); + + bool hasPluginType( const QString& factoryId ) const; + SipPluginFactory* factoryFromPlugin( SipPlugin* p ) const; const QPixmap avatar( const QString& name ) const; public slots: - void addContact( const QString& id ) { qDebug() << Q_FUNC_INFO << id; } - void checkSettings(); - void connectPlugins( bool startup = false, const QString &pluginName = QString() ); - void disconnectPlugins( const QString &pluginName = QString() ); + + void enablePlugin( SipPlugin* p ); + void disablePlugin( SipPlugin* p ); + + void connectPlugin( bool startup = false, const QString &pluginId = QString() ); + void disconnectPlugin( const QString &pluginId = QString() ); + void connectAll(); + void disconnectAll(); + void toggleConnect(); + // create a new plugin of the given name. the name is the value returned in SipPluginFactory::pluginName + // be default sip plugins are NOt connected when created + SipPlugin* createPlugin( const QString& factoryName ); + // load a plugin with the given id + SipPlugin* loadPlugin( const QString& pluginId ); + void removePlugin( SipPlugin* p ); + signals: - void connected(); - void disconnected(); - void authError(); + void connected( SipPlugin* ); + void disconnected( SipPlugin* ); + void authError( SipPlugin* ); + + void stateChanged( SipPlugin* p, SipPlugin::ConnectionState state ); + + void pluginAdded( SipPlugin* p ); + void pluginRemoved( SipPlugin* p ); private slots: void onMessage( const QString&, const QString& ); void onPeerOffline( const QString& ); void onPeerOnline( const QString& ); void onError( int code, const QString& msg ); + void onStateChanged( SipPlugin::ConnectionState ); void onSettingsChanged(); @@ -73,13 +101,18 @@ private slots: private: static SipHandler *s_instance; - QStringList findPlugins(); - bool pluginLoaded( const QString& name ) const; + QStringList findPluginFactories(); + bool pluginLoaded( const QString& pluginId ) const; + void hookUpPlugin( SipPlugin* p ); - void loadPlugins( const QStringList& paths ); - void loadPlugin( const QString& path ); + void loadPluginFactories( const QStringList& paths ); + void loadPluginFactory( const QString& path ); + QString factoryFromId( const QString& pluginId ) const; - QList< SipPlugin* > m_plugins; + QHash< QString, SipPluginFactory* > m_pluginFactories; + QList< SipPlugin* > m_allPlugins; + QList< SipPlugin* > m_enabledPlugins; + QList< SipPlugin* > m_connectedPlugins; bool m_connected; diff --git a/src/libtomahawk/sip/SipModel.cpp b/src/libtomahawk/sip/SipModel.cpp new file mode 100644 index 000000000..8ac36cbcb --- /dev/null +++ b/src/libtomahawk/sip/SipModel.cpp @@ -0,0 +1,213 @@ +/* + Copyright (C) 2011 Leo Franchi + + 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 3 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. If not, see . +*/ + + +#include "SipModel.h" + +#include "tomahawksettings.h" +#include "tomahawk/tomahawkapp.h" +#include "sip/SipHandler.h" +#include "sip/SipPlugin.h" + +SipModel::SipModel( QObject* parent ) + : QAbstractItemModel( parent ) +{ + connect( SipHandler::instance(), SIGNAL( stateChanged( SipPlugin*, SipPlugin::ConnectionState ) ), this, SLOT( pluginStateChanged( SipPlugin* ) ) ); + connect( SipHandler::instance(), SIGNAL( pluginAdded( SipPlugin* ) ), this, SLOT( pluginAdded( SipPlugin* ) ) ); + connect( SipHandler::instance(), SIGNAL( pluginRemoved( SipPlugin* ) ), this, SLOT( pluginRemoved( SipPlugin* ) ) ); + + // TODO disable inline factories for now + /* + foreach( SipPluginFactory* f, SipHandler::instance()->pluginFactories() ) { + if( f->isCreatable() ) + m_factories << f; + } */ + +} + + +SipModel::~SipModel() +{ + +} + +QVariant +SipModel::data( const QModelIndex& index, int role ) const +{ + if( !index.isValid() ) + return QVariant(); + + if( !index.parent().isValid() && index.row() == SipHandler::instance()->allPlugins().count() ) { // last row, this is the factory + if( role == Qt::DisplayRole ) + return tr( "Add New Account..." ); + else if( role == FactoryRole ) + return true; + else + return QVariant(); + } + + if( !index.parent().isValid() ) { // account + QList< SipPlugin* > plugins = SipHandler::instance()->allPlugins(); + Q_ASSERT( index.row() <= plugins.size() ); + SipPlugin* p = plugins[ index.row() ]; + switch( role ) + { + case Qt::DisplayRole: + case SipModel::PluginName: + return p->accountName(); + case SipModel::ConnectionStateRole: + return p->connectionState(); + case SipModel::HasConfig: + return ( p->configWidget() != 0 ); + case SipModel::FactoryRole: + return false; + case Qt::DecorationRole: + return p->icon(); + case SipModel::SipPluginData: + return QVariant::fromValue< QObject* >( p ); + case Qt::CheckStateRole: + return SipHandler::instance()->enabledPlugins().contains( p ) ? Qt::Checked : Qt::Unchecked; + default: + return QVariant(); + } + } + + if( index.parent().isValid() ) { // this is a factory type + SipPluginFactory* p = m_factories.at( index.row() ); + switch( role ) + { + case Qt::DisplayRole: + return p->prettyName(); + case SipModel::FactoryItemRole: + return true; + case SipModel::FactoryItemIcon: + return p->icon(); + case SipModel::SipPluginFactoryData: + return QVariant::fromValue< QObject* >( p ); + default: + return QVariant(); + } + } + + return QVariant(); +} + +bool +SipModel::setData( const QModelIndex& index, const QVariant& value, int role ) +{ + Q_ASSERT( index.isValid() && index.row() <= SipHandler::instance()->allPlugins().count() ); + + if( role == Qt::CheckStateRole ) { + Qt::CheckState state = static_cast< Qt::CheckState >( value.toInt() ); + QList< SipPlugin* > plugins = SipHandler::instance()->allPlugins(); + SipPlugin* p = plugins[ index.row() ]; + + if( state == Qt::Checked && !SipHandler::instance()->enabledPlugins().contains( p ) ) { + SipHandler::instance()->enablePlugin( p ); + } else if( state == Qt::Unchecked ) { + SipHandler::instance()->disablePlugin( p ); + } + dataChanged( index, index ); + + return true; + } + return false; +} + +QModelIndex +SipModel::index( int row, int column, const QModelIndex& parent ) const +{ + if( !parent.isValid() ) + return hasIndex( row, column, parent ) ? createIndex( row, column, 0 ) : QModelIndex(); + +// qDebug() << "Creating index for non-top level row!"; + // it's a child of the Add Account, e.g. a factory + if( hasIndex( row, column, parent ) ) { + return createIndex( row, column, 1 /* magic */ ); + } + + return QModelIndex(); +} + +QModelIndex +SipModel::parent( const QModelIndex& child ) const +{ + if( !child.isValid() ) + return QModelIndex(); + + if( child.internalId() == 1 ) { + return index( SipHandler::instance()->allPlugins().size(), 0, QModelIndex() ); + } + + return QModelIndex(); +} + +int +SipModel::rowCount( const QModelIndex& parent ) const +{ + if( !parent.isValid() ) // invalid root node + return SipHandler::instance()->allPlugins().size() /* TODO inline factories disabled + 1*/; + if( parent.isValid() && !parent.parent().isValid() ) { // top level item + if( parent.row() == SipHandler::instance()->allPlugins().count() ) {// last row, this is the factory + return m_factories.count(); + } + } + + return 0; +} + +int +SipModel::columnCount(const QModelIndex& parent) const +{ + Q_UNUSED( parent ); + return 1; +} + +Qt::ItemFlags +SipModel::flags( const QModelIndex& index ) const +{ + if( index.data( SipModel::FactoryRole ).toBool() || index.data( SipModel::FactoryItemRole ).toBool() ) + return QAbstractItemModel::flags( index ) & ~Qt::ItemIsSelectable; + return QAbstractItemModel::flags( index ) | Qt::ItemIsUserCheckable; +} + +void +SipModel::pluginAdded( SipPlugin* p ) +{ + // we assume sip plugins are added at the end of the list. + Q_ASSERT( SipHandler::instance()->allPlugins().last() == p ); + int size = SipHandler::instance()->allPlugins().count() - 1; + beginInsertRows( QModelIndex(), size, size ); + endInsertRows(); +} + +void +SipModel::pluginRemoved( SipPlugin* p ) +{ + int idx = SipHandler::instance()->allPlugins().indexOf( p ); + beginRemoveRows( QModelIndex(), idx, idx ); + endRemoveRows(); +} + +void +SipModel::pluginStateChanged( SipPlugin* p ) +{ + int at = SipHandler::instance()->allPlugins().indexOf( p ); + QModelIndex idx = index( at, 0, QModelIndex() ); + emit dataChanged( idx, idx ); +} + diff --git a/src/libtomahawk/sip/SipModel.h b/src/libtomahawk/sip/SipModel.h new file mode 100644 index 000000000..18e15cf0b --- /dev/null +++ b/src/libtomahawk/sip/SipModel.h @@ -0,0 +1,67 @@ +/* + + Copyright (C) 2011 Leo Franchi + + 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 3 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. If not, see . +*/ + + +#ifndef SIPMODEL_H +#define SIPMODEL_H + +#include "dllmacro.h" + +#include +#include + +class SipPluginFactory; +class SipPlugin; + +class DLLEXPORT SipModel : public QAbstractItemModel +{ + Q_OBJECT +public: + enum Roles { + PluginName = Qt::UserRole + 15, + ConnectionStateRole = Qt::UserRole + 17, + HasConfig = Qt::UserRole + 18, + FactoryRole = Qt::UserRole + 19, + ErrorString = Qt::UserRole + 20, + FactoryItemRole = Qt::UserRole + 21, + FactoryItemIcon = Qt::UserRole + 22, + SipPluginData = Qt::UserRole + 23, + SipPluginFactoryData = Qt::UserRole + 24 + }; + + explicit SipModel( QObject* parent = 0 ); + virtual ~SipModel(); + + virtual QModelIndex index ( int row, int column, const QModelIndex& parent = QModelIndex() ) const; + virtual QModelIndex parent ( const QModelIndex& child ) const; + virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const; + virtual int rowCount( const QModelIndex& parent = QModelIndex() ) const; + virtual int columnCount( const QModelIndex& parent ) const; + virtual Qt::ItemFlags flags(const QModelIndex& index) const; + virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); + +private slots: + void pluginAdded( SipPlugin* p ); + void pluginRemoved( SipPlugin* p ); + void pluginStateChanged( SipPlugin* p ); + +private: + QList< SipPluginFactory* > m_factories; +}; + +#endif // SIPMODEL_H diff --git a/src/libtomahawk/sip/SipPlugin.cpp b/src/libtomahawk/sip/SipPlugin.cpp index bb849637f..1bde2db64 100644 --- a/src/libtomahawk/sip/SipPlugin.cpp +++ b/src/libtomahawk/sip/SipPlugin.cpp @@ -17,7 +17,28 @@ * along with Tomahawk. If not, see . */ -#include +#include "sip/SipPlugin.h" + +#include + +QString +SipPluginFactory::generateId() +{ + QString uniq = QUuid::createUuid().toString().mid( 1, 8 ); + return factoryId() + "_" + uniq; +} + +SipPlugin::SipPlugin( const QString& pluginId, QObject* parent ) + : QObject( parent ) + , m_pluginId( pluginId ) +{ + +} + +QString SipPlugin::pluginId() const +{ + return m_pluginId; +} QMenu* @@ -33,8 +54,20 @@ SipPlugin::configWidget() return 0; } +QString +SipPlugin::errorMessage() const +{ + return QString(); +} + +QIcon +SipPlugin::icon() const +{ + return QIcon(); +} + void SipPlugin::setProxy( const QNetworkProxy& proxy ) { qDebug() << Q_FUNC_INFO << "Not implemented"; -} \ No newline at end of file +} diff --git a/src/libtomahawk/sip/SipPlugin.h b/src/libtomahawk/sip/SipPlugin.h index bc7640349..d50647412 100644 --- a/src/libtomahawk/sip/SipPlugin.h +++ b/src/libtomahawk/sip/SipPlugin.h @@ -28,21 +28,53 @@ #include "dllmacro.h" +class SipPlugin; + +class DLLEXPORT SipPluginFactory : public QObject +{ + Q_OBJECT +public: + SipPluginFactory() {} + virtual ~SipPluginFactory() {} + + // display name for plugin + virtual QString prettyName() const = 0; + // internal name + virtual QString factoryId() const = 0; + // if the user can create multiple + virtual QIcon icon() const { return QIcon(); } + virtual bool isUnique() const { return false; } + + virtual SipPlugin* createPlugin( const QString& pluginId = QString() ) = 0; + +protected: + QString generateId(); +}; + class DLLEXPORT SipPlugin : public QObject { Q_OBJECT public: enum SipErrorCode { AuthError, ConnectionError }; // Placeholder for errors, to be defined + enum ConnectionState { Disconnected, Connecting, Connected }; + explicit SipPlugin( const QString& pluginId, QObject* parent = 0 ); virtual ~SipPlugin() {} - virtual bool isValid() = 0; - virtual const QString name() = 0; - virtual const QString friendlyName() = 0; - virtual const QString accountName() = 0; + // plugin id is "pluginfactoryname_someuniqueid". get it from SipPluginFactory::generateId + QString pluginId() const; + + virtual bool isValid() const = 0; + virtual const QString name() const = 0; + virtual const QString friendlyName() const = 0; + virtual const QString accountName() const = 0; + virtual ConnectionState connectionState() const = 0; + virtual QString errorMessage() const; virtual QMenu* menu(); virtual QWidget* configWidget(); + virtual void saveConfig() {} // called when the widget has been edited + virtual QIcon icon() const; public slots: virtual bool connectPlugin( bool startup = false ) = 0; @@ -56,8 +88,7 @@ public slots: signals: void error( int, const QString& ); - void connected(); - void disconnected(); + void stateChanged( SipPlugin::ConnectionState state ); void peerOnline( const QString& ); void peerOffline( const QString& ); @@ -72,8 +103,11 @@ signals: void addMenu( QMenu* menu ); void removeMenu( QMenu* menu ); + +private: + QString m_pluginId; }; -Q_DECLARE_INTERFACE( SipPlugin, "tomahawk.Sip/1.0" ) +Q_DECLARE_INTERFACE( SipPluginFactory, "tomahawk.SipFactory/1.0" ) #endif diff --git a/src/libtomahawk/tomahawksettings.cpp b/src/libtomahawk/tomahawksettings.cpp index 9e89383ac..23072ef28 100644 --- a/src/libtomahawk/tomahawksettings.cpp +++ b/src/libtomahawk/tomahawksettings.cpp @@ -25,8 +25,9 @@ #include #include +#include "sip/SipHandler.h" -#define VERSION 2 +#define VERSION 3 TomahawkSettings* TomahawkSettings::s_instance = 0; @@ -46,6 +47,7 @@ TomahawkSettings::TomahawkSettings( QObject* parent ) if( !contains( "configversion") ) { setValue( "configversion", VERSION ); + doInitialSetup(); } else if( value( "configversion" ).toUInt() != VERSION ) { @@ -53,11 +55,15 @@ TomahawkSettings::TomahawkSettings( QObject* parent ) << "new:" << VERSION << "Doing upgrade, if any..."; + int current = value( "configversion" ).toUInt(); + while( current < VERSION ) + { + doUpgrade( current, current + 1 ); + + current++; + } // insert upgrade code here as required setValue( "configversion", VERSION ); - if( contains( "script/resolvers") ) { - setValue( "script/loadedresolvers", value( "script/resolvers" ) ); - } } } @@ -67,6 +73,71 @@ TomahawkSettings::~TomahawkSettings() s_instance = 0; } +void +TomahawkSettings::doInitialSetup() +{ + // by default we add a local network resolver + addSipPlugin( "sipzeroconf_autocreated" ); +} + + +void +TomahawkSettings::doUpgrade( int oldVersion, int newVersion ) +{ + Q_UNUSED( newVersion ); + + if( oldVersion == 1 ) + { + qDebug() << "Migrating config from verson 1 to 2: script resolver config name"; + if( contains( "script/resolvers" ) ) { + setValue( "script/loadedresolvers", value( "script/resolvers" ) ); + remove( "script/resolvers" ); + } + } else if( oldVersion == 2 ) + { + qDebug() << "Migrating config from version 2 to 3: Converting jabber and twitter accounts to new SIP Factory approach"; + // migrate old accounts to new system. only jabber and twitter, and max one each. create a new plugin for each if needed + // not pretty as we hardcode a plugin id and assume that we know how the config layout is, but hey, this is migration after all + if( contains( "jabber/username" ) && contains( "jabber/password" ) ) + { + setValue( "sipjabber_legacy/username", value( "jabber/username" ) ); + setValue( "sipjabber_legacy/password", value( "jabber/password" ) ); + setValue( "sipjabber_legacy/autoconnect", value( "jabber/autoconnect" ) ); + setValue( "sipjabber_legacy/port", value( "jabber/port" ) ); + setValue( "sipjabber_legacy/server", value( "jabber/server" ) ); + + addSipPlugin( "sipjabber_legacy" ); + + remove( "jabber/username" ); + remove( "jabber/password" ); + remove( "jabber/autoconnect" ); + remove( "jabber/server" ); + remove( "jabber/port" ); + } + if( contains( "twitter/ScreenName" ) && contains( "twitter/OAuthToken" ) ) + { + setValue( "siptwitter_legacy/ScreenName", value( "twitter/ScreenName" ) ); + setValue( "siptwitter_legacy/OAuthToken", value( "twitter/OAuthToken" ) ); + setValue( "siptwitter_legacy/OAuthTokenSecret", value( "twitter/OAuthTokenSecret" ) ); + setValue( "siptwitter_legacy/CachedFriendsSinceID", value( "twitter/CachedFriendsSinceID" ) ); + setValue( "siptwitter_legacy/CachedMentionsSinceID", value( "twitter/CachedMentionsSinceID" ) ); + setValue( "siptwitter_legacy/CachedDirectMessagesSinceID", value( "twitter/CachedDirectMessagesSinceID" ) ); + setValue( "siptwitter_legacy/CachedPeers", value( "twitter/CachedPeers" ) ); + setValue( "siptwitter_legacy/AutoConnect", value( "jabber/autoconnect" ) ); + + addSipPlugin( "siptwitter_legacy" ); + remove( "twitter/ScreenName" ); + remove( "twitter/OAuthToken" ); + remove( "twitter/OAuthTokenSecret" ); + remove( "twitter/CachedFriendsSinceID" ); + remove( "twitter/CachedMentionsSinceID" ); + remove( "twitter/CachedDirectMessagesSinceID" ); + } + // create a zeroconf plugin too + addSipPlugin( "sipzeroconf_legacy" ); + } +} + QStringList TomahawkSettings::scannerPaths() @@ -326,76 +397,66 @@ TomahawkSettings::setBookmarkPlaylist( const QString& guid ) setValue( "playlists/bookmark", guid ); } - -bool -TomahawkSettings::jabberAutoConnect() const +QStringList +TomahawkSettings::sipPlugins() const { - return value( "jabber/autoconnect", true ).toBool(); + return value( "sip/allplugins", QStringList() ).toStringList(); } - void -TomahawkSettings::setJabberAutoConnect( bool autoconnect ) +TomahawkSettings::setSipPlugins( const QStringList& plugins ) { - setValue( "jabber/autoconnect", autoconnect ); + setValue( "sip/allplugins", plugins ); } - -unsigned int -TomahawkSettings::jabberPort() const +QStringList +TomahawkSettings::enabledSipPlugins() const { - return value( "jabber/port", 5222 ).toUInt(); + return value( "sip/enabledplugins", QStringList() ).toStringList(); } - void -TomahawkSettings::setJabberPort( int port ) +TomahawkSettings::setEnabledSipPlugins( const QStringList& list ) { - if ( port < 0 ) - return; - setValue( "jabber/port", port ); + setValue( "sip/enabledplugins", list ); } - -QString -TomahawkSettings::jabberServer() const -{ - return value( "jabber/server" ).toString(); -} - - void -TomahawkSettings::setJabberServer( const QString& server ) +TomahawkSettings::enableSipPlugin( const QString& pluginId ) { - setValue( "jabber/server", server ); + QStringList list = enabledSipPlugins(); + list << pluginId; + setEnabledSipPlugins( list ); } - -QString -TomahawkSettings::jabberUsername() const -{ - return value( "jabber/username" ).toString(); -} - - void -TomahawkSettings::setJabberUsername( const QString& username ) +TomahawkSettings::disableSipPlugin( const QString& pluginId ) { - setValue( "jabber/username", username ); + QStringList list = enabledSipPlugins(); + list.removeAll( pluginId ); + setEnabledSipPlugins( list ); } - -QString -TomahawkSettings::jabberPassword() const -{ - return value( "jabber/password" ).toString(); -} - - void -TomahawkSettings::setJabberPassword( const QString& pw ) +TomahawkSettings::addSipPlugin( const QString& pluginId, bool enable ) { - setValue( "jabber/password", pw ); + QStringList list = sipPlugins(); + list << pluginId; + setSipPlugins( list ); + + if ( enable ) + enableSipPlugin( pluginId ); +} + +void +TomahawkSettings::removeSipPlugin( const QString& pluginId ) +{ + QStringList list = sipPlugins(); + list.removeAll( pluginId ); + setSipPlugins( list ); + + if( enabledSipPlugins().contains( pluginId ) ) + disableSipPlugin( pluginId ); } @@ -497,90 +558,6 @@ TomahawkSettings::setLastFmUsername( const QString& username ) setValue( "lastfm/username", username ); } -QString -TomahawkSettings::twitterScreenName() const -{ - return value( "twitter/ScreenName" ).toString(); -} - -void -TomahawkSettings::setTwitterScreenName( const QString& screenName ) -{ - setValue( "twitter/ScreenName", screenName ); -} - -QString -TomahawkSettings::twitterOAuthToken() const -{ - return value( "twitter/OAuthToken" ).toString(); -} - -void -TomahawkSettings::setTwitterOAuthToken( const QString& oauthtoken ) -{ - setValue( "twitter/OAuthToken", oauthtoken ); -} - -QString -TomahawkSettings::twitterOAuthTokenSecret() const -{ - return value( "twitter/OAuthTokenSecret" ).toString(); -} - -void -TomahawkSettings::setTwitterOAuthTokenSecret( const QString& oauthtokensecret ) -{ - setValue( "twitter/OAuthTokenSecret", oauthtokensecret ); -} - -qint64 -TomahawkSettings::twitterCachedFriendsSinceId() const -{ - return value( "twitter/CachedFriendsSinceID", 0 ).toLongLong(); -} - -void -TomahawkSettings::setTwitterCachedFriendsSinceId( qint64 cachedId ) -{ - setValue( "twitter/CachedFriendsSinceID", cachedId ); -} - -qint64 -TomahawkSettings::twitterCachedMentionsSinceId() const -{ - return value( "twitter/CachedMentionsSinceID", 0 ).toLongLong(); -} - -void -TomahawkSettings::setTwitterCachedMentionsSinceId( qint64 cachedId ) -{ - setValue( "twitter/CachedMentionsSinceID", cachedId ); -} - -qint64 -TomahawkSettings::twitterCachedDirectMessagesSinceId() const -{ - return value( "twitter/CachedDirectMessagesSinceID", 0 ).toLongLong(); -} - -void -TomahawkSettings::setTwitterCachedDirectMessagesSinceId( qint64 cachedId ) -{ - setValue( "twitter/CachedDirectMessagesSinceID", cachedId ); -} - -QHash -TomahawkSettings::twitterCachedPeers() const -{ - return value( "twitter/CachedPeers", QHash() ).toHash(); -} - -void -TomahawkSettings::setTwitterCachedPeers( const QHash &cachedPeers ) -{ - setValue( "twitter/CachedPeers", cachedPeers ); -} - bool TomahawkSettings::scrobblingEnabled() const { diff --git a/src/libtomahawk/tomahawksettings.h b/src/libtomahawk/tomahawksettings.h index c9a91391c..366355bb4 100644 --- a/src/libtomahawk/tomahawksettings.h +++ b/src/libtomahawk/tomahawksettings.h @@ -69,24 +69,22 @@ public: QStringList recentlyPlayedPlaylistGuids() const; void appendRecentlyPlayedPlaylist( const Tomahawk::playlist_ptr& playlist ); + /// SIP plugins + // all plugins we know about. loaded, unloaded, enabled, disabled. + void setSipPlugins( const QStringList& plugins ); + QStringList sipPlugins() const; + void setBookmarkPlaylist( const QString& guid ); QString bookmarkPlaylist() const; - /// Jabber settings - bool jabberAutoConnect() const; /// true by default - void setJabberAutoConnect( bool autoconnect = false ); + // just the enabled sip plugins. + void setEnabledSipPlugins( const QStringList& list ); + QStringList enabledSipPlugins() const; + void enableSipPlugin( const QString& pluginId ); + void disableSipPlugin( const QString& pluginId ); - QString jabberUsername() const; - void setJabberUsername( const QString& username ); - - QString jabberPassword() const; - void setJabberPassword( const QString& pw ); - - QString jabberServer() const; - void setJabberServer( const QString& server ); - - unsigned int jabberPort() const; // default is 5222 - void setJabberPort( int port ); + void addSipPlugin( const QString& pluginId, bool enable = true ); + void removeSipPlugin( const QString& pluginId ); /// Network settings enum ExternalAddressMode { Lan, Upnp }; @@ -138,28 +136,6 @@ public: QByteArray lastFmSessionKey() const; void setLastFmSessionKey( const QByteArray& key ); - /// Twitter settings - QString twitterScreenName() const; - void setTwitterScreenName( const QString& screenName ); - - QString twitterOAuthToken() const; - void setTwitterOAuthToken( const QString& oauthtoken ); - - QString twitterOAuthTokenSecret() const; - void setTwitterOAuthTokenSecret( const QString& oauthtokensecret ); - - qint64 twitterCachedFriendsSinceId() const; - void setTwitterCachedFriendsSinceId( qint64 sinceid ); - - qint64 twitterCachedMentionsSinceId() const; - void setTwitterCachedMentionsSinceId( qint64 sinceid ); - - qint64 twitterCachedDirectMessagesSinceId() const; - void setTwitterCachedDirectMessagesSinceId( qint64 sinceid ); - - QHash twitterCachedPeers() const; - void setTwitterCachedPeers( const QHash &cachedPeers ); - /// XMPP Component Settings QString xmppBotServer() const; void setXmppBotServer( const QString &server ); @@ -186,6 +162,9 @@ signals: void recentlyPlayedPlaylistAdded( const Tomahawk::playlist_ptr& playlist ); private: + void doInitialSetup(); + void doUpgrade( int oldVersion, int newVersion ); + static TomahawkSettings* s_instance; }; diff --git a/src/resolverconfigdelegate.cpp b/src/resolverconfigdelegate.cpp index 0f98e49f8..dc291a69b 100644 --- a/src/resolverconfigdelegate.cpp +++ b/src/resolverconfigdelegate.cpp @@ -27,12 +27,11 @@ #include #define PADDING 4 - +#define ICONSIZE 24 ResolverConfigDelegate::ResolverConfigDelegate( QObject* parent ) - : QStyledItemDelegate( parent ) - , m_configPressed( false ) + : ConfigDelegateBase( parent ) { - + connect( this, SIGNAL( configPressed( QModelIndex ) ), this, SLOT( onConfigPressed( QModelIndex ) ) ); } void @@ -61,36 +60,31 @@ ResolverConfigDelegate::paint( QPainter* painter, const QStyleOptionViewItem& op style->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, w ); int rightSplit = itemRect.width(); - int rectW = opt.rect.height() - 4 * PADDING; - QRect confRect = QRect( rightSplit - rectW - 2 * PADDING, 2 * PADDING + top, rectW, rectW ); + QRect confRect = QRect( rightSplit - ICONSIZE - 2 * PADDING, 2 * PADDING + top, ICONSIZE, ICONSIZE ); + // if the resolver has a config widget, paint it first (right-aligned) if( index.data( ResolversModel::HasConfig ).toBool() ) { - // draw it the same size as the check belox QStyleOptionToolButton topt; - topt.font = opt.font; - topt.icon = QIcon( RESPATH "images/configure.png" ); - topt.iconSize = QSize( 16, 16 ); topt.rect = confRect; - topt.subControls = QStyle::SC_ToolButton; - topt.activeSubControls = QStyle::SC_None; - topt.features = QStyleOptionToolButton::None; topt.pos = confRect.topLeft(); - topt.state = m_configPressed ? QStyle::State_On : QStyle::State_Raised; - if( opt.state & QStyle::State_MouseOver || m_configPressed ) - topt.state |= QStyle::State_HasFocus; - style->drawComplexControl( QStyle::CC_ToolButton, &topt, painter, w ); + + drawConfigWrench( painter, opt, topt ); } // draw check - confRect.moveTo( 2 * PADDING, 2 * PADDING + top ); - opt.rect = confRect; - opt.checkState == Qt::Checked ? opt.state |= QStyle::State_On : opt.state |= QStyle::State_Off; - style->drawPrimitive( QStyle::PE_IndicatorViewItemCheck, &opt, painter, w ); + QRect checkRect = confRect; + checkRect.moveTo( 2 * PADDING, 2 * PADDING + top ); + opt.rect = checkRect; + drawCheckBox( opt, painter, w ); itemRect.setX( opt.rect.topRight().x() + PADDING ); painter->save(); painter->setFont( name ); QRect textRect = itemRect.adjusted( PADDING, PADDING, -PADDING, -PADDING ); + + if( index.data( ResolversModel::HasConfig ).toBool() ) + textRect.setRight( confRect.topLeft().x() - PADDING ); + textRect.setBottom( itemRect.height() / 2 + top ); QString nameStr = bfm.elidedText( index.data( ResolversModel::ResolverName ).toString(),Qt::ElideRight, textRect.width() ); painter->drawText( textRect, nameStr ); @@ -106,71 +100,21 @@ ResolverConfigDelegate::paint( QPainter* painter, const QStyleOptionViewItem& op } -QSize -ResolverConfigDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const +QRect +ResolverConfigDelegate::configRectForIndex( const QStyleOptionViewItem& option, const QModelIndex& idx ) const { - int width = QStyledItemDelegate::sizeHint( option, index ).width(); - QStyleOptionViewItemV4 opt = option; - initStyleOption( &opt, index ); + initStyleOption( &opt, idx ); + QRect itemRect = opt.rect; + int top = itemRect.top(); - - QFont name = opt.font; - name.setPointSize( name.pointSize() + 2 ); - name.setBold( true ); - - QFont path = opt.font; - path.setItalic( true ); - path.setPointSize( path.pointSize() - 1 ); - - - QFontMetrics bfm( name ); - QFontMetrics sfm( path ); - return QSize( width, 3 * PADDING + bfm.height() + sfm.height() ); + QRect confRect = QRect( itemRect.width() - ICONSIZE - 2 * PADDING, 2 * PADDING + top, ICONSIZE, ICONSIZE ); + return confRect; } -bool -ResolverConfigDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) + +void +ResolverConfigDelegate::onConfigPressed( const QModelIndex& idx ) { -// qDebug() << "EDITOR EVENT!" << ( event->type() == QEvent::MouseButtonRelease ); - - QStyleOptionViewItemV4 viewOpt( option ); - initStyleOption( &viewOpt, index ); - const QWidget* w = viewOpt.widget; - QStyle* style = w ? w->style() : QApplication::style(); - int top = viewOpt.rect.top(); - - if( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseButtonDblClick ) { - m_configPressed = false; - - int rectW = option.rect.height() - 4 * PADDING; - QRect checkRect = QRect( 2 * PADDING, 2 * PADDING + top, rectW, rectW ); - QMouseEvent* me = static_cast< QMouseEvent* >( event ); - if( me->button() != Qt::LeftButton || !checkRect.contains( me->pos() ) ) - return false; - - // eat the double click events inside the check rect - if( event->type() == QEvent::MouseButtonDblClick ) { - return true; - } - - Qt::CheckState curState = static_cast< Qt::CheckState >( index.data( Qt::CheckStateRole ).toInt() ); - Qt::CheckState newState = curState == Qt::Checked ? Qt::Unchecked : Qt::Checked; - return model->setData( index, newState, Qt::CheckStateRole ); - - } else if( event->type() == QEvent::MouseButtonPress ) { - int rightSplit = viewOpt.rect.width(); - int rectW = viewOpt.rect.height() - 4 * PADDING; - QRect confRect = QRect( rightSplit - rectW - 2 * PADDING, 2 * PADDING + top, rectW, rectW ); - - QMouseEvent* me = static_cast< QMouseEvent* >( event ); - if( me->button() == Qt::LeftButton && confRect.contains( me->pos() ) ) { - m_configPressed = true; - - emit openConfig( index.data( ResolversModel::ResolverPath ).toString() ); - return true; - } - } - - return QStyledItemDelegate::editorEvent( event, model, option, index ); + emit openConfig( idx.data( ResolversModel::ResolverPath ).toString() ); } diff --git a/src/resolverconfigdelegate.h b/src/resolverconfigdelegate.h index 280e63ccb..b376044bf 100644 --- a/src/resolverconfigdelegate.h +++ b/src/resolverconfigdelegate.h @@ -20,23 +20,22 @@ #ifndef RESOLVERCONFIGDELEGATE_H #define RESOLVERCONFIGDELEGATE_H -#include +#include "configdelegatebase.h" -class ResolverConfigDelegate : public QStyledItemDelegate +class ResolverConfigDelegate : public ConfigDelegateBase { Q_OBJECT public: explicit ResolverConfigDelegate(QObject* parent = 0); virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; - virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; - virtual bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ); + virtual QRect configRectForIndex ( const QStyleOptionViewItem& option, const QModelIndex& idx ) const; + +private slots: + void onConfigPressed ( const QModelIndex& ); signals: void openConfig( const QString& resolverPath ); - -private: - bool m_configPressed; }; #endif // RESOLVERCONFIGDELEGATE_H diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index fee043b4b..82466d9e0 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -32,7 +32,6 @@ #endif #include "settingsdialog.h" -#include "ui_settingsdialog.h" #include "ui_proxydialog.h" #include "tomahawk/tomahawkapp.h" #include "musicscanner.h" @@ -42,7 +41,11 @@ #include "scanmanager.h" #include "resolverconfigdelegate.h" #include "resolversmodel.h" -#include "resolverconfigwrapper.h" +#include "delegateconfigwrapper.h" +#include "sip/SipModel.h" +#include "sipconfigdelegate.h" + +#include "ui_stackedsettingsdialog.h" static QString md5( const QByteArray& src ) @@ -53,10 +56,12 @@ md5( const QByteArray& src ) SettingsDialog::SettingsDialog( QWidget *parent ) : QDialog( parent ) - , ui( new Ui::SettingsDialog ) + , ui( new Ui_StackedSettingsDialog ) , m_proxySettings( this ) , m_rejected( false ) , m_testLastFmQuery( 0 ) + , m_sipModel( 0 ) + , m_resolversModel( 0 ) { ui->setupUi( this ); TomahawkSettings* s = TomahawkSettings::instance(); @@ -66,32 +71,37 @@ SettingsDialog::SettingsDialog( QWidget *parent ) ui->checkBoxUpnp->setChecked( s->externalAddressMode() == TomahawkSettings::Upnp ); ui->checkBoxUpnp->setEnabled( !s->preferStaticHostPort() ); - // JABBER - ui->checkBoxJabberAutoConnect->setChecked( s->jabberAutoConnect() ); - ui->jabberUsername->setText( s->jabberUsername() ); - ui->jabberPassword->setText( s->jabberPassword() ); - ui->jabberServer->setText( s->jabberServer() ); - ui->jabberPort->setValue( s->jabberPort() ); + createIcons(); +#ifdef Q_WS_X11 + ui->listWidget->setFrameShape( QFrame::StyledPanel ); + ui->listWidget->setFrameShadow( QFrame::Sunken ); + setContentsMargins( 4, 4, 4, 4 ); +#else + ui->verticalLayout->removeItem( ui->verticalSpacer_3 ); +#endif + + // SIP PLUGINS + SipConfigDelegate* sipdel = new SipConfigDelegate( this ); + ui->accountsView->setItemDelegate( sipdel ); + ui->accountsView->setContextMenuPolicy( Qt::CustomContextMenu ); + + connect( ui->accountsView, SIGNAL( clicked( QModelIndex ) ), this, SLOT( sipItemClicked( QModelIndex ) ) ); + connect( sipdel, SIGNAL( openConfig( SipPlugin* ) ), this, SLOT( openSipConfig( SipPlugin* ) ) ); + connect( ui->accountsView, SIGNAL( customContextMenuRequested( QPoint ) ), this, SLOT( sipContextMenuRequest( QPoint ) ) ); + m_sipModel = new SipModel( this ); + ui->accountsView->setModel( m_sipModel ); + + setupSipButtons(); ui->staticHostName->setText( s->externalHostname() ); ui->staticPort->setValue( s->externalPort() ); ui->proxyButton->setVisible( false ); - // SIP PLUGINS - foreach(SipPlugin *plugin, APP->sipHandler()->plugins()) - { - if(plugin->configWidget()) - { - qDebug() << "Adding configWidget for " << plugin->name(); - ui->tabWidget->addTab(plugin->configWidget(), plugin->friendlyName()); - } - } - // MUSIC SCANNER //FIXME: MULTIPLECOLLECTIONDIRS if ( s->scannerPaths().count() ) - ui->lineEditMusicPath->setText( s->scannerPaths().first() ); + ui->lineEditMusicPath_2->setText( s->scannerPaths().first() ); ui->checkBoxWatchForChanges->setChecked( s->watchForChanges() ); // LAST FM @@ -112,10 +122,12 @@ SettingsDialog::SettingsDialog( QWidget *parent ) connect( ui->addScript, SIGNAL( clicked( bool ) ), this, SLOT( addScriptResolver() ) ); connect( ui->removeScript, SIGNAL( clicked( bool ) ), this, SLOT( removeScriptResolver() ) ); - connect( ui->buttonBrowse, SIGNAL( clicked() ), SLOT( showPathSelector() ) ); + connect( ui->buttonBrowse_2, SIGNAL( clicked() ), SLOT( showPathSelector() ) ); connect( ui->proxyButton, SIGNAL( clicked() ), SLOT( showProxySettings() ) ); connect( ui->checkBoxStaticPreferred, SIGNAL( toggled(bool) ), SLOT( toggleUpnp(bool) ) ); connect( this, SIGNAL( rejected() ), SLOT( onRejected() ) ); + + ui->listWidget->setCurrentRow( 0 ); } @@ -131,16 +143,10 @@ SettingsDialog::~SettingsDialog() s->setPreferStaticHostPort( ui->checkBoxStaticPreferred->checkState() == Qt::Checked ); s->setExternalAddressMode( ui->checkBoxUpnp->checkState() == Qt::Checked ? TomahawkSettings::Upnp : TomahawkSettings::Lan ); - s->setJabberAutoConnect( ui->checkBoxJabberAutoConnect->checkState() == Qt::Checked ); - s->setJabberUsername( ui->jabberUsername->text() ); - s->setJabberPassword( ui->jabberPassword->text() ); - s->setJabberServer( ui->jabberServer->text() ); - s->setJabberPort( ui->jabberPort->value() ); - s->setExternalHostname( ui->staticHostName->text() ); s->setExternalPort( ui->staticPort->value() ); - s->setScannerPaths( QStringList( ui->lineEditMusicPath->text() ) ); + s->setScannerPaths( QStringList( ui->lineEditMusicPath_2->text() ) ); s->setWatchForChanges( ui->checkBoxWatchForChanges->isChecked() ); s->setScrobblingEnabled( ui->checkBoxEnableLastfm->isChecked() ); @@ -158,6 +164,93 @@ SettingsDialog::~SettingsDialog() delete ui; } +void +SettingsDialog::createIcons() +{ + /// Not fun but QListWidget sucks. Do our max-width calculation manually + /// so the icons arre lined up. + // Resolvers is the longest string... in english. fml. + + int maxlen = 0; + QFontMetrics fm( font() ); + QListWidgetItem *accountsButton = new QListWidgetItem( ui->listWidget ); + accountsButton->setIcon( QIcon( RESPATH "images/account-settings.png" ) ); + accountsButton->setText( tr( "Accounts" ) ); + accountsButton->setTextAlignment( Qt::AlignHCenter ); + accountsButton->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled ); + maxlen = fm.width( accountsButton->text() ); + + QListWidgetItem *musicButton = new QListWidgetItem( ui->listWidget ); + musicButton->setIcon( QIcon( RESPATH "images/music-settings.png" ) ); + musicButton->setText( tr( "Music" ) ); + musicButton->setTextAlignment( Qt::AlignHCenter ); + musicButton->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled ); + maxlen = qMax( fm.width( musicButton->text() ), maxlen ); + + QListWidgetItem *lastfmButton = new QListWidgetItem( ui->listWidget ); + lastfmButton->setIcon( QIcon( RESPATH "images/lastfm-settings.png" ) ); + lastfmButton->setText( tr( "Last.fm" ) ); + lastfmButton->setTextAlignment( Qt::AlignHCenter ); + lastfmButton->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled ); + maxlen = qMax( fm.width( lastfmButton->text() ), maxlen ); + + QListWidgetItem *resolversButton = new QListWidgetItem( ui->listWidget ); + resolversButton->setIcon( QIcon( RESPATH "images/resolvers-settings.png" ) ); + resolversButton->setText( tr( "Resolvers" ) ); + resolversButton->setTextAlignment( Qt::AlignHCenter ); + resolversButton->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled ); + maxlen = qMax( fm.width( resolversButton->text() ), maxlen ); + + QListWidgetItem *advancedButton = new QListWidgetItem( ui->listWidget ); + advancedButton->setIcon( QIcon( RESPATH "images/advanced-settings.png" ) ); + advancedButton->setText( tr( "Advanced" ) ); + advancedButton->setTextAlignment( Qt::AlignHCenter ); + advancedButton->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled ); + maxlen = qMax( fm.width( advancedButton->text() ), maxlen ); + + maxlen += 15; // padding + accountsButton->setSizeHint( QSize( maxlen, 60 ) ); + musicButton->setSizeHint( QSize( maxlen, 60 ) ); + lastfmButton->setSizeHint( QSize( maxlen, 60 ) ); + resolversButton->setSizeHint( QSize( maxlen, 60 ) ); + advancedButton->setSizeHint( QSize( maxlen, 60 ) ); + +#ifndef Q_WS_MAC + // doesn't listen to sizehint... + ui->listWidget->setMaximumWidth( maxlen + 8 ); +#endif + + connect( ui->listWidget, SIGNAL( currentItemChanged( QListWidgetItem* ,QListWidgetItem* ) ), this, SLOT( changePage( QListWidgetItem*, QListWidgetItem* ) ) ); +} + +void +SettingsDialog::setupSipButtons() +{ + foreach( SipPluginFactory* f, SipHandler::instance()->pluginFactories() ) { + if( f->isUnique() && SipHandler::instance()->hasPluginType( f->factoryId() ) ) { + continue; + } + + QAction* action = new QAction( f->icon(), f->prettyName(), ui->addSipButton ); + action->setProperty( "factory", QVariant::fromValue< QObject* >( f ) ); + ui->addSipButton->addAction( action ); + + connect( action, SIGNAL( triggered(bool) ), this, SLOT( factoryActionTriggered( bool ) ) ); + } + + connect( ui->removeSipButton, SIGNAL( clicked( bool ) ), this, SLOT( sipPluginDeleted( bool ) ) ); +} + + +void +SettingsDialog::changePage( QListWidgetItem* current, QListWidgetItem* previous ) +{ + if( !current ) + current = previous; + + ui->stackedWidget->setCurrentIndex( ui->listWidget->row(current) ); +} + void SettingsDialog::showPathSelector() @@ -171,7 +264,7 @@ SettingsDialog::showPathSelector() if ( path.isEmpty() ) return; - ui->lineEditMusicPath->setText( path ); + ui->lineEditMusicPath_2->setText( path ); } @@ -375,8 +468,8 @@ SettingsDialog::openResolverConfig( const QString& resolver ) { Tomahawk::ExternalResolver* r = TomahawkApp::instance()->resolverForPath( resolver ); if( r && r->configUI() ) { - ResolverConfigWrapper dialog( r->configUI(), "Resolver Config", this ); - QWeakPointer< ResolverConfigWrapper > watcher( &dialog ); + DelegateConfigWrapper dialog( r->configUI(), "Resolver Config", this ); + QWeakPointer< DelegateConfigWrapper > watcher( &dialog ); int ret = dialog.exec(); if( !watcher.isNull() && ret == QDialog::Accepted ) { // send changed config to resolver @@ -384,3 +477,140 @@ SettingsDialog::openResolverConfig( const QString& resolver ) } } } + +void +SettingsDialog::sipItemClicked( const QModelIndex& item ) +{ + if( item.data( SipModel::FactoryRole ).toBool() ) + if( ui->accountsView->isExpanded( item ) ) + ui->accountsView->collapse( item ); + else + ui->accountsView->expand( item ); + else if( item.data( SipModel::FactoryItemRole ).toBool() ) + sipFactoryClicked( qobject_cast( item.data( SipModel::SipPluginFactoryData ).value< QObject* >() ) ); +} + +void +SettingsDialog::openSipConfig( SipPlugin* p ) +{ + if( p->configWidget() ) { + DelegateConfigWrapper dialog( p->configWidget(), QString("%1 Config" ).arg( p->friendlyName() ), this ); + QWeakPointer< DelegateConfigWrapper > watcher( &dialog ); + int ret = dialog.exec(); + if( !watcher.isNull() && ret == QDialog::Accepted ) { + // send changed config to resolver + p->saveConfig(); + } + } +} + +void +SettingsDialog::factoryActionTriggered( bool ) +{ + Q_ASSERT( sender() && qobject_cast< QAction* >( sender() ) ); + + QAction* a = qobject_cast< QAction* >( sender() ); + Q_ASSERT( qobject_cast< SipPluginFactory* >( a->property( "factory" ).value< QObject* >() ) ); + + SipPluginFactory* f = qobject_cast< SipPluginFactory* >( a->property( "factory" ).value< QObject* >() ); + sipFactoryClicked( f ); +} + + +void +SettingsDialog::sipFactoryClicked( SipPluginFactory* factory ) +{ + //if exited with OK, create it, if not, delete it immediately! + SipPlugin* p = factory->createPlugin(); + bool added = false; + if( p->configWidget() ) { + DelegateConfigWrapper dialog( p->configWidget(), QString("%1 Config" ).arg( p->friendlyName() ), this ); + QWeakPointer< DelegateConfigWrapper > watcher( &dialog ); + int ret = dialog.exec(); + if( !watcher.isNull() && ret == QDialog::Accepted ) { + // send changed config to resolver + p->saveConfig(); + + // accepted, so add it to tomahawk + TomahawkSettings::instance()->addSipPlugin( p->pluginId() ); + SipHandler::instance()->addSipPlugin( p ); + + added = true; + } else { + // canceled, delete it + added = false; + } + } else { + // no config, so just add it + added = true; + TomahawkSettings::instance()->addSipPlugin( p->pluginId() ); + SipHandler::instance()->addSipPlugin( p ); + } + + SipPluginFactory* f = SipHandler::instance()->factoryFromPlugin( p ); + if( added && f && f->isUnique() ) { + // remove from actions list + QAction* toremove = 0; + foreach( QAction* a, ui->addSipButton->actions() ) + { + if( f == qobject_cast< SipPluginFactory* >( a->property( "factory" ).value< QObject* >() ) ) + { + toremove = a; + break; + } + } + if( toremove ) + ui->addSipButton->removeAction( toremove ); + } else if( added == false ) { // user pressed cancel + delete p; + } +} + +void +SettingsDialog::sipContextMenuRequest( const QPoint& p ) +{ + QModelIndex idx = ui->accountsView->indexAt( p ); + // if it's an account, allow to delete + if( idx.isValid() && !idx.data( SipModel::FactoryRole ).toBool() && !idx.data( SipModel::FactoryItemRole ).toBool() ) + { + QList< QAction* > acts; + acts << new QAction( tr( "Delete Account" ), this ); + acts.first()->setProperty( "sipplugin", idx.data( SipModel::SipPluginData ) ); + connect( acts.first(), SIGNAL( triggered( bool ) ), this, SLOT( sipPluginRowDeleted( bool ) ) ); + QMenu::exec( acts, ui->accountsView->mapToGlobal( p ) ); + } +} + +void +SettingsDialog::sipPluginRowDeleted( bool ) +{ + SipPlugin* p = qobject_cast< SipPlugin* >( qobject_cast< QAction* >( sender() )->property( "sipplugin" ).value< QObject* >() ); + SipHandler::instance()->removeSipPlugin( p ); +} + +void +SettingsDialog::sipPluginDeleted( bool ) +{ + QModelIndexList indexes = ui->accountsView->selectionModel()->selectedIndexes(); + // if it's an account, allow to delete + foreach( const QModelIndex& idx, indexes ) + { + if( idx.isValid() && !idx.data( SipModel::FactoryRole ).toBool() && !idx.data( SipModel::FactoryItemRole ).toBool() ) + { + SipPlugin* p = qobject_cast< SipPlugin* >( idx.data( SipModel::SipPluginData ).value< QObject* >() ); + + if( SipPluginFactory* f = SipHandler::instance()->factoryFromPlugin( p ) ) + { + if( f->isUnique() ) // just deleted a unique plugin->re-add to add menu + { + QAction* action = new QAction( f->icon(), f->prettyName(), ui->addSipButton ); + action->setProperty( "factory", QVariant::fromValue< QObject* >( f ) ); + ui->addSipButton->addAction( action ); + + connect( action, SIGNAL( triggered(bool) ), this, SLOT( factoryActionTriggered( bool ) ) ); + } + } + SipHandler::instance()->removeSipPlugin( p ); + } + } +} diff --git a/src/settingsdialog.h b/src/settingsdialog.h index 880c319da..00f1eb4e0 100644 --- a/src/settingsdialog.h +++ b/src/settingsdialog.h @@ -20,7 +20,13 @@ #define SETTINGSDIALOG_H #include +#include +class QListWidgetItem; +class Ui_StackedSettingsDialog; +class SipPluginFactory; +class SipPlugin; +class SipModel; class ResolversModel; class QNetworkReply; @@ -73,13 +79,26 @@ private slots: void scriptSelectionChanged(); void removeScriptResolver(); void openResolverConfig( const QString& ); + void sipItemClicked ( const QModelIndex& ); + void openSipConfig( SipPlugin* ); + void factoryActionTriggered ( bool ); + void sipFactoryClicked( SipPluginFactory* ); + void sipContextMenuRequest( const QPoint& ); + void sipPluginDeleted( bool ); + void sipPluginRowDeleted( bool ); + + void changePage( QListWidgetItem*, QListWidgetItem* ); private: - Ui::SettingsDialog* ui; + void createIcons(); + void setupSipButtons(); + + Ui_StackedSettingsDialog* ui; ProxyDialog m_proxySettings; bool m_rejected; QNetworkReply* m_testLastFmQuery; + SipModel* m_sipModel; ResolversModel* m_resolversModel; }; diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index 57fc1b306..3ccf7d55e 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -25,6 +25,35 @@ 0 + + + Accounts + + + + + + 0 + + + false + + + false + + + true + + + true + + + false + + + + + Jabber diff --git a/src/sip/jabber/jabber.cpp b/src/sip/jabber/jabber.cpp index dab22ac5b..7b1f1c250 100644 --- a/src/sip/jabber/jabber.cpp +++ b/src/sip/jabber/jabber.cpp @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -42,21 +42,21 @@ JabberPlugin::setProxy( QNetworkProxy* proxy ) const QString -JabberPlugin::name() +JabberPlugin::name() const { return QString( MYNAME ); } const QString -JabberPlugin::friendlyName() +JabberPlugin::friendlyName() const { return QString( "Jabber" ); } const QString -JabberPlugin::accountName() +JabberPlugin::accountName() const { return TomahawkSettings::instance()->jabberUsername(); } @@ -128,6 +128,7 @@ JabberPlugin::onConnected() emit addMenu( m_menu ); } + emit connected(); } diff --git a/src/sip/jabber/jabber.h b/src/sip/jabber/jabber.h index b3d868896..53e264c24 100644 --- a/src/sip/jabber/jabber.h +++ b/src/sip/jabber/jabber.h @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -37,10 +37,10 @@ public: virtual ~JabberPlugin() { delete p; } //FIXME: Make this more correct - virtual bool isValid() { return true; } - virtual const QString name(); - virtual const QString friendlyName(); - virtual const QString accountName(); + virtual bool isValid() const { return true; } + virtual const QString name() const; + virtual const QString friendlyName() const; + virtual const QString accountName() const; virtual QMenu* menu(); void setProxy( QNetworkProxy* proxy ); @@ -51,7 +51,7 @@ public slots: void disconnectPlugin() { onDisconnected(); - + if ( p ) p->disconnect(); @@ -84,7 +84,7 @@ private slots: void showAddFriendDialog(); void onConnected(); void onDisconnected(); - + private: Jabber_p* p; QMenu* m_menu; diff --git a/src/sip/jreen/CMakeLists.txt b/src/sip/jreen/CMakeLists.txt index 3cc053545..d439d5164 100644 --- a/src/sip/jreen/CMakeLists.txt +++ b/src/sip/jreen/CMakeLists.txt @@ -20,13 +20,19 @@ set( jabberHeaders avatarmanager.h ) +set( jabberUI + configwidget.ui +) + include_directories( . ${CMAKE_CURRENT_BINARY_DIR} .. ${QT_INCLUDE_DIR} ${LIBJREEN_INCLUDE_DIR} ) +qt4_add_resources( RC_SRCS "resources.qrc" ) +qt4_wrap_ui( jabberUI_H ${jabberUI} ) qt4_wrap_cpp( jabberMoc ${jabberHeaders} ) -add_library( tomahawk_sipjabber SHARED ${jabberSources} ${jabberMoc} ) +add_library( tomahawk_sipjabber SHARED ${jabberSources} ${jabberMoc} ${jabberUI_H} ${RC_SRCS} ) IF( WIN32 ) SET( OS_SPECIFIC_LINK_LIBRARIES @@ -49,3 +55,6 @@ IF( APPLE ) ENDIF( APPLE ) install( TARGETS tomahawk_sipjabber DESTINATION lib${LIB_SUFFIX} ) + + +add_subdirectory(googlewrapper) diff --git a/src/sip/jreen/configwidget.ui b/src/sip/jreen/configwidget.ui new file mode 100644 index 000000000..b11a48282 --- /dev/null +++ b/src/sip/jreen/configwidget.ui @@ -0,0 +1,264 @@ + + + JabberConfig + + + + 0 + 0 + 437 + 207 + + + + Form + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 12 + + + + Configure this Jabber account + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Login Information + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Jabber ID: + + + jabberUsername + + + + + + + + 0 + 0 + + + + e.g. user@example.com + + + + + + + + 0 + 0 + + + + Password: + + + jabberPassword + + + + + + + + 0 + 0 + + + + + + + QLineEdit::Password + + + + + + + + + Connect automatically when Tomahawk starts + + + + + + + + + + true + + + + 0 + 0 + + + + Advanced Jabber Settings + + + + + + + + + 0 + 0 + + + + + 50 + 0 + + + + Server: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + jabberServer + + + + + + + + 0 + 0 + + + + + + + + Port: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 90 + 0 + + + + + 90 + 16777215 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 1 + + + 65535 + + + 5222 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/src/sip/jreen/googlewrapper/CMakeLists.txt b/src/sip/jreen/googlewrapper/CMakeLists.txt new file mode 100644 index 000000000..f035266d0 --- /dev/null +++ b/src/sip/jreen/googlewrapper/CMakeLists.txt @@ -0,0 +1,32 @@ + +# fake google plugin + +set( googleHeaders + ../jabber.h + ../tomahawksipmessage.h + ../tomahawksipmessagefactory.h + ../avatarmanager.h + googlewrapper.h ) + +set( googleSources + ../jabber.cpp + ../tomahawksipmessage.cpp + ../tomahawksipmessagefactory.cpp + ../avatarmanager.cpp + googlewrapper.cpp ) + +add_definitions(-DGOOGLE_WRAPPER) + +qt4_add_resources( RCX_SRCS "resources.qrc" ) + +qt4_wrap_cpp( googleMoc ../jabber.h ../avatarmanager.h googlewrapper.h ) +add_library( tomahawk_sipgoogle SHARED ${googleSources} ${googleMoc} ${googleMoc} ${RCX_SRCS} ) + +target_link_libraries( tomahawk_sipgoogle + ${QT_LIBRARIES} + ${LIBJREEN_LIBRARY} + ${OS_SPECIFIC_LINK_LIBRARIES} + tomahawklib +) + +install( TARGETS tomahawk_sipgoogle DESTINATION lib${LIB_SUFFIX} ) diff --git a/src/sip/jreen/googlewrapper/gmail-logo.png b/src/sip/jreen/googlewrapper/gmail-logo.png new file mode 100644 index 000000000..e31298c1a Binary files /dev/null and b/src/sip/jreen/googlewrapper/gmail-logo.png differ diff --git a/src/sip/jreen/googlewrapper/googlewrapper.cpp b/src/sip/jreen/googlewrapper/googlewrapper.cpp new file mode 100644 index 000000000..9eeae3acd --- /dev/null +++ b/src/sip/jreen/googlewrapper/googlewrapper.cpp @@ -0,0 +1,54 @@ +/* + + Copyright (C) 2011 Leo Franchi + + 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 3 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. If not, see . +*/ + + +#include "googlewrapper.h" +#include + +#include "ui_configwidget.h" + +SipPlugin* +GoogleWrapperFactory::createPlugin( const QString& pluginId ) +{ + return new GoogleWrapper( pluginId.isEmpty() ? generateId() : pluginId ); +} + +QIcon +GoogleWrapperFactory::icon() const +{ + return QIcon( ":/gmail-logo.png" ); +} + +GoogleWrapper::GoogleWrapper ( const QString& pluginID ) + : JabberPlugin ( pluginID ) +{ + m_ui->headerLabel->setText( tr( "Configure this Google Account" ) ); + m_ui->emailLabel->setText( tr( "GMail Address" ) ); + +} + +QIcon +GoogleWrapper::icon() const +{ + return QIcon( ":/gmail-logo.png" ); +} + + +#ifdef GOOGLE_WRAPPER +Q_EXPORT_PLUGIN2( sipfactory, GoogleWrapperFactory ) +#endif diff --git a/src/sip/jreen/googlewrapper/googlewrapper.h b/src/sip/jreen/googlewrapper/googlewrapper.h new file mode 100644 index 000000000..80fe3290a --- /dev/null +++ b/src/sip/jreen/googlewrapper/googlewrapper.h @@ -0,0 +1,51 @@ +/* + Copyright (C) 2011 Leo Franchi + + 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 3 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. If not, see . +*/ + + +#ifndef GOOGLEWRAPPER_H +#define GOOGLEWRAPPER_H + +#include "sip/jreen/jabber.h" + +class SIPDLLEXPORT GoogleWrapperFactory : public SipPluginFactory +{ + Q_OBJECT + Q_INTERFACES( SipPluginFactory ) + +public: + GoogleWrapperFactory() {} + virtual ~GoogleWrapperFactory() {} + + virtual QString prettyName() const { return "GMail"; } + virtual QString factoryId() const { return "sipgoogle"; } + virtual QIcon icon() const; + virtual SipPlugin* createPlugin( const QString& pluginId ); +}; + +class SIPDLLEXPORT GoogleWrapper : public JabberPlugin +{ + Q_OBJECT +public: + GoogleWrapper( const QString& pluginID ); + virtual ~GoogleWrapper() {} + + virtual const QString name() const { return QString( "GMail" ); } + virtual const QString friendlyName() const { return "GMail Friends"; } + virtual QIcon icon() const; +}; + +#endif // GOOGLEWRAPPER_H diff --git a/src/sip/jreen/googlewrapper/resources.qrc b/src/sip/jreen/googlewrapper/resources.qrc new file mode 100644 index 000000000..b3838d682 --- /dev/null +++ b/src/sip/jreen/googlewrapper/resources.qrc @@ -0,0 +1,5 @@ + + +gmail-logo.png + + diff --git a/src/sip/jreen/jabber-icon.png b/src/sip/jreen/jabber-icon.png new file mode 100644 index 000000000..1b5f78b2f Binary files /dev/null and b/src/sip/jreen/jabber-icon.png differ diff --git a/src/sip/jreen/jabber.cpp b/src/sip/jreen/jabber.cpp index 3c4f519c3..f85669947 100644 --- a/src/sip/jreen/jabber.cpp +++ b/src/sip/jreen/jabber.cpp @@ -43,20 +43,44 @@ #include #include -JabberPlugin::JabberPlugin() - : m_menu( 0 ) +#include "ui_configwidget.h" + +SipPlugin* +JabberFactory::createPlugin( const QString& pluginId ) +{ + return new JabberPlugin( pluginId.isEmpty() ? generateId() : pluginId ); +} + +QIcon +JabberFactory::icon() const +{ + return QIcon( ":/jabber-icon.png" ); +} + +JabberPlugin::JabberPlugin( const QString& pluginId ) + : SipPlugin( pluginId ) + , m_menu( 0 ) , m_addFriendAction( 0 ) - , m_connected(false) + , m_state( Disconnected ) { qDebug() << Q_FUNC_INFO; qsrand(QDateTime::currentDateTime().toTime_t()); - // load settings - m_currentUsername = TomahawkSettings::instance()->jabberUsername(); - m_currentServer = TomahawkSettings::instance()->jabberServer(); - m_currentPassword = TomahawkSettings::instance()->jabberPassword(); - m_currentPort = TomahawkSettings::instance()->jabberPort(); + m_configWidget = QWeakPointer< QWidget >( new QWidget ); + m_ui = new Ui_JabberConfig; + m_ui->setupUi( m_configWidget.data() ); + m_configWidget.data()->setVisible( false ); + + m_ui->checkBoxAutoConnect->setChecked( readAutoConnect() ); + m_ui->jabberUsername->setText( accountName() ); + m_ui->jabberPassword->setText( readPassword() ); + m_ui->jabberServer->setText( readServer() ); + m_ui->jabberPort->setValue( readPort() ); + m_currentUsername = accountName(); + m_currentServer = readServer(); + m_currentPassword = readPassword(); + m_currentPort = readPort(); // setup JID object Jreen::JID jid = Jreen::JID( accountName() ); @@ -135,21 +159,21 @@ JabberPlugin::setProxy( const QNetworkProxy &proxy ) const QString -JabberPlugin::name() +JabberPlugin::name() const { return QString( MYNAME ); } const QString -JabberPlugin::friendlyName() +JabberPlugin::friendlyName() const { return QString( "Jabber" ); } const QString -JabberPlugin::accountName() +JabberPlugin::accountName() const { - return TomahawkSettings::instance()->jabberUsername(); + return TomahawkSettings::instance()->value( pluginId() + "/username" ).toString(); } QMenu* @@ -158,31 +182,51 @@ JabberPlugin::menu() return m_menu; } +QWidget* +JabberPlugin::configWidget() +{ + return m_configWidget.data(); +} + +QIcon +JabberPlugin::icon() const +{ + return QIcon( ":/jabber-icon.png" ); +} + + bool JabberPlugin::connectPlugin( bool startup ) { qDebug() << Q_FUNC_INFO; - if ( startup && !TomahawkSettings::instance()->jabberAutoConnect() ) + if ( startup && !readAutoConnect() ) return false; - qDebug() << "Connecting to the XMPP server..." << m_connected; + if(m_client->isConnected()) + { + qDebug() << Q_FUNC_INFO << "Already connected to server, not connecting again..."; + return true; //FIXME: should i return false here?! + } + + qDebug() << "Connecting to the XMPP server..."; + qDebug() << m_client->jid().full(); - //m_client->setServer( m_client->jid().domain() ); qDebug() << m_client->server() << m_client->port(); + //FIXME: we're badly workarounding some missing reconnection api here, to be fixed soon QTimer::singleShot(1000, m_client, SLOT( connectToServer() ) ); - //m_client->connectToServer(); + + m_state = Connecting; + emit stateChanged( m_state ); return true; } void JabberPlugin::disconnectPlugin() { - qDebug() << Q_FUNC_INFO << m_connected; - - if(!m_connected) + if(!m_client->isConnected()) return; foreach(const Jreen::JID &peer, m_peers.keys()) @@ -215,8 +259,6 @@ JabberPlugin::onConnect() emit jidChanged( m_client->jid().full() ); } - emit connected(); - qDebug() << "Connected as:" << m_client->jid().full(); // set presence to least valid value @@ -240,7 +282,8 @@ JabberPlugin::onConnect() //connect( m_room, SIGNAL( messageReceived( Jreen::Message, bool ) ), this, SLOT( onNewMessage( Jreen::Message ) ) ); //connect( m_room, SIGNAL( presenceReceived( Jreen::Presence, const Jreen::MUCRoom::Participant* ) ), this, SLOT( onNewPresence( Jreen::Presence ) ) ); - m_connected = true; + m_state = Connected; + emit stateChanged( m_state ); addMenuHelper(); } @@ -323,14 +366,13 @@ JabberPlugin::onDisconnect( Jreen::Client::DisconnectReason reason ) Q_ASSERT(false); break; } + m_state = Disconnected; + emit stateChanged( m_state ); - emit disconnected(); removeMenuHelper(); if(reconnect) QTimer::singleShot(reconnectInSeconds*1000, this, SLOT(connectPlugin())); - - m_connected = false; } void @@ -435,25 +477,29 @@ void JabberPlugin::checkSettings() { bool reconnect = false; - if ( m_currentUsername != TomahawkSettings::instance()->jabberUsername() ) + if ( m_currentUsername != accountName() ) reconnect = true; - if ( m_currentPassword != TomahawkSettings::instance()->jabberPassword() ) + if ( m_currentPassword != readPassword() ) reconnect = true; - if ( m_currentServer != TomahawkSettings::instance()->jabberServer() ) + if ( m_currentServer != readServer() ) reconnect = true; - if ( m_currentPort != TomahawkSettings::instance()->jabberPort() ) + if ( m_currentPort != readPort() ) reconnect = true; + m_currentUsername = accountName(); + m_currentPassword = readPassword(); + m_currentServer = readServer(); + m_currentPort = readPort(); if ( reconnect ) { qDebug() << Q_FUNC_INFO << "Reconnecting jreen plugin..."; disconnectPlugin(); - m_currentUsername = TomahawkSettings::instance()->jabberUsername(); - m_currentPassword = TomahawkSettings::instance()->jabberPassword(); - m_currentServer = TomahawkSettings::instance()->jabberServer(); - m_currentPort = TomahawkSettings::instance()->jabberPort(); + m_currentUsername = accountName(); + m_currentPassword = readPassword(); + m_currentServer = readServer(); + m_currentPort = readPort(); setupClientHelper(); @@ -844,5 +890,49 @@ void JabberPlugin::onNewAvatar(const QString& jid) } +QString +JabberPlugin::readPassword() +{ + return TomahawkSettings::instance()->value( pluginId() + "/password" ).toString(); +} -Q_EXPORT_PLUGIN2( sip, JabberPlugin ) +int +JabberPlugin::readPort() +{ + return TomahawkSettings::instance()->value( pluginId() + "/port", 5222 ).toInt(); +} + +QString +JabberPlugin::readServer() +{ + return TomahawkSettings::instance()->value( pluginId() + "/server" ).toString(); +} + +bool +JabberPlugin::readAutoConnect() +{ + return TomahawkSettings::instance()->value( pluginId() + "/autoconnect", true ).toBool(); +} + +void +JabberPlugin::saveConfig() +{ + TomahawkSettings::instance()->setValue( pluginId() + "/autoconnect", m_ui->checkBoxAutoConnect->isChecked() ); + TomahawkSettings::instance()->setValue( pluginId() + "/username", m_ui->jabberUsername->text() ); + TomahawkSettings::instance()->setValue( pluginId() + "/password", m_ui->jabberPassword->text() ); + TomahawkSettings::instance()->setValue( pluginId() + "/port", m_ui->jabberPort->value() ); + TomahawkSettings::instance()->setValue( pluginId() + "/server", m_ui->jabberServer->text() ); + + checkSettings(); +} + + +SipPlugin::ConnectionState +JabberPlugin::connectionState() const +{ + return m_state; +} + +#ifndef GOOGLE_WRAPPER +Q_EXPORT_PLUGIN2( sipfactory, JabberFactory ) +#endif diff --git a/src/sip/jreen/jabber.h b/src/sip/jreen/jabber.h index cb0b3c2b8..cf35ed927 100644 --- a/src/sip/jreen/jabber.h +++ b/src/sip/jreen/jabber.h @@ -46,21 +46,41 @@ #include "../sipdllmacro.h" +class Ui_JabberConfig; + +class SIPDLLEXPORT JabberFactory : public SipPluginFactory +{ + Q_OBJECT + Q_INTERFACES( SipPluginFactory ) + +public: + JabberFactory() {} + virtual ~JabberFactory() {} + + virtual QString prettyName() const { return "Jabber"; } + virtual QString factoryId() const { return "sipjabber"; } + virtual QIcon icon() const; + virtual SipPlugin* createPlugin( const QString& pluginId ); +}; + class SIPDLLEXPORT JabberPlugin : public SipPlugin { Q_OBJECT - Q_INTERFACES( SipPlugin ) public: - JabberPlugin(); + JabberPlugin( const QString& pluginId ); virtual ~JabberPlugin(); //FIXME: Make this more correct - virtual bool isValid() { return true; } - virtual const QString name(); - virtual const QString friendlyName(); - virtual const QString accountName(); + virtual bool isValid() const { return true; } + virtual const QString name() const; + virtual const QString friendlyName() const; + virtual const QString accountName() const; + virtual ConnectionState connectionState() const; virtual QMenu* menu(); + virtual QIcon icon() const; + virtual QWidget* configWidget(); + virtual void saveConfig(); signals: void jidChanged( const QString& ); @@ -74,6 +94,9 @@ public slots: void addContact( const QString &jid, const QString& msg = QString() ); void setProxy( const QNetworkProxy &proxy ); +protected: + Ui_JabberConfig* m_ui; // so the google wrapper can change the config dialog a bit + private slots: void showAddFriendDialog(); void onConnect(); @@ -93,6 +116,11 @@ private slots: void onNewAvatar( const QString &jid ); private: + QString readPassword(); + QString readServer(); + bool readAutoConnect(); + int readPort(); + void setupClientHelper(); void addMenuHelper(); void removeMenuHelper(); @@ -100,8 +128,6 @@ private: bool presenceMeansOnline( Jreen::Presence::Type p ); void handlePeerStatus( const Jreen::JID &jid, Jreen::Presence::Type presenceType ); - bool m_connected; - QMenu* m_menu; QAction* m_addFriendAction; @@ -109,6 +135,10 @@ private: QString m_currentPassword; QString m_currentServer; unsigned int m_currentPort; + ConnectionState m_state; + + QWeakPointer< QWidget > m_configWidget; + QString m_currentResource; // sort out diff --git a/src/sip/jreen/resources.qrc b/src/sip/jreen/resources.qrc new file mode 100644 index 000000000..9700ac51f --- /dev/null +++ b/src/sip/jreen/resources.qrc @@ -0,0 +1,5 @@ + + +jabber-icon.png + + diff --git a/src/sip/twitter/CMakeLists.txt b/src/sip/twitter/CMakeLists.txt index f50abba2d..7944173fd 100644 --- a/src/sip/twitter/CMakeLists.txt +++ b/src/sip/twitter/CMakeLists.txt @@ -26,9 +26,10 @@ include_directories( . ${CMAKE_CURRENT_BINARY_DIR} .. ${CMAKE_SOURCE_DIR}/thirdparty/qtweetlib/tomahawk-custom ) +qt4_add_resources( RC_SRCS "resources.qrc" ) qt4_wrap_cpp( twitterMoc ${twitterHeaders} ) qt4_wrap_ui( twitterUI_H ${twitterUI} ) -add_library( tomahawk_siptwitter SHARED ${twitterUI_H} ${twitterSources} ${twitterMoc} ) +add_library( tomahawk_siptwitter SHARED ${twitterUI_H} ${twitterSources} ${twitterMoc} ${RC_SRCS} ) IF( WIN32 ) SET( OS_SPECIFIC_LINK_LIBRARIES diff --git a/src/sip/twitter/resources.qrc b/src/sip/twitter/resources.qrc new file mode 100644 index 000000000..fc7df302f --- /dev/null +++ b/src/sip/twitter/resources.qrc @@ -0,0 +1,5 @@ + + +twitter-icon.png + + diff --git a/src/sip/twitter/twitter-icon.png b/src/sip/twitter/twitter-icon.png new file mode 100644 index 000000000..aab1fca87 Binary files /dev/null and b/src/sip/twitter/twitter-icon.png differ diff --git a/src/sip/twitter/twitter.cpp b/src/sip/twitter/twitter.cpp index 65a9af55e..608e3a2a5 100644 --- a/src/sip/twitter/twitter.cpp +++ b/src/sip/twitter/twitter.cpp @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -35,10 +35,21 @@ static QString s_gotTomahawkRegex = QString( "^(@[a-zA-Z0-9]+ )?(Got Tomahawk\\?) (\\{[a-fA-F0-9\\-]+\\}) (.*)$" ); -TwitterPlugin::TwitterPlugin() - : SipPlugin() +SipPlugin* +TwitterFactory::createPlugin( const QString& pluginId ) +{ + return new TwitterPlugin( pluginId.isEmpty() ? generateId() : pluginId ); +} + +QIcon TwitterFactory::icon() const +{ + return QIcon( ":/twitter-icon.png" ); +} + + +TwitterPlugin::TwitterPlugin( const QString& pluginId ) + : SipPlugin( pluginId ) , m_isAuthed( false ) - , m_isOnline( false ) , m_checkTimer( this ) , m_connectTimer( this ) , m_cachedFriendsSinceId( 0 ) @@ -48,7 +59,7 @@ TwitterPlugin::TwitterPlugin() , m_keyCache() , m_finishedFriends( false ) , m_finishedMentions( false ) - , m_configWidget( 0 ) + , m_state( Disconnected ) { qDebug() << Q_FUNC_INFO; m_checkTimer.setInterval( 60000 ); @@ -58,51 +69,75 @@ TwitterPlugin::TwitterPlugin() m_connectTimer.setInterval( 60000 ); m_connectTimer.setSingleShot( false ); connect( &m_connectTimer, SIGNAL( timeout() ), SLOT( connectTimerFired() ) ); + + m_configWidget = QWeakPointer< TwitterConfigWidget >( new TwitterConfigWidget( this, 0 ) ); + connect( m_configWidget.data(), SIGNAL( twitterAuthed( bool ) ), SLOT( configDialogAuthedSignalSlot( bool ) ) ); + } void TwitterPlugin::configDialogAuthedSignalSlot( bool authed ) { - m_isAuthed = authed; + if ( !authed ) { - TomahawkSettings::instance()->setTwitterScreenName( QString() ); - TomahawkSettings::instance()->setTwitterOAuthToken( QString() ); - TomahawkSettings::instance()->setTwitterOAuthTokenSecret( QString() ); + if( m_isAuthed ) { + m_state = Disconnected; + emit stateChanged( m_state ); + } + + setTwitterScreenName( QString() ); + setTwitterOAuthToken( QString() ); + setTwitterOAuthTokenSecret( QString() ); } + + m_isAuthed = authed; } bool -TwitterPlugin::isValid() +TwitterPlugin::isValid() const { return m_isAuthed; } const QString -TwitterPlugin::name() +TwitterPlugin::name() const { return QString( MYNAME ); } const QString -TwitterPlugin::friendlyName() +TwitterPlugin::friendlyName() const { return tr("Twitter"); } const QString -TwitterPlugin::accountName() +TwitterPlugin::accountName() const { - return QString( TomahawkSettings::instance()->twitterScreenName() ); + if( twitterScreenName().isEmpty() ) + return friendlyName(); + else + return twitterScreenName(); } +QIcon +TwitterPlugin::icon() const +{ + return QIcon( ":/twitter-icon.png" ); +} + + +SipPlugin::ConnectionState +TwitterPlugin::connectionState() const +{ + return m_state; +} + + QWidget* TwitterPlugin::configWidget() { - m_configWidget = new TwitterConfigWidget( this ); - - connect( m_configWidget, SIGNAL( twitterAuthed(bool) ), SLOT( configDialogAuthedSignalSlot(bool) ) ); - - return m_configWidget; + return m_configWidget.data(); } bool @@ -110,9 +145,7 @@ TwitterPlugin::connectPlugin( bool /*startup*/ ) { qDebug() << Q_FUNC_INFO; - TomahawkSettings *settings = TomahawkSettings::instance(); - - m_cachedPeers = settings->twitterCachedPeers(); + m_cachedPeers = twitterCachedPeers(); QList peerlist = m_cachedPeers.keys(); qStableSort( peerlist.begin(), peerlist.end() ); foreach( QString screenName, peerlist ) @@ -122,20 +155,23 @@ TwitterPlugin::connectPlugin( bool /*startup*/ ) qDebug() << "TwitterPlugin : " << screenName << ", key " << prop << ", value " << ( cachedPeer[prop].canConvert< QString >() ? cachedPeer[prop].toString() : QString::number( cachedPeer[prop].toInt() ) ); QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, screenName ), QGenericArgument( "QHash< QString, QVariant >", (const void*)&cachedPeer ) ); } - - if ( settings->twitterOAuthToken().isEmpty() || settings->twitterOAuthTokenSecret().isEmpty() ) + + if ( twitterOAuthToken().isEmpty() || twitterOAuthTokenSecret().isEmpty() ) { qDebug() << "TwitterPlugin has empty Twitter credentials; not connecting"; return m_cachedPeers.isEmpty(); } - + if ( refreshTwitterAuth() ) { - QTweetAccountVerifyCredentials *credVerifier = new QTweetAccountVerifyCredentials( m_twitterAuth.data(), this ); - connect( credVerifier, SIGNAL( parsedUser(const QTweetUser &) ), SLOT( connectAuthVerifyReply(const QTweetUser &) ) ); - credVerifier->verify(); + QTweetAccountVerifyCredentials *credVerifier = new QTweetAccountVerifyCredentials( m_twitterAuth.data(), this ); + connect( credVerifier, SIGNAL( parsedUser(const QTweetUser &) ), SLOT( connectAuthVerifyReply(const QTweetUser &) ) ); + credVerifier->verify(); + + m_state = Connecting; + emit stateChanged( m_state ); } - + return true; } @@ -152,8 +188,6 @@ TwitterPlugin::refreshTwitterAuth() if( m_twitterAuth.isNull() ) return false; - - TomahawkSettings *settings = TomahawkSettings::instance(); m_twitterAuth.data()->setOAuthToken( settings->twitterOAuthToken().toLatin1() ); m_twitterAuth.data()->setOAuthTokenSecret( settings->twitterOAuthTokenSecret().toLatin1() ); @@ -181,7 +215,8 @@ TwitterPlugin::disconnectPlugin() delete m_twitterAuth.data(); m_cachedPeers.empty(); - m_isOnline = false; + m_state = Disconnected; + emit stateChanged( m_state ); } void @@ -191,6 +226,8 @@ TwitterPlugin::connectAuthVerifyReply( const QTweetUser &user ) { qDebug() << "TwitterPlugin could not authenticate to Twitter"; m_isAuthed = false; + m_state = Disconnected; + emit stateChanged( m_state ); } else { @@ -198,7 +235,7 @@ TwitterPlugin::connectAuthVerifyReply( const QTweetUser &user ) m_isAuthed = true; if ( !m_twitterAuth.isNull() ) { - TomahawkSettings::instance()->setTwitterScreenName( user.screenName() ); + setTwitterScreenName( user.screenName() ); m_friendsTimeline = QWeakPointer( new QTweetFriendsTimeline( m_twitterAuth.data(), this ) ); m_mentions = QWeakPointer( new QTweetMentions( m_twitterAuth.data(), this ) ); m_directMessages = QWeakPointer( new QTweetDirectMessages( m_twitterAuth.data(), this ) ); @@ -210,7 +247,8 @@ TwitterPlugin::connectAuthVerifyReply( const QTweetUser &user ) connect( m_directMessageNew.data(), SIGNAL( parsedDirectMessage(const QTweetDMStatus &)), SLOT( directMessagePosted(const QTweetDMStatus &) ) ); connect( m_directMessageNew.data(), SIGNAL( error(QTweetNetBase::ErrorCode, const QString &) ), SLOT( directMessagePostError(QTweetNetBase::ErrorCode, const QString &) ) ); connect( m_directMessageDestroy.data(), SIGNAL( parsedDirectMessage(const QTweetDMStatus &) ), SLOT( directMessageDestroyed(const QTweetDMStatus &) ) ); - m_isOnline = true; + m_state = Connected; + emit stateChanged( m_state ); m_connectTimer.start(); m_checkTimer.start(); QMetaObject::invokeMethod( this, "checkTimerFired", Qt::AutoConnection ); @@ -228,6 +266,8 @@ TwitterPlugin::connectAuthVerifyReply( const QTweetUser &user ) { qDebug() << "TwitterPlugin auth pointer was null!"; m_isAuthed = false; + m_state = Disconnected; + emit stateChanged( m_state ); } } } @@ -240,18 +280,18 @@ TwitterPlugin::checkTimerFired() return; if ( m_cachedFriendsSinceId == 0 ) - m_cachedFriendsSinceId = TomahawkSettings::instance()->twitterCachedFriendsSinceId(); - + m_cachedFriendsSinceId = twitterCachedFriendsSinceId(); + qDebug() << "TwitterPlugin looking at friends timeline since id " << m_cachedFriendsSinceId; - + if ( !m_friendsTimeline.isNull() ) - m_friendsTimeline.data()->fetch( m_cachedFriendsSinceId, 0, 800 ); - + m_friendsTimeline.data()->fetch( m_cachedFriendsSinceId, 0, 800 ); + if ( m_cachedMentionsSinceId == 0 ) - m_cachedMentionsSinceId = TomahawkSettings::instance()->twitterCachedMentionsSinceId(); - + m_cachedMentionsSinceId = twitterCachedMentionsSinceId(); + qDebug() << "TwitterPlugin looking at mentions timeline since id " << m_cachedMentionsSinceId; - + if ( !m_mentions.isNull() ) m_mentions.data()->fetch( m_cachedMentionsSinceId, 0, 800 ); } @@ -262,7 +302,7 @@ TwitterPlugin::connectTimerFired() if ( !isValid() || m_cachedPeers.isEmpty() || m_twitterAuth.isNull() ) return; - QString myScreenName = TomahawkSettings::instance()->twitterScreenName(); + QString myScreenName = twitterScreenName(); QList peerlist = m_cachedPeers.keys(); qStableSort( peerlist.begin(), peerlist.end() ); foreach( QString screenName, peerlist ) @@ -275,20 +315,20 @@ TwitterPlugin::connectTimerFired() m_cachedPeers[screenName] = peerData; continue; } - + if ( QDateTime::currentMSecsSinceEpoch() - peerData["lastseen"].toLongLong() > 1209600000 ) // 2 weeks { qDebug() << "Aging peer " << screenName << " out of cache"; m_cachedPeers.remove( screenName ); continue; } - + if ( !peerData.contains( "host" ) || !peerData.contains( "port" ) || !peerData.contains( "pkey" ) ) { qDebug() << "TwitterPlugin does not have host, port and/or pkey values for " << screenName << " (this is usually *not* a bug or problem but a normal part of the process)"; continue; } - + QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, screenName ), QGenericArgument( "QHash< QString, QVariant >", (const void*)&peerData ) ); } } @@ -296,7 +336,7 @@ TwitterPlugin::connectTimerFired() void TwitterPlugin::parseGotTomahawk( const QRegExp ®ex, const QString &screenName, const QString &text ) { - QString myScreenName = TomahawkSettings::instance()->twitterScreenName(); + QString myScreenName = twitterScreenName(); qDebug() << "TwitterPlugin found an exact matching Got Tomahawk? mention or direct message from user " << screenName << ", now parsing"; regex.exactMatch( text ); if ( text.startsWith( '@' ) && regex.captureCount() >= 2 && regex.cap( 1 ) != QString( '@' + myScreenName ) ) @@ -304,7 +344,7 @@ TwitterPlugin::parseGotTomahawk( const QRegExp ®ex, const QString &screenName qDebug() << "TwitterPlugin skipping mention because it's directed @someone that isn't us"; return; } - + QString node; for ( int i = 0; i < regex.captureCount(); ++i ) { @@ -322,13 +362,13 @@ TwitterPlugin::parseGotTomahawk( const QRegExp ®ex, const QString &screenName } else qDebug() << "TwitterPlugin parsed node " << node << " out of the tweet"; - + if ( screenName == myScreenName && node == Database::instance()->dbid() ) { qDebug() << "My screen name and my dbid found; ignoring"; return; } - + QHash< QString, QVariant > peerData; if( m_cachedPeers.contains( screenName ) ) { @@ -345,14 +385,14 @@ TwitterPlugin::friendsTimelineStatuses( const QList< QTweetStatus > &statuses ) { qDebug() << Q_FUNC_INFO; QRegExp regex( s_gotTomahawkRegex, Qt::CaseSensitive, QRegExp::RegExp2 ); - QString myScreenName = TomahawkSettings::instance()->twitterScreenName(); - + QString myScreenName = twitterScreenName(); + QHash< QString, QTweetStatus > latestHash; foreach ( QTweetStatus status, statuses ) { if ( !regex.exactMatch( status.text() ) ) continue; - + if ( !latestHash.contains( status.user().screenName() ) ) latestHash[status.user().screenName()] = status; else @@ -361,7 +401,7 @@ TwitterPlugin::friendsTimelineStatuses( const QList< QTweetStatus > &statuses ) latestHash[status.user().screenName()] = status; } } - + foreach( QTweetStatus status, latestHash.values() ) { if ( status.id() > m_cachedFriendsSinceId ) @@ -370,9 +410,9 @@ TwitterPlugin::friendsTimelineStatuses( const QList< QTweetStatus > &statuses ) qDebug() << "TwitterPlugin checking mention from " << status.user().screenName() << " with content " << status.text(); parseGotTomahawk( regex, status.user().screenName(), status.text() ); } - - TomahawkSettings::instance()->setTwitterCachedFriendsSinceId( m_cachedFriendsSinceId ); - + + setTwitterCachedFriendsSinceId( m_cachedFriendsSinceId ); + m_finishedFriends = true; QMetaObject::invokeMethod( this, "pollDirectMessages", Qt::AutoConnection ); } @@ -382,13 +422,13 @@ TwitterPlugin::mentionsStatuses( const QList< QTweetStatus > &statuses ) { qDebug() << Q_FUNC_INFO; QRegExp regex( s_gotTomahawkRegex, Qt::CaseSensitive, QRegExp::RegExp2 ); - + QHash< QString, QTweetStatus > latestHash; foreach ( QTweetStatus status, statuses ) { if ( !regex.exactMatch( status.text() ) ) continue; - + if ( !latestHash.contains( status.user().screenName() ) ) latestHash[status.user().screenName()] = status; else @@ -397,18 +437,18 @@ TwitterPlugin::mentionsStatuses( const QList< QTweetStatus > &statuses ) latestHash[status.user().screenName()] = status; } } - + foreach( QTweetStatus status, latestHash.values() ) { if ( status.id() > m_cachedMentionsSinceId ) m_cachedMentionsSinceId = status.id(); - + qDebug() << "TwitterPlugin checking mention from " << status.user().screenName() << " with content " << status.text(); parseGotTomahawk( regex, status.user().screenName(), status.text() ); } - - TomahawkSettings::instance()->setTwitterCachedMentionsSinceId( m_cachedMentionsSinceId ); - + + setTwitterCachedMentionsSinceId( m_cachedMentionsSinceId ); + m_finishedMentions = true; QMetaObject::invokeMethod( this, "pollDirectMessages", Qt::AutoConnection ); } @@ -418,18 +458,18 @@ TwitterPlugin::pollDirectMessages() { if ( !m_finishedMentions || !m_finishedFriends ) return; - + m_finishedFriends = false; m_finishedMentions = false; - + if ( !isValid() ) return; - + if ( m_cachedDirectMessagesSinceId == 0 ) - m_cachedDirectMessagesSinceId = TomahawkSettings::instance()->twitterCachedDirectMessagesSinceId(); - + m_cachedDirectMessagesSinceId = twitterCachedDirectMessagesSinceId(); + qDebug() << "TwitterPlugin looking for direct messages since id " << m_cachedDirectMessagesSinceId; - + if ( !m_directMessages.isNull() ) m_directMessages.data()->fetch( m_cachedDirectMessagesSinceId, 0, 800 ); } @@ -438,10 +478,10 @@ void TwitterPlugin::directMessages( const QList< QTweetDMStatus > &messages ) { qDebug() << Q_FUNC_INFO; - + QRegExp regex( s_gotTomahawkRegex, Qt::CaseSensitive, QRegExp::RegExp2 ); - QString myScreenName = TomahawkSettings::instance()->twitterScreenName(); - + QString myScreenName = twitterScreenName(); + QHash< QString, QTweetDMStatus > latestHash; foreach ( QTweetDMStatus status, messages ) { @@ -458,7 +498,7 @@ TwitterPlugin::directMessages( const QList< QTweetDMStatus > &messages ) if ( port == 0 ) continue; } - + if ( !latestHash.contains( status.senderScreenName() ) ) latestHash[status.senderScreenName()] = status; else @@ -467,13 +507,13 @@ TwitterPlugin::directMessages( const QList< QTweetDMStatus > &messages ) latestHash[status.senderScreenName()] = status; } } - + foreach( QTweetDMStatus status, latestHash.values() ) { qDebug() << "TwitterPlugin checking direct message from " << status.senderScreenName() << " with content " << status.text(); if ( status.id() > m_cachedDirectMessagesSinceId ) m_cachedDirectMessagesSinceId = status.id(); - + if ( regex.exactMatch( status.text() ) ) parseGotTomahawk( regex, status.sender().screenName(), status.text() ); else @@ -494,12 +534,12 @@ TwitterPlugin::directMessages( const QList< QTweetDMStatus > &messages ) continue; } qDebug() << "TwitterPlugin found a peerstart message from " << status.senderScreenName() << " with host " << host << " and port " << port << " and pkey " << pkey << " and node " << splitNode[0] << " destined for node " << splitNode[1]; - - + + QHash< QString, QVariant > peerData = ( m_cachedPeers.contains( status.senderScreenName() ) ) ? m_cachedPeers[status.senderScreenName()].toHash() : QHash< QString, QVariant >(); - + peerData["host"] = QVariant::fromValue< QString >( host ); peerData["port"] = QVariant::fromValue< int >( port ); peerData["pkey"] = QVariant::fromValue< QString >( pkey ); @@ -517,7 +557,7 @@ TwitterPlugin::directMessages( const QList< QTweetDMStatus > &messages ) } } - TomahawkSettings::instance()->setTwitterCachedDirectMessagesSinceId( m_cachedDirectMessagesSinceId ); + setTwitterCachedDirectMessagesSinceId( m_cachedDirectMessagesSinceId ); } void @@ -573,7 +613,7 @@ TwitterPlugin::registerOffer( const QString &screenName, const QHash< QString, Q qDebug() << "TwitterPlugin registering offer to " << friendlyName << " with node " << _peerData["node"].toString() << " and offeredkey " << _peerData["okey"].toString(); m_keyCache << Servent::instance()->createConnectionKey( friendlyName, _peerData["node"].toString(), _peerData["okey"].toString(), false ); } - + if( needToSend && _peerData.contains( "node") ) { qDebug() << "TwitterPlugin needs to send and has node"; @@ -590,12 +630,12 @@ TwitterPlugin::registerOffer( const QString &screenName, const QHash< QString, Q { _peerData["lastseen"] = QString::number( QDateTime::currentMSecsSinceEpoch() ); m_cachedPeers[screenName] = QVariant::fromValue< QHash< QString, QVariant > >( _peerData ); - TomahawkSettings::instance()->setTwitterCachedPeers( m_cachedPeers ); + setTwitterCachedPeers( m_cachedPeers ); } - if ( m_isOnline && _peerData.contains( "host" ) && _peerData.contains( "port" ) && _peerData.contains( "pkey" ) ) + if ( m_state == Connected && _peerData.contains( "host" ) && _peerData.contains( "port" ) && _peerData.contains( "pkey" ) ) QMetaObject::invokeMethod( this, "makeConnection", Q_ARG( QString, screenName ), QGenericArgument( "QHash< QString, QVariant >", (const void*)&_peerData ) ); - + } void @@ -636,7 +676,7 @@ TwitterPlugin::directMessagePosted( const QTweetDMStatus& message ) { qDebug() << Q_FUNC_INFO; qDebug() << "TwitterPlugin sent message to " << message.recipientScreenName() << " containing: " << message.text(); - + } void @@ -662,4 +702,89 @@ TwitterPlugin::checkSettings() connectPlugin( false ); } -Q_EXPORT_PLUGIN2( sip, TwitterPlugin ) + +QString +TwitterPlugin::twitterScreenName() const +{ + return TomahawkSettings::instance()->value( pluginId() + "/ScreenName" ).toString(); +} + +void +TwitterPlugin::setTwitterScreenName( const QString& screenName ) +{ + TomahawkSettings::instance()->setValue( pluginId() + "/ScreenName", screenName ); +} + +QString +TwitterPlugin::twitterOAuthToken() const +{ + return TomahawkSettings::instance()->value( pluginId() + "/OAuthToken" ).toString(); +} + +void +TwitterPlugin::setTwitterOAuthToken( const QString& oauthtoken ) +{ + TomahawkSettings::instance()->setValue( pluginId() + "/OAuthToken", oauthtoken ); +} + +QString +TwitterPlugin::twitterOAuthTokenSecret() const +{ + return TomahawkSettings::instance()->value( pluginId() + "/OAuthTokenSecret" ).toString(); +} + +void +TwitterPlugin::setTwitterOAuthTokenSecret( const QString& oauthtokensecret ) +{ + TomahawkSettings::instance()->setValue( pluginId() + "/OAuthTokenSecret", oauthtokensecret ); +} + +qint64 +TwitterPlugin::twitterCachedFriendsSinceId() const +{ + return TomahawkSettings::instance()->value( pluginId() + "/CachedFriendsSinceID", 0 ).toLongLong(); +} + +void +TwitterPlugin::setTwitterCachedFriendsSinceId( qint64 cachedId ) +{ + TomahawkSettings::instance()->setValue( pluginId() + "/CachedFriendsSinceID", cachedId ); +} + +qint64 +TwitterPlugin::twitterCachedMentionsSinceId() const +{ + return TomahawkSettings::instance()->value( pluginId() + "/CachedMentionsSinceID", 0 ).toLongLong(); +} + +void +TwitterPlugin::setTwitterCachedMentionsSinceId( qint64 cachedId ) +{ + TomahawkSettings::instance()->setValue( pluginId() + "/CachedMentionsSinceID", cachedId ); +} + +qint64 +TwitterPlugin::twitterCachedDirectMessagesSinceId() const +{ + return TomahawkSettings::instance()->value( pluginId() + "/CachedDirectMessagesSinceID", 0 ).toLongLong(); +} + +void +TwitterPlugin::setTwitterCachedDirectMessagesSinceId( qint64 cachedId ) +{ + TomahawkSettings::instance()->setValue( pluginId() + "/CachedDirectMessagesSinceID", cachedId ); +} + +QHash +TwitterPlugin::twitterCachedPeers() const +{ + return TomahawkSettings::instance()->value( pluginId() + "/CachedPeers", QHash() ).toHash(); +} + +void +TwitterPlugin::setTwitterCachedPeers( const QHash &cachedPeers ) +{ + TomahawkSettings::instance()->setValue( pluginId() + "/CachedPeers", cachedPeers ); +} + +Q_EXPORT_PLUGIN2( sipfactory, TwitterFactory ) diff --git a/src/sip/twitter/twitter.h b/src/sip/twitter/twitter.h index 418cc1f07..154fe7d5f 100644 --- a/src/sip/twitter/twitter.h +++ b/src/sip/twitter/twitter.h @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -40,21 +40,36 @@ #define MYNAME "SIPTWITTER" +class SIPDLLEXPORT TwitterFactory : public SipPluginFactory +{ + Q_OBJECT + Q_INTERFACES( SipPluginFactory ) + +public: + TwitterFactory() {} + virtual ~TwitterFactory() {} + + virtual QString prettyName() const { return "Twitter"; } + virtual QString factoryId() const { return "siptwitter"; } + virtual QIcon icon() const; + virtual SipPlugin* createPlugin( const QString& pluginId = QString() ); +}; + class SIPDLLEXPORT TwitterPlugin : public SipPlugin { Q_OBJECT - Q_INTERFACES( SipPlugin ) - + public: - TwitterPlugin(); + TwitterPlugin( const QString& pluginId ); virtual ~TwitterPlugin() {} - - virtual bool isValid(); - virtual const QString name(); - virtual const QString accountName(); - virtual const QString friendlyName(); + virtual bool isValid() const; + virtual const QString name() const; + virtual const QString accountName() const; + virtual const QString friendlyName() const; + virtual ConnectionState connectionState() const; + virtual QIcon icon() const; virtual QWidget* configWidget(); public slots: @@ -98,6 +113,21 @@ private slots: private: bool refreshTwitterAuth(); void parseGotTomahawk( const QRegExp ®ex, const QString &screenName, const QString &text ); + // handle per-plugin config + QString twitterScreenName() const; + void setTwitterScreenName( const QString& screenName ); + QString twitterOAuthToken() const; + void setTwitterOAuthToken( const QString& oauthtoken ); + QString twitterOAuthTokenSecret() const; + void setTwitterOAuthTokenSecret( const QString& oauthtokensecret ); + qint64 twitterCachedFriendsSinceId() const; + void setTwitterCachedFriendsSinceId( qint64 sinceid ); + qint64 twitterCachedMentionsSinceId() const; + void setTwitterCachedMentionsSinceId( qint64 sinceid ); + qint64 twitterCachedDirectMessagesSinceId() const; + void setTwitterCachedDirectMessagesSinceId( qint64 sinceid ); + QHash twitterCachedPeers() const; + void setTwitterCachedPeers( const QHash &cachedPeers ); QWeakPointer< TomahawkOAuthTwitter > m_twitterAuth; QWeakPointer< QTweetFriendsTimeline > m_friendsTimeline; @@ -105,8 +135,8 @@ private: QWeakPointer< QTweetDirectMessages > m_directMessages; QWeakPointer< QTweetDirectMessageNew > m_directMessageNew; QWeakPointer< QTweetDirectMessageDestroy > m_directMessageDestroy; + bool m_isAuthed; - bool m_isOnline; QTimer m_checkTimer; QTimer m_connectTimer; qint64 m_cachedFriendsSinceId; @@ -116,8 +146,12 @@ private: QSet m_keyCache; bool m_finishedFriends; bool m_finishedMentions; + ConnectionState m_state; - TwitterConfigWidget *m_configWidget; + QWeakPointer m_configWidget; + + // for settings access + friend class TwitterConfigWidget; }; #endif diff --git a/src/sip/twitter/twitterconfigwidget.cpp b/src/sip/twitter/twitterconfigwidget.cpp index 379cfea20..580ac3471 100644 --- a/src/sip/twitter/twitterconfigwidget.cpp +++ b/src/sip/twitter/twitterconfigwidget.cpp @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -17,6 +17,7 @@ */ #include "twitterconfigwidget.h" +#include "twitter.h" #include "ui_twitterconfigwidget.h" #include "tomahawksettings.h" @@ -30,7 +31,7 @@ #include -TwitterConfigWidget::TwitterConfigWidget( SipPlugin* plugin, QWidget *parent ) : +TwitterConfigWidget::TwitterConfigWidget( TwitterPlugin* plugin, QWidget *parent ) : QWidget( parent ), ui( new Ui::TwitterConfigWidget ), m_plugin( plugin ) @@ -47,9 +48,8 @@ TwitterConfigWidget::TwitterConfigWidget( SipPlugin* plugin, QWidget *parent ) : ui->twitterTweetComboBox->setCurrentIndex( 0 ); ui->twitterUserTweetLineEdit->setReadOnly( true ); ui->twitterUserTweetLineEdit->setEnabled( false ); - - TomahawkSettings* s = TomahawkSettings::instance(); - if ( s->twitterOAuthToken().isEmpty() || s->twitterOAuthTokenSecret().isEmpty() || s->twitterScreenName().isEmpty() ) + + if ( m_plugin->twitterOAuthToken().isEmpty() || m_plugin->twitterOAuthTokenSecret().isEmpty() || m_plugin->twitterScreenName().isEmpty() ) { ui->twitterStatusLabel->setText( tr( "Status: No saved credentials" ) ); ui->twitterAuthenticateButton->setText( tr( "Authenticate" ) ); @@ -58,12 +58,12 @@ TwitterConfigWidget::TwitterConfigWidget( SipPlugin* plugin, QWidget *parent ) : ui->twitterTweetGotTomahawkButton->setVisible( false ); ui->twitterUserTweetLineEdit->setVisible( false ); ui->twitterTweetComboBox->setVisible( false ); - + emit twitterAuthed( false ); } else { - ui->twitterStatusLabel->setText( tr( "Status: Credentials saved for %1" ).arg( s->twitterScreenName() ) ); + ui->twitterStatusLabel->setText( tr( "Status: Credentials saved for %1" ).arg( m_plugin->twitterScreenName() ) ); ui->twitterAuthenticateButton->setText( tr( "De-authenticate" ) ); ui->twitterInstructionsInfoLabel->setVisible( true ); ui->twitterGlobalTweetLabel->setVisible( true ); @@ -96,11 +96,10 @@ TwitterConfigWidget::authenticateTwitter() qDebug() << Q_FUNC_INFO; TomahawkOAuthTwitter *twitAuth = new TomahawkOAuthTwitter( TomahawkUtils::nam(), this ); twitAuth->authorizePin(); - - TomahawkSettings* s = TomahawkSettings::instance(); - s->setTwitterOAuthToken( twitAuth->oauthToken() ); - s->setTwitterOAuthTokenSecret( twitAuth->oauthTokenSecret() ); - + + m_plugin->setTwitterOAuthToken( twitAuth->oauthToken() ); + m_plugin->setTwitterOAuthTokenSecret( twitAuth->oauthTokenSecret() ); + QTweetAccountVerifyCredentials *credVerifier = new QTweetAccountVerifyCredentials( twitAuth, this ); connect( credVerifier, SIGNAL( parsedUser( const QTweetUser & ) ), SLOT( authenticateVerifyReply( const QTweetUser & ) ) ); connect( credVerifier, SIGNAL( error( QTweetNetBase::ErrorCode, QString ) ), SLOT( authenticateVerifyError( QTweetNetBase::ErrorCode, QString ) ) ); @@ -118,12 +117,11 @@ TwitterConfigWidget::authenticateVerifyReply( const QTweetUser &user ) return; } - TomahawkSettings* s = TomahawkSettings::instance(); - s->setTwitterScreenName( user.screenName() ); - s->setTwitterCachedFriendsSinceId( 0 ); - s->setTwitterCachedMentionsSinceId( 0 ); - - ui->twitterStatusLabel->setText( tr( "Status: Credentials saved for %1" ).arg( s->twitterScreenName() ) ); + m_plugin->setTwitterScreenName( user.screenName() ); + m_plugin->setTwitterCachedFriendsSinceId( 0 ); + m_plugin->setTwitterCachedMentionsSinceId( 0 ); + + ui->twitterStatusLabel->setText( tr( "Status: Credentials saved for %1" ).arg( m_plugin->twitterScreenName() ) ); ui->twitterAuthenticateButton->setText( tr( "De-authenticate" ) ); ui->twitterInstructionsInfoLabel->setVisible( true ); ui->twitterGlobalTweetLabel->setVisible( true ); @@ -132,7 +130,7 @@ TwitterConfigWidget::authenticateVerifyReply( const QTweetUser &user ) ui->twitterTweetComboBox->setVisible( true ); m_plugin->connectPlugin( false ); - + emit twitterAuthed( true ); } @@ -150,11 +148,10 @@ void TwitterConfigWidget::deauthenticateTwitter() { qDebug() << Q_FUNC_INFO; - TomahawkSettings* s = TomahawkSettings::instance(); - s->setTwitterOAuthToken( QString() ); - s->setTwitterOAuthTokenSecret( QString() ); - s->setTwitterScreenName( QString() ); - + m_plugin->setTwitterOAuthToken( QString() ); + m_plugin->setTwitterOAuthTokenSecret( QString() ); + m_plugin->setTwitterScreenName( QString() ); + ui->twitterStatusLabel->setText(tr("Status: No saved credentials")); ui->twitterAuthenticateButton->setText( tr( "Authenticate" ) ); ui->twitterInstructionsInfoLabel->setVisible( false ); @@ -162,7 +159,7 @@ TwitterConfigWidget::deauthenticateTwitter() ui->twitterTweetGotTomahawkButton->setVisible( false ); ui->twitterUserTweetLineEdit->setVisible( false ); ui->twitterTweetComboBox->setVisible( false ); - + emit twitterAuthed( false ); } @@ -180,7 +177,7 @@ TwitterConfigWidget::tweetComboBoxIndexChanged( int index ) ui->twitterUserTweetLineEdit->setReadOnly( false ); ui->twitterUserTweetLineEdit->setEnabled( true ); } - + if( ui->twitterTweetComboBox->currentText() == tr( "Direct Message" ) ) //FIXME: use data! ui->twitterTweetGotTomahawkButton->setText( tr( "Send Message!" ) ); else @@ -191,16 +188,15 @@ void TwitterConfigWidget::startPostGotTomahawkStatus() { m_postGTtype = ui->twitterTweetComboBox->currentText(); - + if ( m_postGTtype != "Global Tweet" && ( ui->twitterUserTweetLineEdit->text().isEmpty() || ui->twitterUserTweetLineEdit->text() == "@" ) ) { QMessageBox::critical( this, tr("Tweetin' Error"), tr("You must enter a user name for this type of tweet.") ); return; } - + qDebug() << "Posting Got Tomahawk status"; - TomahawkSettings* s = TomahawkSettings::instance(); - if ( s->twitterOAuthToken().isEmpty() || s->twitterOAuthTokenSecret().isEmpty() || s->twitterScreenName().isEmpty() ) + if ( m_plugin->twitterOAuthToken().isEmpty() || m_plugin->twitterOAuthTokenSecret().isEmpty() || m_plugin->twitterScreenName().isEmpty() ) { QMessageBox::critical( this, tr("Tweetin' Error"), tr("Your saved credentials could not be loaded.\nYou may wish to try re-authenticating.") ); emit twitterAuthed( false ); @@ -223,11 +219,8 @@ TwitterConfigWidget::postGotTomahawkStatusAuthVerifyReply( const QTweetUser &use emit twitterAuthed( false ); return; } - TomahawkSettings* s = TomahawkSettings::instance(); - s->setTwitterScreenName( user.screenName() ); + m_plugin->setTwitterScreenName( user.screenName() ); TomahawkOAuthTwitter *twitAuth = new TomahawkOAuthTwitter( TomahawkUtils::nam(), this ); - twitAuth->setOAuthToken( s->twitterOAuthToken().toLatin1() ); - twitAuth->setOAuthTokenSecret( s->twitterOAuthTokenSecret().toLatin1() ); if ( m_postGTtype != "Direct Message" ) { QTweetStatusUpdate *statUpdate = new QTweetStatusUpdate( twitAuth, this ); diff --git a/src/sip/twitter/twitterconfigwidget.h b/src/sip/twitter/twitterconfigwidget.h index 85fabce90..3a059bfa8 100644 --- a/src/sip/twitter/twitterconfigwidget.h +++ b/src/sip/twitter/twitterconfigwidget.h @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -28,6 +28,7 @@ #include +class TwitterPlugin; namespace Ui { class TwitterConfigWidget; @@ -38,12 +39,12 @@ class TwitterConfigWidget : public QWidget Q_OBJECT public: - explicit TwitterConfigWidget( SipPlugin* plugin = 0, QWidget *parent = 0 ); + explicit TwitterConfigWidget( TwitterPlugin* plugin = 0, QWidget *parent = 0 ); ~TwitterConfigWidget(); signals: void twitterAuthed( bool authed ); - + private slots: void authDeauthTwitter(); void startPostGotTomahawkStatus(); @@ -58,9 +59,9 @@ private slots: private: void authenticateTwitter(); void deauthenticateTwitter(); - + Ui::TwitterConfigWidget *ui; - SipPlugin *m_plugin; + TwitterPlugin *m_plugin; QString m_postGTtype; }; diff --git a/src/sip/twitter/twitterconfigwidget.ui b/src/sip/twitter/twitterconfigwidget.ui index 1bb728fd0..949b68cbc 100644 --- a/src/sip/twitter/twitterconfigwidget.ui +++ b/src/sip/twitter/twitterconfigwidget.ui @@ -6,10 +6,16 @@ 0 0 - 795 - 509 + 438 + 266 + + + 0 + 0 + + diff --git a/src/sip/zeroconf/CMakeLists.txt b/src/sip/zeroconf/CMakeLists.txt index 199704dff..730678e3c 100644 --- a/src/sip/zeroconf/CMakeLists.txt +++ b/src/sip/zeroconf/CMakeLists.txt @@ -15,12 +15,13 @@ set( zeroconfHeaders tomahawkzeroconf.h ) -include_directories( . ${CMAKE_CURRENT_BINARY_DIR} .. +include_directories( . ${CMAKE_CURRENT_BINARY_DIR} .. ${QT_INCLUDE_DIR} ) +qt4_add_resources( RC_SRCS "resources.qrc" ) qt4_wrap_cpp( zeroconfMoc ${zeroconfHeaders} ) -add_library( tomahawk_sipzeroconf SHARED ${zeroconfSources} ${zeroconfMoc} ) +add_library( tomahawk_sipzeroconf SHARED ${zeroconfSources} ${zeroconfMoc} ${RC_SRCS} ) IF( WIN32 ) SET( OS_SPECIFIC_LINK_LIBRARIES diff --git a/src/sip/zeroconf/resources.qrc b/src/sip/zeroconf/resources.qrc new file mode 100644 index 000000000..32e3d457e --- /dev/null +++ b/src/sip/zeroconf/resources.qrc @@ -0,0 +1,5 @@ + + +zeroconf-icon.png + + diff --git a/src/sip/zeroconf/zeroconf-icon.png b/src/sip/zeroconf/zeroconf-icon.png new file mode 100644 index 000000000..7371f8eae Binary files /dev/null and b/src/sip/zeroconf/zeroconf-icon.png differ diff --git a/src/sip/zeroconf/zeroconf.cpp b/src/sip/zeroconf/zeroconf.cpp index 7e6bbf955..d169ba41f 100644 --- a/src/sip/zeroconf/zeroconf.cpp +++ b/src/sip/zeroconf/zeroconf.cpp @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -20,24 +20,43 @@ #include +SipPlugin* +ZeroconfFactory::createPlugin( const QString& pluginId ) +{ + return new ZeroconfPlugin( pluginId.isEmpty() ? generateId() : pluginId ); +} + const QString -ZeroconfPlugin::name() +ZeroconfPlugin::name() const { return QString( MYNAME ); } const QString -ZeroconfPlugin::accountName() +ZeroconfPlugin::accountName() const { - return QString(); + return QString( MYNAME ); } const QString -ZeroconfPlugin::friendlyName() +ZeroconfPlugin::friendlyName() const { - return QString( "Zeroconf" ); + return QString( MYNAME ); } +SipPlugin::ConnectionState +ZeroconfPlugin::connectionState() const +{ + return m_state; +} + +QIcon +ZeroconfFactory::icon() const +{ + return QIcon( ":/zeroconf-icon.png" ); +} + + bool ZeroconfPlugin::connectPlugin( bool /*startup*/ ) { @@ -47,7 +66,7 @@ ZeroconfPlugin::connectPlugin( bool /*startup*/ ) SLOT( lanHostFound( QString, int, QString, QString ) ) ); m_zeroconf->advertise(); - m_isOnline = true; + m_state = Connected; foreach( const QStringList& nodeSet, m_cachedNodes ) { @@ -61,12 +80,19 @@ ZeroconfPlugin::connectPlugin( bool /*startup*/ ) void ZeroconfPlugin::disconnectPlugin() { - m_isOnline = false; + m_state = Disconnected; delete m_zeroconf; m_zeroconf = 0; } +QIcon +ZeroconfPlugin::icon() const +{ + return QIcon( ":/zeroconf-icon.png" ); +} + + void ZeroconfPlugin::lanHostFound( const QString& host, int port, const QString& name, const QString& nodeid ) { @@ -75,7 +101,7 @@ ZeroconfPlugin::lanHostFound( const QString& host, int port, const QString& name qDebug() << "Found LAN host:" << host << port << nodeid; - if ( !m_isOnline ) + if ( m_state != Connected ) { qDebug() << "Not online, so not connecting."; QStringList nodeSet; @@ -83,11 +109,11 @@ ZeroconfPlugin::lanHostFound( const QString& host, int port, const QString& name m_cachedNodes.append( nodeSet ); return; } - + if ( !Servent::instance()->connectedToSession( nodeid ) ) Servent::instance()->connectToPeer( host, port, "whitelist", name, nodeid ); else qDebug() << "Already connected to" << host; } -Q_EXPORT_PLUGIN2( sip, ZeroconfPlugin ) +Q_EXPORT_PLUGIN2( sipfactory, ZeroconfFactory ) diff --git a/src/sip/zeroconf/zeroconf.h b/src/sip/zeroconf/zeroconf.h index 059f4727a..6457e319e 100644 --- a/src/sip/zeroconf/zeroconf.h +++ b/src/sip/zeroconf/zeroconf.h @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -24,17 +24,33 @@ #include "../sipdllmacro.h" -#define MYNAME "SIPZEROCONF" +#define MYNAME "Local Network" + +class SIPDLLEXPORT ZeroconfFactory : public SipPluginFactory +{ + Q_OBJECT + Q_INTERFACES( SipPluginFactory ) +public: + ZeroconfFactory() {} + virtual ~ZeroconfFactory() {} + + virtual QString factoryId() const { return "sipzeroconf"; } + virtual QString prettyName() const { return "Local Network"; } + virtual bool isUnique() const { return true; } + virtual QIcon icon() const; + + virtual SipPlugin* createPlugin ( const QString& pluginId = QString() ); +}; class SIPDLLEXPORT ZeroconfPlugin : public SipPlugin { Q_OBJECT - Q_INTERFACES( SipPlugin ) public: - ZeroconfPlugin() - : m_zeroconf( 0 ) - , m_isOnline( false ) + ZeroconfPlugin( const QString& pluginId ) + : SipPlugin( pluginId ) + , m_zeroconf( 0 ) + , m_state( Disconnected ) , m_cachedNodes() { qDebug() << Q_FUNC_INFO; @@ -44,11 +60,13 @@ public: { qDebug() << Q_FUNC_INFO; } - - virtual bool isValid() { return true; } - virtual const QString name(); - virtual const QString friendlyName(); - virtual const QString accountName(); + + virtual const QString name() const; + virtual const QString friendlyName() const; + virtual const QString accountName() const; + virtual ConnectionState connectionState() const; + virtual bool isValid() const { return true; }; + virtual QIcon icon() const; public slots: virtual bool connectPlugin( bool startup ); @@ -77,7 +95,7 @@ private slots: private: TomahawkZeroconf* m_zeroconf; - bool m_isOnline; + ConnectionState m_state; QVector m_cachedNodes; }; diff --git a/src/sipconfigdelegate.cpp b/src/sipconfigdelegate.cpp new file mode 100644 index 000000000..4223f3a59 --- /dev/null +++ b/src/sipconfigdelegate.cpp @@ -0,0 +1,253 @@ +/* + Copyright (C) 2011 Leo Franchi + + 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 3 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. If not, see . +*/ + + +#include "sipconfigdelegate.h" + +#include "sip/SipModel.h" +#include "sip/SipPlugin.h" +#include "utils/tomahawkutils.h" +#include +#include + +#define ICONSIZE 24 +SipConfigDelegate::SipConfigDelegate( QObject* parent ) + : ConfigDelegateBase ( parent ) +{ + connect( this, SIGNAL( configPressed( QModelIndex ) ), this, SLOT( askedForEdit( QModelIndex ) ) ); +} + +bool +SipConfigDelegate::editorEvent ( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ) +{ + return ConfigDelegateBase::editorEvent( event, model, option, index ); +} + +void +SipConfigDelegate::paint ( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const +{ + QStyleOptionViewItemV4 opt = option; + initStyleOption( &opt, index ); + QRect itemRect = opt.rect; + int top = itemRect.top(); + int mid = itemRect.height() / 2; + + // one line bold for account name + // space below it fro an error + // checkbox, icon, name, online/offline status, config icon + QFont name = opt.font; + name.setPointSize( name.pointSize() + 2 ); + name.setBold( true ); + + QFont error = opt.font; + error.setItalic( true ); + error.setPointSize( error.pointSize() - 2 ); + + // draw the background + const QWidget* w = opt.widget; + QStyle* style = w ? w->style() : QApplication::style(); + style->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, w ); + + int checkLeftEdge = 8; + int iconLeftEdge = checkLeftEdge + ICONSIZE + PADDING; + int textLeftEdge = iconLeftEdge + ICONSIZE + PADDING; + + if( index.data( SipModel::FactoryRole ).toBool() ) { // this is the "add new account" row + // draw a border and background + painter->save(); + painter->setRenderHints( QPainter::Antialiasing ); + painter->setBrush( QApplication::palette().color( QPalette::Active, QPalette::Highlight ).lighter( 150 ) ); + QPainterPath roundedRect; + roundedRect.addRoundedRect( itemRect.adjusted( 1, 1, -1, -1 ), 3, 3 ); + painter->drawPath( roundedRect ); + painter->setBrush( QApplication::palette().color( QPalette::Active, QPalette::Highlight ).lighter( 170 ) ); + painter->fillPath( roundedRect, painter->brush() ); + painter->restore(); + + // draw "+" icon in checkbox column + int rectW = 18; + int diff = ( ICONSIZE/ 2 ) - ( rectW / 2) ; + int pos = ( mid ) - ( rectW / 2 ); + QRect plusRect = QRect( checkLeftEdge + diff, pos + top, rectW, rectW ); + QPixmap p( RESPATH "images/list-add.png" ); + painter->drawPixmap( plusRect, p ); + + // draw text + QFont f = opt.font; + f.setPointSize( f.pointSize() ); + f.setBold( true ); + QFontMetrics fm( f ); + QString text = index.data( Qt::DisplayRole ).toString(); + QRect textR = fm.boundingRect( text ); + textR.moveLeft( textLeftEdge ); + textR.moveTop( mid - ( textR.height() / 2 ) + top ); + textR.setRight( itemRect.right() ); + painter->setFont( f ); + painter->drawText( textR, text ); + } else if( index.data( SipModel::FactoryItemRole ).toBool() ) { // this is an account type + +// ConfigDelegateBase::paint( painter, opt, index ); +// int indent = 10; + // draw a border and background + painter->save(); + painter->setRenderHints( QPainter::Antialiasing ); + painter->setBrush( QApplication::palette().color( QPalette::Active, QPalette::Highlight ).lighter( 170 ) ); + QPainterPath roundedRect; + roundedRect.addRoundedRect( itemRect.adjusted( 1, 1, -1, -1 ), 3, 3 ); + painter->drawPath( roundedRect ); + painter->setBrush( QApplication::palette().color( QPalette::Active, QPalette::Highlight ).lighter( 180 ) ); + painter->fillPath( roundedRect, painter->brush() ); + painter->restore(); + + QIcon icon = index.data( SipModel::FactoryItemIcon ).value< QIcon >(); + if( !icon.isNull() ) { + int rectW = 18; + int diff = ( ICONSIZE/ 2 ) - ( rectW / 2) ; + int pos = ( mid ) - ( rectW / 2 ); + QRect rect = QRect( checkLeftEdge + diff, pos + top, rectW, rectW ); + QPixmap p( icon.pixmap( rect.size() ) ); + painter->drawPixmap( rect, p ); + } + + // draw text + QFont f = opt.font; + f.setPointSize( f.pointSize() ); + f.setBold( true ); + QFontMetrics fm( f ); + QString text = index.data( Qt::DisplayRole ).toString(); + QRect textR = fm.boundingRect( text ); + textR.moveLeft( textLeftEdge ); + textR.moveTop( mid - ( textR.height() / 2 ) + top ); + textR.setRight( itemRect.right() ); + painter->setFont( f ); + painter->drawText( textR, text ); + } else { // this is an existing account to show + // draw checkbox first + int pos = ( mid ) - ( ICONSIZE / 2 ); + QRect checkRect = QRect( checkLeftEdge, pos + top, ICONSIZE, ICONSIZE ); + opt.rect = checkRect; + drawCheckBox( opt, painter, w ); + + // draw the icon if it exists + pos = ( mid ) - ( ICONSIZE / 2 ); + if( !index.data( Qt::DecorationRole ).value< QIcon >().isNull() ) { + QRect prect = QRect( iconLeftEdge, pos + top, ICONSIZE, ICONSIZE ); + + painter->save(); + painter->drawPixmap( prect, index.data( Qt::DecorationRole ).value< QIcon >().pixmap( prect.size() ) ); + painter->restore(); + } + + // from the right edge--config status and online/offline + QRect confRect = QRect( itemRect.width() - ICONSIZE - 2 * PADDING, mid - ICONSIZE / 2 + top, ICONSIZE, ICONSIZE ); + if( index.data( SipModel::HasConfig ).toBool() ) { + + QStyleOptionToolButton topt; + topt.rect = confRect; + topt.pos = confRect.topLeft(); + + drawConfigWrench( painter, opt, topt ); + } + + // draw the online/offline status + int statusIconSize = 10; + int statusX = confRect.left() - 2*PADDING - statusIconSize; + QFont statusF = opt.font; + statusF.setPointSize( statusF.pointSize() - 2 ); + QFontMetrics statusFM( statusF ); + + QPixmap p; + QString statusText; + if( index.data( SipModel::ConnectionStateRole ).toInt() == SipPlugin::Connected ) { + p = QPixmap( RESPATH "images/sipplugin-online.png" ); + statusText = tr( "Online" ); + } else { + p = QPixmap( RESPATH "images/sipplugin-offline.png" ); + statusText = tr( "Offline" ); + } + p = p.scaled( statusIconSize, statusIconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation ); + painter->drawPixmap( statusX, mid - statusIconSize / 2 + top, statusIconSize, statusIconSize, p ); + int width = statusFM.width( statusText ); + statusX = statusX - PADDING - width; + painter->save(); + painter->setFont( statusF ); + painter->drawText( QRect( statusX, mid - statusFM.height() / 2 + top, width, statusFM.height() ), statusText ); + painter->restore(); + + // name + painter->save(); + QFontMetrics namefm( name ); + int nameHeight = namefm.boundingRect( "test" ).height(); + // pos will the top-left point of the text rect + pos = mid - ( nameHeight / 2 ); + // TODO bound with config icon and offline/online status + width = itemRect.width() - textLeftEdge; + + if( !index.data( SipModel::ErrorString ).toString().isEmpty() ) { // error, show that too + QRect errorRect( textLeftEdge, mid + top, width, mid - PADDING ); + + QFontMetrics errorFm( error ); + QString str = errorFm.elidedText( index.data( SipModel::ErrorString ).toString(), Qt::ElideRight, errorRect.width() ); + painter->setFont( error ); + painter->drawText( errorRect, str ); + + pos = mid - errorRect.height() - 2; // move the name rect up + } + QString nameStr = namefm.elidedText( index.data( Qt::DisplayRole ).toString(), Qt::ElideRight, width ); + painter->setFont( name ); + painter->drawText( QRect( textLeftEdge, pos + top, width, nameHeight ), nameStr ); + painter->restore(); + } +} + +QRect +SipConfigDelegate::configRectForIndex( const QStyleOptionViewItem& option, const QModelIndex& idx ) const +{ + QStyleOptionViewItemV4 opt = option; + initStyleOption( &opt, idx ); + QRect itemRect = opt.rect; + QRect confRect = QRect( itemRect.width() - ICONSIZE - 2 * PADDING, (opt.rect.height() / 2) - ICONSIZE / 2 + opt.rect.top(), ICONSIZE, ICONSIZE ); + return confRect; +} + + +QSize +SipConfigDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const +{ + if( index.data( SipModel::FactoryRole ).toBool() || index.data( SipModel::FactoryItemRole ).toBool() ) { // this is the "add new account" row + // enough space for one line of text + QStyleOptionViewItemV4 opt = option; + initStyleOption( &opt, index ); + int width = QStyledItemDelegate::sizeHint( option, index ).width(); + + QFont name = opt.font; + name.setPointSize( name.pointSize() + 1 ); + name.setBold( true ); + QFontMetrics sfm( name ); + return QSize( width, 3 * PADDING + sfm.height() ); + } else { // this is an existing account to show + return ConfigDelegateBase::sizeHint( option, index ); + } +} + +void +SipConfigDelegate::askedForEdit( const QModelIndex& idx ) +{ + emit openConfig( qobject_cast< SipPlugin* >( idx.data( SipModel::SipPluginData ).value< QObject* >() ) ); +} + + diff --git a/src/sipconfigdelegate.h b/src/sipconfigdelegate.h new file mode 100644 index 000000000..c69ed9edc --- /dev/null +++ b/src/sipconfigdelegate.h @@ -0,0 +1,45 @@ +/* + Copyright (C) 2011 Leo Franchi + + 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 3 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. If not, see . +*/ + + +#ifndef SIPCONFIGDELEGATE_H +#define SIPCONFIGDELEGATE_H + +#include "configdelegatebase.h" + +class SipPlugin; +class SipPluginFactory; +class SipConfigDelegate : public ConfigDelegateBase +{ + Q_OBJECT +public: + SipConfigDelegate( QObject* parent = 0); + + virtual void paint ( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; + virtual bool editorEvent ( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ); + virtual QSize sizeHint ( const QStyleOptionViewItem& option, const QModelIndex& index ) const; + + virtual QRect configRectForIndex( const QStyleOptionViewItem& option, const QModelIndex& idx ) const; +private slots: + void askedForEdit( const QModelIndex& idx ); + +signals: + void sipFactoryClicked( SipPluginFactory* ); + void openConfig( SipPlugin* ); +}; + +#endif // SIPCONFIGDELEGATE_H diff --git a/src/sourcetree/items/categoryitems.cpp b/src/sourcetree/items/categoryitems.cpp index 2ba177d20..da1121631 100644 --- a/src/sourcetree/items/categoryitems.cpp +++ b/src/sourcetree/items/categoryitems.cpp @@ -167,7 +167,6 @@ CategoryItem::insertItems( QList< SourceTreeItem* > items ) curCount--; beginRowsAdded( curCount, curCount + items.size() - 1 ); foreach( SourceTreeItem* item, items ) { - int index = m_showAdd ? children().count() - 1 : children().count(); insertChild( children().count() - 1, item ); } endRowsAdded(); @@ -180,4 +179,4 @@ CategoryItem::activate() if( m_category == SourcesModel::StationsCategory ) { // TODO activate stations page } -} \ No newline at end of file +} diff --git a/src/sourcetree/sourcesmodel.cpp b/src/sourcetree/sourcesmodel.cpp index 4577b3264..61adc5f0b 100644 --- a/src/sourcetree/sourcesmodel.cpp +++ b/src/sourcetree/sourcesmodel.cpp @@ -44,7 +44,7 @@ SourcesModel::SourcesModel( QObject* parent ) appendItem( source_ptr() ); // add misc children of root node - GenericPageItem* recent = new GenericPageItem( this, m_rootItem->children().at( 0 ), tr( "Recently Played" ), QIcon( RESPATH "images/recently-played.png" ), + new GenericPageItem( this, m_rootItem->children().at( 0 ), tr( "Recently Played" ), QIcon( RESPATH "images/recently-played.png" ), boost::bind( &ViewManager::showWelcomePage, ViewManager::instance() ), boost::bind( &ViewManager::welcomeWidget, ViewManager::instance() ) ); @@ -193,10 +193,9 @@ SourcesModel::flags( const QModelIndex& index ) const void SourcesModel::appendItem( const Tomahawk::source_ptr& source ) { - beginInsertRows( QModelIndex(), rowCount(), rowCount() ); // append to end - CollectionItem* item = new CollectionItem( this, m_rootItem, source ); + new CollectionItem( this, m_rootItem, source ); endInsertRows(); } @@ -339,7 +338,7 @@ SourcesModel::linkSourceItemToPage( SourceTreeItem* item, ViewPage* p ) // TODO handle removal m_sourceTreeLinks[ p ] = item; - if( m_viewPageDelayedCacheItem = p ) + if( m_viewPageDelayedCacheItem == p ) emit selectRequest( indexFromItem( item ) ); m_viewPageDelayedCacheItem = 0; diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp index 3caf2b908..a504126ed 100644 --- a/src/sourcetree/sourcetreeview.cpp +++ b/src/sourcetree/sourcetreeview.cpp @@ -411,14 +411,12 @@ SourceDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, co CollectionItem* colItem = qobject_cast< CollectionItem* >( item ); Q_ASSERT( colItem ); bool status = !( !colItem || colItem->source().isNull() || !colItem->source()->isOnline() ); - + QPixmap avatar( RESPATH "images/user-avatar.png" ); QString tracks; QString name = index.data().toString(); int figWidth = 0; - QRect iconRect = option.rect.adjusted( 4, 6, -option.rect.width() + option.rect.height() - 12 + 4, -6 ); - QPixmap avatar = index.data( Qt::DecorationRole ).value< QIcon >().pixmap( iconRect.size() ); if ( status && colItem && !colItem->source().isNull() ) { tracks = QString::number( colItem->source()->trackCount() ); @@ -428,6 +426,7 @@ SourceDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, co name = colItem->source()->friendlyName(); } + QRect iconRect = option.rect.adjusted( 4, 6, -option.rect.width() + option.rect.height() - 12 + 4, -6 ); painter->drawPixmap( iconRect, avatar.scaledToHeight( iconRect.height(), Qt::SmoothTransformation ) ); diff --git a/src/stackedsettingsdialog.ui b/src/stackedsettingsdialog.ui new file mode 100644 index 000000000..2bb477cca --- /dev/null +++ b/src/stackedsettingsdialog.ui @@ -0,0 +1,676 @@ + + + StackedSettingsDialog + + + + 0 + 0 + 641 + 370 + + + + Tomahawk Settings + + + + 0 + + + 2 + + + 0 + + + 0 + + + + + + + + + + 16777215 + 16777215 + + + + Qt::NoFocus + + + QFrame::StyledPanel + + + QFrame::Sunken + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + true + + + + 48 + 48 + + + + QListView::Static + + + QListView::Adjust + + + QListView::Batched + + + 1 + + + QListView::IconMode + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 1 + 4 + + + + + + + + + + 0 + + + + + 0 + + + + + Accounts + + + + + + Connect to your friends with Google Chat, Twitter, and more. + + + + + + + + + 0 + + + false + + + false + + + true + + + true + + + false + + + + + + + + + ... + + + + :/data/images/list-add.png:/data/images/list-add.png + + + QToolButton::InstantPopup + + + + + + + ... + + + + :/data/images/list-remove.png:/data/images/list-remove.png + + + QToolButton::DelayedPopup + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + 0 + + + + + + 50 + false + + + + Local Music Information + + + + 2 + + + + + Path to scan for music files: + + + + + + + + + + + + ... + + + + + + + + + + 0 + 0 + + + + Watch for changes + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + + + + + Last.fm Login + + + + 2 + + + + + Qt::LeftToRight + + + Scrobble tracks to Last.fm + + + + + + + + + Username: + + + + + + + + + + Password: + + + + + + + QLineEdit::Password + + + + + + + + + + + Test Login + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + 0 + + + + + Script Resolvers + + + + 2 + + + + + Script resolver search for a given track to make it playable. + + + + + + + + + true + + + QAbstractItemView::SingleSelection + + + false + + + true + + + false + + + true + + + true + + + + + + + + + + + + + :/data/images/list-add.png:/data/images/list-add.png + + + + + + + + + + + :/data/images/list-remove.png:/data/images/list-remove.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + 0 + + + + + + 0 + 0 + + + + Advanced Network Settings + + + + 2 + + + + + + + + + + + 0 + 0 + + + + If you're having difficulty connecting to peers, try setting this to your external IP address/host name and a port number (default 50210). Make sure to forward that port to this machine! + + + true + + + + + + + + + + 0 + 0 + + + + Static Host Name: + + + + + + + + + + + 0 + 0 + + + + Static Port: + + + + + + + 65535 + + + 50210 + + + + + + + + + Qt::RightToLeft + + + Always use static host name/port? (Overrides UPnP discovery/port forwarding) + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Proxy Settings... + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::RightToLeft + + + Playdar HTTP API + + + true + + + + + + + Qt::RightToLeft + + + Use UPnP to establish port forward + + + true + + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + StackedSettingsDialog + accept() + + + 222 + 347 + + + 157 + 274 + + + + + buttonBox + rejected() + StackedSettingsDialog + reject() + + + 290 + 353 + + + 286 + 274 + + + + + diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index d4c8e0947..2be55293e 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -247,7 +247,6 @@ TomahawkApp::init() QNetworkProxy::setApplicationProxy( *TomahawkUtils::proxy() ); qDebug() << "Init SIP system."; - m_sipHandler = new SipHandler( this ); #ifndef TOMAHAWK_HEADLESS if ( !m_headless ) @@ -323,6 +322,12 @@ TomahawkApp::audioControls() } #endif +SipHandler* +TomahawkApp::sipHandler() +{ + return SipHandler::instance(); +} + void TomahawkApp::registerMetaTypes() { @@ -501,14 +506,15 @@ TomahawkApp::setupSIP() qDebug() << Q_FUNC_INFO; //FIXME: jabber autoconnect is really more, now that there is sip -- should be renamed and/or split out of jabber-specific settings - if( !arguments().contains( "--nosip" ) && TomahawkSettings::instance()->jabberAutoConnect() ) + if( !arguments().contains( "--nosip" ) ) { #ifdef GLOOX_FOUND m_xmppBot = new XMPPBot( this ); #endif qDebug() << "Connecting SIP classes"; - m_sipHandler->connectPlugins( true ); + SipHandler::instance()->loadFromConfig( true ); + // m_sipHandler->setProxy( *TomahawkUtils::proxy() ); } diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp index 56fdcb1ea..6885e24eb 100644 --- a/src/tomahawkwindow.cpp +++ b/src/tomahawkwindow.cpp @@ -179,7 +179,9 @@ TomahawkWindow::TomahawkWindow( QWidget* parent ) statusBar()->addPermanentWidget( m_audioControls, 1 ); // propagate sip menu - foreach( SipPlugin *plugin, APP->sipHandler()->plugins() ) + connect( SipHandler::instance(), SIGNAL( pluginAdded( SipPlugin* ) ), this, SLOT( onSipPluginAdded( SiPlugin* ) ) ); + connect( SipHandler::instance(), SIGNAL( pluginRemoved( SipPlugin* ) ), this, SLOT( onSipPluginRemoved( SiPlugin* ) ) ); + foreach( SipPlugin *plugin, APP->sipHandler()->allPlugins() ) { connect( plugin, SIGNAL( addMenu( QMenu* ) ), this, SLOT( pluginMenuAdded( QMenu* ) ) ); connect( plugin, SIGNAL( removeMenu( QMenu* ) ), this, SLOT( pluginMenuRemoved( QMenu* ) ) ); @@ -501,6 +503,19 @@ TomahawkWindow::onSipDisconnected() ui->actionToggleConnect->setText( tr( "Go &online" ) ); } +void +TomahawkWindow::onSipPluginAdded( SipPlugin* p ) +{ + connect( p, SIGNAL( addMenu( QMenu* ) ), this, SLOT( pluginMenuAdded( QMenu* ) ) ); + connect( p, SIGNAL( removeMenu( QMenu* ) ), this, SLOT( pluginMenuRemoved( QMenu* ) ) ); +} + +void +TomahawkWindow::onSipPluginRemoved( SipPlugin* p ) +{ + Q_UNUSED( p ); +} + void TomahawkWindow::onSipError() diff --git a/src/tomahawkwindow.h b/src/tomahawkwindow.h index ae5073461..f86a5b54d 100644 --- a/src/tomahawkwindow.h +++ b/src/tomahawkwindow.h @@ -27,6 +27,7 @@ #include "result.h" +class SipPlugin; class SourceTreeView; class QAction; @@ -83,6 +84,9 @@ private slots: void showAboutTomahawk(); void checkForUpdates(); + void onSipPluginAdded( SipPlugin* p ); + void onSipPluginRemoved( SipPlugin* p ); + void minimize(); void maximize(); diff --git a/thirdparty/jreen b/thirdparty/jreen index 8f995f246..40fd6b0a3 160000 --- a/thirdparty/jreen +++ b/thirdparty/jreen @@ -1 +1 @@ -Subproject commit 8f995f246637f533feb7124744e113034a32b505 +Subproject commit 40fd6b0a3dbc729fdf1db8490848b43d5b1f57f5