summaryrefslogtreecommitdiffstats
path: root/libk3b/core
diff options
context:
space:
mode:
Diffstat (limited to 'libk3b/core')
-rw-r--r--libk3b/core/Makefile.am19
-rw-r--r--libk3b/core/k3b_export.h33
-rw-r--r--libk3b/core/k3bcore.cpp375
-rw-r--r--libk3b/core/k3bcore.h181
-rw-r--r--libk3b/core/k3bdataevent.h48
-rw-r--r--libk3b/core/k3bdefaultexternalprograms.cpp1030
-rw-r--r--libk3b/core/k3bdefaultexternalprograms.h143
-rw-r--r--libk3b/core/k3bexternalbinmanager.cpp389
-rw-r--r--libk3b/core/k3bexternalbinmanager.h162
-rw-r--r--libk3b/core/k3bglobals.cpp634
-rw-r--r--libk3b/core/k3bglobals.h257
-rw-r--r--libk3b/core/k3bglobalsettings.cpp61
-rw-r--r--libk3b/core/k3bglobalsettings.h70
-rw-r--r--libk3b/core/k3bjob.cpp253
-rw-r--r--libk3b/core/k3bjob.h311
-rw-r--r--libk3b/core/k3bjobhandler.h64
-rw-r--r--libk3b/core/k3bprocess.cpp452
-rw-r--r--libk3b/core/k3bprocess.h204
-rw-r--r--libk3b/core/k3bprogressinfoevent.h85
-rw-r--r--libk3b/core/k3bsimplejobhandler.cpp62
-rw-r--r--libk3b/core/k3bsimplejobhandler.h61
-rw-r--r--libk3b/core/k3bthread.cpp221
-rw-r--r--libk3b/core/k3bthread.h95
-rw-r--r--libk3b/core/k3bthreadjob.cpp161
-rw-r--r--libk3b/core/k3bthreadjob.h89
-rw-r--r--libk3b/core/k3bversion.cpp318
-rw-r--r--libk3b/core/k3bversion.h141
27 files changed, 5919 insertions, 0 deletions
diff --git a/libk3b/core/Makefile.am b/libk3b/core/Makefile.am
new file mode 100644
index 0000000..3764d86
--- /dev/null
+++ b/libk3b/core/Makefile.am
@@ -0,0 +1,19 @@
+AM_CPPFLAGS = -I$(srcdir)/../../libk3bdevice -I$(srcdir)/../plugin -I$(srcdir)/../tools $(all_includes)
+
+noinst_LTLIBRARIES = libk3bcore.la
+
+libk3bcore_la_LIBADD = $(LIB_KIO) $(ARTSC_LIB)
+
+libk3bcore_la_LDFLAGS = $(all_libraries)
+
+libk3bcore_la_SOURCES = k3bcore.cpp k3bglobals.cpp k3bdefaultexternalprograms.cpp \
+ k3bexternalbinmanager.cpp k3bversion.cpp k3bprocess.cpp k3bjob.cpp \
+ k3bthread.cpp k3bthreadjob.cpp k3bglobalsettings.cpp k3bsimplejobhandler.cpp
+
+include_HEADERS = k3bcore.h k3bdefaultexternalprograms.h k3bexternalbinmanager.h \
+ k3bprocess.h k3bversion.h k3bglobals.h k3bjob.h k3bthread.h \
+ k3bthreadjob.h k3bglobalsettings.h k3bjobhandler.h \
+ k3b_export.h k3bjobhandler.h k3bsimplejobhandler.h
+
+METASOURCES = AUTO
+
diff --git a/libk3b/core/k3b_export.h b/libk3b/core/k3b_export.h
new file mode 100644
index 0000000..b6272f1
--- /dev/null
+++ b/libk3b/core/k3b_export.h
@@ -0,0 +1,33 @@
+/*
+ *
+ * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $
+ * Copyright (c) 2005 Laurent Montel <montel@kde.org>
+ * Copyright (C) 2005-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_EXPORT_H_
+#define _K3B_EXPORT_H_
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef __KDE_HAVE_GCC_VISIBILITY
+#define LIBK3B_NO_EXPORT __attribute__ ((visibility("hidden")))
+#define LIBK3B_EXPORT __attribute__ ((visibility("default")))
+#else
+#define LIBK3B_NO_EXPORT
+#define LIBK3B_EXPORT
+#endif
+
+#endif
+
diff --git a/libk3b/core/k3bcore.cpp b/libk3b/core/k3bcore.cpp
new file mode 100644
index 0000000..c10fec0
--- /dev/null
+++ b/libk3b/core/k3bcore.cpp
@@ -0,0 +1,375 @@
+/*
+ *
+ * $Id: k3bcore.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include <config.h>
+
+#include "k3bcore.h"
+#include "k3bjob.h"
+
+#include <k3bdevicemanager.h>
+#ifdef HAVE_HAL
+#include <k3bhalconnection.h>
+#endif
+#include <k3bexternalbinmanager.h>
+#include <k3bdefaultexternalprograms.h>
+#include <k3bglobals.h>
+#include <k3bversion.h>
+#include <k3bjob.h>
+#include <k3bthreadwidget.h>
+#include <k3bglobalsettings.h>
+#include <k3bpluginmanager.h>
+
+#include <klocale.h>
+#include <kconfig.h>
+#include <kaboutdata.h>
+#include <kstandarddirs.h>
+#include <kapplication.h>
+
+#include <qptrlist.h>
+#include <qthread.h>
+#include <qmutex.h>
+
+
+static Qt::HANDLE s_guiThreadHandle = QThread::currentThread();
+
+// We cannot use QWaitCondition here since the event might be handled faster
+// than the thread starts the waiting
+class DeviceBlockingEventDoneCondition {
+public:
+ DeviceBlockingEventDoneCondition()
+ : m_done(false) {
+ }
+
+ void done() {
+ m_doneMutex.lock();
+ m_done = true;
+ m_doneMutex.unlock();
+ }
+
+ void wait() {
+ while( true ) {
+ m_doneMutex.lock();
+ bool done = m_done;
+ m_doneMutex.unlock();
+ if( done )
+ return;
+ }
+ }
+
+private:
+ QMutex m_doneMutex;
+ bool m_done;
+};
+
+class DeviceBlockingEvent : public QCustomEvent
+{
+public:
+ DeviceBlockingEvent( bool block_, K3bDevice::Device* dev, DeviceBlockingEventDoneCondition* cond_, bool* success_ )
+ : QCustomEvent( QEvent::User + 33 ),
+ block(block_),
+ device(dev),
+ cond(cond_),
+ success(success_) {
+ }
+
+ bool block;
+ K3bDevice::Device* device;
+ DeviceBlockingEventDoneCondition* cond;
+ bool* success;
+};
+
+
+class K3bCore::Private {
+public:
+ Private()
+ : version( LIBK3B_VERSION ),
+ config(0),
+ deleteConfig(false),
+ deviceManager(0),
+ externalBinManager(0),
+ pluginManager(0),
+ globalSettings(0) {
+ }
+
+ K3bVersion version;
+ KConfig* config;
+ bool deleteConfig;
+ K3bDevice::DeviceManager* deviceManager;
+ K3bExternalBinManager* externalBinManager;
+ K3bPluginManager* pluginManager;
+ K3bGlobalSettings* globalSettings;
+
+ QValueList<K3bJob*> runningJobs;
+ QValueList<K3bDevice::Device*> blockedDevices;
+};
+
+
+
+K3bCore* K3bCore::s_k3bCore = 0;
+
+
+
+K3bCore::K3bCore( QObject* parent, const char* name )
+ : QObject( parent, name )
+{
+ d = new Private();
+
+ if( s_k3bCore )
+ qFatal("ONLY ONE INSTANCE OF K3BCORE ALLOWED!");
+ s_k3bCore = this;
+
+ // create the thread widget instance in the GUI thread
+ K3bThreadWidget::instance();
+}
+
+
+K3bCore::~K3bCore()
+{
+ s_k3bCore = 0;
+
+ delete d->globalSettings;
+ delete d;
+}
+
+
+K3bDevice::DeviceManager* K3bCore::deviceManager() const
+{
+ const_cast<K3bCore*>(this)->initDeviceManager();
+ return d->deviceManager;
+}
+
+
+K3bExternalBinManager* K3bCore::externalBinManager() const
+{
+ const_cast<K3bCore*>(this)->initExternalBinManager();
+ return d->externalBinManager;
+}
+
+
+K3bPluginManager* K3bCore::pluginManager() const
+{
+ const_cast<K3bCore*>(this)->initPluginManager();
+ return d->pluginManager;
+}
+
+
+K3bGlobalSettings* K3bCore::globalSettings() const
+{
+ const_cast<K3bCore*>(this)->initGlobalSettings();
+ return d->globalSettings;
+}
+
+
+const K3bVersion& K3bCore::version() const
+{
+ return d->version;
+}
+
+
+KConfig* K3bCore::config() const
+{
+ if( !d->config ) {
+ kdDebug() << "(K3bCore) opening k3b config file." << endl;
+ kdDebug() << "(K3bCore) while I am a " << className() << endl;
+ d->deleteConfig = true;
+ d->config = new KConfig( "k3brc" );
+ }
+
+ return d->config;
+}
+
+
+void K3bCore::init()
+{
+ initGlobalSettings();
+ initExternalBinManager();
+ initDeviceManager();
+ initPluginManager();
+
+ // load the plugins before doing anything else
+ // they might add external bins
+ pluginManager()->loadAll();
+
+ externalBinManager()->search();
+
+#ifdef HAVE_HAL
+ connect( K3bDevice::HalConnection::instance(), SIGNAL(deviceAdded(const QString&)),
+ deviceManager(), SLOT(addDevice(const QString&)) );
+ connect( K3bDevice::HalConnection::instance(), SIGNAL(deviceRemoved(const QString&)),
+ deviceManager(), SLOT(removeDevice(const QString&)) );
+ QStringList devList = K3bDevice::HalConnection::instance()->devices();
+ if( devList.isEmpty() )
+ deviceManager()->scanBus();
+ else
+ for( unsigned int i = 0; i < devList.count(); ++i )
+ deviceManager()->addDevice( devList[i] );
+#else
+ deviceManager()->scanBus();
+#endif
+}
+
+
+void K3bCore::initGlobalSettings()
+{
+ if( !d->globalSettings )
+ d->globalSettings = new K3bGlobalSettings();
+}
+
+
+void K3bCore::initExternalBinManager()
+{
+ if( !d->externalBinManager ) {
+ d->externalBinManager = new K3bExternalBinManager( this );
+ K3b::addDefaultPrograms( d->externalBinManager );
+ }
+}
+
+
+void K3bCore::initDeviceManager()
+{
+ if( !d->deviceManager )
+ d->deviceManager = new K3bDevice::DeviceManager( this );
+}
+
+
+void K3bCore::initPluginManager()
+{
+ if( !d->pluginManager )
+ d->pluginManager = new K3bPluginManager( this );
+}
+
+
+void K3bCore::readSettings( KConfig* cnf )
+{
+ KConfig* c = cnf;
+ if( !c )
+ c = config();
+
+ QString oldGrp = c->group();
+
+ globalSettings()->readSettings( c );
+ deviceManager()->readConfig( c );
+ externalBinManager()->readConfig( c );
+
+ c->setGroup( oldGrp );
+}
+
+
+void K3bCore::saveSettings( KConfig* cnf )
+{
+ KConfig* c = cnf;
+ if( !c )
+ c = config();
+
+ QString oldGrp = c->group();
+
+ c->setGroup( "General Options" );
+ c->writeEntry( "config version", version() );
+
+ deviceManager()->saveConfig( c );
+ externalBinManager()->saveConfig( c );
+ d->globalSettings->saveSettings( c );
+
+ c->setGroup( oldGrp );
+}
+
+
+void K3bCore::registerJob( K3bJob* job )
+{
+ d->runningJobs.append( job );
+ emit jobStarted( job );
+ if( K3bBurnJob* bj = dynamic_cast<K3bBurnJob*>( job ) )
+ emit burnJobStarted( bj );
+}
+
+
+void K3bCore::unregisterJob( K3bJob* job )
+{
+ d->runningJobs.remove( job );
+ emit jobFinished( job );
+ if( K3bBurnJob* bj = dynamic_cast<K3bBurnJob*>( job ) )
+ emit burnJobFinished( bj );
+}
+
+
+bool K3bCore::jobsRunning() const
+{
+ return !d->runningJobs.isEmpty();
+}
+
+
+const QValueList<K3bJob*>& K3bCore::runningJobs() const
+{
+ return d->runningJobs;
+}
+
+
+bool K3bCore::blockDevice( K3bDevice::Device* dev )
+{
+ if( QThread::currentThread() == s_guiThreadHandle ) {
+ return internalBlockDevice( dev );
+ }
+ else {
+ bool success = false;
+ DeviceBlockingEventDoneCondition w;
+ QApplication::postEvent( this, new DeviceBlockingEvent( true, dev, &w, &success ) );
+ w.wait();
+ return success;
+ }
+}
+
+
+void K3bCore::unblockDevice( K3bDevice::Device* dev )
+{
+ if( QThread::currentThread() == s_guiThreadHandle ) {
+ internalUnblockDevice( dev );
+ }
+ else {
+ DeviceBlockingEventDoneCondition w;
+ QApplication::postEvent( this, new DeviceBlockingEvent( false, dev, &w, 0 ) );
+ w.wait();
+ }
+}
+
+
+bool K3bCore::internalBlockDevice( K3bDevice::Device* dev )
+{
+ if( !d->blockedDevices.contains( dev ) ) {
+ d->blockedDevices.append( dev );
+ return true;
+ }
+ else
+ return false;
+}
+
+
+void K3bCore::internalUnblockDevice( K3bDevice::Device* dev )
+{
+ d->blockedDevices.remove( dev );
+}
+
+
+void K3bCore::customEvent( QCustomEvent* e )
+{
+ if( DeviceBlockingEvent* de = dynamic_cast<DeviceBlockingEvent*>(e) ) {
+ if( de->block )
+ *de->success = internalBlockDevice( de->device );
+ else
+ internalUnblockDevice( de->device );
+ de->cond->done();
+ }
+}
+
+#include "k3bcore.moc"
diff --git a/libk3b/core/k3bcore.h b/libk3b/core/k3bcore.h
new file mode 100644
index 0000000..ce73e32
--- /dev/null
+++ b/libk3b/core/k3bcore.h
@@ -0,0 +1,181 @@
+/*
+ *
+ * $Id: k3bcore.h 733470 2007-11-06 12:10:29Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_CORE_H_
+#define _K3B_CORE_H_
+
+#include <qobject.h>
+#include <qvaluelist.h>
+
+#include "k3b_export.h"
+
+
+#define LIBK3B_VERSION "1.0.5"
+
+#define k3bcore K3bCore::k3bCore()
+
+
+class K3bExternalBinManager;
+class K3bVersion;
+class KConfig;
+class KAboutData;
+class K3bJob;
+class K3bBurnJob;
+class K3bGlobalSettings;
+class K3bPluginManager;
+class QCustomEvent;
+
+
+namespace K3bDevice {
+ class DeviceManager;
+ class Device;
+}
+
+
+/**
+ * The K3b core takes care of the managers.
+ * This has been separated from K3bApplication to
+ * make creating a K3bPart easy.
+ * This is the heart of the K3b system. Every plugin may use this
+ * to get the information it needs.
+ */
+class LIBK3B_EXPORT K3bCore : public QObject
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Although K3bCore is a singlelton it's constructor is not private to make inheritance
+ * possible. Just make sure to only create one instance.
+ */
+ K3bCore( QObject* parent = 0, const char* name = 0 );
+ virtual ~K3bCore();
+
+ const QValueList<K3bJob*>& runningJobs() const;
+
+ /**
+ * Equals to !runningJobs().isEmpty()
+ */
+ bool jobsRunning() const;
+
+ /**
+ * The default implementation calls add four initXXX() methods,
+ * scans for devices, applications, and reads the global settings.
+ */
+ virtual void init();
+
+ /**
+ * @param c if 0 K3bCore uses the K3b configuration
+ */
+ virtual void readSettings( KConfig* c = 0 );
+
+ /**
+ * @param c if 0 K3bCore uses the K3b configuration
+ */
+ virtual void saveSettings( KConfig* c = 0 );
+
+ /**
+ * If this is reimplemented it is recommended to also reimplement
+ * init().
+ */
+ virtual K3bDevice::DeviceManager* deviceManager() const;
+
+ /**
+ * Returns the external bin manager from K3bCore.
+ *
+ * By default K3bCore only adds the default programs:
+ * cdrecord, cdrdao, growisofs, mkisofs, dvd+rw-format, readcd
+ *
+ * If you need other programs you have to add them manually like this:
+ * <pre>externalBinManager()->addProgram( new K3bNormalizeProgram() );</pre>
+ */
+ K3bExternalBinManager* externalBinManager() const;
+ K3bPluginManager* pluginManager() const;
+
+ /**
+ * Global settings used throughout libk3b. Change the settings directly in the
+ * K3bGlobalSettings object. They will be saved by K3bCore::saveSettings
+ */
+ K3bGlobalSettings* globalSettings() const;
+
+ /**
+ * returns the version of the library as defined by LIBK3B_VERSION
+ */
+ const K3bVersion& version() const;
+
+ /**
+ * Default implementation returns the K3b configuration from k3brc.
+ * Normally this should not be used.
+ */
+ virtual KConfig* config() const;
+
+ /**
+ * Used by the writing jobs to block a device.
+ * This makes sure no device is used twice within libk3b
+ *
+ * When using this method in a job be aware that reimplementations might
+ * open dialogs and resulting in a blocking call.
+ *
+ * This method calls internalBlockDevice() to do the actual work.
+ */
+ bool blockDevice( K3bDevice::Device* );
+ void unblockDevice( K3bDevice::Device* );
+
+ static K3bCore* k3bCore() { return s_k3bCore; }
+
+ signals:
+ /**
+ * Emitted once a new job has been started. This includes burn jobs.
+ */
+ void jobStarted( K3bJob* );
+ void burnJobStarted( K3bBurnJob* );
+ void jobFinished( K3bJob* );
+ void burnJobFinished( K3bBurnJob* );
+
+ public slots:
+ /**
+ * Every running job registers itself with the core.
+ * For now this is only used to determine if some job
+ * is running.
+ */
+ void registerJob( K3bJob* job );
+ void unregisterJob( K3bJob* job );
+
+ protected:
+ /**
+ * Reimplement this to add additonal checks.
+ *
+ * This method is thread safe. blockDevice makes sure
+ * it is only executed in the GUI thread.
+ */
+ virtual bool internalBlockDevice( K3bDevice::Device* );
+ virtual void internalUnblockDevice( K3bDevice::Device* );
+
+ virtual void initGlobalSettings();
+ virtual void initExternalBinManager();
+ virtual void initDeviceManager();
+ virtual void initPluginManager();
+
+ virtual void customEvent( QCustomEvent* e );
+
+ private:
+ class Private;
+ Private* d;
+
+ static K3bCore* s_k3bCore;
+};
+
+#endif
diff --git a/libk3b/core/k3bdataevent.h b/libk3b/core/k3bdataevent.h
new file mode 100644
index 0000000..b6a4334
--- /dev/null
+++ b/libk3b/core/k3bdataevent.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * $Id: k3bdataevent.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef K3B_DATA_EVENT_H
+#define K3B_DATA_EVENT_H
+
+#include <qevent.h>
+
+
+/**
+ * Custom event class for posting events corresponding to the
+ * K3bJob signals. This is useful for a threaded job since
+ * in that case it's not possible to emit signals that directly
+ * change the GUI (see QThread docu).
+ */
+class K3bDataEvent : public QCustomEvent
+{
+ public:
+ // make sure we get not in the way of K3bProgressInfoEvent
+ static const int EVENT_TYPE = QEvent::User + 100;
+
+ K3bDataEvent( const char* data, int len )
+ : QCustomEvent( EVENT_TYPE ),
+ m_data(data),
+ m_length(len)
+ {}
+
+ const char* data() const { return m_data; }
+ int length() const { return m_length; }
+
+ private:
+ const char* m_data;
+ int m_length;
+};
+
+#endif
diff --git a/libk3b/core/k3bdefaultexternalprograms.cpp b/libk3b/core/k3bdefaultexternalprograms.cpp
new file mode 100644
index 0000000..b654d22
--- /dev/null
+++ b/libk3b/core/k3bdefaultexternalprograms.cpp
@@ -0,0 +1,1030 @@
+/*
+ *
+ * $Id: k3bdefaultexternalprograms.cpp 731898 2007-11-02 08:22:18Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bdefaultexternalprograms.h"
+#include "k3bexternalbinmanager.h"
+#include <k3bglobals.h>
+
+#include <qfile.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <qobject.h>
+#include <qregexp.h>
+#include <qtextstream.h>
+
+#include <k3bprocess.h>
+#include <kdebug.h>
+
+#include <unistd.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+
+
+
+void K3b::addDefaultPrograms( K3bExternalBinManager* m )
+{
+ m->addProgram( new K3bCdrecordProgram(false) );
+ m->addProgram( new K3bMkisofsProgram() );
+ m->addProgram( new K3bReadcdProgram() );
+ m->addProgram( new K3bCdrdaoProgram() );
+ m->addProgram( new K3bGrowisofsProgram() );
+ m->addProgram( new K3bDvdformatProgram() );
+ // m->addProgram( new K3bDvdBooktypeProgram() );
+}
+
+
+void K3b::addTranscodePrograms( K3bExternalBinManager* m )
+{
+ static const char* transcodeTools[] = { "transcode",
+ 0, // K3b 1.0 only uses the transcode binary
+ "tcprobe",
+ "tccat",
+ "tcscan",
+ "tcextract",
+ "tcdecode",
+ 0 };
+
+ for( int i = 0; transcodeTools[i]; ++i )
+ m->addProgram( new K3bTranscodeProgram( transcodeTools[i] ) );
+}
+
+
+void K3b::addVcdimagerPrograms( K3bExternalBinManager* m )
+{
+ // don't know if we need more vcdTools in the future (vcdxrip)
+ static const char* vcdTools[] = { "vcdxbuild",
+ "vcdxminfo",
+ "vcdxrip",
+ 0 };
+
+ for( int i = 0; vcdTools[i]; ++i )
+ m->addProgram( new K3bVcdbuilderProgram( vcdTools[i] ) );
+}
+
+
+K3bCdrecordProgram::K3bCdrecordProgram( bool dvdPro )
+ : K3bExternalProgram( dvdPro ? "cdrecord-prodvd" : "cdrecord" ),
+ m_dvdPro(dvdPro)
+{
+}
+
+
+//
+// This is a hack for Debian based systems which use
+// a wrapper cdrecord script to call cdrecord.mmap or cdrecord.shm
+// depending on the kernel version.
+// For 2.0.x and 2.2.x kernels the shm version is used. In all
+// other cases it's the mmap version.
+//
+// But since it may be that someone manually installed cdrecord
+// replacing the wrapper we check if cdrecord is a script.
+//
+static QString& debianWeirdnessHack( QString& path )
+{
+ if( QFile::exists( path + ".mmap" ) ) {
+ kdDebug() << "(K3bCdrecordProgram) checking for Debian cdrecord wrapper script." << endl;
+ if( QFileInfo( path ).size() < 1024 ) {
+ kdDebug() << "(K3bCdrecordProgram) Debian Wrapper script size fits. Checking file." << endl;
+ QFile f( path );
+ f.open( IO_ReadOnly );
+ QString s = QTextStream( &f ).read();
+ if( s.contains( "cdrecord.mmap" ) && s.contains( "cdrecord.shm" ) ) {
+ kdDebug() << "(K3bCdrecordProgram) Found Debian Wrapper script." << endl;
+ QString ext;
+ if( K3b::kernelVersion().versionString().left(3) > "2.2" )
+ ext = ".mmap";
+ else
+ ext = ".shm";
+
+ kdDebug() << "(K3bCdrecordProgram) Using cdrecord" << ext << endl;
+
+ path += ext;
+ }
+ }
+ }
+
+ return path;
+}
+
+
+bool K3bCdrecordProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ bool wodim = false;
+ QString path = p;
+ QFileInfo fi( path );
+ if( fi.isDir() ) {
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+
+ if( QFile::exists( path + "wodim" ) ) {
+ wodim = true;
+ path += "wodim";
+ }
+ else if( QFile::exists( path + "cdrecord" ) ) {
+ path += "cdrecord";
+ }
+ else
+ return false;
+ }
+
+ debianWeirdnessHack( path );
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ K3bProcessOutputCollector out( &vp );
+
+ vp << path << "-version";
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ int pos = -1;
+ if( wodim ) {
+ pos = out.output().find( "Wodim" );
+ }
+ else if( m_dvdPro ) {
+ pos = out.output().find( "Cdrecord-ProDVD" );
+ }
+ else {
+ pos = out.output().find( "Cdrecord" );
+ }
+
+ if( pos < 0 )
+ return false;
+
+ pos = out.output().find( QRegExp("[0-9]"), pos );
+ if( pos < 0 )
+ return false;
+
+ int endPos = out.output().find( QRegExp("\\s"), pos+1 );
+ if( endPos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = path;
+ bin->version = out.output().mid( pos, endPos-pos );
+
+ if( wodim )
+ bin->addFeature( "wodim" );
+
+ pos = out.output().find( "Copyright") + 14;
+ endPos = out.output().find( "\n", pos );
+
+ // cdrecord does not use local encoding for the copyright statement but plain latin1
+ bin->copyright = QString::fromLatin1( out.output().mid( pos, endPos-pos ).local8Bit() ).stripWhiteSpace();
+ }
+ else {
+ kdDebug() << "(K3bCdrecordProgram) could not start " << path << endl;
+ return false;
+ }
+
+ if( !m_dvdPro && bin->version.suffix().endsWith( "-dvd" ) ) {
+ bin->addFeature( "dvd-patch" );
+ bin->version = QString(bin->version.versionString()).remove("-dvd");
+ }
+
+ // probe features
+ KProcess fp;
+ out.setProcess( &fp );
+ fp << path << "-help";
+ if( fp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ if( out.output().contains( "gracetime" ) )
+ bin->addFeature( "gracetime" );
+ if( out.output().contains( "-overburn" ) )
+ bin->addFeature( "overburn" );
+ if( out.output().contains( "-text" ) )
+ bin->addFeature( "cdtext" );
+ if( out.output().contains( "-clone" ) )
+ bin->addFeature( "clone" );
+ if( out.output().contains( "-tao" ) )
+ bin->addFeature( "tao" );
+ if( out.output().contains( "cuefile=" ) &&
+ ( wodim || bin->version > K3bVersion( 2, 1, -1, "a14") ) ) // cuefile handling was still buggy in a14
+ bin->addFeature( "cuefile" );
+
+ // new mode 2 options since cdrecord 2.01a12
+ // we use both checks here since the help was not updated in 2.01a12 yet (well, I
+ // just double-checked and the help page is proper but there is no harm in having
+ // two checks)
+ // and the version check does not handle versions like 2.01-dvd properly
+ if( out.output().contains( "-xamix" ) ||
+ bin->version >= K3bVersion( 2, 1, -1, "a12" ) ||
+ wodim )
+ bin->addFeature( "xamix" );
+
+ // check if we run cdrecord as root
+ struct stat s;
+ if( !::stat( QFile::encodeName(path), &s ) ) {
+ if( (s.st_mode & S_ISUID) && s.st_uid == 0 )
+ bin->addFeature( "suidroot" );
+ }
+ }
+ else {
+ kdDebug() << "(K3bCdrecordProgram) could not start " << bin->path << endl;
+ delete bin;
+ return false;
+ }
+
+ if( bin->version < K3bVersion( 2, 0 ) && !wodim )
+ bin->addFeature( "outdated" );
+
+ // FIXME: are these version correct?
+ if( bin->version >= K3bVersion("1.11a38") || wodim )
+ bin->addFeature( "plain-atapi" );
+ if( bin->version > K3bVersion("1.11a17") || wodim )
+ bin->addFeature( "hacked-atapi" );
+
+ if( bin->version >= K3bVersion( 2, 1, 1, "a02" ) || wodim )
+ bin->addFeature( "short-track-raw" );
+
+ if( bin->version >= K3bVersion( 2, 1, -1, "a13" ) || wodim )
+ bin->addFeature( "audio-stdin" );
+
+ if( bin->version >= K3bVersion( "1.11a02" ) || wodim )
+ bin->addFeature( "burnfree" );
+ else
+ bin->addFeature( "burnproof" );
+
+ addBin( bin );
+ return true;
+}
+
+
+
+K3bMkisofsProgram::K3bMkisofsProgram()
+ : K3bExternalProgram( "mkisofs" )
+{
+}
+
+bool K3bMkisofsProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ bool genisoimage = false;
+ QString path = p;
+ QFileInfo fi( path );
+ if( fi.isDir() ) {
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+
+ if( QFile::exists( path + "genisoimage" ) ) {
+ genisoimage = true;
+ path += "genisoimage";
+ }
+ else if( QFile::exists( path + "mkisofs" ) ) {
+ path += "mkisofs";
+ }
+ else
+ return false;
+ }
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ vp << path << "-version";
+ K3bProcessOutputCollector out( &vp );
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ int pos = -1;
+ if( genisoimage )
+ pos = out.output().find( "genisoimage" );
+ else
+ pos = out.output().find( "mkisofs" );
+
+ if( pos < 0 )
+ return false;
+
+ pos = out.output().find( QRegExp("[0-9]"), pos );
+ if( pos < 0 )
+ return false;
+
+ int endPos = out.output().find( ' ', pos+1 );
+ if( endPos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = path;
+ bin->version = out.output().mid( pos, endPos-pos );
+
+ if( genisoimage )
+ bin->addFeature( "genisoimage" );
+ }
+ else {
+ kdDebug() << "(K3bMkisofsProgram) could not start " << path << endl;
+ return false;
+ }
+
+
+
+ // probe features
+ KProcess fp;
+ fp << path << "-help";
+ out.setProcess( &fp );
+ if( fp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ if( out.output().contains( "-udf" ) )
+ bin->addFeature( "udf" );
+ if( out.output().contains( "-dvd-video" ) )
+ bin->addFeature( "dvd-video" );
+ if( out.output().contains( "-joliet-long" ) )
+ bin->addFeature( "joliet-long" );
+ if( out.output().contains( "-xa" ) )
+ bin->addFeature( "xa" );
+ if( out.output().contains( "-sectype" ) )
+ bin->addFeature( "sectype" );
+
+ // check if we run mkisofs as root
+ struct stat s;
+ if( !::stat( QFile::encodeName(path), &s ) ) {
+ if( (s.st_mode & S_ISUID) && s.st_uid == 0 )
+ bin->addFeature( "suidroot" );
+ }
+ }
+ else {
+ kdDebug() << "(K3bMkisofsProgram) could not start " << bin->path << endl;
+ delete bin;
+ return false;
+ }
+
+ if( bin->version < K3bVersion( 1, 14) && !genisoimage )
+ bin->addFeature( "outdated" );
+
+ if( bin->version >= K3bVersion( 1, 15, -1, "a40" ) || genisoimage )
+ bin->addFeature( "backslashed_filenames" );
+
+ if ( genisoimage && bin->version >= K3bVersion( 1, 1, 4 ) )
+ bin->addFeature( "no-4gb-limit" );
+
+ if ( !genisoimage && bin->version >= K3bVersion( 2, 1, 1, "a32" ) )
+ bin->addFeature( "no-4gb-limit" );
+
+ addBin(bin);
+ return true;
+}
+
+
+K3bReadcdProgram::K3bReadcdProgram()
+ : K3bExternalProgram( "readcd" )
+{
+}
+
+bool K3bReadcdProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ bool readom = false;
+ QString path = p;
+ QFileInfo fi( path );
+ if( fi.isDir() ) {
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+
+ if( QFile::exists( path + "readom" ) ) {
+ readom = true;
+ path += "readom";
+ }
+ else if( QFile::exists( path + "readcd" ) ) {
+ path += "readcd";
+ }
+ else
+ return false;
+ }
+
+ if( !QFile::exists( path ) )
+ return false;
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ vp << path << "-version";
+ K3bProcessOutputCollector out( &vp );
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ int pos = -1;
+ if( readom )
+ pos = out.output().find( "readom" );
+ else
+ pos = out.output().find( "readcd" );
+ if( pos < 0 )
+ return false;
+
+ pos = out.output().find( QRegExp("[0-9]"), pos );
+ if( pos < 0 )
+ return false;
+
+ int endPos = out.output().find( ' ', pos+1 );
+ if( endPos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = path;
+ bin->version = out.output().mid( pos, endPos-pos );
+
+ if( readom )
+ bin->addFeature( "readom" );
+ }
+ else {
+ kdDebug() << "(K3bMkisofsProgram) could not start " << path << endl;
+ return false;
+ }
+
+
+
+ // probe features
+ KProcess fp;
+ fp << path << "-help";
+ out.setProcess( &fp );
+ if( fp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ if( out.output().contains( "-clone" ) )
+ bin->addFeature( "clone" );
+
+ // check if we run mkisofs as root
+ struct stat s;
+ if( !::stat( QFile::encodeName(path), &s ) ) {
+ if( (s.st_mode & S_ISUID) && s.st_uid == 0 )
+ bin->addFeature( "suidroot" );
+ }
+ }
+ else {
+ kdDebug() << "(K3bReadcdProgram) could not start " << bin->path << endl;
+ delete bin;
+ return false;
+ }
+
+
+ // FIXME: are these version correct?
+ if( bin->version >= K3bVersion("1.11a38") || readom )
+ bin->addFeature( "plain-atapi" );
+ if( bin->version > K3bVersion("1.11a17") || readom )
+ bin->addFeature( "hacked-atapi" );
+
+ addBin(bin);
+ return true;
+}
+
+
+K3bCdrdaoProgram::K3bCdrdaoProgram()
+ : K3bExternalProgram( "cdrdao" )
+{
+}
+
+bool K3bCdrdaoProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ QString path = p;
+ QFileInfo fi( path );
+ if( fi.isDir() ) {
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+ path.append("cdrdao");
+ }
+
+ if( !QFile::exists( path ) )
+ return false;
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ vp << path ;
+ K3bProcessOutputCollector out( &vp );
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ int pos = out.output().find( "Cdrdao version" );
+ if( pos < 0 )
+ return false;
+
+ pos = out.output().find( QRegExp("[0-9]"), pos );
+ if( pos < 0 )
+ return false;
+
+ int endPos = out.output().find( ' ', pos+1 );
+ if( endPos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = path;
+ bin->version = out.output().mid( pos, endPos-pos );
+
+ pos = out.output().find( "(C)", endPos+1 ) + 4;
+ endPos = out.output().find( '\n', pos );
+ bin->copyright = out.output().mid( pos, endPos-pos );
+ }
+ else {
+ kdDebug() << "(K3bCdrdaoProgram) could not start " << path << endl;
+ return false;
+ }
+
+
+
+ // probe features
+ KProcess fp;
+ fp << path << "write" << "-h";
+ out.setProcess( &fp );
+ if( fp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ if( out.output().contains( "--overburn" ) )
+ bin->addFeature( "overburn" );
+ if( out.output().contains( "--multi" ) )
+ bin->addFeature( "multisession" );
+
+ if( out.output().contains( "--buffer-under-run-protection" ) )
+ bin->addFeature( "disable-burnproof" );
+
+ // check if we run cdrdao as root
+ struct stat s;
+ if( !::stat( QFile::encodeName(path), &s ) ) {
+ if( (s.st_mode & S_ISUID) && s.st_uid == 0 )
+ bin->addFeature( "suidroot" );
+ }
+ }
+ else {
+ kdDebug() << "(K3bCdrdaoProgram) could not start " << bin->path << endl;
+ delete bin;
+ return false;
+ }
+
+
+ // SuSE 9.0 ships with a patched cdrdao 1.1.7 which contains an updated libschily
+ // Gentoo ships with a patched cdrdao 1.1.7 which contains scglib support
+ if( bin->version > K3bVersion( 1, 1, 7 ) ||
+ bin->version == K3bVersion( 1, 1, 7, "-gentoo" ) ||
+ bin->version == K3bVersion( 1, 1, 7, "-suse" ) ) {
+ // bin->addFeature( "plain-atapi" );
+ bin->addFeature( "hacked-atapi" );
+ }
+
+ if( bin->version >= K3bVersion( 1, 1, 8 ) )
+ bin->addFeature( "plain-atapi" );
+
+ addBin(bin);
+ return true;
+}
+
+
+K3bTranscodeProgram::K3bTranscodeProgram( const QString& transcodeProgram )
+ : K3bExternalProgram( transcodeProgram ),
+ m_transcodeProgram( transcodeProgram )
+{
+}
+
+bool K3bTranscodeProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ QString path = p;
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+
+ QString appPath = path + m_transcodeProgram;
+
+ if( !QFile::exists( appPath ) )
+ return false;
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ vp << appPath << "-v";
+ K3bProcessOutputCollector out( &vp );
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ int pos = out.output().find( "transcode v" );
+ if( pos < 0 )
+ return false;
+
+ pos += 11;
+
+ int endPos = out.output().find( QRegExp("[\\s\\)]"), pos+1 );
+ if( endPos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = appPath;
+ bin->version = out.output().mid( pos, endPos-pos );
+ }
+ else {
+ kdDebug() << "(K3bTranscodeProgram) could not start " << appPath << endl;
+ return false;
+ }
+
+ //
+ // Check features
+ //
+ QString modInfoBin = path + "tcmodinfo";
+ KProcess modp;
+ modp << modInfoBin << "-p";
+ out.setProcess( &modp );
+ if( modp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ QString modPath = out.output().stripWhiteSpace();
+ QDir modDir( modPath );
+ if( !modDir.entryList( "*export_xvid*", QDir::Files ).isEmpty() )
+ bin->addFeature( "xvid" );
+ if( !modDir.entryList( "*export_lame*", QDir::Files ).isEmpty() )
+ bin->addFeature( "lame" );
+ if( !modDir.entryList( "*export_ffmpeg*", QDir::Files ).isEmpty() )
+ bin->addFeature( "ffmpeg" );
+ if( !modDir.entryList( "*export_ac3*", QDir::Files ).isEmpty() )
+ bin->addFeature( "ac3" );
+ }
+
+ addBin(bin);
+ return true;
+}
+
+
+
+K3bVcdbuilderProgram::K3bVcdbuilderProgram( const QString& p )
+ : K3bExternalProgram( p ),
+ m_vcdbuilderProgram( p )
+{
+}
+
+bool K3bVcdbuilderProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ QString path = p;
+ QFileInfo fi( path );
+ if( fi.isDir() ) {
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+ path.append(m_vcdbuilderProgram);
+ }
+
+ if( !QFile::exists( path ) )
+ return false;
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ vp << path << "-V";
+ K3bProcessOutputCollector out( &vp );
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ int pos = out.output().find( "GNU VCDImager" );
+ if( pos < 0 )
+ return false;
+
+ pos += 14;
+
+ int endPos = out.output().find( QRegExp("[\\n\\)]"), pos+1 );
+ if( endPos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = path;
+ bin->version = out.output().mid( pos, endPos-pos ).stripWhiteSpace();
+
+ pos = out.output().find( "Copyright" ) + 14;
+ endPos = out.output().find( "\n", pos );
+ bin->copyright = out.output().mid( pos, endPos-pos ).stripWhiteSpace();
+ }
+ else {
+ kdDebug() << "(K3bVcdbuilderProgram) could not start " << path << endl;
+ return false;
+ }
+
+ addBin(bin);
+ return true;
+}
+
+
+K3bNormalizeProgram::K3bNormalizeProgram()
+ : K3bExternalProgram( "normalize-audio" )
+{
+}
+
+
+bool K3bNormalizeProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ QString path = p;
+ QFileInfo fi( path );
+ if( fi.isDir() ) {
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+ path.append("normalize-audio");
+ }
+
+ if( !QFile::exists( path ) )
+ return false;
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ K3bProcessOutputCollector out( &vp );
+
+ vp << path << "--version";
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ int pos = out.output().find( "normalize" );
+ if( pos < 0 )
+ return false;
+
+ pos = out.output().find( QRegExp("\\d"), pos );
+ if( pos < 0 )
+ return false;
+
+ int endPos = out.output().find( QRegExp("\\s"), pos+1 );
+ if( endPos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = path;
+ bin->version = out.output().mid( pos, endPos-pos );
+
+ pos = out.output().find( "Copyright" )+14;
+ endPos = out.output().find( "\n", pos );
+ bin->copyright = out.output().mid( pos, endPos-pos ).stripWhiteSpace();
+ }
+ else {
+ kdDebug() << "(K3bCdrecordProgram) could not start " << path << endl;
+ return false;
+ }
+
+ addBin( bin );
+ return true;
+}
+
+
+K3bGrowisofsProgram::K3bGrowisofsProgram()
+ : K3bExternalProgram( "growisofs" )
+{
+}
+
+bool K3bGrowisofsProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ QString path = p;
+ QFileInfo fi( path );
+ if( fi.isDir() ) {
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+ path.append("growisofs");
+ }
+
+ if( !QFile::exists( path ) )
+ return false;
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ K3bProcessOutputCollector out( &vp );
+
+ vp << path << "-version";
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ int pos = out.output().find( "growisofs" );
+ if( pos < 0 )
+ return false;
+
+ pos = out.output().find( QRegExp("\\d"), pos );
+ if( pos < 0 )
+ return false;
+
+ int endPos = out.output().find( ",", pos+1 );
+ if( endPos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = path;
+ bin->version = out.output().mid( pos, endPos-pos );
+ }
+ else {
+ kdDebug() << "(K3bGrowisofsProgram) could not start " << path << endl;
+ return false;
+ }
+
+ // fixed Copyright:
+ bin->copyright = "Andy Polyakov <appro@fy.chalmers.se>";
+
+ // check if we run growisofs as root
+ struct stat s;
+ if( !::stat( QFile::encodeName(path), &s ) ) {
+ if( (s.st_mode & S_ISUID) && s.st_uid == 0 )
+ bin->addFeature( "suidroot" );
+ }
+
+ addBin( bin );
+ return true;
+}
+
+
+K3bDvdformatProgram::K3bDvdformatProgram()
+ : K3bExternalProgram( "dvd+rw-format" )
+{
+}
+
+bool K3bDvdformatProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ QString path = p;
+ QFileInfo fi( path );
+ if( fi.isDir() ) {
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+ path.append("dvd+rw-format");
+ }
+
+ if( !QFile::exists( path ) )
+ return false;
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ K3bProcessOutputCollector out( &vp );
+
+ vp << path;
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ // different locales make searching for the +- char difficult
+ // so we simply ignore it.
+ int pos = out.output().find( QRegExp("DVD.*RAM format utility") );
+ if( pos < 0 )
+ return false;
+
+ pos = out.output().find( "version", pos );
+ if( pos < 0 )
+ return false;
+
+ pos += 8;
+
+ // the version ends in a dot.
+ int endPos = out.output().find( QRegExp("\\.\\D"), pos );
+ if( endPos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = path;
+ bin->version = out.output().mid( pos, endPos-pos );
+ }
+ else {
+ kdDebug() << "(K3bDvdformatProgram) could not start " << path << endl;
+ return false;
+ }
+
+ // fixed Copyright:
+ bin->copyright = "Andy Polyakov <appro@fy.chalmers.se>";
+
+ // check if we run dvd+rw-format as root
+ struct stat s;
+ if( !::stat( QFile::encodeName(path), &s ) ) {
+ if( (s.st_mode & S_ISUID) && s.st_uid == 0 )
+ bin->addFeature( "suidroot" );
+ }
+
+ addBin( bin );
+ return true;
+}
+
+
+K3bDvdBooktypeProgram::K3bDvdBooktypeProgram()
+ : K3bExternalProgram( "dvd+rw-booktype" )
+{
+}
+
+bool K3bDvdBooktypeProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ QString path = p;
+ QFileInfo fi( path );
+ if( fi.isDir() ) {
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+ path.append("dvd+rw-booktype");
+ }
+
+ if( !QFile::exists( path ) )
+ return false;
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ K3bProcessOutputCollector out( &vp );
+
+ vp << path;
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ int pos = out.output().find( "dvd+rw-booktype" );
+ if( pos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = path;
+ // No version information. Create dummy version
+ bin->version = K3bVersion( 1, 0, 0 );
+ }
+ else {
+ kdDebug() << "(K3bDvdBooktypeProgram) could not start " << path << endl;
+ return false;
+ }
+
+ addBin( bin );
+ return true;
+}
+
+
+
+K3bCdda2wavProgram::K3bCdda2wavProgram()
+ : K3bExternalProgram( "cdda2wav" )
+{
+}
+
+bool K3bCdda2wavProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ QString path = p;
+ QFileInfo fi( path );
+ if( fi.isDir() ) {
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+ path.append("cdda2wav");
+ }
+
+ if( !QFile::exists( path ) )
+ return false;
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ K3bProcessOutputCollector out( &vp );
+
+ vp << path << "-h";
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ int pos = out.output().find( "cdda2wav" );
+ if( pos < 0 )
+ return false;
+
+ pos = out.output().find( "Version", pos );
+ if( pos < 0 )
+ return false;
+
+ pos += 8;
+
+ // the version does not end in a space but the kernel info
+ int endPos = out.output().find( QRegExp("[^\\d\\.]"), pos );
+ if( endPos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = path;
+ bin->version = out.output().mid( pos, endPos-pos );
+
+ // features (we do this since the cdda2wav help says that the short
+ // options will disappear soon)
+ if( out.output().find( "-info-only" ) )
+ bin->addFeature( "info-only" ); // otherwise use the -J option
+ if( out.output().find( "-no-infofile" ) )
+ bin->addFeature( "no-infofile" ); // otherwise use the -H option
+ if( out.output().find( "-gui" ) )
+ bin->addFeature( "gui" ); // otherwise use the -g option
+ if( out.output().find( "-bulk" ) )
+ bin->addFeature( "bulk" ); // otherwise use the -B option
+ if( out.output().find( "dev=" ) )
+ bin->addFeature( "dev" ); // otherwise use the -B option
+ }
+ else {
+ kdDebug() << "(K3bCdda2wavProgram) could not start " << path << endl;
+ return false;
+ }
+
+ // check if we run as root
+ struct stat s;
+ if( !::stat( QFile::encodeName(path), &s ) ) {
+ if( (s.st_mode & S_ISUID) && s.st_uid == 0 )
+ bin->addFeature( "suidroot" );
+ }
+
+ addBin( bin );
+ return true;
+}
+
diff --git a/libk3b/core/k3bdefaultexternalprograms.h b/libk3b/core/k3bdefaultexternalprograms.h
new file mode 100644
index 0000000..3212727
--- /dev/null
+++ b/libk3b/core/k3bdefaultexternalprograms.h
@@ -0,0 +1,143 @@
+/*
+ *
+ * $Id: k3bdefaultexternalprograms.h 623768 2007-01-15 13:33:55Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+
+#ifndef _K3B_DEFAULT_EXTERNAL_BIN_PROGRAMS_H_
+#define _K3B_DEFAULT_EXTERNAL_BIN_PROGRAMS_H_
+
+#include "k3bexternalbinmanager.h"
+#include "k3b_export.h"
+class K3bExternalBinManager;
+
+namespace K3b
+{
+ LIBK3B_EXPORT void addDefaultPrograms( K3bExternalBinManager* );
+ LIBK3B_EXPORT void addTranscodePrograms( K3bExternalBinManager* );
+ LIBK3B_EXPORT void addVcdimagerPrograms( K3bExternalBinManager* );
+}
+
+
+class LIBK3B_EXPORT K3bCdrecordProgram : public K3bExternalProgram
+{
+ public:
+ K3bCdrecordProgram( bool dvdPro );
+
+ bool scan( const QString& );
+
+ private:
+ bool m_dvdPro;
+};
+
+
+class LIBK3B_EXPORT K3bMkisofsProgram : public K3bExternalProgram
+{
+ public:
+ K3bMkisofsProgram();
+
+ bool scan( const QString& );
+};
+
+
+class LIBK3B_EXPORT K3bReadcdProgram : public K3bExternalProgram
+{
+ public:
+ K3bReadcdProgram();
+
+ bool scan( const QString& );
+};
+
+
+class LIBK3B_EXPORT K3bCdrdaoProgram : public K3bExternalProgram
+{
+ public:
+ K3bCdrdaoProgram();
+
+ bool scan( const QString& );
+};
+
+
+class LIBK3B_EXPORT K3bTranscodeProgram : public K3bExternalProgram
+{
+ public:
+ K3bTranscodeProgram( const QString& transcodeProgram );
+
+ bool scan( const QString& );
+
+ // no user parameters (yet)
+ bool supportsUserParameters() const { return false; }
+
+ private:
+ QString m_transcodeProgram;
+};
+
+
+class LIBK3B_EXPORT K3bVcdbuilderProgram : public K3bExternalProgram
+{
+ public:
+ K3bVcdbuilderProgram( const QString& );
+
+ bool scan( const QString& );
+
+ private:
+ QString m_vcdbuilderProgram;
+};
+
+
+class LIBK3B_EXPORT K3bNormalizeProgram : public K3bExternalProgram
+{
+ public:
+ K3bNormalizeProgram();
+
+ bool scan( const QString& );
+};
+
+
+class LIBK3B_EXPORT K3bGrowisofsProgram : public K3bExternalProgram
+{
+ public:
+ K3bGrowisofsProgram();
+
+ bool scan( const QString& );
+};
+
+
+class LIBK3B_EXPORT K3bDvdformatProgram : public K3bExternalProgram
+{
+ public:
+ K3bDvdformatProgram();
+
+ bool scan( const QString& );
+};
+
+
+class LIBK3B_EXPORT K3bDvdBooktypeProgram : public K3bExternalProgram
+{
+ public:
+ K3bDvdBooktypeProgram();
+
+ bool scan( const QString& );
+};
+
+
+class LIBK3B_EXPORT K3bCdda2wavProgram : public K3bExternalProgram
+{
+ public:
+ K3bCdda2wavProgram();
+
+ bool scan( const QString& );
+};
+
+#endif
diff --git a/libk3b/core/k3bexternalbinmanager.cpp b/libk3b/core/k3bexternalbinmanager.cpp
new file mode 100644
index 0000000..2b21a85
--- /dev/null
+++ b/libk3b/core/k3bexternalbinmanager.cpp
@@ -0,0 +1,389 @@
+/*
+ *
+ * $Id: k3bexternalbinmanager.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bexternalbinmanager.h"
+
+#include <kdebug.h>
+#include <kprocess.h>
+#include <kconfig.h>
+#include <kdeversion.h>
+
+#include <qstring.h>
+#include <qregexp.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qptrlist.h>
+
+#include <unistd.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+
+
+
+QString K3bExternalBinManager::m_noPath = "";
+
+
+// ///////////////////////////////////////////////////////////
+//
+// K3BEXTERNALBIN
+//
+// ///////////////////////////////////////////////////////////
+
+K3bExternalBin::K3bExternalBin( K3bExternalProgram* p )
+ : m_program(p)
+{
+}
+
+
+bool K3bExternalBin::isEmpty() const
+{
+ return !version.isValid();
+}
+
+
+const QString& K3bExternalBin::name() const
+{
+ return m_program->name();
+}
+
+
+bool K3bExternalBin::hasFeature( const QString& f ) const
+{
+ return m_features.contains( f );
+}
+
+
+void K3bExternalBin::addFeature( const QString& f )
+{
+ m_features.append( f );
+}
+
+
+const QStringList& K3bExternalBin::userParameters() const
+{
+ return m_program->userParameters();
+}
+
+
+
+// ///////////////////////////////////////////////////////////
+//
+// K3BEXTERNALPROGRAM
+//
+// ///////////////////////////////////////////////////////////
+
+
+K3bExternalProgram::K3bExternalProgram( const QString& name )
+ : m_name( name )
+{
+ m_bins.setAutoDelete( true );
+}
+
+
+K3bExternalProgram::~K3bExternalProgram()
+{
+}
+
+
+const K3bExternalBin* K3bExternalProgram::mostRecentBin() const
+{
+ QPtrListIterator<K3bExternalBin> it( m_bins );
+ K3bExternalBin* bin = *it;
+ ++it;
+ while( *it ) {
+ if( it.current()->version > bin->version )
+ bin = *it;
+ ++it;
+ }
+ return bin;
+}
+
+
+void K3bExternalProgram::addBin( K3bExternalBin* bin )
+{
+ if( !m_bins.contains( bin ) ) {
+ // insertion sort
+ // the first bin in the list is always the one used
+ // so we default to using the newest one
+ K3bExternalBin* oldBin = m_bins.first();
+ while( oldBin && oldBin->version > bin->version )
+ oldBin = m_bins.next();
+
+ m_bins.insert( oldBin ? m_bins.at() : m_bins.count(), bin );
+ }
+}
+
+void K3bExternalProgram::setDefault( const K3bExternalBin* bin )
+{
+ if( m_bins.contains( bin ) )
+ m_bins.take( m_bins.find( bin ) );
+
+ // the first bin in the list is always the one used
+ m_bins.insert( 0, bin );
+}
+
+
+void K3bExternalProgram::setDefault( const QString& path )
+{
+ for( QPtrListIterator<K3bExternalBin> it( m_bins ); it.current(); ++it ) {
+ if( it.current()->path == path ) {
+ setDefault( it.current() );
+ return;
+ }
+ }
+}
+
+
+void K3bExternalProgram::addUserParameter( const QString& p )
+{
+ if( !m_userParameters.contains( p ) )
+ m_userParameters.append(p);
+}
+
+
+
+// ///////////////////////////////////////////////////////////
+//
+// K3BEXTERNALBINMANAGER
+//
+// ///////////////////////////////////////////////////////////
+
+
+K3bExternalBinManager::K3bExternalBinManager( QObject* parent, const char* name )
+ : QObject( parent, name )
+{
+}
+
+
+K3bExternalBinManager::~K3bExternalBinManager()
+{
+ clear();
+}
+
+
+bool K3bExternalBinManager::readConfig( KConfig* c )
+{
+ loadDefaultSearchPath();
+
+ c->setGroup( "External Programs" );
+
+ if( c->hasKey( "search path" ) )
+ setSearchPath( c->readPathListEntry( "search path" ) );
+
+ search();
+
+ for ( QMap<QString, K3bExternalProgram*>::iterator it = m_programs.begin(); it != m_programs.end(); ++it ) {
+ K3bExternalProgram* p = it.data();
+ if( c->hasKey( p->name() + " default" ) ) {
+ p->setDefault( c->readEntry( p->name() + " default" ) );
+ }
+ if( c->hasKey( p->name() + " user parameters" ) ) {
+ QStringList list = c->readListEntry( p->name() + " user parameters" );
+ for( QStringList::iterator strIt = list.begin(); strIt != list.end(); ++strIt )
+ p->addUserParameter( *strIt );
+ }
+ if( c->hasKey( p->name() + " last seen newest version" ) ) {
+ K3bVersion lastMax( c->readEntry( p->name() + " last seen newest version" ) );
+ // now search for a newer version and use it (because it was installed after the last
+ // K3b run and most users would probably expect K3b to use a newly installed version)
+ const K3bExternalBin* newestBin = p->mostRecentBin();
+ if( newestBin && newestBin->version > lastMax )
+ p->setDefault( newestBin );
+ }
+ }
+
+ return true;
+}
+
+bool K3bExternalBinManager::saveConfig( KConfig* c )
+{
+ c->setGroup( "External Programs" );
+ c->writePathEntry( "search path", m_searchPath );
+
+ for ( QMap<QString, K3bExternalProgram*>::iterator it = m_programs.begin(); it != m_programs.end(); ++it ) {
+ K3bExternalProgram* p = it.data();
+ if( p->defaultBin() )
+ c->writeEntry( p->name() + " default", p->defaultBin()->path );
+
+ c->writeEntry( p->name() + " user parameters", p->userParameters() );
+
+ const K3bExternalBin* newestBin = p->mostRecentBin();
+ if( newestBin )
+ c->writeEntry( p->name() + " last seen newest version", newestBin->version );
+ }
+
+ return true;
+}
+
+
+bool K3bExternalBinManager::foundBin( const QString& name )
+{
+ if( m_programs.find( name ) == m_programs.end() )
+ return false;
+ else
+ return (m_programs[name]->defaultBin() != 0);
+}
+
+
+const QString& K3bExternalBinManager::binPath( const QString& name )
+{
+ if( m_programs.find( name ) == m_programs.end() )
+ return m_noPath;
+
+ if( m_programs[name]->defaultBin() != 0 )
+ return m_programs[name]->defaultBin()->path;
+ else
+ return m_noPath;
+}
+
+
+const K3bExternalBin* K3bExternalBinManager::binObject( const QString& name )
+{
+ if( m_programs.find( name ) == m_programs.end() )
+ return 0;
+
+ return m_programs[name]->defaultBin();
+}
+
+
+void K3bExternalBinManager::addProgram( K3bExternalProgram* p )
+{
+ m_programs.insert( p->name(), p );
+}
+
+
+void K3bExternalBinManager::clear()
+{
+ for( QMap<QString, K3bExternalProgram*>::Iterator it = m_programs.begin(); it != m_programs.end(); ++it )
+ delete it.data();
+ m_programs.clear();
+}
+
+
+void K3bExternalBinManager::search()
+{
+ if( m_searchPath.isEmpty() )
+ loadDefaultSearchPath();
+
+ for( QMap<QString, K3bExternalProgram*>::iterator it = m_programs.begin(); it != m_programs.end(); ++it ) {
+ it.data()->clear();
+ }
+
+ // do not search one path twice
+ QStringList paths;
+ for( QStringList::const_iterator it = m_searchPath.begin(); it != m_searchPath.end(); ++it ) {
+ QString p = *it;
+ if( p[p.length()-1] == '/' )
+ p.truncate( p.length()-1 );
+ if( !paths.contains( p ) && !paths.contains( p + "/" ) )
+ paths.append(p);
+ }
+
+ // get the environment path variable
+ char* env_path = ::getenv("PATH");
+ if( env_path ) {
+ QStringList env_pathList = QStringList::split(":", QString::fromLocal8Bit(env_path));
+ for( QStringList::const_iterator it = env_pathList.begin(); it != env_pathList.end(); ++it ) {
+ QString p = *it;
+ if( p[p.length()-1] == '/' )
+ p.truncate( p.length()-1 );
+ if( !paths.contains( p ) && !paths.contains( p + "/" ) )
+ paths.append(p);
+ }
+ }
+
+
+ for( QStringList::const_iterator it = paths.begin(); it != paths.end(); ++it )
+ for( QMap<QString, K3bExternalProgram*>::iterator pit = m_programs.begin(); pit != m_programs.end(); ++pit )
+ pit.data()->scan(*it);
+
+ // TESTING
+ // /////////////////////////
+ const K3bExternalBin* bin = program("cdrecord")->defaultBin();
+
+ if( !bin ) {
+ kdDebug() << "(K3bExternalBinManager) Probing cdrecord failed" << endl;
+ }
+ else {
+ kdDebug() << "(K3bExternalBinManager) Cdrecord " << bin->version << " features: "
+ << bin->features().join( ", " ) << endl;
+
+ if( bin->version >= K3bVersion("1.11a02") )
+ kdDebug() << "(K3bExternalBinManager) "
+ << bin->version.majorVersion() << " " << bin->version.minorVersion() << " " << bin->version.patchLevel()
+ << " " << bin->version.suffix()
+ << " seems to be cdrecord version >= 1.11a02, using burnfree instead of burnproof" << endl;
+ if( bin->version >= K3bVersion("1.11a31") )
+ kdDebug() << "(K3bExternalBinManager) seems to be cdrecord version >= 1.11a31, support for Just Link via burnfree "
+ << "driveroption" << endl;
+ }
+}
+
+
+K3bExternalProgram* K3bExternalBinManager::program( const QString& name ) const
+{
+ if( m_programs.find( name ) == m_programs.end() )
+ return 0;
+ else
+ return m_programs[name];
+}
+
+
+void K3bExternalBinManager::loadDefaultSearchPath()
+{
+ static const char* defaultSearchPaths[] = { "/usr/bin/",
+ "/usr/local/bin/",
+ "/usr/sbin/",
+ "/usr/local/sbin/",
+ "/opt/schily/bin/",
+ "/sbin",
+ 0 };
+
+ m_searchPath.clear();
+ for( int i = 0; defaultSearchPaths[i]; ++i ) {
+ m_searchPath.append( defaultSearchPaths[i] );
+ }
+}
+
+
+void K3bExternalBinManager::setSearchPath( const QStringList& list )
+{
+ loadDefaultSearchPath();
+
+ for( QStringList::const_iterator it = list.begin(); it != list.end(); ++it ) {
+ if( !m_searchPath.contains( *it ) )
+ m_searchPath.append( *it );
+ }
+}
+
+
+void K3bExternalBinManager::addSearchPath( const QString& path )
+{
+ if( !m_searchPath.contains( path ) )
+ m_searchPath.append( path );
+}
+
+
+
+const K3bExternalBin* K3bExternalBinManager::mostRecentBinObject( const QString& name )
+{
+ if( K3bExternalProgram* p = program( name ) )
+ return p->mostRecentBin();
+ else
+ return 0;
+}
+
+#include "k3bexternalbinmanager.moc"
+
diff --git a/libk3b/core/k3bexternalbinmanager.h b/libk3b/core/k3bexternalbinmanager.h
new file mode 100644
index 0000000..e7fe601
--- /dev/null
+++ b/libk3b/core/k3bexternalbinmanager.h
@@ -0,0 +1,162 @@
+/*
+ *
+ * $Id: k3bexternalbinmanager.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef K3B_EXTERNAL_BIN_MANAGER_H
+#define K3B_EXTERNAL_BIN_MANAGER_H
+
+#include <qmap.h>
+#include <qobject.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qptrlist.h>
+#include "k3b_export.h"
+#include "k3bversion.h"
+
+class KConfig;
+class KProcess;
+
+
+class K3bExternalProgram;
+
+
+/**
+ * A K3bExternalBin represents an installed version of a program.
+ * All K3bExternalBin objects are managed by K3bExternalPrograms.
+ *
+ * A bin may have certain features that are represented by a string.
+ */
+class LIBK3B_EXPORT K3bExternalBin
+{
+ public:
+ K3bExternalBin( K3bExternalProgram* );
+ virtual ~K3bExternalBin() {}
+
+ K3bVersion version;
+ QString path;
+ QString copyright;
+
+ const QString& name() const;
+ bool isEmpty() const;
+ const QStringList& userParameters() const;
+ const QStringList& features() const { return m_features; }
+
+ bool hasFeature( const QString& ) const;
+ void addFeature( const QString& );
+
+ K3bExternalProgram* program() const { return m_program; }
+
+ private:
+ QStringList m_features;
+ K3bExternalProgram* m_program;
+};
+
+
+/**
+ * This is the main class that represents a program
+ * It's scan method has to be reimplemented for every program
+ * It manages a list of K3bExternalBin-objects that each represent
+ * one installed version of the program.
+ */
+class LIBK3B_EXPORT K3bExternalProgram
+{
+ public:
+ K3bExternalProgram( const QString& name );
+ virtual ~K3bExternalProgram();
+
+ const K3bExternalBin* defaultBin() const { return m_bins.getFirst(); }
+ const K3bExternalBin* mostRecentBin() const;
+
+ void addUserParameter( const QString& );
+ void setUserParameters( const QStringList& list ) { m_userParameters = list; }
+
+ const QStringList& userParameters() const { return m_userParameters; }
+ const QString& name() const { return m_name; }
+
+ void addBin( K3bExternalBin* );
+ void clear() { m_bins.clear(); }
+ void setDefault( const K3bExternalBin* );
+ void setDefault( const QString& path );
+
+ const QPtrList<K3bExternalBin>& bins() const { return m_bins; }
+
+ /**
+ * this scans for the program in the given path,
+ * adds the found bin object to the list and returnes true.
+ * if nothing could be found false is returned.
+ */
+ virtual bool scan( const QString& ) {return false;}//= 0;
+
+ /**
+ * reimplement this if it does not make sense to have the user be able
+ * to specify additional parameters
+ */
+ virtual bool supportsUserParameters() const { return true; }
+
+ private:
+ QString m_name;
+ QStringList m_userParameters;
+ QPtrList<K3bExternalBin> m_bins;
+};
+
+
+class LIBK3B_EXPORT K3bExternalBinManager : public QObject
+{
+ Q_OBJECT
+
+ public:
+ K3bExternalBinManager( QObject* parent = 0, const char* name = 0 );
+ ~K3bExternalBinManager();
+
+ void search();
+
+ /**
+ * read config and add changes to current map.
+ * Takes care of setting the config group
+ */
+ bool readConfig( KConfig* );
+
+ /**
+ * Takes care of setting the config group
+ */
+ bool saveConfig( KConfig* );
+
+ bool foundBin( const QString& name );
+ const QString& binPath( const QString& name );
+ const K3bExternalBin* binObject( const QString& name );
+ const K3bExternalBin* mostRecentBinObject( const QString& name );
+
+ K3bExternalProgram* program( const QString& ) const;
+ const QMap<QString, K3bExternalProgram*>& programs() const { return m_programs; }
+
+ /** always extends the default searchpath */
+ void setSearchPath( const QStringList& );
+ void addSearchPath( const QString& );
+ void loadDefaultSearchPath();
+
+ const QStringList& searchPath() const { return m_searchPath; }
+
+ void addProgram( K3bExternalProgram* );
+ void clear();
+
+ private:
+ QMap<QString, K3bExternalProgram*> m_programs;
+ QStringList m_searchPath;
+
+ static QString m_noPath; // used for binPath() to return const string
+
+ QString m_gatheredOutput;
+};
+
+#endif
diff --git a/libk3b/core/k3bglobals.cpp b/libk3b/core/k3bglobals.cpp
new file mode 100644
index 0000000..fc5a4e1
--- /dev/null
+++ b/libk3b/core/k3bglobals.cpp
@@ -0,0 +1,634 @@
+/*
+ *
+ * $Id: k3bglobals.cpp 659634 2007-04-30 14:51:32Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include <config.h>
+
+
+#include "k3bglobals.h"
+#include <k3bversion.h>
+#include <k3bdevice.h>
+#include <k3bdevicemanager.h>
+#include <k3bdeviceglobals.h>
+#include <k3bexternalbinmanager.h>
+#include <k3bcore.h>
+#include <k3bhalconnection.h>
+
+#include <kdeversion.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kconfig.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kio/job.h>
+#include <kio/netaccess.h>
+#include <kurl.h>
+#include <dcopref.h>
+#include <kprocess.h>
+
+#include <qdatastream.h>
+#include <qdir.h>
+#include <qfile.h>
+
+#include <cmath>
+#include <sys/utsname.h>
+#include <sys/stat.h>
+
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+# include <sys/param.h>
+# include <sys/mount.h>
+# include <sys/endian.h>
+# define bswap_16(x) bswap16(x)
+# define bswap_32(x) bswap32(x)
+# define bswap_64(x) bswap64(x)
+#else
+# include <byteswap.h>
+#endif
+#ifdef HAVE_SYS_STATVFS_H
+# include <sys/statvfs.h>
+#endif
+#ifdef HAVE_SYS_VFS_H
+# include <sys/vfs.h>
+#endif
+
+
+/*
+struct Sample {
+ unsigned char msbLeft;
+ unsigned char lsbLeft;
+ unsigned char msbRight;
+ unsigned char lsbRight;
+
+ short left() const {
+ return ( msbLeft << 8 ) | lsbLeft;
+ }
+ short right() const {
+ return ( msbRight << 8 ) | lsbRight;
+ }
+ void left( short d ) {
+ msbLeft = d >> 8;
+ lsbLeft = d;
+ }
+ void right( short d ) {
+ msbRight = d >> 8;
+ lsbRight = d;
+ }
+};
+*/
+
+QString K3b::framesToString( int h, bool showFrames )
+{
+ int m = h / 4500;
+ int s = (h % 4500) / 75;
+ int f = h % 75;
+
+ QString str;
+
+ if( showFrames ) {
+ // cdrdao needs the MSF format where 1 second has 75 frames!
+ str.sprintf( "%.2i:%.2i:%.2i", m, s, f );
+ }
+ else
+ str.sprintf( "%.2i:%.2i", m, s );
+
+ return str;
+}
+
+/*QString K3b::sizeToTime(long size)
+{
+ int h = size / sizeof(Sample) / 588;
+ return framesToString(h, false);
+}*/
+
+
+Q_INT16 K3b::swapByteOrder( const Q_INT16& i )
+{
+ return bswap_16( i );
+ //((i << 8) & 0xff00) | ((i >> 8 ) & 0xff);
+}
+
+
+Q_INT32 K3b::swapByteOrder( const Q_INT32& i )
+{
+ //return ((i << 24) & 0xff000000) | ((i << 8) & 0xff0000) | ((i >> 8) & 0xff00) | ((i >> 24) & 0xff );
+ return bswap_32( i );
+}
+
+
+Q_INT64 K3b::swapByteOrder( const Q_INT64& i )
+{
+ return bswap_64( i );
+}
+
+
+int K3b::round( double d )
+{
+ return (int)( floor(d) + 0.5 <= d ? ceil(d) : floor(d) );
+}
+
+
+QString K3b::findUniqueFilePrefix( const QString& _prefix, const QString& path )
+{
+ QString url;
+ if( path.isEmpty() || !QFile::exists(path) )
+ url = defaultTempPath();
+ else
+ url = prepareDir( path );
+
+ QString prefix = _prefix;
+ if( prefix.isEmpty() )
+ prefix = "k3b_";
+
+ // now create the unique prefix
+ QDir dir( url );
+ QStringList entries = dir.entryList( QDir::DefaultFilter, QDir::Name );
+ int i = 0;
+ for( QStringList::iterator it = entries.begin();
+ it != entries.end(); ++it ) {
+ if( (*it).startsWith( prefix + QString::number(i) ) ) {
+ i++;
+ it = entries.begin();
+ }
+ }
+
+ return url + prefix + QString::number(i);
+}
+
+
+QString K3b::findTempFile( const QString& ending, const QString& d )
+{
+ return findUniqueFilePrefix( "k3b_", d ) + ( ending.isEmpty() ? QString::null : (QString::fromLatin1(".") + ending) );
+}
+
+
+QString K3b::defaultTempPath()
+{
+ QString oldGroup = kapp->config()->group();
+ kapp->config()->setGroup( "General Options" );
+ QString url = kapp->config()->readPathEntry( "Temp Dir", KGlobal::dirs()->resourceDirs( "tmp" ).first() );
+ kapp->config()->setGroup( oldGroup );
+ return prepareDir(url);
+}
+
+
+QString K3b::prepareDir( const QString& dir )
+{
+ return (dir + (dir[dir.length()-1] != '/' ? "/" : ""));
+}
+
+
+QString K3b::parentDir( const QString& path )
+{
+ QString parent = path;
+ if( path[path.length()-1] == '/' )
+ parent.truncate( parent.length()-1 );
+
+ int pos = parent.findRev( '/' );
+ if( pos >= 0 )
+ parent.truncate( pos+1 );
+ else // relative path, do anything...
+ parent = "/";
+
+ return parent;
+}
+
+
+QString K3b::fixupPath( const QString& path )
+{
+ QString s;
+ bool lastWasSlash = false;
+ for( unsigned int i = 0; i < path.length(); ++i ) {
+ if( path[i] == '/' ) {
+ if( !lastWasSlash ) {
+ lastWasSlash = true;
+ s.append( "/" );
+ }
+ }
+ else {
+ lastWasSlash = false;
+ s.append( path[i] );
+ }
+ }
+
+ return s;
+}
+
+
+K3bVersion K3b::kernelVersion()
+{
+ // initialize kernel version
+ K3bVersion v;
+ utsname unameinfo;
+ if( ::uname(&unameinfo) == 0 ) {
+ v = QString::fromLocal8Bit( unameinfo.release );
+ kdDebug() << "kernel version: " << v << endl;
+ }
+ else
+ kdError() << "could not determine kernel version." << endl;
+ return v;
+}
+
+
+K3bVersion K3b::simpleKernelVersion()
+{
+ return kernelVersion().simplify();
+}
+
+
+QString K3b::systemName()
+{
+ QString v;
+ utsname unameinfo;
+ if( ::uname(&unameinfo) == 0 ) {
+ v = QString::fromLocal8Bit( unameinfo.sysname );
+ }
+ else
+ kdError() << "could not determine system name." << endl;
+ return v;
+}
+
+
+bool K3b::kbFreeOnFs( const QString& path, unsigned long& size, unsigned long& avail )
+{
+ struct statvfs fs;
+ if( ::statvfs( QFile::encodeName(path), &fs ) == 0 ) {
+ unsigned long kBfak = fs.f_frsize/1024;
+
+ size = fs.f_blocks*kBfak;
+ avail = fs.f_bavail*kBfak;
+
+ return true;
+ }
+ else
+ return false;
+}
+
+
+KIO::filesize_t K3b::filesize( const KURL& url )
+{
+ if( url.isLocalFile() ) {
+ k3b_struct_stat buf;
+ if ( !k3b_stat( QFile::encodeName( url.path() ), &buf ) ) {
+ return (KIO::filesize_t)buf.st_size;
+ }
+ }
+
+ KIO::UDSEntry uds;
+ KIO::NetAccess::stat( url, uds, 0 );
+ for( KIO::UDSEntry::const_iterator it = uds.begin(); it != uds.end(); ++it ) {
+ if( (*it).m_uds == KIO::UDS_SIZE ) {
+ return (*it).m_long;
+ }
+ }
+
+ return ( KIO::filesize_t )0;
+}
+
+
+KIO::filesize_t K3b::imageFilesize( const KURL& url )
+{
+ KIO::filesize_t size = K3b::filesize( url );
+ int cnt = 0;
+ while( KIO::NetAccess::exists( KURL::fromPathOrURL( url.url() + '.' + QString::number(cnt).rightJustify( 3, '0' ) ), true ) )
+ size += K3b::filesize( KURL::fromPathOrURL( url.url() + '.' + QString::number(cnt++).rightJustify( 3, '0' ) ) );
+ return size;
+}
+
+
+QString K3b::cutFilename( const QString& name, unsigned int len )
+{
+ if( name.length() > len ) {
+ QString ret = name;
+
+ // determine extension (we think of an extension to be at most 5 chars in length)
+ int pos = name.find( '.', -6 );
+ if( pos > 0 )
+ len -= (name.length() - pos);
+
+ ret.truncate( len );
+
+ if( pos > 0 )
+ ret.append( name.mid( pos ) );
+
+ return ret;
+ }
+ else
+ return name;
+}
+
+
+QString K3b::removeFilenameExtension( const QString& name )
+{
+ QString v = name;
+ int dotpos = v.findRev( '.' );
+ if( dotpos > 0 )
+ v.truncate( dotpos );
+ return v;
+}
+
+
+QString K3b::appendNumberToFilename( const QString& name, int num, unsigned int maxlen )
+{
+ // determine extension (we think of an extension to be at most 5 chars in length)
+ QString result = name;
+ QString ext;
+ int pos = name.find( '.', -6 );
+ if( pos > 0 ) {
+ ext = name.mid(pos);
+ result.truncate( pos );
+ }
+
+ ext.prepend( QString::number(num) );
+ result.truncate( maxlen - ext.length() );
+
+ return result + ext;
+}
+
+
+bool K3b::plainAtapiSupport()
+{
+ // FIXME: what about BSD?
+ return ( K3b::simpleKernelVersion() >= K3bVersion( 2, 5, 40 ) );
+}
+
+
+bool K3b::hackedAtapiSupport()
+{
+ // IMPROVEME!!!
+ // FIXME: since when does the kernel support this?
+ return ( K3b::simpleKernelVersion() >= K3bVersion( 2, 4, 0 ) );
+}
+
+
+QString K3b::externalBinDeviceParameter( K3bDevice::Device* dev, const K3bExternalBin* bin )
+{
+#ifdef Q_OS_LINUX
+ //
+ // experimental: always use block devices on 2.6 kernels
+ //
+ if( simpleKernelVersion() >= K3bVersion( 2, 6, 0 ) )
+ return dev->blockDeviceName();
+ else
+#endif
+ if( dev->interfaceType() == K3bDevice::SCSI )
+ return dev->busTargetLun();
+ else if( (plainAtapiSupport() && bin->hasFeature("plain-atapi") ) )
+ return dev->blockDeviceName();
+ else
+ return QString("ATAPI:%1").arg(dev->blockDeviceName());
+}
+
+
+int K3b::writingAppFromString( const QString& s )
+{
+ if( s.lower() == "cdrdao" )
+ return K3b::CDRDAO;
+ else if( s.lower() == "cdrecord" )
+ return K3b::CDRECORD;
+ else if( s.lower() == "dvdrecord" )
+ return K3b::DVDRECORD;
+ else if( s.lower() == "growisofs" )
+ return K3b::GROWISOFS;
+ else if( s.lower() == "dvd+rw-format" )
+ return K3b::DVD_RW_FORMAT;
+ else
+ return K3b::DEFAULT;
+}
+
+
+QString K3b::writingModeString( int mode )
+{
+ if( mode == WRITING_MODE_AUTO )
+ return i18n("Auto");
+ else
+ return K3bDevice::writingModeString( mode );
+}
+
+
+QString K3b::resolveLink( const QString& file )
+{
+ QFileInfo f( file );
+ QStringList steps( f.absFilePath() );
+ while( f.isSymLink() ) {
+ QString p = f.readLink();
+ if( !p.startsWith( "/" ) )
+ p.prepend( f.dirPath(true) + "/" );
+ f.setFile( p );
+ if( steps.contains( f.absFilePath() ) ) {
+ kdDebug() << "(K3b) symlink loop detected." << endl;
+ break;
+ }
+ else
+ steps.append( f.absFilePath() );
+ }
+ return f.absFilePath();
+}
+
+
+K3bDevice::Device* K3b::urlToDevice( const KURL& deviceUrl )
+{
+ if( deviceUrl.protocol() == "media" || deviceUrl.protocol() == "system" ) {
+ kdDebug() << "(K3b) Asking mediamanager for " << deviceUrl.fileName() << endl;
+ DCOPRef mediamanager("kded", "mediamanager");
+ DCOPReply reply = mediamanager.call("properties(QString)", deviceUrl.fileName());
+ QStringList properties = reply;
+ if( !reply.isValid() || properties.count() < 6 ) {
+ kdError() << "(K3b) Invalid reply from mediamanager" << endl;
+ return 0;
+ }
+ else {
+ kdDebug() << "(K3b) Reply from mediamanager " << properties[5] << endl;
+ return k3bcore->deviceManager()->findDevice( properties[5] );
+ }
+ }
+
+ return k3bcore->deviceManager()->findDevice( deviceUrl.path() );
+}
+
+
+KURL K3b::convertToLocalUrl( const KURL& url )
+{
+ if( !url.isLocalFile() ) {
+#if KDE_IS_VERSION(3,4,91)
+ return KIO::NetAccess::mostLocalURL( url, 0 );
+#else
+#ifndef UDS_LOCAL_PATH
+#define UDS_LOCAL_PATH (72 | KIO::UDS_STRING)
+#else
+ using namespace KIO;
+#endif
+ KIO::UDSEntry e;
+ if( KIO::NetAccess::stat( url, e, 0 ) ) {
+ const KIO::UDSEntry::ConstIterator end = e.end();
+ for( KIO::UDSEntry::ConstIterator it = e.begin(); it != end; ++it ) {
+ if( (*it).m_uds == UDS_LOCAL_PATH && !(*it).m_str.isEmpty() )
+ return KURL::fromPathOrURL( (*it).m_str );
+ }
+ }
+#endif
+ }
+
+ return url;
+}
+
+
+KURL::List K3b::convertToLocalUrls( const KURL::List& urls )
+{
+ KURL::List r;
+ for( KURL::List::const_iterator it = urls.constBegin(); it != urls.constEnd(); ++it )
+ r.append( convertToLocalUrl( *it ) );
+ return r;
+}
+
+
+Q_INT16 K3b::fromLe16( char* data )
+{
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ return swapByteOrder( *((Q_INT16*)data) );
+#else
+ return *((Q_INT16*)data);
+#endif
+}
+
+
+Q_INT32 K3b::fromLe32( char* data )
+{
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ return swapByteOrder( *((Q_INT32*)data) );
+#else
+ return *((Q_INT32*)data);
+#endif
+}
+
+
+Q_INT64 K3b::fromLe64( char* data )
+{
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ return swapByteOrder( *((Q_INT64*)data) );
+#else
+ return *((Q_INT64*)data);
+#endif
+}
+
+
+QString K3b::findExe( const QString& name )
+{
+ // first we search the path
+ QString bin = KStandardDirs::findExe( name );
+
+ // then go on with our own little list
+ if( bin.isEmpty() )
+ bin = KStandardDirs::findExe( name, k3bcore->externalBinManager()->searchPath().join(":") );
+
+ return bin;
+}
+
+
+bool K3b::isMounted( K3bDevice::Device* dev )
+{
+ if( !dev )
+ return false;
+
+ return !KIO::findDeviceMountPoint( dev->blockDeviceName() ).isEmpty();
+}
+
+
+bool K3b::unmount( K3bDevice::Device* dev )
+{
+ if( !dev )
+ return false;
+
+ QString mntDev = dev->blockDeviceName();
+
+#if KDE_IS_VERSION(3,4,0)
+ // first try to unmount it the standard way
+ if( KIO::NetAccess::synchronousRun( KIO::unmount( mntDev, false ), 0 ) )
+ return true;
+#endif
+
+ QString umountBin = K3b::findExe( "umount" );
+ if( !umountBin.isEmpty() ) {
+ KProcess p;
+ p << umountBin;
+ p << "-l"; // lazy unmount
+ p << dev->blockDeviceName();
+ p.start( KProcess::Block );
+ if( !p.exitStatus() )
+ return true;
+ }
+
+ // now try pmount
+ QString pumountBin = K3b::findExe( "pumount" );
+ if( !pumountBin.isEmpty() ) {
+ KProcess p;
+ p << pumountBin;
+ p << "-l"; // lazy unmount
+ p << dev->blockDeviceName();
+ p.start( KProcess::Block );
+ return !p.exitStatus();
+ }
+ else {
+#ifdef HAVE_HAL
+ return !K3bDevice::HalConnection::instance()->unmount( dev );
+#else
+ return false;
+#endif
+ }
+}
+
+
+bool K3b::mount( K3bDevice::Device* dev )
+{
+ if( !dev )
+ return false;
+
+ QString mntDev = dev->blockDeviceName();
+
+#if KDE_IS_VERSION(3,4,0)
+ // first try to mount it the standard way
+ if( KIO::NetAccess::synchronousRun( KIO::mount( true, 0, mntDev, false ), 0 ) )
+ return true;
+#endif
+
+#ifdef HAVE_HAL
+ if( !K3bDevice::HalConnection::instance()->mount( dev ) )
+ return true;
+#endif
+
+ // now try pmount
+ QString pmountBin = K3b::findExe( "pmount" );
+ if( !pmountBin.isEmpty() ) {
+ KProcess p;
+ p << pmountBin;
+ p << mntDev;
+ p.start( KProcess::Block );
+ return !p.exitStatus();
+ }
+ return false;
+}
+
+
+bool K3b::eject( K3bDevice::Device* dev )
+{
+#ifdef HAVE_HAL
+ if( !K3bDevice::HalConnection::instance()->eject( dev ) )
+ return true;
+#endif
+
+ if( K3b::isMounted( dev ) )
+ K3b::unmount( dev );
+
+ return dev->eject();
+}
diff --git a/libk3b/core/k3bglobals.h b/libk3b/core/k3bglobals.h
new file mode 100644
index 0000000..2795630
--- /dev/null
+++ b/libk3b/core/k3bglobals.h
@@ -0,0 +1,257 @@
+/*
+ *
+ * $Id: k3bglobals.h 630384 2007-02-05 09:33:17Z mlaurent $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BGLOBALS_H
+#define K3BGLOBALS_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <qstring.h>
+#include <qfile.h>
+#include <kio/global.h>
+#include <kurl.h>
+#include <k3bdevicetypes.h>
+#include "k3b_export.h"
+
+class KConfig;
+class K3bVersion;
+class K3bExternalBin;
+
+
+#include <sys/stat.h>
+
+
+#ifdef HAVE_STAT64
+#define k3b_struct_stat struct stat64
+#define k3b_stat ::stat64
+#define k3b_lstat ::lstat64
+#else
+#define k3b_struct_stat struct stat
+#define k3b_stat ::stat
+#define k3b_lstat ::lstat
+#endif
+
+
+namespace K3bDevice {
+ class Device;
+}
+
+namespace K3b
+{
+ enum WritingApp {
+ DEFAULT = 1,
+ CDRECORD = 2,
+ CDRDAO = 4,
+ DVDRECORD = 8,
+ GROWISOFS = 16,
+ DVD_RW_FORMAT = 32
+ };
+
+ LIBK3B_EXPORT int writingAppFromString( const QString& );
+
+ /**
+ * DATA_MODE_AUTO - let K3b determine the best mode
+ * MODE1 - refers to the default Yellow book mode1
+ * MODE2 - refers to CDROM XA mode2 form1
+ */
+ enum DataMode {
+ DATA_MODE_AUTO,
+ MODE1,
+ MODE2
+ };
+
+ /**
+ * The sector size denotes the number of bytes K3b provides per sector.
+ * This is based on the sizes cdrecord's -data, -xa, and -xamix parameters
+ * demand.
+ */
+ enum SectorSize {
+ SECTORSIZE_AUDIO = 2352,
+ SECTORSIZE_DATA_2048 = 2048,
+ SECTORSIZE_DATA_2048_SUBHEADER = 2056,
+ SECTORSIZE_DATA_2324 = 2324,
+ SECTORSIZE_DATA_2324_SUBHEADER = 2332,
+ SECTORSIZE_RAW = 2448
+ };
+
+ /**
+ * AUTO - let K3b determine the best mode
+ * TAO - Track at once
+ * DAO - Disk at once (or session at once)
+ * RAW - Raw mode
+ *
+ * may be or'ed together (except for WRITING_MODE_AUTO of course)
+ */
+ enum WritingMode {
+ WRITING_MODE_AUTO = 0,
+ TAO = K3bDevice::WRITINGMODE_TAO,
+ DAO = K3bDevice::WRITINGMODE_SAO,
+ RAW = K3bDevice::WRITINGMODE_RAW,
+ WRITING_MODE_INCR_SEQ = K3bDevice::WRITINGMODE_INCR_SEQ, // Incremental Sequential
+ WRITING_MODE_RES_OVWR = K3bDevice::WRITINGMODE_RES_OVWR // Restricted Overwrite
+ };
+
+ LIBK3B_EXPORT QString writingModeString( int );
+
+ LIBK3B_EXPORT QString framesToString( int h, bool showFrames = true );
+ /*LIBK3B_EXPORT QString sizeToTime( long size );*/
+
+ LIBK3B_EXPORT Q_INT16 swapByteOrder( const Q_INT16& i );
+ LIBK3B_EXPORT Q_INT32 swapByteOrder( const Q_INT32& i );
+ LIBK3B_EXPORT Q_INT64 swapByteOrder( const Q_INT64& i );
+
+ int round( double );
+
+ /**
+ * This checks the free space on the filesystem path is in.
+ * We use this since we encountered problems with the KDE version.
+ * @returns true on success.
+ */
+ LIBK3B_EXPORT bool kbFreeOnFs( const QString& path, unsigned long& size, unsigned long& avail );
+
+ /**
+ * Cut a filename preserving the extension
+ */
+ LIBK3B_EXPORT QString cutFilename( const QString& name, unsigned int len );
+
+ LIBK3B_EXPORT QString removeFilenameExtension( const QString& name );
+
+ /**
+ * Append a number to a filename preserving the extension.
+ * The resulting name's length will not exceed @p maxlen
+ */
+ LIBK3B_EXPORT QString appendNumberToFilename( const QString& name, int num, unsigned int maxlen );
+
+ LIBK3B_EXPORT QString findUniqueFilePrefix( const QString& _prefix = QString::null, const QString& path = QString::null );
+
+ /**
+ * Find a unique filename in directory d (if d is empty the method uses the defaultTempPath)
+ */
+ LIBK3B_EXPORT QString findTempFile( const QString& ending = QString::null, const QString& d = QString::null );
+
+ /**
+ * Wrapper around KStandardDirs::findExe which searches the PATH and some additional
+ * directories to find system tools which are normally only in root's PATH.
+ */
+ LIBK3B_EXPORT QString findExe( const QString& name );
+
+ /**
+ * get the default K3b temp path to store image files
+ */
+ LIBK3B_EXPORT QString defaultTempPath();
+
+ /**
+ * makes sure a path ends with a "/"
+ */
+ LIBK3B_EXPORT QString prepareDir( const QString& dir );
+
+ /**
+ * returns the parent dir of a path.
+ * CAUTION: this does only work well with absolut paths.
+ *
+ * Example: /usr/share/doc -> /usr/share/
+ */
+ QString parentDir( const QString& path );
+
+ /**
+ * For now this just replaces multiple occurrences of / with a single /
+ */
+ LIBK3B_EXPORT QString fixupPath( const QString& );
+
+ /**
+ * resolves a symlinks completely. Meaning it also handles links to links to links...
+ */
+ LIBK3B_EXPORT QString resolveLink( const QString& );
+
+ LIBK3B_EXPORT K3bVersion kernelVersion();
+
+ /**
+ * Kernel version stripped of all suffixes
+ */
+ LIBK3B_EXPORT K3bVersion simpleKernelVersion();
+
+ QString systemName();
+
+ LIBK3B_EXPORT KIO::filesize_t filesize( const KURL& );
+
+ /**
+ * Calculate the total size of an image file. This also includes
+ * images splitted by a K3bFileSplitter.
+ *
+ * \returns the total size of the image file at url
+ */
+ LIBK3B_EXPORT KIO::filesize_t imageFilesize( const KURL& url );
+
+ /**
+ * true if the kernel supports ATAPI devices without SCSI emulation.
+ * use in combination with the K3bExternalProgram feature "plain-atapi"
+ */
+ LIBK3B_EXPORT bool plainAtapiSupport();
+
+ /**
+ * true if the kernel supports ATAPI devices without SCSI emulation
+ * via the ATAPI: pseudo stuff
+ * use in combination with the K3bExternalProgram feature "hacked-atapi"
+ */
+ LIBK3B_EXPORT bool hackedAtapiSupport();
+
+ /**
+ * Used to create a parameter for cdrecord, cdrdao or readcd.
+ * Takes care of SCSI and ATAPI.
+ */
+ QString externalBinDeviceParameter( K3bDevice::Device* dev, const K3bExternalBin* );
+
+ /**
+ * Convert an url pointing to a local device to a K3bDevice.
+ * Supports media:// and system::// urls.
+ */
+ LIBK3B_EXPORT K3bDevice::Device* urlToDevice( const KURL& deviceUrl );
+
+ /**
+ * Tries to convert urls from local protocols != "file" to file (for now supports media:/)
+ */
+ LIBK3B_EXPORT KURL convertToLocalUrl( const KURL& url );
+ LIBK3B_EXPORT KURL::List convertToLocalUrls( const KURL::List& l );
+
+ LIBK3B_EXPORT Q_INT16 fromLe16( char* );
+ LIBK3B_EXPORT Q_INT32 fromLe32( char* );
+ LIBK3B_EXPORT Q_INT64 fromLe64( char* );
+
+ LIBK3B_EXPORT bool isMounted( K3bDevice::Device* );
+
+ /**
+ * Tries to unmount the device ignoring its actual mounting state.
+ * This method uses both KIO::unmount and pumount if available.
+ */
+ LIBK3B_EXPORT bool unmount( K3bDevice::Device* );
+
+ /**
+ * Tries to mount the medium. Since K3b does not gather any information
+ * about mount points the only methods used are pmount and HAL::mount
+ */
+ LIBK3B_EXPORT bool mount( K3bDevice::Device* );
+
+ /**
+ * Ejects the medium in the device or simply opens the tray.
+ * This method improves over K3bDevice::Device::eject in that it
+ * unmounts before ejecting and introduces HAL support.
+ */
+ LIBK3B_EXPORT bool eject( K3bDevice::Device* );
+}
+
+#endif
diff --git a/libk3b/core/k3bglobalsettings.cpp b/libk3b/core/k3bglobalsettings.cpp
new file mode 100644
index 0000000..6f58592
--- /dev/null
+++ b/libk3b/core/k3bglobalsettings.cpp
@@ -0,0 +1,61 @@
+/*
+ *
+ * $Id: k3bglobalsettings.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bglobalsettings.h"
+
+#include <kconfig.h>
+
+
+K3bGlobalSettings::K3bGlobalSettings()
+ : m_eject(true),
+ m_burnfree(true),
+ m_overburn(false),
+ m_useManualBufferSize(false),
+ m_bufferSize(4),
+ m_force(false)
+{
+}
+
+
+void K3bGlobalSettings::readSettings( KConfig* c )
+{
+ QString lastG = c->group();
+ c->setGroup( "General Options" );
+
+ m_eject = !c->readBoolEntry( "No cd eject", false );
+ m_burnfree = c->readBoolEntry( "burnfree", true );
+ m_overburn = c->readBoolEntry( "Allow overburning", false );
+ m_useManualBufferSize = c->readBoolEntry( "Manual buffer size", false );
+ m_bufferSize = c->readNumEntry( "Fifo buffer", 4 );
+ m_force = c->readBoolEntry( "Force unsafe operations", false );
+
+ c->setGroup( lastG );
+}
+
+
+void K3bGlobalSettings::saveSettings( KConfig* c )
+{
+ QString lastG = c->group();
+ c->setGroup( "General Options" );
+
+ c->writeEntry( "No cd eject", !m_eject );
+ c->writeEntry( "burnfree", m_burnfree );
+ c->writeEntry( "Allow overburning", m_overburn );
+ c->writeEntry( "Manual buffer size", m_useManualBufferSize );
+ c->writeEntry( "Fifo buffer", m_bufferSize );
+ c->writeEntry( "Force unsafe operations", m_force );
+
+ c->setGroup( lastG );
+}
diff --git a/libk3b/core/k3bglobalsettings.h b/libk3b/core/k3bglobalsettings.h
new file mode 100644
index 0000000..1194789
--- /dev/null
+++ b/libk3b/core/k3bglobalsettings.h
@@ -0,0 +1,70 @@
+/*
+ *
+ * $Id: k3bglobalsettings.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_GLOBAL_SETTINGS_H_
+#define _K3B_GLOBAL_SETTINGS_H_
+#include "k3b_export.h"
+class KConfig;
+
+/**
+ * Some global settings used throughout K3b.
+ */
+class LIBK3B_EXPORT K3bGlobalSettings
+{
+ public:
+ K3bGlobalSettings();
+
+ /**
+ * This method takes care of settings the config group
+ */
+ void readSettings( KConfig* );
+
+ /**
+ * This method takes care of settings the config group
+ */
+ void saveSettings( KConfig* );
+
+ bool ejectMedia() const { return m_eject; }
+ bool burnfree() const { return m_burnfree; }
+ bool overburn() const { return m_overburn; }
+ bool useManualBufferSize() const { return m_useManualBufferSize; }
+ int bufferSize() const { return m_bufferSize; }
+
+ /**
+ * If force is set to true K3b will continue in certain "unsafe" situations.
+ * The most common being a medium not suitable for the writer in terms of
+ * writing speed.
+ * Compare cdrecord's parameter -force
+ */
+ bool force() const { return m_force; }
+
+ void setEjectMedia( bool b ) { m_eject = b; }
+ void setBurnfree( bool b ) { m_burnfree = b; }
+ void setOverburn( bool b ) { m_overburn = b; }
+ void setUseManualBufferSize( bool b ) { m_useManualBufferSize = b; }
+ void setBufferSize( int size ) { m_bufferSize = size; }
+ void setForce( bool b ) { m_force = b; }
+
+ private:
+ bool m_eject;
+ bool m_burnfree;
+ bool m_overburn;
+ bool m_useManualBufferSize;
+ int m_bufferSize;
+ bool m_force;
+};
+
+
+#endif
diff --git a/libk3b/core/k3bjob.cpp b/libk3b/core/k3bjob.cpp
new file mode 100644
index 0000000..b545107
--- /dev/null
+++ b/libk3b/core/k3bjob.cpp
@@ -0,0 +1,253 @@
+/*
+ *
+ * $Id: k3bjob.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bjob.h"
+#include <k3bglobals.h>
+#include <k3bcore.h>
+
+#include <klocale.h>
+#include <kprocess.h>
+
+#include <qstringlist.h>
+#include <kdebug.h>
+
+
+class K3bJob::Private
+{
+public:
+};
+
+
+const char* K3bJob::DEFAULT_SIGNAL_CONNECTION = "K3bJobDefault";
+
+
+K3bJob::K3bJob( K3bJobHandler* handler, QObject* parent, const char* name )
+ : QObject( parent, name ),
+ m_jobHandler( handler ),
+ m_canceled(false),
+ m_active(false)
+{
+ connect( this, SIGNAL(canceled()),
+ this, SLOT(slotCanceled()) );
+}
+
+K3bJob::~K3bJob()
+{
+ //
+ // Normally a job (or the user of a job should take care of this
+ // but we do this here for security reasons.
+ //
+ if( m_active )
+ jobFinished( false );
+}
+
+
+void K3bJob::setJobHandler( K3bJobHandler* jh )
+{
+ m_jobHandler = jh;
+}
+
+
+void K3bJob::jobStarted()
+{
+ m_canceled = false;
+ m_active = true;
+
+ if( jobHandler() && jobHandler()->isJob() )
+ static_cast<K3bJob*>(jobHandler())->registerSubJob( this );
+ else
+ k3bcore->registerJob( this );
+
+ emit started();
+}
+
+
+void K3bJob::jobFinished( bool success )
+{
+ m_active = false;
+
+ if( jobHandler() && jobHandler()->isJob() )
+ static_cast<K3bJob*>(jobHandler())->unregisterSubJob( this );
+ else
+ k3bcore->unregisterJob( this );
+
+ emit finished( success );
+}
+
+
+void K3bJob::slotCanceled()
+{
+ m_canceled = true;
+}
+
+
+int K3bJob::waitForMedia( K3bDevice::Device* device,
+ int mediaState,
+ int mediaType,
+ const QString& message )
+{
+ // TODO: What about: emit newSubTask( i18n("Waiting for media") );
+ return m_jobHandler->waitForMedia( device, mediaState, mediaType, message );
+}
+
+
+bool K3bJob::questionYesNo( const QString& text,
+ const QString& caption,
+ const QString& yesText,
+ const QString& noText )
+{
+ return m_jobHandler->questionYesNo( text, caption, yesText, noText );
+}
+
+
+void K3bJob::blockingInformation( const QString& text,
+ const QString& caption )
+{
+ return m_jobHandler->blockingInformation( text, caption );
+}
+
+
+void K3bJob::connectSubJob( K3bJob* subJob,
+ const char* finishedSlot,
+ bool connectProgress,
+ const char* progressSlot,
+ const char* subProgressSlot,
+ const char* processedSizeSlot,
+ const char* processedSubSizeSlot )
+{
+ connect( subJob, SIGNAL(newTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) );
+ connect( subJob, SIGNAL(newSubTask(const QString&)), this, SLOT(slotNewSubTask(const QString&)) );
+ connect( subJob, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+ connect( subJob, SIGNAL(infoMessage(const QString&, int)),
+ this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( subJob, SIGNAL(finished(bool)), this, finishedSlot );
+
+ if( connectProgress ) {
+ connect( subJob, SIGNAL(percent(int)),
+ this, progressSlot != 0 ? progressSlot : SIGNAL(subPercent(int)) );
+ if( subProgressSlot )
+ connect( subJob, SIGNAL(subPercent(int)), this, subProgressSlot );
+ connect( subJob, SIGNAL(processedSize(int, int)),
+ this, processedSizeSlot != 0 ? processedSizeSlot : SIGNAL(processedSubSize(int, int)) );
+ if( processedSubSizeSlot )
+ connect( subJob, SIGNAL(processedSubSize(int, int)), this, processedSubSizeSlot );
+ }
+}
+
+
+void K3bJob::connectSubJob( K3bJob* subJob,
+ const char* finishedSlot,
+ const char* newTaskSlot,
+ const char* newSubTaskSlot,
+ const char* progressSlot,
+ const char* subProgressSlot,
+ const char* processedSizeSlot,
+ const char* processedSubSizeSlot )
+{
+ // standard connections
+ connect( subJob, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+ connect( subJob, SIGNAL(infoMessage(const QString&, int)),
+ this, SIGNAL(infoMessage(const QString&, int)) );
+
+ // task connections
+ if( newTaskSlot == DEFAULT_SIGNAL_CONNECTION )
+ connect( subJob, SIGNAL(newTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) );
+ else if( newTaskSlot )
+ connect( subJob, SIGNAL(newTask(const QString&)), this, newTaskSlot );
+
+ if( newSubTaskSlot == DEFAULT_SIGNAL_CONNECTION )
+ connect( subJob, SIGNAL(newSubTask(const QString&)), this, SLOT(slotNewSubTask(const QString&)) );
+ else if( newSubTaskSlot )
+ connect( subJob, SIGNAL(newSubTask(const QString&)), this, newSubTaskSlot );
+
+ if( finishedSlot && finishedSlot != DEFAULT_SIGNAL_CONNECTION )
+ connect( subJob, SIGNAL(finished(bool)), this, finishedSlot );
+
+ // progress
+ if( progressSlot == DEFAULT_SIGNAL_CONNECTION )
+ connect( subJob, SIGNAL(percent(int)), this, SIGNAL(subPercent(int)) );
+ else if( progressSlot )
+ connect( subJob, SIGNAL(percent(int)), this, progressSlot );
+
+ if( subProgressSlot && subProgressSlot != DEFAULT_SIGNAL_CONNECTION )
+ connect( subJob, SIGNAL(subPercent(int)), this, subProgressSlot );
+
+ // processed size
+ if( processedSizeSlot == DEFAULT_SIGNAL_CONNECTION )
+ connect( subJob, SIGNAL(processedSize(int, int)), this, SIGNAL(processedSubSize(int, int)) );
+ else if( processedSizeSlot )
+ connect( subJob, SIGNAL(processedSize(int, int)), this, processedSizeSlot );
+
+ if( processedSubSizeSlot && processedSubSizeSlot != DEFAULT_SIGNAL_CONNECTION )
+ connect( subJob, SIGNAL(processedSubSize(int, int)), this, processedSubSizeSlot );
+}
+
+
+unsigned int K3bJob::numRunningSubJobs() const
+{
+ return m_runningSubJobs.count();
+}
+
+
+void K3bJob::slotNewSubTask( const QString& str )
+{
+ emit infoMessage( str, INFO );
+}
+
+
+void K3bJob::registerSubJob( K3bJob* job )
+{
+ m_runningSubJobs.append( job );
+}
+
+
+void K3bJob::unregisterSubJob( K3bJob* job )
+{
+ m_runningSubJobs.removeRef( job );
+}
+
+
+
+
+class K3bBurnJob::Private
+{
+public:
+};
+
+
+
+K3bBurnJob::K3bBurnJob( K3bJobHandler* handler, QObject* parent, const char* name )
+ : K3bJob( handler, parent, name ),
+ m_writeMethod( K3b::DEFAULT )
+{
+ d = new Private;
+}
+
+
+K3bBurnJob::~K3bBurnJob()
+{
+ delete d;
+}
+
+
+int K3bBurnJob::supportedWritingApps() const
+{
+ return K3b::DEFAULT | K3b::CDRDAO | K3b::CDRECORD;
+}
+
+#include "k3bjob.moc"
diff --git a/libk3b/core/k3bjob.h b/libk3b/core/k3bjob.h
new file mode 100644
index 0000000..59e1f9b
--- /dev/null
+++ b/libk3b/core/k3bjob.h
@@ -0,0 +1,311 @@
+/*
+ *
+ * $Id: k3bjob.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BJOB_H
+#define K3BJOB_H
+
+#include <qobject.h>
+#include <qptrlist.h>
+#include "k3bjobhandler.h"
+#include "k3b_export.h"
+
+class K3bDoc;
+namespace K3bDevice {
+ class Device;
+}
+
+
+/**
+ * This is the baseclass for all the jobs in K3b which actually do the work like burning a cd!
+ * The K3bJob object takes care of registering with the k3bcore or with a parent K3bJob.
+ *
+ * Every job has a jobhandler which can be another job (in which case the job is handled as
+ * a subjob) or an arbitrary class implementing the K3bJobHandler interface.
+ *
+ * A Job should never create any widgets. User interaction should be done through the methods
+ * questionYesNo, waitForMedia.
+ *
+ * @author Sebastian Trueg
+ */
+class LIBK3B_EXPORT K3bJob : public QObject, public K3bJobHandler
+{
+ Q_OBJECT
+
+ public:
+ virtual ~K3bJob();
+
+ /**
+ * \reimplemented from K3bJobHandler
+ */
+ bool isJob() const { return true; }
+
+ K3bJobHandler* jobHandler() const { return m_jobHandler; }
+
+ /**
+ * Is the job active?
+ * The default implementation is based on the jobStarted() and jobFinished()
+ * methods and there is normally no need to reimplement this.
+ */
+ virtual bool active() const { return m_active; }
+
+ /**
+ * The default implementation is based on the canceled() signal.
+ *
+ * This means that one cannot count on this value being valid
+ * in a slot connected to the canceled() signal. It is, however, save
+ * to call this method from a slot connected to the finished() signal
+ * in case the job makes proper usage of the jobStarted/jobFinished
+ * methods.
+ */
+ virtual bool hasBeenCanceled() const { return m_canceled; }
+
+ virtual QString jobDescription() const { return "K3bJob"; }
+ virtual QString jobDetails() const { return QString::null; }
+
+ /**
+ * @returns the number of running subjobs.
+ * this is useful for proper cancellation of jobs.
+ */
+ unsigned int numRunningSubJobs() const;
+
+ const QPtrList<K3bJob>& runningSubJobs() const { return m_runningSubJobs; }
+
+ /**
+ * \deprecated
+ */
+ virtual void connectSubJob( K3bJob* subJob,
+ const char* finishedSlot = 0,
+ bool progress = false,
+ const char* progressSlot = 0,
+ const char* subProgressSot = 0,
+ const char* processedSizeSlot = 0,
+ const char* processedSubSizeSlot = 0 );
+
+ static const char* DEFAULT_SIGNAL_CONNECTION;
+
+ /**
+ * \param newTaskSlot If DEFAULT_SIGNAL_CONNECTION the newTask signal from the subjob will
+ * be connected to the newSubTask signal
+ * \param newSubTaskSlot If DEFAULT_SIGNAL_CONNECTION the newSubTask signal from the subjob
+ * will create an infoMessage signal
+ * \param progressSlot If DEFAULT_SIGNAL_CONNECTION the percent signal of the subjob will be
+ * connected to the subPercent signal.
+ * debuggingOutput and infoMessage will always be direcctly connected.
+ *
+ * If a parameter is set to 0 it will not be connected at all
+ */
+ virtual void connectSubJob( K3bJob* subJob,
+ const char* finishedSlot = DEFAULT_SIGNAL_CONNECTION,
+ const char* newTaskSlot = DEFAULT_SIGNAL_CONNECTION,
+ const char* newSubTaskSlot = DEFAULT_SIGNAL_CONNECTION,
+ const char* progressSlot = DEFAULT_SIGNAL_CONNECTION,
+ const char* subProgressSlot = DEFAULT_SIGNAL_CONNECTION,
+ const char* processedSizeSlot = DEFAULT_SIGNAL_CONNECTION,
+ const char* processedSubSizeSlot = DEFAULT_SIGNAL_CONNECTION );
+
+ /**
+ * Message types to be used in combination with the infoMessage signal.
+ *
+ * \see infoMessage()
+ */
+ enum MessageType {
+ INFO, /**< Informational message. For example a message that informs the user about what is
+ currently going on */
+ WARNING, /**< A warning message. Something did not go perfectly but the job may continue. */
+ ERROR, /**< An error. Only use this message type if the job will actually fail afterwards
+ with a call to jobFinished( false ) */
+ SUCCESS /**< This message type may be used to inform the user that a sub job has
+ been successfully finished. */
+ };
+
+ /**
+ * reimplemented from K3bJobHandler
+ */
+ int waitForMedia( K3bDevice::Device*,
+ int mediaState = K3bDevice::STATE_EMPTY,
+ int mediaType = K3bDevice::MEDIA_WRITABLE_CD,
+ const QString& message = QString::null );
+
+ /**
+ * reimplemented from K3bJobHandler
+ */
+ bool questionYesNo( const QString& text,
+ const QString& caption = QString::null,
+ const QString& yesText = QString::null,
+ const QString& noText = QString::null );
+
+ /**
+ * reimplemented from K3bJobHandler
+ */
+ void blockingInformation( const QString& text,
+ const QString& caption = QString::null );
+
+ public slots:
+ /**
+ * This is the slot that starts the job. The first call should always
+ * be jobStarted().
+ *
+ * Once the job has finished it has to call jobFinished() with the result as
+ * a parameter.
+ *
+ * \see jobStarted()
+ * \see jobFinished()
+ */
+ virtual void start() = 0;
+
+ /**
+ * This slot should cancel the job. The job has to emit the canceled() signal and make a call
+ * to jobFinished().
+ * It is not important to do any of those two directly in this slot though.
+ */
+ virtual void cancel() = 0;
+
+ void setJobHandler( K3bJobHandler* );
+
+ signals:
+ void infoMessage( const QString& msg, int type );
+ void percent( int p );
+ void subPercent( int p );
+ void processedSize( int processed, int size );
+ void processedSubSize( int processed, int size );
+ void newTask( const QString& job );
+ void newSubTask( const QString& job );
+ void debuggingOutput(const QString&, const QString&);
+ void data( const char* data, int len );
+ void nextTrack( int track, int numTracks );
+
+ void canceled();
+
+ /**
+ * Emitted once the job has been started. Never emit this signal directly.
+ * Use jobStarted() instead, otherwise the job will not be properly registered
+ */
+ void started();
+
+ /**
+ * Emitted once the job has been finshed. Never emit this signal directly.
+ * Use jobFinished() instead, otherwise the job will not be properly deregistered
+ */
+ void finished( bool success );
+
+ protected:
+ /**
+ * \param hdl the handler of the job. This allows for some user interaction without
+ * specifying any details (like the GUI).
+ * The job handler can also be another job. In that case this job is a sub job
+ * and will be part of the parents running sub jobs.
+ *
+ * \see runningSubJobs()
+ * \see numRunningSubJobs()
+ */
+ K3bJob( K3bJobHandler* hdl, QObject* parent = 0, const char* name = 0 );
+
+ /**
+ * Call this in start() to properly register the job and emit the started() signal.
+ * Do never emit the started() signal manually.
+ *
+ * Always call K3bJob::jobStarted in reimplementations.
+ */
+ virtual void jobStarted();
+
+ /**
+ * Call this at the end of the job to properly deregister the job and emit the finished() signal.
+ * Do never emit the started() signal manually.
+ *
+ * Always call K3bJob::jobFinished in reimplementations.
+ */
+ virtual void jobFinished( bool success );
+
+ private slots:
+ void slotCanceled();
+ void slotNewSubTask( const QString& str );
+
+ private:
+ void registerSubJob( K3bJob* );
+ void unregisterSubJob( K3bJob* );
+
+ K3bJobHandler* m_jobHandler;
+ QPtrList<K3bJob> m_runningSubJobs;
+
+ bool m_canceled;
+ bool m_active;
+
+ class Private;
+ Private* d;
+};
+
+
+/**
+ * Every job used to actually burn a medium is derived from K3bBurnJob.
+ * This class implements additional signals like buffer status or writing speed
+ * as well as a handling of the used writing application.
+ */
+class LIBK3B_EXPORT K3bBurnJob : public K3bJob
+{
+ Q_OBJECT
+
+ public:
+ K3bBurnJob( K3bJobHandler* hdl, QObject* parent = 0, const char* name = 0 );
+ virtual ~K3bBurnJob();
+
+ /**
+ * The writing device used by this job.
+ */
+ virtual K3bDevice::Device* writer() const { return 0; }
+
+ /**
+ * use K3b::WritingApp
+ */
+ int writingApp() const { return m_writeMethod; }
+
+ /**
+ * K3b::WritingApp "ored" together
+ */
+ virtual int supportedWritingApps() const;
+
+ public slots:
+ /**
+ * use K3b::WritingApp
+ */
+ void setWritingApp( int w ) { m_writeMethod = w; }
+
+ signals:
+ void bufferStatus( int );
+
+ void deviceBuffer( int );
+
+ /**
+ * @param speed current writing speed in Kb
+ * @param multiplicator use 150 for CDs and 1380 for DVDs
+ * FIXME: maybe one should be able to ask the burnjob if it burns a CD or a DVD and remove the
+ * multiplicator parameter)
+ */
+ void writeSpeed( int speed, int multiplicator );
+
+ /**
+ * This signal may be used to inform when the burning starts or ends
+ * The BurningProgressDialog for example uses it to enable and disable
+ * the buffer and writing speed displays.
+ */
+ void burning(bool);
+
+ private:
+ int m_writeMethod;
+
+ class Private;
+ Private* d;
+};
+#endif
diff --git a/libk3b/core/k3bjobhandler.h b/libk3b/core/k3bjobhandler.h
new file mode 100644
index 0000000..1262e0e
--- /dev/null
+++ b/libk3b/core/k3bjobhandler.h
@@ -0,0 +1,64 @@
+/*
+ *
+ * $Id: k3bjobhandler.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_JOB_HANDLER_H_
+#define _K3B_JOB_HANDLER_H_
+
+
+#include <k3bdiskinfo.h>
+#include <k3bdevice.h>
+
+
+/**
+ * See @p K3bJobProgressDialog as an example for the usage of
+ * the K3bJobHandler interface.
+ */
+class K3bJobHandler
+{
+ public:
+ K3bJobHandler() {}
+ virtual ~K3bJobHandler() {}
+
+ /**
+ * \return true if the handler itself is also a job
+ */
+ virtual bool isJob() const { return false; }
+
+ /**
+ * @return K3bDevice::MediaType on success,
+ * 0 if forced (no media info available),
+ * and -1 on error (canceled)
+ */
+ virtual int waitForMedia( K3bDevice::Device*,
+ int mediaState = K3bDevice::STATE_EMPTY,
+ int mediaType = K3bDevice::MEDIA_WRITABLE_CD,
+ const QString& message = QString::null ) = 0;
+
+ // FIXME: use KGuiItem
+ virtual bool questionYesNo( const QString& text,
+ const QString& caption = QString::null,
+ const QString& yesText = QString::null,
+ const QString& noText = QString::null ) = 0;
+
+ /**
+ * Use this if you need the user to do something before the job is able to continue.
+ * In all other cases an infoMessage should be used.
+ */
+ virtual void blockingInformation( const QString& text,
+ const QString& caption = QString::null ) = 0;
+
+};
+
+#endif
diff --git a/libk3b/core/k3bprocess.cpp b/libk3b/core/k3bprocess.cpp
new file mode 100644
index 0000000..35ddff4
--- /dev/null
+++ b/libk3b/core/k3bprocess.cpp
@@ -0,0 +1,452 @@
+/*
+ *
+ * $Id: k3bprocess.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+
+#include "k3bprocess.h"
+#include "k3bexternalbinmanager.h"
+
+#include <qstringlist.h>
+#include <qsocketnotifier.h>
+#include <qptrqueue.h>
+#include <qapplication.h>
+
+#include <kdebug.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+
+class K3bProcess::Data
+{
+public:
+ QString unfinishedStdoutLine;
+ QString unfinishedStderrLine;
+
+ int dupStdoutFd;
+ int dupStdinFd;
+
+ bool rawStdin;
+ bool rawStdout;
+
+ int in[2];
+ int out[2];
+
+ bool suppressEmptyLines;
+};
+
+
+K3bProcess::K3bProcess()
+ : KProcess(),
+ m_bSplitStdout(false)
+{
+ d = new Data();
+ d->dupStdinFd = d->dupStdoutFd = -1;
+ d->rawStdout = d->rawStdin = false;
+ d->in[0] = d->in[1] = -1;
+ d->out[0] = d->out[1] = -1;
+ d->suppressEmptyLines = true;
+}
+
+K3bProcess::~K3bProcess()
+{
+ delete d;
+}
+
+
+K3bProcess& K3bProcess::operator<<( const K3bExternalBin* bin )
+{
+ return this->operator<<( bin->path );
+}
+
+K3bProcess& K3bProcess::operator<<( const QString& arg )
+{
+ static_cast<KProcess*>(this)->operator<<( arg );
+ return *this;
+}
+
+K3bProcess& K3bProcess::operator<<( const char* arg )
+{
+ static_cast<KProcess*>(this)->operator<<( arg );
+ return *this;
+}
+
+K3bProcess& K3bProcess::operator<<( const QCString& arg )
+{
+ static_cast<KProcess*>(this)->operator<<( arg );
+ return *this;
+}
+
+K3bProcess& K3bProcess::operator<<( const QStringList& args )
+{
+ static_cast<KProcess*>(this)->operator<<( args );
+ return *this;
+}
+
+
+bool K3bProcess::start( RunMode run, Communication com )
+{
+ if( com & Stderr ) {
+ connect( this, SIGNAL(receivedStderr(KProcess*, char*, int)),
+ this, SLOT(slotSplitStderr(KProcess*, char*, int)) );
+ }
+ if( com & Stdout ) {
+ connect( this, SIGNAL(receivedStdout(KProcess*, char*, int)),
+ this, SLOT(slotSplitStdout(KProcess*, char*, int)) );
+ }
+
+ return KProcess::start( run, com );
+}
+
+
+void K3bProcess::slotSplitStdout( KProcess*, char* data, int len )
+{
+ if( m_bSplitStdout ) {
+ QStringList lines = splitOutput( data, len, d->unfinishedStdoutLine, d->suppressEmptyLines );
+
+ for( QStringList::iterator it = lines.begin(); it != lines.end(); ++it ) {
+ QString& str = *it;
+
+ // just to be sure since something in splitOutput does not do this right
+ if( d->suppressEmptyLines && str.isEmpty() )
+ continue;
+
+ emit stdoutLine( str );
+ }
+ }
+}
+
+
+void K3bProcess::slotSplitStderr( KProcess*, char* data, int len )
+{
+ QStringList lines = splitOutput( data, len, d->unfinishedStderrLine, d->suppressEmptyLines );
+
+ for( QStringList::iterator it = lines.begin(); it != lines.end(); ++it ) {
+ QString& str = *it;
+
+ // just to be sure since something in splitOutput does not do this right
+ if( d->suppressEmptyLines && str.isEmpty() )
+ continue;
+
+ emit stderrLine( str );
+ }
+}
+
+
+QStringList K3bProcess::splitOutput( char* data, int len,
+ QString& unfinishedLine, bool suppressEmptyLines )
+{
+ //
+ // The stderr splitting is mainly used for parsing of messages
+ // That's why we simplify the data before proceeding
+ //
+
+ QString buffer;
+ for( int i = 0; i < len; i++ ) {
+ if( data[i] == '\b' ) {
+ while( data[i] == '\b' ) // we replace multiple backspaces with a single line feed
+ i++;
+ buffer += '\n';
+ }
+ if( data[i] == '\r' )
+ buffer += '\n';
+ else if( data[i] == '\t' ) // replace tabs with a single space
+ buffer += " ";
+ else
+ buffer += data[i];
+ }
+
+ QStringList lines = QStringList::split( '\n', buffer, !suppressEmptyLines );
+
+ // in case we suppress empty lines we need to handle the following separately
+ // to make sure we join unfinished lines correctly
+ if( suppressEmptyLines && buffer[0] == '\n' )
+ lines.prepend( QString::null );
+
+ if( !unfinishedLine.isEmpty() ) {
+ lines.first().prepend( unfinishedLine );
+ unfinishedLine.truncate(0);
+
+ kdDebug() << "(K3bProcess) joined line: '" << (lines.first()) << "'" << endl;
+ }
+
+ QStringList::iterator it;
+
+ // check if line ends with a newline
+ // if not save the last line because it is not finished
+ QChar c = buffer.right(1).at(0);
+ bool hasUnfinishedLine = ( c != '\n' && c != '\r' && c != QChar(46) ); // What is unicode 46?? It is printed as a point
+ if( hasUnfinishedLine ) {
+ kdDebug() << "(K3bProcess) found unfinished line: '" << lines.last() << "'" << endl;
+ kdDebug() << "(K3bProcess) last char: '" << buffer.right(1) << "'" << endl;
+ unfinishedLine = lines.last();
+ it = lines.end();
+ --it;
+ lines.remove(it);
+ }
+
+ return lines;
+}
+
+
+int K3bProcess::setupCommunication( Communication comm )
+{
+ if( KProcess::setupCommunication( comm ) ) {
+
+ //
+ // Setup our own socketpair
+ //
+
+ if( d->rawStdin ) {
+ if( socketpair(AF_UNIX, SOCK_STREAM, 0, d->in) == 0 ) {
+ fcntl(d->in[0], F_SETFD, FD_CLOEXEC);
+ fcntl(d->in[1], F_SETFD, FD_CLOEXEC);
+ }
+ else
+ return 0;
+ }
+
+ if( d->rawStdout ) {
+ if( socketpair(AF_UNIX, SOCK_STREAM, 0, d->out) == 0 ) {
+ fcntl(d->out[0], F_SETFD, FD_CLOEXEC);
+ fcntl(d->out[1], F_SETFD, FD_CLOEXEC);
+ }
+ else {
+ if( d->rawStdin || d->dupStdinFd ) {
+ close(d->in[0]);
+ close(d->in[1]);
+ }
+ return 0;
+ }
+ }
+
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
+void K3bProcess::commClose()
+{
+ if( d->rawStdin ) {
+ close(d->in[1]);
+ d->in[1] = -1;
+ }
+ if( d->rawStdout ) {
+ close(d->out[0]);
+ d->out[0] = -1;
+ }
+
+ KProcess::commClose();
+}
+
+
+int K3bProcess::commSetupDoneP()
+{
+ int ok = KProcess::commSetupDoneP();
+
+ if( d->rawStdin )
+ close(d->in[0]);
+ if( d->rawStdout )
+ close(d->out[1]);
+
+ d->in[0] = d->out[1] = -1;
+
+ return ok;
+}
+
+
+int K3bProcess::commSetupDoneC()
+{
+ int ok = KProcess::commSetupDoneC();
+
+ if( d->dupStdoutFd != -1 ) {
+ //
+ // make STDOUT_FILENO a duplicate of d->dupStdoutFd such that writes to STDOUT_FILENO are "redirected"
+ // to d->dupStdoutFd
+ //
+ if( ::dup2( d->dupStdoutFd, STDOUT_FILENO ) < 0 ) {
+ kdDebug() << "(K3bProcess) Error while dup( " << d->dupStdoutFd << ", " << STDOUT_FILENO << endl;
+ ok = 0;
+ }
+ }
+ else if( d->rawStdout ) {
+ if( ::dup2( d->out[1], STDOUT_FILENO ) < 0 ) {
+ kdDebug() << "(K3bProcess) Error while dup( " << d->out[1] << ", " << STDOUT_FILENO << endl;
+ ok = 0;
+ }
+ }
+
+ if( d->dupStdinFd != -1 ) {
+ if( ::dup2( d->dupStdinFd, STDIN_FILENO ) < 0 ) {
+ kdDebug() << "(K3bProcess) Error while dup( " << d->dupStdinFd << ", " << STDIN_FILENO << endl;
+ ok = 0;
+ }
+ }
+ else if( d->rawStdin ) {
+ if( ::dup2( d->in[0], STDIN_FILENO ) < 0 ) {
+ kdDebug() << "(K3bProcess) Error while dup( " << d->in[0] << ", " << STDIN_FILENO << endl;
+ ok = 0;
+ }
+ }
+
+ return ok;
+}
+
+
+
+int K3bProcess::stdinFd() const
+{
+ if( d->rawStdin )
+ return d->in[1];
+ else if( d->dupStdinFd != -1 )
+ return d->dupStdinFd;
+ else
+ return -1;
+}
+
+int K3bProcess::stdoutFd() const
+{
+ if( d->rawStdout )
+ return d->out[0];
+ else if( d->dupStdoutFd != -1 )
+ return d->dupStdoutFd;
+ else
+ return -1;
+}
+
+
+void K3bProcess::dupStdout( int fd )
+{
+ writeToFd( fd );
+}
+
+void K3bProcess::dupStdin( int fd )
+{
+ readFromFd( fd );
+}
+
+
+void K3bProcess::writeToFd( int fd )
+{
+ d->dupStdoutFd = fd;
+ if( fd != -1 )
+ d->rawStdout = false;
+}
+
+void K3bProcess::readFromFd( int fd )
+{
+ d->dupStdinFd = fd;
+ if( fd != -1 )
+ d->rawStdin = false;
+}
+
+
+void K3bProcess::setRawStdin(bool b)
+{
+ if( b ) {
+ d->rawStdin = true;
+ d->dupStdinFd = -1;
+ }
+ else
+ d->rawStdin = false;
+}
+
+
+void K3bProcess::setRawStdout(bool b)
+{
+ if( b ) {
+ d->rawStdout = true;
+ d->dupStdoutFd = -1;
+ }
+ else
+ d->rawStdout = false;
+}
+
+
+void K3bProcess::setSuppressEmptyLines( bool b )
+{
+ d->suppressEmptyLines = b;
+}
+
+
+bool K3bProcess::closeStdin()
+{
+ if( d->rawStdin ) {
+ close(d->in[1]);
+ d->in[1] = -1;
+ return true;
+ }
+ else
+ return KProcess::closeStdin();
+}
+
+
+bool K3bProcess::closeStdout()
+{
+ if( d->rawStdout ) {
+ close(d->out[0]);
+ d->out[0] = -1;
+ return true;
+ }
+ else
+ return KProcess::closeStdout();
+}
+
+
+K3bProcessOutputCollector::K3bProcessOutputCollector( KProcess* p )
+ : m_process(0)
+{
+ setProcess( p );
+}
+
+void K3bProcessOutputCollector::setProcess( KProcess* p )
+{
+ if( m_process )
+ m_process->disconnect( this );
+
+ m_process = p;
+ if( p ) {
+ connect( p, SIGNAL(receivedStdout(KProcess*, char*, int)),
+ this, SLOT(slotGatherStdout(KProcess*, char*, int)) );
+ connect( p, SIGNAL(receivedStderr(KProcess*, char*, int)),
+ this, SLOT(slotGatherStderr(KProcess*, char*, int)) );
+ }
+
+ m_gatheredOutput.truncate( 0 );
+ m_stderrOutput.truncate( 0 );
+ m_stdoutOutput.truncate( 0 );
+}
+
+void K3bProcessOutputCollector::slotGatherStderr( KProcess*, char* data, int len )
+{
+ m_gatheredOutput.append( QString::fromLocal8Bit( data, len ) );
+ m_stderrOutput.append( QString::fromLocal8Bit( data, len ) );
+}
+
+void K3bProcessOutputCollector::slotGatherStdout( KProcess*, char* data, int len )
+{
+ m_gatheredOutput.append( QString::fromLocal8Bit( data, len ) );
+ m_stdoutOutput.append( QString::fromLocal8Bit( data, len ) );
+}
+
+
+#include "k3bprocess.moc"
diff --git a/libk3b/core/k3bprocess.h b/libk3b/core/k3bprocess.h
new file mode 100644
index 0000000..959bda1
--- /dev/null
+++ b/libk3b/core/k3bprocess.h
@@ -0,0 +1,204 @@
+/*
+ *
+ * $Id: k3bprocess.h 621644 2007-01-09 12:53:09Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3B_PROCESS_H
+#define K3B_PROCESS_H
+
+
+#include <kprocess.h>
+#include <qstring.h>
+#include "k3b_export.h"
+
+class K3bExternalBin;
+
+
+/**
+ * This is an enhanced KProcess.
+ * It splits the stderr output to lines making sure the client gets every line as it
+ * was written by the process.
+ * Aditionally one may set raw stdout and stdin handling using the stdin() and stdout() methods
+ * to get the process' file descriptors.
+ * Last but not least K3bProcess is able to duplicate stdout making it possible to connect two
+ * K3bProcesses like used in K3bDataJob to duplicate mkisofs' stdout to the stdin of the writer
+ * (cdrecord or cdrdao)
+ */
+class LIBK3B_EXPORT K3bProcess : public KProcess
+{
+ Q_OBJECT
+
+ public:
+ class OutputCollector;
+
+ public:
+ K3bProcess();
+ ~K3bProcess();
+
+ /**
+ * In the future this might also set the nice value
+ */
+ K3bProcess& operator<<( const K3bExternalBin* );
+
+ K3bProcess& operator<<( const QString& arg );
+ K3bProcess& operator<<( const char* arg );
+ K3bProcess& operator<<( const QCString& arg );
+ K3bProcess& operator<<( const QStringList& args );
+
+ bool start( RunMode run = NotifyOnExit, Communication com = NoCommunication );
+
+ /**
+ * get stdin file descriptor
+ * Only makes sense while process is running.
+ *
+ * Only use with setRawStdin
+ */
+ int stdinFd() const;
+
+ /**
+ * get stdout file descriptor
+ * Only makes sense while process is running.
+ *
+ * Only use with setRawStdout
+ */
+ int stdoutFd() const;
+
+ /**
+ * @deprecated use writeToFd
+ */
+ void dupStdout( int fd );
+
+ /**
+ * @deprecated use readFromFd
+ */
+ void dupStdin( int fd );
+
+ /**
+ * Make the process write to @fd instead of Stdout.
+ * This means you won't get any stdoutReady() or receivedStdout()
+ * signals anymore.
+ *
+ * Only use this before starting the process.
+ */
+ void writeToFd( int fd );
+
+ /**
+ * Make the process read from @fd instead of Stdin.
+ * This means you won't get any wroteStdin()
+ * signals anymore.
+ *
+ * Only use this before starting the process.
+ */
+ void readFromFd( int fd );
+
+ /**
+ * If set true the process' stdin fd will be available
+ * through @stdinFd.
+ * Be aware that you will not get any wroteStdin signals
+ * anymore.
+ *
+ * Only use this before starting the process.
+ */
+ void setRawStdin(bool b);
+
+ /**
+ * If set true the process' stdout fd will be available
+ * through @stdoutFd.
+ * Be aware that you will not get any stdoutReady or receivedStdout
+ * signals anymore.
+ *
+ * Only use this before starting the process.
+ */
+ void setRawStdout(bool b);
+
+ public slots:
+ void setSplitStdout( bool b ) { m_bSplitStdout = b; }
+
+ /**
+ * default is true
+ */
+ void setSuppressEmptyLines( bool b );
+
+ bool closeStdin();
+ bool closeStdout();
+
+ private slots:
+ void slotSplitStderr( KProcess*, char*, int );
+ void slotSplitStdout( KProcess*, char*, int );
+
+ signals:
+ void stderrLine( const QString& line );
+ void stdoutLine( const QString& line );
+
+ /**
+ * Gets emitted if raw stdout mode has been requested
+ * The data has to be read from @p fd.
+ */
+ void stdoutReady( int fd );
+
+ protected:
+ /**
+ * reimplemeted from KProcess
+ */
+ int commSetupDoneP();
+
+ /**
+ * reimplemeted from KProcess
+ */
+ int commSetupDoneC();
+
+ /**
+ * reimplemeted from KProcess
+ */
+ int setupCommunication( Communication comm );
+
+ /**
+ * reimplemeted from KProcess
+ */
+ void commClose();
+
+ private:
+ static QStringList splitOutput( char*, int, QString&, bool );
+
+ class Data;
+ Data* d;
+
+ bool m_bSplitStdout;
+};
+
+class LIBK3B_EXPORT K3bProcessOutputCollector: public QObject
+{
+ Q_OBJECT
+
+ public:
+ K3bProcessOutputCollector( KProcess* );
+ void setProcess( KProcess* );
+
+ const QString& output() const { return m_gatheredOutput; }
+ const QString& stderrOutput() const { return m_stderrOutput; }
+ const QString& stdoutOutput() const { return m_stdoutOutput; }
+
+ private slots:
+ void slotGatherStderr( KProcess*, char*, int );
+ void slotGatherStdout( KProcess*, char*, int );
+
+ private:
+ QString m_gatheredOutput;
+ QString m_stderrOutput;
+ QString m_stdoutOutput;
+ KProcess* m_process;
+};
+
+
+#endif
diff --git a/libk3b/core/k3bprogressinfoevent.h b/libk3b/core/k3bprogressinfoevent.h
new file mode 100644
index 0000000..0e77718
--- /dev/null
+++ b/libk3b/core/k3bprogressinfoevent.h
@@ -0,0 +1,85 @@
+/*
+ *
+ * $Id: k3bprogressinfoevent.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef K3B_PROGRESS_INFO_EVENT_H
+#define K3B_PROGRESS_INFO_EVENT_H
+
+#include <qevent.h>
+#include <qstring.h>
+
+
+/**
+ * Custom event class for posting events corresponding to the
+ * K3bJob signals. This is useful for a threaded job since
+ * in that case it's not possible to emit signals that directly
+ * change the GUI (see QThread docu).
+ */
+class K3bProgressInfoEvent : public QCustomEvent
+{
+ public:
+ K3bProgressInfoEvent( int type )
+ : QCustomEvent( type ),
+ m_type(type)
+ {}
+
+ K3bProgressInfoEvent( int type, const QString& v1, const QString& v2 = QString::null,
+ int value1 = 0, int value2 = 0 )
+ : QCustomEvent( type ),
+ m_type( type),
+ m_firstValue(value1),
+ m_secondValue(value2),
+ m_firstString(v1),
+ m_secondString(v2)
+ {}
+
+ K3bProgressInfoEvent( int type, int value1, int value2 = 0 )
+ : QCustomEvent( type ),
+ m_type( type),
+ m_firstValue(value1),
+ m_secondValue(value2)
+ {}
+
+ int type() const { return m_type; }
+ const QString& firstString() const { return m_firstString; }
+ const QString& secondString() const { return m_secondString; }
+ int firstValue() const { return m_firstValue; }
+ int secondValue() const { return m_secondValue; }
+
+ enum K3bProgressInfoEventType {
+ Progress = QEvent::User + 1,
+ SubProgress,
+ ProcessedSize,
+ ProcessedSubSize,
+ InfoMessage,
+ Started,
+ Canceled,
+ Finished,
+ NewTask,
+ NewSubTask,
+ DebuggingOutput,
+ BufferStatus,
+ WriteSpeed,
+ NextTrack
+ };
+
+ private:
+ int m_type;
+ int m_firstValue;
+ int m_secondValue;
+ QString m_firstString;
+ QString m_secondString;
+};
+
+#endif
diff --git a/libk3b/core/k3bsimplejobhandler.cpp b/libk3b/core/k3bsimplejobhandler.cpp
new file mode 100644
index 0000000..eaf7cd6
--- /dev/null
+++ b/libk3b/core/k3bsimplejobhandler.cpp
@@ -0,0 +1,62 @@
+/*
+ *
+ * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $
+ * Copyright (C) 2006 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bsimplejobhandler.h"
+
+
+K3bSimpleJobHandler::K3bSimpleJobHandler( QObject* parent )
+ : QObject( parent ),
+ K3bJobHandler()
+{
+}
+
+K3bSimpleJobHandler::~K3bSimpleJobHandler()
+{
+}
+
+int K3bSimpleJobHandler::waitForMedia( K3bDevice::Device* dev,
+ int mediaState,
+ int mediaType,
+ const QString& message )
+{
+ Q_UNUSED( dev );
+ Q_UNUSED( mediaState );
+ Q_UNUSED( mediaType );
+ Q_UNUSED( message );
+
+ return 0;
+}
+
+bool K3bSimpleJobHandler::questionYesNo( const QString& text,
+ const QString& caption,
+ const QString& yesText,
+ const QString& noText )
+{
+ Q_UNUSED( text );
+ Q_UNUSED( caption );
+ Q_UNUSED( yesText );
+ Q_UNUSED( noText );
+
+ return true;
+}
+
+void K3bSimpleJobHandler::blockingInformation( const QString& text,
+ const QString& caption )
+{
+ Q_UNUSED( text );
+ Q_UNUSED( caption );
+}
+
+#include "k3bsimplejobhandler.moc"
diff --git a/libk3b/core/k3bsimplejobhandler.h b/libk3b/core/k3bsimplejobhandler.h
new file mode 100644
index 0000000..f84064e
--- /dev/null
+++ b/libk3b/core/k3bsimplejobhandler.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $
+ * Copyright (C) 2006 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_SIMPLE_JOB_HANDLER_H_
+#define _K3B_SIMPLE_JOB_HANDLER_H_
+
+#include <k3b_export.h>
+
+#include <qobject.h>
+#include <k3bjobhandler.h>
+
+
+/**
+ * This is a simplified job handler which just consumes the
+ * job handler calls without doing anything.
+ * Use it for very simple jobs that don't need the job handler
+ * methods.
+ */
+class LIBK3B_EXPORT K3bSimpleJobHandler : public QObject, public K3bJobHandler
+{
+ Q_OBJECT
+
+ public:
+ K3bSimpleJobHandler( QObject* parent = 0 );
+ ~K3bSimpleJobHandler();
+
+ /*
+ * \return 0
+ */
+ int waitForMedia( K3bDevice::Device*,
+ int mediaState = K3bDevice::STATE_EMPTY,
+ int mediaType = K3bDevice::MEDIA_WRITABLE_CD,
+ const QString& message = QString::null );
+ /**
+ * \return true
+ */
+ bool questionYesNo( const QString& text,
+ const QString& caption = QString::null,
+ const QString& yesText = QString::null,
+ const QString& noText = QString::null );
+
+ /**
+ * Does nothing
+ */
+ void blockingInformation( const QString& text,
+ const QString& caption = QString::null );
+};
+
+#endif
diff --git a/libk3b/core/k3bthread.cpp b/libk3b/core/k3bthread.cpp
new file mode 100644
index 0000000..07414ad
--- /dev/null
+++ b/libk3b/core/k3bthread.cpp
@@ -0,0 +1,221 @@
+/*
+ *
+ * $Id: k3bthread.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bthread.h"
+#include "k3bprogressinfoevent.h"
+#include "k3bdataevent.h"
+
+#include <kdebug.h>
+
+#include <qapplication.h>
+
+
+static QPtrList<K3bThread> s_threads;
+
+
+void K3bThread::waitUntilFinished()
+{
+ QPtrListIterator<K3bThread> it( s_threads );
+ while( it.current() ) {
+ kdDebug() << "Waiting for thread " << it.current() << endl;
+ it.current()->wait();
+ ++it;
+ }
+
+ kdDebug() << "Thread waiting done." << endl;
+}
+
+
+class K3bThread::Private
+{
+public:
+ Private()
+ : eventHandler( 0 ) {
+ }
+
+ QObject* eventHandler;
+};
+
+
+K3bThread::K3bThread( QObject* eventHandler )
+ : QThread()
+{
+ d = new Private;
+ d->eventHandler = eventHandler;
+
+ s_threads.append(this);
+}
+
+
+K3bThread::K3bThread( unsigned int stackSize, QObject* eventHandler )
+ : QThread( stackSize )
+{
+ d = new Private;
+ d->eventHandler = eventHandler;
+
+ s_threads.append(this);
+}
+
+
+K3bThread::~K3bThread()
+{
+ s_threads.removeRef(this);
+ delete d;
+}
+
+
+void K3bThread::setProgressInfoEventHandler( QObject* eventHandler )
+{
+ d->eventHandler = eventHandler;
+}
+
+QString K3bThread::jobDescription() const
+{
+ return QString::null;
+}
+
+
+QString K3bThread::jobDetails() const
+{
+ return QString::null;
+}
+
+
+void K3bThread::init()
+{
+ // do nothing...
+}
+
+
+void K3bThread::cancel()
+{
+ if( running() ) {
+ terminate();
+ if( d->eventHandler ) {
+ emitCanceled();
+ emitFinished(false);
+ }
+ }
+}
+
+
+void K3bThread::emitInfoMessage( const QString& msg, int type )
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler,
+ new K3bProgressInfoEvent( K3bProgressInfoEvent::InfoMessage, msg, QString::null, type ) );
+ else
+ kdWarning() << "(K3bThread) call to emitInfoMessage() without eventHandler." << endl;
+}
+
+void K3bThread::emitPercent( int p )
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler,
+ new K3bProgressInfoEvent( K3bProgressInfoEvent::Progress, p ) );
+ else
+ kdWarning() << "(K3bThread) call to emitPercent() without eventHandler." << endl;
+}
+
+void K3bThread::emitSubPercent( int p )
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler,
+ new K3bProgressInfoEvent( K3bProgressInfoEvent::SubProgress, p ) );
+ else
+ kdWarning() << "(K3bThread) call to emitSubPercent() without eventHandler." << endl;
+}
+
+void K3bThread::emitStarted()
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler, new K3bProgressInfoEvent( K3bProgressInfoEvent::Started ) );
+ else
+ kdWarning() << "(K3bThread) call to emitStarted() without eventHandler." << endl;
+}
+
+void K3bThread::emitCanceled()
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler, new K3bProgressInfoEvent( K3bProgressInfoEvent::Canceled ) );
+ else
+ kdWarning() << "(K3bThread) call to emitCanceled() without eventHandler." << endl;
+}
+
+void K3bThread::emitFinished( bool success )
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler, new K3bProgressInfoEvent( K3bProgressInfoEvent::Finished, success ) );
+ else
+ kdWarning() << "(K3bThread) call to emitFinished() without eventHandler." << endl;
+}
+
+void K3bThread::emitProcessedSize( int p, int size )
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler, new K3bProgressInfoEvent( K3bProgressInfoEvent::ProcessedSize, p, size ) );
+ else
+ kdWarning() << "(K3bThread) call to emitProcessedSize() without eventHandler." << endl;
+}
+
+void K3bThread::emitProcessedSubSize( int p, int size )
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler, new K3bProgressInfoEvent( K3bProgressInfoEvent::ProcessedSubSize, p, size ) );
+ else
+ kdWarning() << "(K3bThread) call to emitProcessedSubSize() without eventHandler." << endl;
+}
+
+void K3bThread::emitNewTask( const QString& job )
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler, new K3bProgressInfoEvent( K3bProgressInfoEvent::NewTask, job ) );
+ else
+ kdWarning() << "(K3bThread) call to emitNewTask() without eventHandler." << endl;
+}
+
+void K3bThread::emitNewSubTask( const QString& job )
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler, new K3bProgressInfoEvent( K3bProgressInfoEvent::NewSubTask, job ) );
+ else
+ kdWarning() << "(K3bThread) call to emitNewSubTask() without eventHandler." << endl;
+}
+
+void K3bThread::emitDebuggingOutput(const QString& group, const QString& text)
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler, new K3bProgressInfoEvent( K3bProgressInfoEvent::DebuggingOutput, group, text ) );
+ else
+ kdWarning() << "(K3bThread) call to emitDebuggingOutput() without eventHandler." << endl;
+}
+
+void K3bThread::emitData( const char* data, int len )
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler, new K3bDataEvent( data, len ) );
+ else
+ kdWarning() << "(K3bThread) call to emitData() without eventHandler." << endl;
+}
+
+void K3bThread::emitNextTrack( int t, int n )
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler, new K3bProgressInfoEvent( K3bProgressInfoEvent::NextTrack, t, n ) );
+ else
+ kdWarning() << "(K3bThread) call to emitNextTrack() without eventHandler." << endl;
+}
+
diff --git a/libk3b/core/k3bthread.h b/libk3b/core/k3bthread.h
new file mode 100644
index 0000000..f7e68fc
--- /dev/null
+++ b/libk3b/core/k3bthread.h
@@ -0,0 +1,95 @@
+/*
+ *
+ * $Id: k3bthread.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_THREAD_H_
+#define _K3B_THREAD_H_
+
+#include <qthread.h>
+#include "k3b_export.h"
+
+class QObject;
+
+/**
+ * The threaded couterpart to K3bJob
+ * instead of emitting the information signals
+ * one has to use the emitXXX methods which will post
+ * K3bProgressInfoEvents to the eventhandler.
+ *
+ * K3bThreadJob can be used to automatically wrap the thread in a K3bJob.
+ *
+ * As in K3bJob it is important to call emitStarted and emitFinished.
+ *
+ * See K3bThreadJob for more information.
+ */
+class LIBK3B_EXPORT K3bThread : public QThread
+{
+ public:
+ K3bThread( QObject* eventHandler = 0 );
+ K3bThread( unsigned int stackSize, QObject* eventHandler = 0 );
+ virtual ~K3bThread();
+
+ void setProgressInfoEventHandler( QObject* eventHandler );
+
+ /**
+ * Initialize the thread before starting it in the GUi thread.
+ * K3bThreadJob automatically calls this.
+ *
+ * The default implementation does nothing.
+ */
+ virtual void init();
+
+ /**
+ * to provide the same api like K3bJob
+ * the default implementation calls terminate and
+ * emitCancled() and emitFinished(false)
+ */
+ virtual void cancel();
+
+ virtual QString jobDescription() const;
+ virtual QString jobDetails() const;
+
+ /**
+ * waits until all running K3bThread have finished.
+ * This is used by K3bApplication.
+ */
+ static void waitUntilFinished();
+
+ protected:
+ virtual void run() = 0;
+
+ /**
+ * uses the K3bJob::MessageType enum
+ */
+ void emitInfoMessage( const QString& msg, int type );
+ void emitPercent( int p );
+ void emitSubPercent( int p );
+ void emitStarted();
+ void emitCanceled();
+ void emitFinished( bool success );
+ void emitProcessedSize( int processed, int size );
+ void emitProcessedSubSize( int processed, int size );
+ void emitNewTask( const QString& job );
+ void emitNewSubTask( const QString& job );
+ void emitDebuggingOutput(const QString&, const QString&);
+ void emitData( const char* data, int len );
+ void emitNextTrack( int track, int trackNum );
+
+ private:
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/core/k3bthreadjob.cpp b/libk3b/core/k3bthreadjob.cpp
new file mode 100644
index 0000000..a13f10a
--- /dev/null
+++ b/libk3b/core/k3bthreadjob.cpp
@@ -0,0 +1,161 @@
+/*
+ *
+ * $Id: k3bthreadjob.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bthreadjob.h"
+#include "k3bthread.h"
+#include "k3bprogressinfoevent.h"
+#include "k3bdataevent.h"
+
+#include <kdebug.h>
+#include <kapplication.h>
+
+
+
+K3bThreadJob::K3bThreadJob( K3bJobHandler* jh, QObject* parent, const char* name )
+ : K3bJob( jh, parent, name ),
+ m_running(false)
+{
+}
+
+
+K3bThreadJob::K3bThreadJob( K3bThread* thread, K3bJobHandler* jh, QObject* parent, const char* name )
+ : K3bJob( jh, parent, name ),
+ m_running(false)
+{
+ setThread(thread);
+}
+
+
+K3bThreadJob::~K3bThreadJob()
+{
+}
+
+
+QString K3bThreadJob::jobDescription() const
+{
+ if( m_thread )
+ return m_thread->jobDescription();
+ else
+ return QString::null;
+}
+
+
+QString K3bThreadJob::jobDetails() const
+{
+ if( m_thread )
+ return m_thread->jobDetails();
+ else
+ return QString::null;
+}
+
+
+void K3bThreadJob::setThread( K3bThread* t )
+{
+ m_thread = t;
+ m_thread->setProgressInfoEventHandler(this);
+}
+
+
+void K3bThreadJob::start()
+{
+ if( m_thread ) {
+ if( !m_running ) {
+ m_thread->setProgressInfoEventHandler(this);
+ m_running = true;
+ m_thread->init();
+ m_thread->start();
+ }
+ else
+ kdDebug() << "(K3bThreadJob) thread not finished yet." << endl;
+ }
+ else {
+ kdError() << "(K3bThreadJob) no job set." << endl;
+ jobFinished(false);
+ }
+}
+
+
+void K3bThreadJob::cancel()
+{
+ m_thread->cancel();
+ // wait for the thread to finish
+ // m_thread->wait();
+}
+
+
+void K3bThreadJob::cleanupJob( bool success )
+{
+ Q_UNUSED( success );
+}
+
+
+void K3bThreadJob::customEvent( QCustomEvent* e )
+{
+ if( K3bDataEvent* de = dynamic_cast<K3bDataEvent*>(e) ) {
+ emit data( de->data(), de->length() );
+ }
+ else {
+ K3bProgressInfoEvent* be = static_cast<K3bProgressInfoEvent*>(e);
+ switch( be->type() ) {
+ case K3bProgressInfoEvent::Progress:
+ emit percent( be->firstValue() );
+ break;
+ case K3bProgressInfoEvent::SubProgress:
+ emit subPercent( be->firstValue() );
+ break;
+ case K3bProgressInfoEvent::ProcessedSize:
+ emit processedSize( be->firstValue(), be->secondValue() );
+ break;
+ case K3bProgressInfoEvent::ProcessedSubSize:
+ emit processedSubSize( be->firstValue(), be->secondValue() );
+ break;
+ case K3bProgressInfoEvent::InfoMessage:
+ emit infoMessage( be->firstString(), be->firstValue() );
+ break;
+ case K3bProgressInfoEvent::Started:
+ jobStarted();
+ break;
+ case K3bProgressInfoEvent::Canceled:
+ emit canceled();
+ break;
+ case K3bProgressInfoEvent::Finished:
+ // we wait until the thred really finished
+ // although this may be dangerous if some thread
+ // emits the finished signal although it has not finished yet
+ // but makes a lot stuff easier.
+ kdDebug() << "(K3bThreadJob) waiting for the thread to finish." << endl;
+ m_thread->wait();
+ kdDebug() << "(K3bThreadJob) thread finished." << endl;
+ cleanupJob( be->firstValue() );
+ m_running = false;
+ jobFinished( be->firstValue() );
+ break;
+ case K3bProgressInfoEvent::NewTask:
+ emit newTask( be->firstString() );
+ break;
+ case K3bProgressInfoEvent::NewSubTask:
+ emit newSubTask( be->firstString() );
+ break;
+ case K3bProgressInfoEvent::DebuggingOutput:
+ emit debuggingOutput( be->firstString(), be->secondString() );
+ break;
+ case K3bProgressInfoEvent::NextTrack:
+ emit nextTrack( be->firstValue(), be->secondValue() );
+ break;
+ }
+ }
+}
+
+#include "k3bthreadjob.moc"
diff --git a/libk3b/core/k3bthreadjob.h b/libk3b/core/k3bthreadjob.h
new file mode 100644
index 0000000..25919f1
--- /dev/null
+++ b/libk3b/core/k3bthreadjob.h
@@ -0,0 +1,89 @@
+/*
+ *
+ * $Id: k3bthreadjob.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_THREAD_JOB_H_
+#define _K3B_THREAD_JOB_H_
+
+#include "k3bjob.h"
+#include "k3b_export.h"
+class QCustomEvent;
+class K3bThread;
+
+
+/**
+ * A Wrapper to use a K3bThread just like a K3bJob.
+ * Usage:
+ * <pre>
+ * K3bThread* thread = new MySuperThread(...);
+ * K3bThreadJob* job = new K3bThreadJob( thread, ... );
+ * K3bBurnProgressDialog d;
+ * d.setJob(job);
+ * job->start();
+ * d.exec();
+ * delete job;
+ * </pre>
+ * Be aware that K3bThreadJob'd destructor does NOT delete the thread.
+ */
+class LIBK3B_EXPORT K3bThreadJob : public K3bJob
+{
+ Q_OBJECT
+
+ public:
+ K3bThreadJob( K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ K3bThreadJob( K3bThread*, K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ virtual ~K3bThreadJob();
+
+ void setThread( K3bThread* t );
+ K3bThread* thread() const { return m_thread; }
+
+ /**
+ * \reimplemented from K3bJob
+ *
+ * \return true if the job has been started and has not yet
+ * emitted the finished signal
+ */
+ virtual bool active() const { return m_running; }
+
+ virtual QString jobDescription() const;
+ virtual QString jobDetails() const;
+
+ public slots:
+ virtual void start();
+ virtual void cancel();
+
+ protected:
+ /**
+ * converts K3bThread events to K3bJob signals
+ */
+ virtual void customEvent( QCustomEvent* );
+
+ /**
+ * Reimplement this method to do some housekeeping once
+ * the thread has finished.
+ *
+ * The default implementation does nothing.
+ *
+ * \param success True if the thread finished successfully
+ */
+ virtual void cleanupJob( bool success );
+
+ private:
+ K3bThread* m_thread;
+ bool m_running;
+};
+
+#endif
+
diff --git a/libk3b/core/k3bversion.cpp b/libk3b/core/k3bversion.cpp
new file mode 100644
index 0000000..f7af248
--- /dev/null
+++ b/libk3b/core/k3bversion.cpp
@@ -0,0 +1,318 @@
+/*
+ *
+ * $Id: k3bversion.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bversion.h"
+
+#include <qregexp.h>
+#include <kdebug.h>
+
+
+K3bVersion::K3bVersion()
+ : m_majorVersion( -1 ),
+ m_minorVersion( -1 ),
+ m_patchLevel( -1 )
+{
+}
+
+K3bVersion::K3bVersion( const K3bVersion& v )
+ : m_versionString( v.versionString() ),
+ m_majorVersion( v.majorVersion() ),
+ m_minorVersion( v.minorVersion() ),
+ m_patchLevel( v.patchLevel() ),
+ m_suffix( v.suffix() )
+{
+}
+
+K3bVersion::K3bVersion( const QString& version )
+{
+ setVersion( version );
+}
+
+K3bVersion::K3bVersion( int majorVersion,
+ int minorVersion,
+ int patchlevel,
+ const QString& suffix )
+{
+ setVersion( majorVersion, minorVersion, patchlevel, suffix );
+}
+
+void K3bVersion::setVersion( const QString& v )
+{
+ QString suffix;
+ splitVersionString( v.stripWhiteSpace(), m_majorVersion, suffix );
+ if( m_majorVersion >= 0 ) {
+ if( suffix.startsWith(".") ) {
+ suffix = suffix.mid( 1 );
+ splitVersionString( suffix, m_minorVersion, suffix );
+ if( m_minorVersion < 0 ) {
+ kdDebug() << "(K3bVersion) suffix must not start with a dot!" << endl;
+ m_majorVersion = -1;
+ m_minorVersion = -1;
+ m_patchLevel = -1;
+ m_suffix = "";
+ }
+ else {
+ if( suffix.startsWith(".") ) {
+ suffix = suffix.mid( 1 );
+ splitVersionString( suffix, m_patchLevel, suffix );
+ if( m_patchLevel < 0 ) {
+ kdDebug() << "(K3bVersion) suffix must not start with a dot!" << endl;
+ m_majorVersion = -1;
+ m_minorVersion = -1;
+ m_patchLevel = -1;
+ m_suffix = "";
+ }
+ else {
+ m_suffix = suffix;
+ }
+ }
+ else {
+ m_patchLevel = -1;
+ m_suffix = suffix;
+ }
+ }
+ }
+ else {
+ m_minorVersion = -1;
+ m_patchLevel = -1;
+ m_suffix = suffix;
+ }
+ }
+
+ m_versionString = createVersionString( m_majorVersion, m_minorVersion, m_patchLevel, m_suffix );
+}
+
+
+// splits the leading number from s and puts it in num
+// the dot is removed and the rest put in suffix
+// if s does not start with a digit or the first non-digit char is not a dot
+// suffix = s and num = -1 is returned
+void K3bVersion::splitVersionString( const QString& s, int& num, QString& suffix )
+{
+ int pos = s.find( QRegExp("\\D") );
+ if( pos < 0 ) {
+ num = s.toInt();
+ suffix = "";
+ }
+ else if( pos == 0 ) {
+ num = -1;
+ suffix = s;
+ }
+ else {
+ num = s.left( pos ).toInt();
+ suffix = s.mid( pos );
+ }
+}
+
+
+bool K3bVersion::isValid() const
+{
+ return (m_majorVersion >= 0);
+}
+
+
+void K3bVersion::setVersion( int majorVersion,
+ int minorVersion,
+ int patchlevel,
+ const QString& suffix )
+{
+ m_majorVersion = majorVersion;
+ m_minorVersion = minorVersion;
+ m_patchLevel = patchlevel;
+ m_suffix = suffix;
+ m_versionString = createVersionString( majorVersion, minorVersion, patchlevel, suffix );
+}
+
+K3bVersion& K3bVersion::operator=( const QString& v )
+{
+ setVersion( v );
+ return *this;
+}
+
+K3bVersion K3bVersion::simplify() const
+{
+ K3bVersion v( *this );
+ v.m_suffix.truncate(0);
+ return v;
+}
+
+QString K3bVersion::createVersionString( int majorVersion,
+ int minorVersion,
+ int patchlevel,
+ const QString& suffix )
+{
+ if( majorVersion >= 0 ) {
+ QString s = QString::number(majorVersion);
+
+ if( minorVersion > -1 ) {
+ s.append( QString(".%1").arg(minorVersion) );
+ if( patchlevel > -1 )
+ s.append( QString(".%1").arg(patchlevel) );
+ }
+
+ if( !suffix.isNull() )
+ s.append( suffix );
+
+ return s;
+ }
+ else
+ return "";
+}
+
+
+int K3bVersion::compareSuffix( const QString& suffix1, const QString& suffix2 )
+{
+ static QRegExp rcRx( "rc(\\d+)" );
+ static QRegExp preRx( "pre(\\d+)" );
+ static QRegExp betaRx( "beta(\\d+)" );
+ static QRegExp alphaRx( "a(?:lpha)?(\\d+)" );
+
+ // first we check if one of the suffixes (or both are empty) becasue that case if simple
+ if( suffix1.isEmpty() ) {
+ if( suffix2.isEmpty() )
+ return 0;
+ else
+ return 1; // empty greater than the non-empty (should we treat something like 1.0a as greater than 1.0?)
+ }
+ else if( suffix2.isEmpty() )
+ return -1;
+
+ // now search for our special suffixes
+ if( rcRx.exactMatch( suffix1 ) ) {
+ int v1 = rcRx.cap(1).toInt();
+
+ if( rcRx.exactMatch( suffix2 ) ) {
+ int v2 = rcRx.cap(1).toInt();
+ return ( v1 == v2 ? 0 : ( v1 < v2 ? -1 : 1 ) );
+ }
+ else if( preRx.exactMatch( suffix2 ) ||
+ betaRx.exactMatch( suffix2 ) ||
+ alphaRx.exactMatch( suffix2 ) )
+ return 1; // rc > than all the others
+ else
+ return QString::compare( suffix1, suffix2 );
+ }
+
+ else if( preRx.exactMatch( suffix1 ) ) {
+ int v1 = preRx.cap(1).toInt();
+
+ if( rcRx.exactMatch( suffix2 ) ) {
+ return -1; // pre is less than rc
+ }
+ else if( preRx.exactMatch( suffix2 ) ) {
+ int v2 = preRx.cap(1).toInt();
+ return ( v1 == v2 ? 0 : ( v1 < v2 ? -1 : 1 ) );
+ }
+ else if( betaRx.exactMatch( suffix2 ) ||
+ alphaRx.exactMatch( suffix2 ) )
+ return 1; // pre is greater than beta or alpha
+ else
+ return QString::compare( suffix1, suffix2 );
+ }
+
+ else if( betaRx.exactMatch( suffix1 ) ) {
+ int v1 = betaRx.cap(1).toInt();
+
+ if( rcRx.exactMatch( suffix2 ) ||
+ preRx.exactMatch( suffix2 ) )
+ return -1; // beta is less than rc or pre
+ else if( betaRx.exactMatch( suffix2 ) ) {
+ int v2 = betaRx.cap(1).toInt();
+ return ( v1 == v2 ? 0 : ( v1 < v2 ? -1 : 1 ) );
+ }
+ else if( alphaRx.exactMatch( suffix2 ) )
+ return 1; // beta is greater then alpha
+ else
+ return QString::compare( suffix1, suffix2 );
+ }
+
+ else if( alphaRx.exactMatch( suffix1 ) ) {
+ int v1 = alphaRx.cap(1).toInt();
+
+ if( rcRx.exactMatch( suffix2 ) ||
+ preRx.exactMatch( suffix2 ) ||
+ betaRx.exactMatch( suffix2 ) )
+ return -1; // alpha is less than all the others
+ else if( alphaRx.exactMatch( suffix2 ) ) {
+ int v2 = alphaRx.cap(1).toInt();
+ return ( v1 == v2 ? 0 : ( v1 < v2 ? -1 : 1 ) );
+ }
+ else
+ return QString::compare( suffix1, suffix2 );
+ }
+
+ else
+ return QString::compare( suffix1, suffix2 );
+}
+
+
+bool operator<( const K3bVersion& v1, const K3bVersion& v2 )
+{
+ // both version objects need to be valid
+
+ if( v1.majorVersion() == v2.majorVersion() ) {
+
+ // 1 == 1.0
+ if( ( v1.minorVersion() == v2.minorVersion() )
+ ||
+ ( v1.minorVersion() == -1 && v2.minorVersion() == 0 )
+ ||
+ ( v2.minorVersion() == -1 && v1.minorVersion() == 0 )
+ )
+ {
+ // 1.0 == 1.0.0
+ if( ( v1.patchLevel() == v2.patchLevel() )
+ ||
+ ( v1.patchLevel() == -1 && v2.patchLevel() == 0 )
+ ||
+ ( v2.patchLevel() == -1 && v1.patchLevel() == 0 )
+ )
+ {
+ return K3bVersion::compareSuffix( v1.suffix(), v2.suffix() ) < 0;
+ }
+ else
+ return ( v1.patchLevel() < v2.patchLevel() );
+ }
+ else
+ return ( v1.minorVersion() < v2.minorVersion() );
+ }
+ else
+ return ( v1.majorVersion() < v2.majorVersion() );
+}
+
+bool operator>( const K3bVersion& v1, const K3bVersion& v2 )
+{
+ return operator<( v2, v1 );
+}
+
+
+bool operator==( const K3bVersion& v1, const K3bVersion& v2 )
+{
+ return ( v1.majorVersion() == v2.majorVersion() &&
+ v1.minorVersion() == v2.minorVersion() &&
+ v1.patchLevel() == v2.patchLevel() &&
+ K3bVersion::compareSuffix( v1.suffix(), v2.suffix() ) == 0 );
+}
+
+
+bool operator<=( const K3bVersion& v1, const K3bVersion& v2 )
+{
+ return ( operator<( v1, v2 ) || operator==( v1, v2 ) );
+}
+
+bool operator>=( const K3bVersion& v1, const K3bVersion& v2 )
+{
+ return ( operator>( v1, v2 ) || operator==( v1, v2 ) );
+}
diff --git a/libk3b/core/k3bversion.h b/libk3b/core/k3bversion.h
new file mode 100644
index 0000000..a6e3aee
--- /dev/null
+++ b/libk3b/core/k3bversion.h
@@ -0,0 +1,141 @@
+/*
+ *
+ * $Id: k3bversion.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_VERSION_H_
+#define _K3B_VERSION_H_
+
+#include <qstring.h>
+#include "k3b_export.h"
+/**
+ * \brief Representation of a version.
+ *
+ * K3bVersion represents a version consisting of a major version (accessible via majorVersion()),
+ * a minor version (accessible via minorVersion()), a patchLevel (accessible via patchLevel()),
+ * and a suffix (accessible via suffix()).
+ *
+ * The major version is mandatory while all other fields are optional (in case of the minor version
+ * and the patchlevel -1 means that the field is undefined).
+ *
+ * K3bVersion tries to treat version suffixes in an "intelligent" way to properly compare versions
+ * (see compareSuffix() for more details).
+ *
+ * K3bVersion may also be used everywhere a QString is needed as it automatically converts to a
+ * string representation using createVersionString().
+ */
+class LIBK3B_EXPORT K3bVersion
+{
+ public:
+ /**
+ * construct an empty version object
+ * which is invalid
+ * @ see isValid()
+ */
+ K3bVersion();
+
+ /**
+ * copy constructor
+ */
+ K3bVersion( const K3bVersion& );
+
+ /**
+ * this constructor tries to parse the given version string
+ */
+ K3bVersion( const QString& version );
+
+ /**
+ * sets the version and generates a version string from it
+ */
+ K3bVersion( int majorVersion, int minorVersion, int pachlevel = -1, const QString& suffix = QString::null );
+
+ /**
+ * tries to parse the version string
+ * used by the constructor
+ */
+ void setVersion( const QString& );
+
+ bool isValid() const;
+
+ /**
+ * sets the version and generates a version string from it
+ * used by the constructor
+ *
+ * If minorVersion or pachlevel are -1 they will not be used when generating the version string.
+ */
+ void setVersion( int majorVersion, int minorVersion = -1, int patchlevel = -1, const QString& suffix = QString::null );
+
+ const QString& versionString() const { return m_versionString; }
+ int majorVersion() const { return m_majorVersion; }
+ int minorVersion() const { return m_minorVersion; }
+ int patchLevel() const { return m_patchLevel; }
+ const QString& suffix() const { return m_suffix; }
+
+ /**
+ * just to make it possible to use as a QString
+ */
+ operator const QString& () const { return m_versionString; }
+ K3bVersion& operator=( const QString& v );
+
+ /**
+ * \return A new K3bVersion object which equals this one except that the suffix is empty.
+ */
+ K3bVersion simplify() const;
+
+ /**
+ * If minorVersion or pachlevel are -1 they will not be used when generating the version string.
+ * If minorVersion is -1 patchlevel will be ignored.
+ */
+ static QString createVersionString( int majorVersion,
+ int minorVersion = -1,
+ int patchlevel = -1,
+ const QString& suffix = QString::null );
+
+ /**
+ * "Intelligent" comparison of two version suffixes.
+ *
+ * This method checks for the following types of suffixes and treats them in the
+ * following order:
+ *
+ * [empty prefix] > rcX > preX > betaX > alphaX = aX (where X is a number)
+ *
+ * Every other suffixes are compared alphanumerical.
+ * An empty prefix is always considered newer than an unknown non-emtpy suffix (e.g. not one of the above.)
+ *
+ * @return \li -1 if suffix1 is less than suffix2
+ * \li 0 if suffix1 equals suffix2 (be aware that this is not the same as comparing to strings as
+ * alphaX equals aX in this case.)
+ * \li 1 if suffix1 is greater than suffix2
+ */
+ static int compareSuffix( const QString& suffix1, const QString& suffix2 );
+
+ private:
+ static void splitVersionString( const QString& s, int& num, QString& suffix );
+
+ QString m_versionString;
+ int m_majorVersion;
+ int m_minorVersion;
+ int m_patchLevel;
+ QString m_suffix;
+};
+
+
+LIBK3B_EXPORT bool operator<( const K3bVersion& v1, const K3bVersion& v2 );
+LIBK3B_EXPORT bool operator>( const K3bVersion& v1, const K3bVersion& v2 );
+LIBK3B_EXPORT bool operator==( const K3bVersion& v1, const K3bVersion& v2 );
+LIBK3B_EXPORT bool operator<=( const K3bVersion& v1, const K3bVersion& v2 );
+LIBK3B_EXPORT bool operator>=( const K3bVersion& v1, const K3bVersion& v2 );
+
+
+#endif