Make the ServiceListDelegate use less handcoded values and more general variables...
[amarok.git] / src / mediumpluginmanager.cpp
blob6c558fb3a95a885685bfa4ff9f2339c3d4ff14b7
1 //
2 // C++ Implementation: mediumpluginmanager
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 "mediumpluginmanager.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 MediumPluginManagerDialog::MediumPluginManagerDialog()
63 : KPageDialog( Amarok::mainWindow() )
65 setObjectName( "mediumpluginmanagerdialog" );
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 MediumPluginManager( 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 MediumPluginManagerDialog::~MediumPluginManagerDialog()
99 delete m_manager;
102 void
103 MediumPluginManagerDialog::slotOk()
105 m_manager->finished();
106 //KPageDialog::slotOk();
107 slotButtonClicked( Ok );
110 MediumPluginManager::MediumPluginManager( QWidget *widget, const bool nographics )
111 : m_widget( widget )
113 detectDevices( false, nographics );
115 connect( this, SIGNAL( selectedPlugin( const Medium*, const QString ) ), MediaBrowser::instance(), SLOT( pluginSelected( const Medium*, const QString ) ) );
118 MediumPluginManager::~MediumPluginManager()
122 bool
123 MediumPluginManager::hasChanged()
125 bool temp = m_hasChanged;
126 m_hasChanged = false;
127 return temp;
130 void
131 MediumPluginManager::slotChanged()//slot
133 m_hasChanged = true;
134 emit changed();
137 bool
138 MediumPluginManager::detectDevices( const bool redetect, const bool nographics )
140 bool foundNew = false;
141 KConfigGroup config = Amarok::config( "MediaBrowser" );
142 if( redetect )
143 DeviceManager::instance()->reconcileMediumMap();
144 MediumMap mmap = MediaDeviceManager::instance()->getMediumMap();
145 for( MediumMap::Iterator it = mmap.begin(); it != mmap.end(); it++ )
147 if( !config.readEntry( (*it)->id(), QString() ).isEmpty() &&
148 config.readEntry( (*it)->id(), QString() ) == "deleted" && !redetect)
150 debug() << "skipping: deleted";
151 continue;
154 bool skipflag = false;
156 for( DeviceList::Iterator dit = m_deviceList.begin();
157 dit != m_deviceList.end();
158 dit++ )
160 if( (*it)->id() == (*dit)->medium()->id() )
162 skipflag = true;
163 debug() << "skipping: already listed";
167 if( m_deletedMap.contains( (*it)->id() ) && !(*it)->isAutodetected() )
169 skipflag = true;
170 debug() << "skipping: deleted & not autodetect";
173 if( skipflag )
174 continue;
176 if( m_deletedMap.contains( (*it)->id() ) )
177 m_deletedMap.remove( (*it)->id() );
179 MediaDeviceConfig *dev = new MediaDeviceConfig( *it, this, nographics, m_widget );
180 m_deviceList.append( dev );
181 connect( dev, SIGNAL(deleteMedium(Medium *)), SLOT(deleteMedium(Medium *)) );
183 foundNew = true;
186 return foundNew;
189 void
190 MediumPluginManager::redetectDevices()
192 if( !detectDevices( true ) )
194 Amarok::StatusBar::instance()->longMessageThreadSafe( i18n("No new media devices were found. If you feel this is an\n"
195 "error, ensure that the DBUS and HAL daemons are running\n"
196 "and KDE was built with support for them. You can test this\n"
197 "by running\n"
198 " \"qdbus org.kde.kded /modules/mediamanager fullList\"\n"
199 "in a Konsole window.") );
201 else
202 slotChanged();
205 void
206 MediumPluginManager::deleteMedium( Medium *medium )
208 for( DeviceList::Iterator it = m_deviceList.begin();
209 it != m_deviceList.end();
210 it++ )
212 if( (*it)->medium() == medium )
214 m_deletedMap[medium->id()] = medium;
215 m_deviceList.remove( *it );
216 break;
219 slotChanged();
222 void
223 MediumPluginManager::finished()
225 for( DeviceList::Iterator it = m_deviceList.begin();
226 it != m_deviceList.end();
227 it++ )
229 if( (*it)->plugin() != (*it)->oldPlugin() )
231 (*it)->setOldPlugin( (*it)->plugin() );
232 emit selectedPlugin( (*it)->medium(), (*it)->plugin() );
234 (*it)->configButton()->setEnabled( (*it)->pluginCombo()->currentText() != i18n( "Do not handle" ) );
237 KConfigGroup config = Amarok::config( "MediaBrowser" );
238 for( DeletedMap::Iterator dit = m_deletedMap.begin();
239 dit != m_deletedMap.end();
240 ++dit )
242 if( dit.data()->isAutodetected() )
243 config.writeEntry( dit.data()->id(), "deleted" );
244 else
245 config.deleteEntry( dit.data()->id() );
246 MediaDeviceManager::instance()->removeManualDevice( dit.data() );
248 m_deletedMap.clear();
251 void
252 MediumPluginManager::newDevice()
254 DEBUG_BLOCK
255 ManualDeviceAdder* mda = new ManualDeviceAdder( this );
256 if( mda->exec() == QDialog::Accepted && mda->successful() )
258 if( !Amarok::config( "MediaBrowser" ).readEntry( mda->getMedium()->id(), QString() ).isNull() )
260 //abort! Can't have the same device defined twice...should never
261 //happen due to name checking earlier...right?
262 Amarok::StatusBar::instance()->longMessageThreadSafe( i18n("Sorry, you cannot define two devices\n"
263 "with the same name and mountpoint!") );
265 else
267 Medium *newdev = new Medium( mda->getMedium() );
268 Amarok::config( "MediaBrowser" ).writeEntry( newdev->id(), mda->getPlugin() );
269 MediaDeviceManager::instance()->addManualDevice( newdev );
270 detectDevices();
273 delete mda;
274 slotChanged();
277 /////////////////////////////////////////////////////////////////////
279 ManualDeviceAdder::ManualDeviceAdder( MediumPluginManager* mpm )
280 : KPageDialog( Amarok::mainWindow() )
282 setObjectName( "manualdeviceadder" );
283 setModal( true );
284 setButtons( Ok | Cancel );
285 setDefaultButton( Ok );
288 m_mpm = mpm;
289 m_successful = false;
290 m_newMed = 0;
292 kapp->setTopWidget( this );
293 setCaption( KDialog::makeStandardCaption( i18n( "Add New Device") ) );
295 KHBox* hbox = new KHBox( this );
296 setMainWidget( hbox );
297 hbox->setSpacing( KDialog::spacingHint() );
299 KVBox* vbox1 = new KVBox( hbox );
301 new QLabel( i18n( "Select the plugin to use with this device:"), vbox1 );
302 m_mdaCombo = new KComboBox( false, vbox1 );
303 m_mdaCombo->setObjectName( "m_mdacombo" );
304 m_mdaCombo->addItem( i18n( "Do not handle" ) );
305 for( KService::List::ConstIterator it = MediaBrowser::instance()->getPlugins().begin();
306 it != MediaBrowser::instance()->getPlugins().end();
307 ++it )
308 m_mdaCombo->addItem( (*it)->name() );
310 new QLabel( "", vbox1 );
311 QLabel* nameLabel = new QLabel( vbox1 );
312 nameLabel->setText( i18n( "Enter a &name for this device (required):" ) );
313 m_mdaName = new HintLineEdit( QString(), vbox1);
314 nameLabel->setBuddy( m_mdaName );
315 m_mdaName->setHint( i18n( "Example: My_Ipod" ) );
316 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." ) );
318 new QLabel( "", vbox1 );
319 QLabel* mpLabel = new QLabel( vbox1 );
320 mpLabel->setText( i18n( "Enter the &mount point of the device, if applicable:" ) );
321 m_mdaMountPoint = new HintLineEdit( QString(), vbox1);
322 mpLabel->setBuddy( m_mdaMountPoint );
323 m_mdaMountPoint->setHint( i18n( "Example: /mnt/ipod" ) );
324 m_mdaMountPoint->setToolTip( i18n( "Enter the device's mount point. Some devices (such as iRiver iFP 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." ) );
326 connect( m_mdaCombo, SIGNAL( activated(const QString&) ), this, SLOT( comboChanged(const QString&) ) );
329 ManualDeviceAdder::~ManualDeviceAdder()
331 delete m_newMed;
332 delete m_mdaName;
333 delete m_mdaMountPoint;
336 void
337 ManualDeviceAdder::slotButtonClicked( KDialog::ButtonCode button)
339 if( button != KDialog::Ok )
340 KDialog::slotButtonClicked( button );
341 if( getMedium( true ) && !getMedium()->name().isEmpty() &&
342 MediaDeviceManager::instance()->getDevice( getMedium()->name() ) == NULL )
344 m_successful = true;
345 slotButtonClicked( Ok );
347 else
349 Amarok::StatusBar::instance()->longMessageThreadSafe( i18n("Sorry, every device must have a name and\n"
350 "you cannot define two devices with the\n"
351 "same name. These names must be unique\n"
352 "across autodetected devices as well.\n") );
356 void
357 ManualDeviceAdder::comboChanged( const QString &string )
359 //best thing to do here would be to find out if the plugin selected
360 //has m_hasMountPoint set to false...but any way to do this
361 //without instantiating it? This way will suffice for now...
362 if( MediaBrowser::instance()->getInternalPluginName( string ) == "ifp-mediadevice" ||
363 MediaBrowser::instance()->getInternalPluginName( string ) == "daap-mediadevice" ||
364 MediaBrowser::instance()->getInternalPluginName( string ) == "mtp-mediadevice" ||
365 MediaBrowser::instance()->getInternalPluginName( string ) == "njb-mediadevice" )
367 m_comboOldText = m_mdaMountPoint->text();
368 m_mdaMountPoint->setText( QString() );
369 m_mdaMountPoint->setEnabled(false);
371 else if( m_mdaMountPoint->isEnabled() == false )
373 m_mdaMountPoint->setText( m_comboOldText );
374 m_mdaMountPoint->setEnabled(true);
376 m_selectedPlugin = MediaBrowser::instance()->getInternalPluginName( string );
379 Medium*
380 ManualDeviceAdder::getMedium( bool recreate )
382 if( !recreate )
383 return m_newMed;
385 if( m_newMed && recreate )
387 delete m_newMed;
388 m_newMed = 0;
391 if( m_mdaMountPoint->isEnabled() == false &&
392 m_mdaName->text().isNull() )
393 return NULL;
394 if( m_mdaMountPoint->text().isNull() &&
395 m_mdaName->text().isNull() )
396 return NULL;
397 QString id = "manual|" + m_mdaName->text() + '|' +
398 ( m_mdaMountPoint->text().isNull() ||
399 m_mdaMountPoint->isEnabled() == false ?
400 "(null)" : m_mdaMountPoint->text() );
401 m_newMed = new Medium( id, m_mdaName->text() );
402 m_newMed->setAutodetected( false );
403 m_newMed->setMountPoint( m_mdaMountPoint->text() );
404 return m_newMed;
407 MediaDeviceConfig::MediaDeviceConfig( Medium *medium, MediumPluginManager *mgr, const bool nographics, QWidget *parent, const char *name )
408 : KHBox( parent )
409 , m_manager( mgr )
410 , m_medium( medium )
411 , m_configButton( 0 )
412 , m_removeButton( 0 )
413 , m_new( true )
415 if( !m_medium )
416 return;
418 setObjectName( name );
420 KConfigGroup config = Amarok::config( "MediaBrowser" );
421 m_oldPlugin = config.readEntry( m_medium->id(), QString() );
422 if( !m_oldPlugin.isEmpty() )
423 m_new = false;
425 setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) );
426 setSpacing( 5 );
428 const QString labelTextNone = i18n( "(none)" );
429 QString row = "<tr><td>%1</td><td>%2</td></tr>";
430 QString table;
431 table += row.arg( Qt::escape( i18n( "Autodetected:" ) ),
432 Qt::escape( medium->isAutodetected() ? i18n("Yes") : i18n("No") ) );
433 table += row.arg( Qt::escape( i18n( "ID:" ) ),
434 Qt::escape( medium->id() ) );
435 table += row.arg( Qt::escape( i18n( "Name:" ) ),
436 Qt::escape( medium->name() ) );
437 table += row.arg( Qt::escape( i18n( "Label:" ) ),
438 Qt::escape( medium->label().isEmpty() ? labelTextNone : medium->label() ) );
439 table += row.arg( Qt::escape( i18n( "User Label:" ) ),
440 Qt::escape( medium->userLabel().isEmpty() ? labelTextNone : medium->userLabel() ) );
441 table += row.arg( Qt::escape( i18n( "Device Node:" ) ),
442 Qt::escape( medium->deviceNode().isEmpty() ? labelTextNone : medium->deviceNode() ) );
443 table += row.arg( Qt::escape( i18n( "Mount Point:" ) ),
444 Qt::escape( medium->mountPoint().isEmpty() ? labelTextNone : medium->mountPoint() ) );
445 table += row.arg( Qt::escape( i18n( "Mime Type:" ) ),
446 Qt::escape( medium->mimeType().isEmpty() ? labelTextNone : medium->mimeType() ) );
448 QString title = Qt::escape( i18n( "Device information for %1").arg(medium->name() ) );
449 QString details = QString( "<em>%1</em><br />" "<table>%2</table>" ).arg( title, table );
451 (void)new QLabel( i18n("Name: "), this );
452 (void)new QLabel( medium->name(), this );
453 (void)new K3ActiveLabel( i18n( "(<a href='whatsthis:%1'>Details</a>)" )
454 .arg( Amarok::escapeHTMLAttr( details ) ), this );
456 (void)new QLabel( i18n("Plugin:"), this );
457 m_pluginCombo = new KComboBox( false, this );
458 m_pluginCombo->addItem( i18n( "Do not handle" ) );
460 for( KService::List::ConstIterator it = MediaBrowser::instance()->getPlugins().begin();
461 it != MediaBrowser::instance()->getPlugins().end();
462 ++it ){
463 m_pluginCombo->addItem( (*it)->name() );
464 if ( (*it)->property( "X-KDE-Amarok-name" ).toString() == config.readEntry( medium->id(), QString() ) )
465 m_pluginCombo->setCurrentItem( (*it)->name() );
468 m_configButton = new KPushButton( KIcon(KIcon( Amarok::icon( "configure" ) )), QString(), this );
469 connect( m_configButton, SIGNAL(clicked()), SLOT(configureDevice()) );
470 m_configButton->setEnabled( !m_new && m_pluginCombo->currentText() != i18n( "Do not handle" ) );
471 m_configButton->setToolTip( i18n( "Configure device settings" ) );
473 m_removeButton = new KPushButton( i18n( "Remove" ), this );
474 connect( m_removeButton, SIGNAL(clicked()), SLOT(deleteDevice()) );
475 m_removeButton->setToolTip( i18n( "Remove entries corresponding to this device from configuration file" ) );
477 connect( m_pluginCombo, SIGNAL(activated(const QString&)), m_manager, SLOT(slotChanged()) );
478 connect( this, SIGNAL(changed()), m_manager, SLOT(slotChanged()) );
480 if( !nographics )
481 show();
484 MediaDeviceConfig::~MediaDeviceConfig()
488 bool
489 MediaDeviceConfig::isNew()
491 return m_new;
494 Medium *
495 MediaDeviceConfig::medium()
497 return m_medium;
500 QString
501 MediaDeviceConfig::plugin()
503 return MediaBrowser::instance()->getInternalPluginName( m_pluginCombo->currentText() );
506 QString
507 MediaDeviceConfig::oldPlugin()
509 return m_oldPlugin;
512 void
513 MediaDeviceConfig::setOldPlugin( const QString &oldPlugin )
515 m_oldPlugin = oldPlugin;
518 QAbstractButton *
519 MediaDeviceConfig::configButton()
521 return m_configButton;
524 QAbstractButton *
525 MediaDeviceConfig::removeButton()
527 return m_removeButton;
530 KComboBox *
531 MediaDeviceConfig::pluginCombo()
533 return m_pluginCombo;
536 void
537 MediaDeviceConfig::configureDevice() //slot
539 DeviceConfigureDialog* dcd = new DeviceConfigureDialog( m_medium );
540 dcd->exec();
541 delete dcd;
544 void
545 MediaDeviceConfig::deleteDevice() //slot
547 //TODO:save something in amarokrc such that it's not shown again until hit autoscan
548 //m_deletedMap[medium->id()] = medium;
549 emit deleteMedium( medium() );
550 delete this;
553 #include "mediumpluginmanager.moc"