some more work on collabsible albums. I think I will need to optimize the playlist...
[amarok.git] / src / MediaDevicePluginManager.cpp
blob2870f74e2941123831094345029d154e0954c1d4
1 //
2 // C++ Implementation: mediadevicepluginmanager
3 //
4 // Description:
5 //
6 //
7 // Authors: Jeff Mitchell <kde-dev@emailgoeshere.com>, (C) 2005, 2006
8 // Martin Aumueller <aumuell@reserv.at>, (C) 2006
9 //
10 // Copyright: See COPYING file that comes with this distribution
13 #include "mediadevicepluginmanager.h"
15 #include "amarok.h"
16 #include "debug.h"
17 #include "deviceconfiguredialog.h"
18 #include "mediadevicemanager.h"
19 #include "devicemanager.h"
20 #include "hintlineedit.h"
21 #include "mediabrowser.h"
22 #include "medium.h"
23 #include "plugin/pluginconfig.h"
24 #include "pluginmanager.h"
25 #include "statusbar.h"
27 #include <k3activelabel.h>
28 #include <KApplication>
29 #include <KComboBox>
30 #include <KConfig>
31 #include <KIconLoader>
32 #include <KLineEdit>
33 #include <KLocale>
34 #include <KPushButton>
35 #include <KVBox>
37 #include <QAbstractButton>
38 #include <q3groupbox.h>
39 #include <QLabel>
40 #include <QLayout>
41 #include <QSignalMapper>
42 #include <QToolTip>
45 using Amarok::escapeHTMLAttr;
47 typedef QMap<QString, Medium*> MediumMap;
49 namespace Amarok
51 QString escapeHTMLAttr( const QString &s )
53 return QString(s).replace( "%", "%25" ).replace( "'", "%27" ).replace( "\"", "%22" ).replace( "#", "%23" ).replace( "?", "%3F" );
56 QString unescapeHTMLAttr( const QString &s )
58 return QString(s).replace( "%3F", "?" ).replace( "%23", "#" ).replace( "%22", "\"" ).replace( "%27", "'" ).replace( "%25", "%" );
62 MediaDevicePluginManagerDialog::MediaDevicePluginManagerDialog()
63 : KPageDialog( Amarok::mainWindow() )
65 setObjectName( "mediadevicepluginmanagerdialog" );
66 setModal( false );
67 setButtons( Ok | Cancel );
68 setDefaultButton( Ok );
70 kapp->setTopWidget( this );
71 setCaption( KDialog::makeStandardCaption( i18n( "Manage Devices and Plugins" ) ) );
73 KVBox *vbox = new KVBox( this );
74 setMainWidget( vbox );
76 vbox->setSpacing( KDialog::spacingHint() );
77 vbox->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) );
79 m_location = new Q3GroupBox( 1, Qt::Vertical, i18n( "Devices" ), vbox );
80 m_location->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ) );
81 m_devicesBox = new KVBox( m_location );
82 m_devicesBox->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) );
84 m_manager = new MediaDevicePluginManager( m_devicesBox );
86 KHBox *hbox = new KHBox( vbox );
87 KPushButton *detectDevices = new KPushButton( i18n( "Autodetect Devices" ), hbox);
88 detectDevices->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) );
89 connect( detectDevices, SIGNAL( clicked() ), m_manager, SLOT( redetectDevices() ) );
91 KPushButton *addButton = new KPushButton( i18n( "Add Device..." ), hbox );
92 addButton->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) );
93 connect( addButton, SIGNAL( clicked() ), m_manager, SLOT( newDevice() ) );
94 connect( this,SIGNAL(okClicked()),this,SLOT(slotOk()));
97 MediaDevicePluginManagerDialog::~MediaDevicePluginManagerDialog()
99 delete m_manager;
102 void
103 MediaDevicePluginManagerDialog::slotOk()
105 m_manager->finished();
106 slotButtonClicked( Ok );
109 MediaDevicePluginManager::MediaDevicePluginManager( QWidget *widget, const bool nographics )
110 : m_widget( widget )
112 detectDevices( false, nographics );
114 connect( this, SIGNAL( selectedPlugin( const QString &, const QString ) ), MediaBrowser::instance(), SLOT( pluginSelected( const QString &, const QString ) ) );
117 MediaDevicePluginManager::~MediaDevicePluginManager()
121 bool
122 MediaDevicePluginManager::hasChanged()
124 bool temp = m_hasChanged;
125 m_hasChanged = false;
126 return temp;
129 void
130 MediaDevicePluginManager::slotChanged()//slot
132 m_hasChanged = true;
133 emit changed();
136 bool
137 MediaDevicePluginManager::detectDevices( const bool redetect, const bool nographics )
139 bool foundNew = false;
140 KConfigGroup config = Amarok::config( "PortableDevices" );
141 if( redetect )
142 MediaDeviceCache::instance()->refreshCache();
143 QStringList deviceList = MediaDeviceCache::instance()->getAll();
144 foreach( QString device, deviceList )
146 if( !config.readEntry( device, QString() ).isEmpty() &&
147 config.readEntry( device, QString() ) == "deleted" && !redetect)
149 debug() << "skipping: deleted";
150 continue;
153 bool skipflag = false;
155 for( DeviceList::Iterator dit = m_deviceList.begin();
156 dit != m_deviceList.end();
157 dit++ )
159 if( device == (*dit)->uid() )
161 skipflag = true;
162 debug() << "skipping: already listed";
166 if( m_deletedMap.contains( device ) && !(*it)->isAutodetected() )
168 skipflag = true;
169 debug() << "skipping: deleted & not autodetect";
172 if( skipflag )
173 continue;
175 if( m_deletedMap.contains( device ) )
176 m_deletedMap.remove( device );
178 MediaDeviceConfig *dev = new MediaDeviceConfig( device, this, nographics, m_widget );
179 m_deviceList.append( dev );
180 connect( dev, SIGNAL(deleteDevice(QString &)), SLOT(deleteDevice(QString &)) );
182 foundNew = true;
185 return foundNew;
188 void
189 MediaDevicePluginManager::redetectDevices()
191 if( !detectDevices( true ) )
193 Amarok::StatusBar::instance()->longMessageThreadSafe( i18n("No new media devices were found. If you feel this is an\n"
194 "error, ensure that the DBUS and HAL daemons are running\n"
195 "and KDE was built with support for them. You can test this\n"
196 "by running\n"
197 " \"qdbus org.kde.kded /modules/mediamanager fullList\"\n"
198 "in a Konsole window.") );
200 else
201 slotChanged();
204 void
205 MediaDevicePluginManager::deleteDevice( const QString &uid )
207 for( DeviceList::Iterator it = m_deviceList.begin();
208 it != m_deviceList.end();
209 it++ )
211 if( (*it)->uid() == uid )
213 m_deletedMap[uid] = true;
214 m_deviceList.remove( *it );
215 break;
218 slotChanged();
221 void
222 MediaDevicePluginManager::finished()
224 for( DeviceList::Iterator it = m_deviceList.begin();
225 it != m_deviceList.end();
226 it++ )
228 if( (*it)->plugin() != (*it)->oldPlugin() )
230 (*it)->setOldPlugin( (*it)->plugin() );
231 emit selectedPlugin( (*it)->uid(), (*it)->plugin() );
233 (*it)->configButton()->setEnabled( (*it)->pluginCombo()->currentText() != i18n( "Do not handle" ) );
236 KConfigGroup config = Amarok::config( "MediaBrowser" );
237 for( DeletedMap::Iterator dit = m_deletedMap.begin();
238 dit != m_deletedMap.end();
239 ++dit )
241 if( MediaDeviceCache::instance()->getType( dit.key() ) == MediaDeviceCache::SolidType )
242 config.writeEntry( dit.key(), "deleted" );
243 else
244 config.deleteEntry( dit.key() );
245 MediaDeviceCache::instance()->refreshCache();
247 m_deletedMap.clear();
250 void
251 MediaDevicePluginManager::newDevice()
253 DEBUG_BLOCK
254 ManualDeviceAdder* mda = new ManualDeviceAdder( this );
255 if( mda->exec() == QDialog::Accepted && mda->successful() )
257 if( !Amarok::config( "PortableDevices" ).readEntry( mda->id(), QString() ).isNull() )
259 //abort! Can't have the same device defined twice...should never
260 //happen due to name checking earlier...right?
261 Amarok::StatusBar::instance()->longMessageThreadSafe( i18n("Sorry, you cannot define two devices\n"
262 "with the same name and mountpoint!") );
264 else
266 Amarok::config( "PortableDevices" ).writeEntry( mda->uid(), mda->getPlugin() );
267 MediaDeviceCache::instance()->refreshCache();
268 detectDevices();
271 delete mda;
272 slotChanged();
275 /////////////////////////////////////////////////////////////////////
277 ManualDeviceAdder::ManualDeviceAdder( MediaDevicePluginManager* mpm )
278 : KPageDialog( Amarok::mainWindow() )
280 setObjectName( "manualdeviceadder" );
281 setModal( true );
282 setButtons( Ok | Cancel );
283 setDefaultButton( Ok );
286 m_mpm = mpm;
287 m_successful = false;
288 m_newMed = 0;
290 kapp->setTopWidget( this );
291 setCaption( KDialog::makeStandardCaption( i18n( "Add New Device") ) );
293 KHBox* hbox = new KHBox( this );
294 setMainWidget( hbox );
295 hbox->setSpacing( KDialog::spacingHint() );
297 KVBox* vbox1 = new KVBox( hbox );
299 new QLabel( i18n( "Select the plugin to use with this device:"), vbox1 );
300 m_mdaCombo = new KComboBox( false, vbox1 );
301 m_mdaCombo->setObjectName( "m_mdacombo" );
302 m_mdaCombo->addItem( i18n( "Do not handle" ) );
303 for( KService::List::ConstIterator it = MediaBrowser::instance()->getPlugins().begin();
304 it != MediaBrowser::instance()->getPlugins().end();
305 ++it )
306 m_mdaCombo->addItem( (*it)->name() );
308 new QLabel( "", vbox1 );
309 QLabel* nameLabel = new QLabel( vbox1 );
310 nameLabel->setText( i18n( "Enter a &name for this device (required):" ) );
311 m_mdaName = new HintLineEdit( QString(), vbox1);
312 nameLabel->setBuddy( m_mdaName );
313 m_mdaName->setHint( i18n( "Example: My_Ipod" ) );
314 m_mdaName->setToolTip( i18n( "Enter a name for the device. The name must be unique across all devices, including autodetected devices. It must not contain the pipe ( | ) character." ) );
316 new QLabel( "", vbox1 );
317 QLabel* mpLabel = new QLabel( vbox1 );
318 mpLabel->setText( i18n( "Enter the &mount point of the device, if applicable:" ) );
319 m_mdaMountPoint = new HintLineEdit( QString(), vbox1);
320 mpLabel->setBuddy( m_mdaMountPoint );
321 m_mdaMountPoint->setHint( i18n( "Example: /mnt/ipod" ) );
322 m_mdaMountPoint->setToolTip( i18n( "Enter the device's mount point. Some devices (such as MTP devices) may not have a mount point and this can be ignored. All other devices (iPods, UMS/VFAT devices) should enter the mount point here." ) );
324 connect( m_mdaCombo, SIGNAL( activated(const QString&) ), this, SLOT( comboChanged(const QString&) ) );
327 ManualDeviceAdder::~ManualDeviceAdder()
329 delete m_newMed;
330 delete m_mdaName;
331 delete m_mdaMountPoint;
334 void
335 ManualDeviceAdder::slotButtonClicked( KDialog::ButtonCode button)
337 if( button != KDialog::Ok )
338 KDialog::slotButtonClicked( button );
339 if( getMedium( true ) && !getMedium()->name().isEmpty() &&
340 MediaDeviceManager::instance()->getDevice( getMedium()->name() ) == NULL )
342 m_successful = true;
343 slotButtonClicked( Ok );
345 else
347 Amarok::StatusBar::instance()->longMessageThreadSafe( i18n("Sorry, every device must have a name and\n"
348 "you cannot define two devices with the\n"
349 "same name. These names must be unique\n"
350 "across autodetected devices as well.\n") );
354 void
355 ManualDeviceAdder::comboChanged( const QString &string )
357 //best thing to do here would be to find out if the plugin selected
358 //has m_hasMountPoint set to false...but any way to do this
359 //without instantiating it? This way will suffice for now...
360 if( MediaBrowser::instance()->getInternalPluginName( string ) == "ifp-mediadevice" ||
361 MediaBrowser::instance()->getInternalPluginName( string ) == "daap-mediadevice" ||
362 MediaBrowser::instance()->getInternalPluginName( string ) == "mtp-mediadevice" ||
363 MediaBrowser::instance()->getInternalPluginName( string ) == "njb-mediadevice" )
365 m_comboOldText = m_mdaMountPoint->text();
366 m_mdaMountPoint->setText( QString() );
367 m_mdaMountPoint->setEnabled(false);
369 else if( m_mdaMountPoint->isEnabled() == false )
371 m_mdaMountPoint->setText( m_comboOldText );
372 m_mdaMountPoint->setEnabled(true);
374 m_selectedPlugin = MediaBrowser::instance()->getInternalPluginName( string );
377 Medium*
378 ManualDeviceAdder::getMedium( bool recreate )
380 if( !recreate )
381 return m_newMed;
383 if( m_newMed && recreate )
385 delete m_newMed;
386 m_newMed = 0;
389 if( m_mdaMountPoint->isEnabled() == false &&
390 m_mdaName->text().isNull() )
391 return NULL;
392 if( m_mdaMountPoint->text().isNull() &&
393 m_mdaName->text().isNull() )
394 return NULL;
395 QString id = "manual|" + m_mdaName->text() + '|' +
396 ( m_mdaMountPoint->text().isNull() ||
397 m_mdaMountPoint->isEnabled() == false ?
398 "(null)" : m_mdaMountPoint->text() );
399 m_newMed = new Medium( id, m_mdaName->text() );
400 m_newMed->setAutodetected( false );
401 m_newMed->setMountPoint( m_mdaMountPoint->text() );
402 return m_newMed;
405 MediaDeviceConfig::MediaDeviceConfig( QString uid, MediaDevicePluginManager *mgr, const bool nographics, QWidget *parent, const char *name )
406 : KHBox( parent )
407 , m_manager( mgr )
408 , m_uid( uid )
409 , m_configButton( 0 )
410 , m_removeButton( 0 )
411 , m_new( true )
413 setObjectName( name );
415 KConfigGroup config = Amarok::config( "PortableDevices" );
416 m_oldPlugin = config.readEntry( m_uid, QString() );
417 if( !m_oldPlugin.isEmpty() )
418 m_new = false;
420 setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) );
421 setSpacing( 5 );
423 const QString labelTextNone = i18n( "(none)" );
424 QString row = "<tr><td>%1</td><td>%2</td></tr>";
425 QString table;
426 table += row.arg( Qt::escape( i18n( "Autodetected:" ) ),
427 Qt::escape( MediaDeviceCache::instance()->deviceType( m_uid ) == MediaDeviceCache::SolidType ? i18n("Yes") : i18n("No") ) );
428 table += row.arg( Qt::escape( i18n( "Unique ID:" ) ),
429 Qt::escape( m_uid ) );
430 if( MediaDeviceCache::instance()->deviceType( m_uid ) == MediaDeviceCache::SolidType )
432 Solid::Device device( m_uid );
433 if( device.isValid() )
435 if( !device.vendor().isEmpty() )
436 table += row.arg( Qt::escape( i18n( "Vendor:" ) ),
437 Qt::escape( device.vendor() ) );
438 if( !device.product().isEmpty() )
439 table += row.arg( Qt::escape( i18n( "Product:" ) ),
440 Qt::escape( medium->label().isEmpty() ? labelTextNone : medium->label() ) );
444 QString title = Qt::escape( i18n( "Device information for %1").arg( m_uid ) );
445 QString details = QString( "<em>%1</em><br />" "<table>%2</table>" ).arg( title, table );
447 (void)new QLabel( i18n("Name: "), this );
448 (void)new QLabel( m_uid, this );
449 (void)new K3ActiveLabel( i18n( "(<a href='whatsthis:%1'>Details</a>)" )
450 .arg( Amarok::escapeHTMLAttr( details ) ), this );
452 (void)new QLabel( i18n("Plugin:"), this );
453 m_pluginCombo = new KComboBox( false, this );
454 m_pluginCombo->addItem( i18n( "Do not handle" ) );
456 for( KService::List::ConstIterator it = MediaBrowser::instance()->getPlugins().begin();
457 it != MediaBrowser::instance()->getPlugins().end();
458 ++it ){
459 m_pluginCombo->addItem( (*it)->name() );
460 if ( (*it)->property( "X-KDE-Amarok-name" ).toString() == config.readEntry( m_uid, QString() ) )
461 m_pluginCombo->setCurrentItem( (*it)->name() );
464 m_configButton = new KPushButton( KIcon(KIcon( Amarok::icon( "configure" ) )), QString(), this );
465 connect( m_configButton, SIGNAL(clicked()), SLOT(configureDevice()) );
466 m_configButton->setEnabled( !m_new && m_pluginCombo->currentText() != i18n( "Do not handle" ) );
467 m_configButton->setToolTip( i18n( "Configure device settings" ) );
469 m_removeButton = new KPushButton( i18n( "Remove" ), this );
470 connect( m_removeButton, SIGNAL(clicked()), SLOT(deleteDevice()) );
471 m_removeButton->setToolTip( i18n( "Remove entries corresponding to this device from configuration file" ) );
473 connect( m_pluginCombo, SIGNAL(activated(const QString&)), m_manager, SLOT(slotChanged()) );
474 connect( this, SIGNAL(changed()), m_manager, SLOT(slotChanged()) );
476 if( !nographics )
477 show();
480 MediaDeviceConfig::~MediaDeviceConfig()
484 void
485 MediaDeviceConfig::configureDevice() //slot
487 DeviceConfigureDialog* dcd = new DeviceConfigureDialog( m_uid );
488 dcd->exec();
489 delete dcd;
492 void
493 MediaDeviceConfig::deleteDevice() //slot
495 emit deleteMedium( medium() );
496 delete this;
499 #include "mediadevicepluginmanager.moc"