diff options
Diffstat (limited to 'libk3b/tools')
91 files changed, 14609 insertions, 0 deletions
diff --git a/libk3b/tools/Makefile.am b/libk3b/tools/Makefile.am new file mode 100644 index 0000000..d48a295 --- /dev/null +++ b/libk3b/tools/Makefile.am @@ -0,0 +1,44 @@ +AM_CPPFLAGS= -I$(srcdir)/../../src \ + -I$(srcdir)/../core \ + -I$(srcdir)/../../libk3bdevice \ + -I$(srcdir)/.. \ + $(all_includes) + +noinst_LTLIBRARIES = libk3btools.la + +libk3btools_la_LIBADD = libisofs/libisofs.la + +libk3btools_la_LDFLAGS = $(all_libraries) + +libk3btools_la_SOURCES = k3bwavefilewriter.cpp k3bbusywidget.cpp k3bdeviceselectiondialog.cpp \ + k3bmd5job.cpp k3btitlelabel.cpp k3bcutcombobox.cpp \ + k3bstringutils.cpp k3bdevicecombobox.cpp kcutlabel.cpp \ + k3bstdguiitems.cpp k3bvalidators.cpp k3bthroughputestimator.cpp \ + k3biso9660.cpp k3bmultichoicedialog.cpp k3bdevicehandler.cpp \ + k3bcdparanoialib.cpp k3blistview.cpp k3bmsfedit.cpp \ + k3bcdtextvalidator.cpp k3bintvalidator.cpp k3bexceptions.cpp \ + k3bprogressdialog.cpp k3btoolbox.cpp k3bpushbutton.cpp \ + k3blistviewitemanimator.cpp k3bthreadwidget.cpp k3bradioaction.cpp \ + k3bsignalwaiter.cpp k3blibdvdcss.cpp k3biso9660backend.cpp \ + k3bpipe.cpp k3bchecksumpipe.cpp k3btoolbutton.cpp \ + k3bintmapcombobox.cpp k3bdirsizejob.cpp k3brichtextlabel.cpp \ + k3btempfile.cpp k3bactivepipe.cpp k3bfilesplitter.cpp \ + k3bfilesysteminfo.cpp + +include_HEADERS = k3bwavefilewriter.h k3bbusywidget.h k3bdeviceselectiondialog.h \ + k3bmd5job.h k3bcutcombobox.h k3bstringutils.h \ + k3bdevicecombobox.h kcutlabel.h k3bstdguiitems.h \ + k3bvalidators.h k3bthroughputestimator.h k3biso9660.h \ + k3bmultichoicedialog.h k3bdevicehandler.h k3bcdparanoialib.h \ + k3blistview.h k3bmsfedit.h k3bcdtextvalidator.h \ + k3bintvalidator.h k3bexceptions.h k3bprogressdialog.h \ + k3btoolbox.h k3bpushbutton.h k3blistviewitemanimator.h \ + k3bthreadwidget.h k3bradioaction.h k3bsignalwaiter.h \ + k3biso9660backend.h k3bpipe.h k3bdirsizejob.h \ + k3bchecksumpipe.h k3btoolbutton.h k3bintmapcombobox.h \ + k3brichtextlabel.h k3btempfile.h k3bactivepipe.h \ + k3bfilesplitter.h k3bfilesysteminfo.h + +METASOURCES = AUTO + +SUBDIRS = libisofs diff --git a/libk3b/tools/k3bactivepipe.cpp b/libk3b/tools/k3bactivepipe.cpp new file mode 100644 index 0000000..5a1e575 --- /dev/null +++ b/libk3b/tools/k3bactivepipe.cpp @@ -0,0 +1,255 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bactivepipe.h" + +#include <k3bpipe.h> + +#include <kdebug.h> + +#include <qthread.h> +#include <qiodevice.h> + +#include <unistd.h> + + +class K3bActivePipe::Private : public QThread +{ +public: + Private( K3bActivePipe* pipe ) : + m_pipe( pipe ), + fdToReadFrom(-1), + fdToWriteTo(-1), + sourceIODevice(0), + sinkIODevice(0), + closeFdToReadFrom(false), + closeFdToWriteTo(false) { + } + + void run() { + kdDebug() << "(K3bActivePipe) started thread." << endl; + bytesRead = bytesWritten = 0; + buffer.resize( 10*2048 ); + ssize_t r = 0; + while( ( r = m_pipe->read( buffer.data(), buffer.size() ) ) > 0 ) { + + bytesRead += r; + + // write it out + ssize_t w = 0; + ssize_t ww = 0; + while( w < r ) { + if( ( ww = m_pipe->write( buffer.data()+w, r-w ) ) > 0 ) { + w += ww; + bytesWritten += ww; + } + else { + kdDebug() << "(K3bActivePipe) write failed." << endl; + close( closeWhenDone ); + return; + } + } + } + // kdDebug() << "(K3bActivePipe) thread done: " << r << " (total bytes read/written: " << bytesRead << "/" << bytesWritten << ")" << endl; + close( closeWhenDone ); + } + + int readFd() const { + if( fdToReadFrom == -1 ) + return pipeIn.out(); + else + return fdToReadFrom; + } + + int writeFd() const { + if( fdToWriteTo == -1 ) + return pipeOut.in(); + else + return fdToWriteTo; + } + + void close( bool closeAll ) { + if( sourceIODevice ) + sourceIODevice->close(); + if( sinkIODevice ) + sinkIODevice->close(); + + if( closeAll ) { + pipeIn.close(); + pipeOut.close(); + if( fdToWriteTo != -1 && + closeFdToWriteTo ) + ::close( fdToWriteTo ); + + if( fdToReadFrom != -1 && + closeFdToReadFrom ) + ::close( fdToReadFrom ); + } + } + +private: + K3bActivePipe* m_pipe; + +public: + int fdToReadFrom; + int fdToWriteTo; + K3bPipe pipeIn; + K3bPipe pipeOut; + + QIODevice* sourceIODevice; + QIODevice* sinkIODevice; + + bool closeWhenDone; + bool closeFdToReadFrom; + bool closeFdToWriteTo; + + QByteArray buffer; + + Q_UINT64 bytesRead; + Q_UINT64 bytesWritten; +}; + + +K3bActivePipe::K3bActivePipe() +{ + d = new Private( this ); +} + + +K3bActivePipe::~K3bActivePipe() +{ + delete d; +} + + +bool K3bActivePipe::open( bool closeWhenDone ) +{ + if( d->running() ) + return false; + + d->closeWhenDone = closeWhenDone; + + if( d->sourceIODevice ) { + if( !d->sourceIODevice->open( IO_ReadOnly ) ) + return false; + } + else if( d->fdToReadFrom == -1 && !d->pipeIn.open() ) { + return false; + } + + if( d->sinkIODevice ) { + if( !d->sinkIODevice->open( IO_WriteOnly ) ) + return false; + } + else if( d->fdToWriteTo == -1 && !d->pipeOut.open() ) { + close(); + return false; + } + + kdDebug() << "(K3bActivePipe) successfully opened pipe." << endl; + + d->start(); + return true; +} + + +void K3bActivePipe::close() +{ + d->pipeIn.closeIn(); + d->wait(); + d->close( true ); +} + + +void K3bActivePipe::readFromFd( int fd, bool close ) +{ + d->fdToReadFrom = fd; + d->sourceIODevice = 0; + d->closeFdToReadFrom = close; +} + + +void K3bActivePipe::writeToFd( int fd, bool close ) +{ + d->fdToWriteTo = fd; + d->sinkIODevice = 0; + d->closeFdToWriteTo = close; +} + + +void K3bActivePipe::readFromIODevice( QIODevice* dev ) +{ + d->fdToReadFrom = -1; + d->sourceIODevice = dev; +} + + +void K3bActivePipe::writeToIODevice( QIODevice* dev ) +{ + d->fdToWriteTo = -1; + d->sinkIODevice = dev; +} + + +int K3bActivePipe::in() const +{ + return d->pipeIn.in(); +} + + +int K3bActivePipe::out() const +{ + return d->pipeOut.out(); +} + + +int K3bActivePipe::read( char* data, int max ) +{ + if( d->sourceIODevice ) + return d->sourceIODevice->readBlock( data, max ); + else + return ::read( d->readFd(), data, max ); +} + + +int K3bActivePipe::write( char* data, int max ) +{ + if( d->sinkIODevice ) + return d->sinkIODevice->writeBlock( data, max ); + else + return ::write( d->writeFd(), data, max ); +} + + +bool K3bActivePipe::pumpSync() +{ + if( open( true ) ) + d->wait(); + else + return false; + return true; +} + + +Q_UINT64 K3bActivePipe::bytesRead() const +{ + return d->bytesRead; +} + + +Q_UINT64 K3bActivePipe::bytesWritten() const +{ + return d->bytesWritten; +} diff --git a/libk3b/tools/k3bactivepipe.h b/libk3b/tools/k3bactivepipe.h new file mode 100644 index 0000000..367646d --- /dev/null +++ b/libk3b/tools/k3bactivepipe.h @@ -0,0 +1,134 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_ACTIVE_PIPE_H_ +#define _K3B_ACTIVE_PIPE_H_ + +#include <qcstring.h> + +#include <k3b_export.h> + + +class QIODevice; + + +/** + * The active pipe pumps data from a source to a sink using an + * additional thread. + */ +class LIBK3B_EXPORT K3bActivePipe +{ + public: + K3bActivePipe(); + virtual ~K3bActivePipe(); + + /** + * Opens the pipe and thus starts the + * pumping. + * + * \param closeWhenDone If true the pipes will be closed + * once all data has been read. + */ + virtual bool open( bool closeWhenDone = false ); + + /** + * Opens the pipe syncroneously and blocks until all data has been + * pumped through. + * The pipe is closed afterwards. + */ + bool pumpSync(); + + /** + * Close the pipe + */ + virtual void close(); + + /** + * Set the file descriptor to read from. If this is -1 (the default) then + * data has to be piped into the in() file descriptor. + * + * \param fd The file descriptor to read from. + * \param close If true the reading file descriptor will be closed on a call to close() + */ + void readFromFd( int fd, bool close = false ); + + /** + * Set the file descriptor to write to. If this is -1 (the default) then + * data has to read from the out() file descriptor. + * + * \param fd The file descriptor to write to. + * \param close If true the reading file descriptor will be closed on a call to close() + */ + void writeToFd( int fd, bool close = false ); + + /** + * Read from a QIODevice instead of a file descriptor. + * The device will be opened IO_ReadOnly and closed + * afterwards. + */ + void readFromIODevice( QIODevice* dev ); + + /** + * Write to a QIODevice instead of a file descriptor. + * The device will be opened IO_WriteOnly and closed + * afterwards. + */ + void writeToIODevice( QIODevice* dev ); + + /** + * The file descriptor to write into + * Only valid if no source has been set + */ + int in() const; + + /** + * The file descriptor to read from + * Only valid if no sink has been set + */ + int out() const; + + /** + * The number of bytes that have been read. + */ + Q_UINT64 bytesRead() const; + + /** + * The number of bytes that have been written. + */ + Q_UINT64 bytesWritten() const; + + protected: + /** + * Reads the data from the source. + * The default implementation reads from the file desc + * set via readFromFd or from in() + */ + virtual int read( char* data, int max ); + + /** + * Write the data to the sink. + * The default implementation writes to the file desc + * set via writeToFd or out() + * + * Can be reimplememented to further process the data. + */ + virtual int write( char* data, int max ); + + private: + class Private; + Private* d; +}; + +#endif diff --git a/libk3b/tools/k3bbusywidget.cpp b/libk3b/tools/k3bbusywidget.cpp new file mode 100644 index 0000000..d222107 --- /dev/null +++ b/libk3b/tools/k3bbusywidget.cpp @@ -0,0 +1,103 @@ +/* + * + * $Id: k3bbusywidget.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bbusywidget.h" + +#include <qtimer.h> +#include <qpainter.h> + +#include <kglobalsettings.h> + + +K3bBusyWidget::K3bBusyWidget( QWidget* parent, const char* name ) + : QFrame( parent, name ) +{ + m_busyTimer = new QTimer( this ); + m_iBusyPosition = 0; + + connect( m_busyTimer, SIGNAL(timeout()), this, SLOT(animateBusy()) ); + + m_bBusy = false; +} + +K3bBusyWidget::~K3bBusyWidget() +{ +} + + +void K3bBusyWidget::showBusy( bool b ) +{ + m_bBusy = b; + +// if( b ) { +// m_iBusyCounter++; +// } +// else if( m_iBusyCounter > 0 ) { +// m_iBusyCounter--; +// } + + if( m_bBusy ) { + if( !m_busyTimer->isActive() ) + m_busyTimer->start( 500 ); + } + else { + if( m_busyTimer->isActive() ) + m_busyTimer->stop(); + update(); + m_iBusyPosition = 0; + } +} + + +void K3bBusyWidget::animateBusy() +{ + m_iBusyPosition++; + update(); +} + + +QSize K3bBusyWidget::sizeHint() const +{ + return minimumSizeHint(); +} + + +QSize K3bBusyWidget::minimumSizeHint() const +{ + return QSize( 2*frameWidth() + 62, 10 ); +} + + +void K3bBusyWidget::drawContents( QPainter* p ) +{ + QRect rect = contentsRect(); + + int squareSize = 8; + + int pos = 2 + m_iBusyPosition * (squareSize + 2); + + // check if the position is in the visible area + if( pos + 8 + 2> rect.width() ) { + m_iBusyPosition = 0; + pos = 2; + } + + // p->eraseRect( rect ); + if( m_bBusy ) + p->fillRect( pos, (rect.height() - squareSize)/2, squareSize, squareSize, KGlobalSettings::highlightColor() ); +} + + +#include "k3bbusywidget.moc" diff --git a/libk3b/tools/k3bbusywidget.h b/libk3b/tools/k3bbusywidget.h new file mode 100644 index 0000000..2a6934c --- /dev/null +++ b/libk3b/tools/k3bbusywidget.h @@ -0,0 +1,54 @@ +/* + * + * $Id: k3bbusywidget.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_BUSY_WIDGET_H +#define K3B_BUSY_WIDGET_H + + +#include <qframe.h> +#include "k3b_export.h" + +class QPainter; +class QTimer; + + +class LIBK3B_EXPORT K3bBusyWidget : public QFrame +{ + Q_OBJECT + + public: + K3bBusyWidget( QWidget* parent = 0, const char* name = 0 ); + ~K3bBusyWidget(); + + void showBusy( bool b ); + + QSize sizeHint() const; + QSize minimumSizeHint() const; + + protected: + void drawContents( QPainter* p ); + + private slots: + void animateBusy(); + + private: + bool m_bBusy; + int m_iBusyPosition; + + QTimer* m_busyTimer; +}; + + +#endif diff --git a/libk3b/tools/k3bcdparanoialib.cpp b/libk3b/tools/k3bcdparanoialib.cpp new file mode 100644 index 0000000..5976941 --- /dev/null +++ b/libk3b/tools/k3bcdparanoialib.cpp @@ -0,0 +1,783 @@ +/* + * + * $Id: k3bcdparanoialib.cpp 621693 2007-01-09 14:38:25Z trueg $ + * Copyright (C) 2003-2007 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bcdparanoialib.h" + +#include <k3bdevice.h> +#include <k3btoc.h> +#include <k3bmsf.h> + +#include <kdebug.h> + +#include <dlfcn.h> + +#include <qfile.h> +#include <qmutex.h> + + +static bool s_haveLibCdio = false; + + +void* K3bCdparanoiaLib::s_libInterface = 0; +void* K3bCdparanoiaLib::s_libParanoia = 0; +int K3bCdparanoiaLib::s_counter = 0; + + +#define CDDA_IDENTIFY s_haveLibCdio ? "cdio_cddap_identify" : "cdda_identify" +#define CDDA_CLOSE s_haveLibCdio ? "cdio_cddap_close" : "cdda_close" +#define CDDA_OPEN s_haveLibCdio ? "cdio_cddap_open" : "cdda_open" +#define CDDA_TRACK_FIRSTSECTOR s_haveLibCdio ? "cdio_cddap_track_firstsector" : "cdda_track_firstsector" +#define CDDA_TRACK_LASTSECTOR s_haveLibCdio ? "cdio_cddap_track_lastsector" : "cdda_track_lastsector" +#define CDDA_VERBOSE_SET s_haveLibCdio ? "cdio_cddap_verbose_set" : "cdda_verbose_set" +#define CDDA_DISC_FIRSTSECTOR s_haveLibCdio ? "cdio_cddap_disc_firstsector" : "cdda_disc_firstsector" + +#define PARANOIA_INIT s_haveLibCdio ? "cdio_paranoia_init" : "paranoia_init" +#define PARANOIA_FREE s_haveLibCdio ? "cdio_paranoia_free" : "paranoia_free" +#define PARANOIA_MODESET s_haveLibCdio ? "cdio_paranoia_modeset" : "paranoia_modeset" +#define PARANOIA_SEEK s_haveLibCdio ? "cdio_paranoia_seek" : "paranoia_seek" +#define PARANOIA_READ_LIMITED s_haveLibCdio ? "cdio_paranoia_read_limited" : "paranoia_read_limited" + + +// from cdda_paranoia.h +#define PARANOIA_CB_READ 0 +#define PARANOIA_CB_VERIFY 1 +#define PARANOIA_CB_FIXUP_EDGE 2 +#define PARANOIA_CB_FIXUP_ATOM 3 +#define PARANOIA_CB_SCRATCH 4 +#define PARANOIA_CB_REPAIR 5 +#define PARANOIA_CB_SKIP 6 +#define PARANOIA_CB_DRIFT 7 +#define PARANOIA_CB_BACKOFF 8 +#define PARANOIA_CB_OVERLAP 9 +#define PARANOIA_CB_FIXUP_DROPPED 10 +#define PARANOIA_CB_FIXUP_DUPED 11 +#define PARANOIA_CB_READERR 12 + + + +static void paranoiaCallback( long, int status ) +{ + // do nothing so far.... + return; + + switch( status ) { + case -1: + break; + case -2: + break; + case PARANOIA_CB_READ: + // no problem + // does only this mean that the sector has been read? +// m_lastReadSector = sector; // this seems to be rather useless +// m_readSectors++; + break; + case PARANOIA_CB_VERIFY: + break; + case PARANOIA_CB_FIXUP_EDGE: + break; + case PARANOIA_CB_FIXUP_ATOM: + break; + case PARANOIA_CB_SCRATCH: + // scratch detected + break; + case PARANOIA_CB_REPAIR: + break; + case PARANOIA_CB_SKIP: + // skipped sector + break; + case PARANOIA_CB_DRIFT: + break; + case PARANOIA_CB_BACKOFF: + break; + case PARANOIA_CB_OVERLAP: + // sector does not seem to contain the current + // sector but the amount of overlapped data + // m_overlap = sector; + break; + case PARANOIA_CB_FIXUP_DROPPED: + break; + case PARANOIA_CB_FIXUP_DUPED: + break; + case PARANOIA_CB_READERR: + break; + } +} + + + +extern "C" { + struct cdrom_drive; + struct cdrom_paranoia; + + // HINT: these pointers must NOT have the same name like the actual methods! + // I added "cdda_" as prefix + // Before doing that K3b crashed in cdda_open! + // Can anyone please explain that to me? + + // cdda_interface + cdrom_drive* (*cdda_cdda_identify)(const char*, int, char**); + int (*cdda_cdda_open)(cdrom_drive *d); + int (*cdda_cdda_close)(cdrom_drive *d); + long (*cdda_cdda_track_firstsector)( cdrom_drive*, int ); + long (*cdda_cdda_track_lastsector)( cdrom_drive*, int ); + long (*cdda_cdda_disc_firstsector)(cdrom_drive *d); + void (*cdda_cdda_verbose_set)(cdrom_drive *d,int err_action, int mes_action); + + // cdda_paranoia + cdrom_paranoia* (*cdda_paranoia_init)(cdrom_drive*); + void (*cdda_paranoia_free)(cdrom_paranoia *p); + void (*cdda_paranoia_modeset)(cdrom_paranoia *p, int mode); + int16_t* (*cdda_paranoia_read_limited)(cdrom_paranoia *p, void(*callback)(long,int), int); + long (*cdda_paranoia_seek)(cdrom_paranoia *p,long seek,int mode); +} + +// from cdda_paranoia.h +#define PARANOIA_MODE_FULL 0xff +#define PARANOIA_MODE_DISABLE 0 + +#define PARANOIA_MODE_VERIFY 1 +#define PARANOIA_MODE_FRAGMENT 2 +#define PARANOIA_MODE_OVERLAP 4 +#define PARANOIA_MODE_SCRATCH 8 +#define PARANOIA_MODE_REPAIR 16 +#define PARANOIA_MODE_NEVERSKIP 32 + + + +/** + * Internal class used by K3bCdparanoiaLib + */ +class K3bCdparanoiaLibData +{ + public: + K3bCdparanoiaLibData( K3bDevice::Device* dev ) + : m_device(dev), + m_drive(0), + m_paranoia(0), + m_currentSector(0) { + s_dataMap.insert( dev, this ); + } + + ~K3bCdparanoiaLibData() { + paranoiaFree(); + + s_dataMap.erase( m_device ); + } + + K3bDevice::Device* device() const { return m_device; } + void paranoiaModeSet( int ); + bool paranoiaInit(); + void paranoiaFree(); + int16_t* paranoiaRead( void(*callback)(long,int), int maxRetries ); + long paranoiaSeek( long, int ); + long firstSector( int ); + long lastSector( int ); + long sector() const { return m_currentSector; } + + static K3bCdparanoiaLibData* data( K3bDevice::Device* dev ) { + QMap<K3bDevice::Device*, K3bCdparanoiaLibData*>::const_iterator it = s_dataMap.find( dev ); + if( it == s_dataMap.constEnd() ) + return new K3bCdparanoiaLibData( dev ); + else + return *it; + } + + static void freeAll() { + // clean up all K3bCdparanoiaLibData instances + for( QMap<K3bDevice::Device*, K3bCdparanoiaLibData*>::iterator it = s_dataMap.begin(); + it != s_dataMap.end(); ++it ) + delete it.data(); + } + + private: + // + // We have exactly one instance of K3bCdparanoiaLibData per device + // + static QMap<K3bDevice::Device*, K3bCdparanoiaLibData*> s_dataMap; + + K3bDevice::Device* m_device; + + cdrom_drive* m_drive; + cdrom_paranoia* m_paranoia; + + long m_currentSector; + + QMutex mutex; +}; + + +QMap<K3bDevice::Device*, K3bCdparanoiaLibData*> K3bCdparanoiaLibData::s_dataMap; + +bool K3bCdparanoiaLibData::paranoiaInit() +{ + mutex.lock(); + + if( m_drive ) + paranoiaFree(); + + // since we use cdparanoia to open the device it is important to close + // the device here + m_device->close(); + + m_drive = cdda_cdda_identify( QFile::encodeName(m_device->blockDeviceName()), 0, 0 ); + if( m_drive == 0 ) { + mutex.unlock(); + return false; + } + + // cdda_cdda_verbose_set( m_drive, 1, 1 ); + + cdda_cdda_open( m_drive ); + m_paranoia = cdda_paranoia_init( m_drive ); + if( m_paranoia == 0 ) { + mutex.unlock(); + paranoiaFree(); + return false; + } + + m_currentSector = 0; + + mutex.unlock(); + + return true; +} + + +void K3bCdparanoiaLibData::paranoiaFree() +{ + mutex.lock(); + + if( m_paranoia ) { + cdda_paranoia_free( m_paranoia ); + m_paranoia = 0; + } + if( m_drive ) { + cdda_cdda_close( m_drive ); + m_drive = 0; + } + + mutex.unlock(); +} + + +void K3bCdparanoiaLibData::paranoiaModeSet( int mode ) +{ + mutex.lock(); + cdda_paranoia_modeset( m_paranoia, mode ); + mutex.unlock(); +} + + +int16_t* K3bCdparanoiaLibData::paranoiaRead( void(*callback)(long,int), int maxRetries ) +{ + if( m_paranoia ) { + mutex.lock(); + int16_t* data = cdda_paranoia_read_limited( m_paranoia, callback, maxRetries ); + if( data ) + m_currentSector++; + mutex.unlock(); + return data; + } + else + return 0; +} + + +long K3bCdparanoiaLibData::firstSector( int track ) +{ + if( m_drive ) { + mutex.lock(); + long sector = cdda_cdda_track_firstsector( m_drive, track ); + mutex.unlock(); + return sector; + } + else + return -1; +} + +long K3bCdparanoiaLibData::lastSector( int track ) +{ + if( m_drive ) { + mutex.lock(); + long sector = cdda_cdda_track_lastsector(m_drive, track ); + mutex.unlock(); + return sector; + } + else + return -1; +} + + +long K3bCdparanoiaLibData::paranoiaSeek( long sector, int mode ) +{ + if( m_paranoia ) { + mutex.lock(); + m_currentSector = cdda_paranoia_seek( m_paranoia, sector, mode ); + mutex.unlock(); + return m_currentSector; + } + else + return -1; +} + + + +class K3bCdparanoiaLib::Private +{ +public: + Private() + : device(0), + currentSector(0), + startSector(0), + lastSector(0), + status(S_OK), + paranoiaLevel(0), + neverSkip(true), + maxRetries(5), + data(0) { + } + + ~Private() { + } + + void updateParanoiaMode() { + // from cdrdao 1.1.7 + int paranoiaMode = PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP; + + switch( paranoiaLevel ) { + case 0: + paranoiaMode = PARANOIA_MODE_DISABLE; + break; + + case 1: + paranoiaMode |= PARANOIA_MODE_OVERLAP; + paranoiaMode &= ~PARANOIA_MODE_VERIFY; + break; + + case 2: + paranoiaMode &= ~(PARANOIA_MODE_SCRATCH|PARANOIA_MODE_REPAIR); + break; + } + + if( neverSkip ) + paranoiaMode |= PARANOIA_MODE_NEVERSKIP; + + data->paranoiaModeSet( paranoiaMode ); + } + + // high-level api + K3bDevice::Device* device; + K3bDevice::Toc toc; + long currentSector; + long startSector; + long lastSector; + int status; + unsigned int currentTrack; + int paranoiaLevel; + bool neverSkip; + int maxRetries; + + K3bCdparanoiaLibData* data; +}; + + +K3bCdparanoiaLib::K3bCdparanoiaLib() +{ + d = new Private(); + s_counter++; +} + + +K3bCdparanoiaLib::~K3bCdparanoiaLib() +{ + delete d; + s_counter--; + if( s_counter == 0 ) { + K3bCdparanoiaLibData::freeAll(); + + // cleanup the dynamically loaded lib + dlclose( s_libInterface ); + dlclose( s_libParanoia ); + s_libInterface = 0; + s_libParanoia = 0; + } +} + + +bool K3bCdparanoiaLib::load() +{ + cdda_cdda_identify = (cdrom_drive* (*) (const char*, int, char**))dlsym( s_libInterface, CDDA_IDENTIFY ); + cdda_cdda_open = (int (*) (cdrom_drive*))dlsym( s_libInterface, CDDA_OPEN ); + cdda_cdda_close = (int (*) (cdrom_drive*))dlsym( s_libInterface, CDDA_CLOSE ); + cdda_cdda_track_firstsector = (long (*)(cdrom_drive*, int))dlsym( s_libInterface, CDDA_TRACK_FIRSTSECTOR ); + cdda_cdda_track_lastsector = (long (*)(cdrom_drive*, int))dlsym( s_libInterface, CDDA_TRACK_LASTSECTOR ); + cdda_cdda_verbose_set = (void (*)(cdrom_drive *d,int err_action, int mes_action))dlsym( s_libInterface, CDDA_VERBOSE_SET ); + cdda_cdda_disc_firstsector = (long (*)(cdrom_drive *d))dlsym( s_libInterface, CDDA_DISC_FIRSTSECTOR ); + + cdda_paranoia_init = (cdrom_paranoia* (*)(cdrom_drive*))dlsym( s_libParanoia, PARANOIA_INIT ); + cdda_paranoia_free = (void (*)(cdrom_paranoia *p))dlsym( s_libParanoia, PARANOIA_FREE ); + cdda_paranoia_modeset = (void (*)(cdrom_paranoia *p, int mode))dlsym( s_libParanoia, PARANOIA_MODESET ); + cdda_paranoia_read_limited = (int16_t* (*)(cdrom_paranoia *p, void(*callback)(long,int), int))dlsym( s_libParanoia, PARANOIA_READ_LIMITED ); + cdda_paranoia_seek = (long (*)(cdrom_paranoia *p,long seek,int mode))dlsym( s_libParanoia, PARANOIA_SEEK ); + + // check if all symbols could be resoled + if( cdda_cdda_identify == 0 ) { + kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'cdda_identify'" << endl; + return false; + } + if( cdda_cdda_open == 0 ) { + kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'cdda_open'" << endl; + return false; + } + if( cdda_cdda_close == 0 ) { + kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'cdda_close'" << endl; + return false; + } + if( cdda_cdda_track_firstsector == 0 ) { + kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'cdda_track_firstsector'" << endl; + return false; + } + if( cdda_cdda_track_lastsector == 0 ) { + kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'cdda_track_lastsector'" << endl; + return false; + } + if( cdda_cdda_disc_firstsector == 0 ) { + kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'cdda_disc_firstsector'" << endl; + return false; + } + if( cdda_cdda_verbose_set == 0 ) { + kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'cdda_verbose_set'" << endl; + return false; + } + + if( cdda_paranoia_init == 0 ) { + kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'paranoia_init'" << endl; + return false; + } + if( cdda_paranoia_free == 0 ) { + kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'paranoia_free'" << endl; + return false; + } + if( cdda_paranoia_modeset == 0 ) { + kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'paranoia_modeset'" << endl; + return false; + } + if( cdda_paranoia_read_limited == 0 ) { + kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'paranoia_read_limited'" << endl; + return false; + } + if( cdda_paranoia_seek == 0 ) { + kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'paranoia_seek'" << endl; + return false; + } + + return true; +} + + + +K3bCdparanoiaLib* K3bCdparanoiaLib::create() +{ + // check if libcdda_interface is avalilable + if( s_libInterface == 0 ) { + s_haveLibCdio = false; + + s_libInterface = dlopen( "libcdda_interface.so.0", RTLD_NOW|RTLD_GLOBAL ); + + // try the redhat & Co. location + if( s_libInterface == 0 ) + s_libInterface = dlopen( "cdda/libcdda_interface.so.0", RTLD_NOW|RTLD_GLOBAL ); + + // try the new cdio lib + if( s_libInterface == 0 ) { + s_libInterface = dlopen( "libcdio_cdda.so", RTLD_NOW|RTLD_GLOBAL ); + s_haveLibCdio = true; + } + + if( s_libInterface == 0 ) { + kdDebug() << "(K3bCdparanoiaLib) Error while loading libcdda_interface. " << endl; + return 0; + } + + + s_libParanoia = dlopen( "libcdda_paranoia.so.0", RTLD_NOW ); + + // try the redhat & Co. location + if( s_libParanoia == 0 ) + s_libParanoia = dlopen( "cdda/libcdda_paranoia.so.0", RTLD_NOW ); + + // try the new cdio lib + if( s_haveLibCdio && s_libParanoia == 0 ) + s_libParanoia = dlopen( "libcdio_paranoia.so.0", RTLD_NOW ); + + if( s_libParanoia == 0 ) { + kdDebug() << "(K3bCdparanoiaLib) Error while loading libcdda_paranoia. " << endl; + dlclose( s_libInterface ); + s_libInterface = 0; + return 0; + } + } + + K3bCdparanoiaLib* lib = new K3bCdparanoiaLib(); + if( !lib->load() ) { + kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve all symbols!" << endl; + delete lib; + return 0; + } + return lib; +} + + +bool K3bCdparanoiaLib::initParanoia( K3bDevice::Device* dev, const K3bDevice::Toc& toc ) +{ + if( !dev ) { + kdError() << "(K3bCdparanoiaLib::initParanoia) dev = 0!" << endl; + return false; + } + + close(); + + d->device = dev; + d->toc = toc; + if( d->toc.isEmpty() ) { + kdDebug() << "(K3bCdparanoiaLib) empty toc." << endl; + cleanup(); + return false; + } + + if( d->toc.contentType() == K3bDevice::DATA ) { + kdDebug() << "(K3bCdparanoiaLib) No audio tracks found." << endl; + cleanup(); + return false; + } + + // + // Get the appropriate data instance for this device + // + d->data = K3bCdparanoiaLibData::data( dev ); + + if( d->data->paranoiaInit() ) { + d->startSector = d->currentSector = d->lastSector = 0; + + return true; + } + else { + cleanup(); + return false; + } +} + + +bool K3bCdparanoiaLib::initParanoia( K3bDevice::Device* dev ) +{ + return initParanoia( dev, dev->readToc() ); +} + + +void K3bCdparanoiaLib::close() +{ + cleanup(); +} + + +void K3bCdparanoiaLib::cleanup() +{ + if( d->data ) + d->data->paranoiaFree(); + d->device = 0; + d->currentSector = 0; +} + + +bool K3bCdparanoiaLib::initReading() +{ + if( d->device ) { + // find first audio track + K3bDevice::Toc::const_iterator trackIt = d->toc.begin(); + while( (*trackIt).type() != K3bDevice::Track::AUDIO ) { + ++trackIt; + } + + long start = (*trackIt).firstSector().lba(); + + // find last audio track + while( trackIt != d->toc.end() && (*trackIt).type() == K3bDevice::Track::AUDIO ) + ++trackIt; + --trackIt; + + long end = (*trackIt).lastSector().lba(); + + return initReading( start, end ); + } + else { + kdDebug() << "(K3bCdparanoiaLib) initReading without initParanoia." << endl; + return false; + } +} + + +bool K3bCdparanoiaLib::initReading( unsigned int track ) +{ + if( d->device ) { + if( track <= d->toc.count() ) { + const K3bDevice::Track& k3bTrack = d->toc[track-1]; + if( k3bTrack.type() == K3bDevice::Track::AUDIO ) { + return initReading( k3bTrack.firstSector().lba(), k3bTrack.lastSector().lba() ); + } + else { + kdDebug() << "(K3bCdparanoiaLib) Track " << track << " no audio track." << endl; + return false; + } + } + else { + kdDebug() << "(K3bCdparanoiaLib) Track " << track << " too high." << endl; + return false; + } + } + else { + kdDebug() << "(K3bCdparanoiaLib) initReading without initParanoia." << endl; + return false; + } +} + + +bool K3bCdparanoiaLib::initReading( long start, long end ) +{ + kdDebug() << "(K3bCdparanoiaLib) initReading( " << start << ", " << end << " )" << endl; + + if( d->device ) { + if( d->toc.firstSector().lba() <= start && + d->toc.lastSector().lba() >= end ) { + d->startSector = d->currentSector = start; + d->lastSector = end; + + // determine track number + d->currentTrack = 1; + while( d->toc[d->currentTrack-1].lastSector() < start ) + d->currentTrack++; + + // let the paranoia stuff point to the startSector + d->data->paranoiaSeek( start, SEEK_SET ); + return true; + } + else { + kdDebug() << "(K3bCdparanoiaLib) " << start << " and " << end << " out of range." << endl; + return false; + } + } + else { + kdDebug() << "(K3bCdparanoiaLib) initReading without initParanoia." << endl; + return false; + } +} + + +char* K3bCdparanoiaLib::read( int* statusCode, unsigned int* track, bool littleEndian ) +{ + if( d->currentSector > d->lastSector ) { + kdDebug() << "(K3bCdparanoiaLib) finished ripping. read " + << (d->currentSector - d->startSector) << " sectors." << endl + << " current sector: " << d->currentSector << endl; + d->status = S_OK; + if( statusCode ) + *statusCode = d->status; + return 0; + } + + if( d->currentSector != d->data->sector() ) { + kdDebug() << "(K3bCdparanoiaLib) need to seek before read. Looks as if we are reusing the paranoia instance." << endl; + if( !d->data->paranoiaSeek( d->currentSector, SEEK_SET ) ) + return 0; + } + + // + // The paranoia data could have been used by someone else before + // and setting the paranoia mode is fast + // + d->updateParanoiaMode(); + + Q_INT16* data = d->data->paranoiaRead( paranoiaCallback, d->maxRetries ); + + char* charData = reinterpret_cast<char*>(data); + +#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN + if( littleEndian ) { +#else + if( !littleEndian ) { +#endif + for( int i = 0; i < CD_FRAMESIZE_RAW-1; i+=2 ) { + char b = charData[i]; + charData[i] = charData[i+1]; + charData[i+1] = b; + } + } + + + if( data ) + d->status = S_OK; + else + d->status = S_ERROR; // We may skip this sector if we'd like... + + if( statusCode ) + *statusCode = d->status; + + if( track ) + *track = d->currentTrack; + + d->currentSector++; + + if( d->toc[d->currentTrack-1].lastSector() < d->currentSector ) + d->currentTrack++; + + return charData; +} + + +int K3bCdparanoiaLib::status() const +{ + return d->status; +} + + +const K3bDevice::Toc& K3bCdparanoiaLib::toc() const +{ + return d->toc; +} + + +long K3bCdparanoiaLib::rippedDataLength() const +{ + return d->lastSector - d->startSector + 1; +} + + +void K3bCdparanoiaLib::setParanoiaMode( int m ) +{ + d->paranoiaLevel = m; +} + + +void K3bCdparanoiaLib::setNeverSkip( bool b ) +{ + d->neverSkip = b; +} + + +void K3bCdparanoiaLib::setMaxRetries( int r ) +{ + d->maxRetries = r; +} diff --git a/libk3b/tools/k3bcdparanoialib.h b/libk3b/tools/k3bcdparanoialib.h new file mode 100644 index 0000000..70504de --- /dev/null +++ b/libk3b/tools/k3bcdparanoialib.h @@ -0,0 +1,161 @@ +/* + * + * $Id: k3bcdparanoialib.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_CDPARANOIA_LIB_H +#define K3B_CDPARANOIA_LIB_H + +// from cdda_interface.h +#define CD_FRAMESIZE_RAW 2352 + + +#include <qstring.h> + +#include <sys/types.h> +#include "k3b_export.h" + +namespace K3bDevice { + class Device; + class Toc; +} + + +/** + * K3bCdparanoiaLib is a convenience wrapper around libcdda_interface + * and libcdda_paranoia. + * + * It uses four paranoia levels 0-3 which can be set via setParanoiaMode + * and are used the same way as in cdrdao: + * \li 0: No checking, data is copied directly from the drive. + * \li 1: Perform overlapped reading to avoid jitter. + * \li 2: Like 1 but with additional checks of the read audio data. + * \li 3: Like 2 but with additional scratch detection and repair. + * + * K3bCdparanoiaLib is based on a shared data approach which makes sure + * that each device can only be opened once. This is necessary since + * libcdda_interface opens the device exclusively on most distributions. + * + * However, it is perfectly possible to have two instances of K3bCdparanoiaLib + * accessing the same device at the same time. K3bCdparanoiaLib will take care + * of the syncing and seeking issues automatically. + * + * K3bCdparanoiaLib is thread-safe. + * + * Usage: + * <pre> + * K3bCdparanoiaLib lib; + * lib.initParanoia( mydevice ); + * lib.initReading( tracknumber ); + * while( char* data = lib.read() ) + * dosomethingcoolwithdata( data ); + * </pre> + */ +class LIBK3B_EXPORT K3bCdparanoiaLib +{ + public: + ~K3bCdparanoiaLib(); + + /** default: 1 */ + void setParanoiaMode( int ); + void setNeverSkip( bool b ); + + /** default: 5 */ + void setMaxRetries( int ); + + /** + * This will read the Toc and initialize some stuff. + * It will also call paranoiaInit( const QString& ) + */ + bool initParanoia( K3bDevice::Device* dev ); + + /** + * Use for faster initialization without reading the toc + */ + bool initParanoia( K3bDevice::Device* dev, const K3bDevice::Toc& ); + + void close(); + + /** + * Call this after initParanoia to set the data to rip. + * + * Rip all audio tracks. + */ + bool initReading(); + + /** + * Call this after initParanoia to set the data to rip. + */ + bool initReading( unsigned int track ); + + /** + * Call this after initParanoia to set the data to rip. + */ + bool initReading( long startSector, long endSector ); + + /** + * Read data. + * \param statusCode If not 0 will be set. + * \param track the tracknumer the data belongs to + * + * This method takes care of swapping the byte-order depending on the + * machine type. + * + * \return The read sector data or 0 if all data within the specified range + * has been read or an error has occured. + */ + char* read( int* statusCode = 0, unsigned int* track = 0, bool littleEndian = true ); + + /** + * This only is valid after a call to read() + */ + int status() const; + + enum Status { + S_OK, + S_ERROR + // to be extended with Jitter and stuff... + }; + + /** + * Only valid after a call to initParanoia() + */ + const K3bDevice::Toc& toc() const; + + long rippedDataLength() const; + + /** + * returns 0 if the cdparanoialib could not + * be found on the system. + * Otherwise you have to take care of + * deleting. + */ + static K3bCdparanoiaLib* create(); + + private: + void cleanup(); + + K3bCdparanoiaLib(); + bool load(); + + class Private; + Private* d; + + static void* s_libInterface; + static void* s_libParanoia; + static int s_counter; +}; + + +#endif diff --git a/libk3b/tools/k3bcdtextvalidator.cpp b/libk3b/tools/k3bcdtextvalidator.cpp new file mode 100644 index 0000000..8b3c97a --- /dev/null +++ b/libk3b/tools/k3bcdtextvalidator.cpp @@ -0,0 +1,42 @@ +/* + * + * $Id: k3bcdtextvalidator.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bcdtextvalidator.h" + +K3bCdTextValidator::K3bCdTextValidator(QObject *parent, const char *name) + : K3bLatin1Validator(parent, name) +{ +} + + +K3bCdTextValidator::~K3bCdTextValidator() +{ +} + + +QValidator::State K3bCdTextValidator::validate( QString& input, int& pos ) const +{ + if( input.length() > 160 ) + return Invalid; + + // forbid some characters that might introduce problems + for( unsigned int i = 0; i < input.length(); ++i ) { + if( input[i] == '/' || input[i] == '"' || input[i] == '\\' ) + return Invalid; + } + + return K3bLatin1Validator::validate( input, pos ); +} diff --git a/libk3b/tools/k3bcdtextvalidator.h b/libk3b/tools/k3bcdtextvalidator.h new file mode 100644 index 0000000..48ada05 --- /dev/null +++ b/libk3b/tools/k3bcdtextvalidator.h @@ -0,0 +1,33 @@ +/* + * + * $Id: k3bcdtextvalidator.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 _K3BCDTEXTVALIDATOR_H_ +#define _K3BCDTEXTVALIDATOR_H_ + + +#include <k3bvalidators.h> +#include "k3b_export.h" + +class LIBK3B_EXPORT K3bCdTextValidator : public K3bLatin1Validator +{ + public: + K3bCdTextValidator(QObject *parent = 0, const char *name = 0); + ~K3bCdTextValidator(); + + State validate( QString& input, int& pos ) const; +}; + +#endif diff --git a/libk3b/tools/k3bchecksumpipe.cpp b/libk3b/tools/k3bchecksumpipe.cpp new file mode 100644 index 0000000..21d308a --- /dev/null +++ b/libk3b/tools/k3bchecksumpipe.cpp @@ -0,0 +1,99 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bchecksumpipe.h" + +#include <kmdcodec.h> +#include <kdebug.h> + +#include <unistd.h> + + +class K3bChecksumPipe::Private +{ +public: + Private() + : checksumType(MD5) { + } + + void update( const char* in, int len ) { + switch( checksumType ) { + case MD5: + md5.update( in, len ); + break; + } + } + + void reset() { + switch( checksumType ) { + case MD5: + md5.reset(); + break; + } + } + + int checksumType; + + KMD5 md5; +}; + + +K3bChecksumPipe::K3bChecksumPipe() + : K3bActivePipe() +{ + d = new Private(); +} + + +K3bChecksumPipe::~K3bChecksumPipe() +{ + delete d; +} + + +bool K3bChecksumPipe::open( bool closeWhenDone ) +{ + return open( MD5, closeWhenDone ); +} + + +bool K3bChecksumPipe::open( Type type, bool closeWhenDone ) +{ + if( K3bActivePipe::open( closeWhenDone ) ) { + d->reset(); + d->checksumType = type; + return true; + } + else + return false; +} + + +QCString K3bChecksumPipe::checksum() const +{ + switch( d->checksumType ) { + case MD5: + return d->md5.hexDigest(); + } + + return QCString(); +} + + +int K3bChecksumPipe::write( char* data, int max ) +{ + d->update( data, max ); + return K3bActivePipe::write( data, max ); +} diff --git a/libk3b/tools/k3bchecksumpipe.h b/libk3b/tools/k3bchecksumpipe.h new file mode 100644 index 0000000..88adc74 --- /dev/null +++ b/libk3b/tools/k3bchecksumpipe.h @@ -0,0 +1,66 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_CHECKSUM_PIPE_H_ +#define _K3B_CHECKSUM_PIPE_H_ + +#include <k3bactivepipe.h> + +#include <k3b_export.h> + + +/** + * The checksum pipe calculates the checksum of the data + * passed through it. + */ +class LIBK3B_EXPORT K3bChecksumPipe : public K3bActivePipe +{ + public: + K3bChecksumPipe(); + ~K3bChecksumPipe(); + + enum Type { + MD5 + }; + + /** + * \reimplemented + * Defaults to MD5 checksum + */ + bool open( bool closeWhenDone = false ); + + /** + * Opens the pipe and thus starts the + * checksum calculation + * + * \param closeWhenDone If true the pipes will be closed + * once all data has been read. + */ + bool open( Type type, bool closeWhenDone = false ); + + /** + * Get the calculated checksum + */ + QCString checksum() const; + + protected: + int write( char* data, int max ); + + private: + class Private; + Private* d; +}; + +#endif diff --git a/libk3b/tools/k3bcutcombobox.cpp b/libk3b/tools/k3bcutcombobox.cpp new file mode 100644 index 0000000..d4f516b --- /dev/null +++ b/libk3b/tools/k3bcutcombobox.cpp @@ -0,0 +1,230 @@ +/* + * + * $Id: k3bcutcombobox.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bcutcombobox.h" + +#include <k3bstringutils.h> + +#include <qfontmetrics.h> +#include <qevent.h> +#include <qstringlist.h> +#include <qrect.h> +#include <qsize.h> +#include <qpixmap.h> +#include <qstyle.h> +#include <qsizepolicy.h> + + +class K3bCutComboBox::Private +{ +public: + Private() { + method = CUT; + } + + QStringList originalItems; + + int method; + int width; +}; + + +K3bCutComboBox::K3bCutComboBox( QWidget* parent, const char* name ) + : KComboBox( parent, name ) +{ + d = new Private(); + // setSizePolicy( QSizePolicy::Maximum, sizePolicy().horData(), sizePolicy().hasHeightForWidth() ); +} + + +K3bCutComboBox::K3bCutComboBox( int method, QWidget* parent, const char* name ) + : KComboBox( parent, name ) +{ + d = new Private(); + d->method = method; +} + + +K3bCutComboBox::~K3bCutComboBox() +{ + delete d; +} + + +void K3bCutComboBox::setMethod( int m ) +{ + d->method = m; + cutText(); +} + + +QSize K3bCutComboBox::sizeHint() const +{ +// QSize s(KComboBox::sizeHint()); + +// for( int i = 0; i < count(); i++ ) { +// int w = fontMetrics().width(d->originalItems[i]) + +// ( d->pixmaps[i].isNull() ? 0 : d->pixmaps[i].width() + 4); +// if( w > s.width() ) +// s.setWidth( w ); +// } + + return KComboBox::sizeHint(); +} + +QSize K3bCutComboBox::minimumSizeHint() const +{ + return KComboBox::minimumSizeHint(); +} + + +void K3bCutComboBox::setCurrentText( const QString& s ) +{ + int i; + for( i = 0; i < count(); i++ ) + if ( d->originalItems[i] == s ) + break; + if ( i < count() ) { + setCurrentItem(i); + } + else if( !d->originalItems.isEmpty() ) { + d->originalItems[currentItem()] = s; + cutText(); + } +} + + +void K3bCutComboBox::insertStringList( const QStringList&, int ) +{ + // FIXME +} + + +void K3bCutComboBox::insertStrList( const QStrList&, int ) +{ + // FIXME +} + +void K3bCutComboBox::insertStrList( const QStrList*, int ) +{ + // FIXME +} + +void K3bCutComboBox::insertStrList( const char**, int, int) +{ + // FIXME +} + +void K3bCutComboBox::insertItem( const QString& text, int index ) +{ + insertItem( QPixmap(), text, index ); +} + +void K3bCutComboBox::insertItem( const QPixmap& pix, int i ) +{ + insertItem( pix, "", i ); +} + +void K3bCutComboBox::insertItem( const QPixmap& pixmap, const QString& text, int index ) +{ + if( index != -1 ) + d->originalItems.insert( d->originalItems.at(index), text ); + else + d->originalItems.append( text ); + + if( !pixmap.isNull() ) + KComboBox::insertItem( pixmap, "xx", index ); + else + KComboBox::insertItem( "xx", index ); + + cutText(); +} + +void K3bCutComboBox::removeItem( int i ) +{ + d->originalItems.erase( d->originalItems.at(i) ); + KComboBox::removeItem( i ); +} + +void K3bCutComboBox::changeItem( const QString& s, int i ) +{ + d->originalItems[i] = s; + cutText(); +} + +void K3bCutComboBox::changeItem( const QPixmap& pix, const QString& s, int i ) +{ + KComboBox::changeItem( pix, i ); + changeItem( s, i ); +} + + +QString K3bCutComboBox::text( int i ) const +{ + if( i < (int)d->originalItems.count() ) + return d->originalItems[i]; + else + return QString::null; +} + + +QString K3bCutComboBox::currentText() const +{ + if( currentItem() < (int)d->originalItems.count() ) + return d->originalItems[currentItem()]; + else + return QString::null; +} + + +void K3bCutComboBox::clear() +{ + KComboBox::clear(); + d->originalItems.clear(); +} + +void K3bCutComboBox::resizeEvent( QResizeEvent* e ) +{ + cutText(); + + KComboBox::resizeEvent(e); +} + + +void K3bCutComboBox::cutText() +{ + d->width = QStyle::visualRect( style().querySubControlMetrics(QStyle::CC_ComboBox, this, + QStyle::SC_ComboBoxEditField), this ).width(); + + for( int i = 0; i < (int)d->originalItems.count(); ++i ) { + int w = d->width; + if ( pixmap(i) && !pixmap(i)->isNull() ) + w -= ( pixmap(i)->width() + 4 ); + + QString text; + if( d->method == SQUEEZE ) + text = K3b::squeezeTextToWidth( fontMetrics(), d->originalItems[i], w ); + else + text = K3b::cutToWidth( fontMetrics(), d->originalItems[i], w ); + + // now insert the cut text + if( pixmap(i) ) + KComboBox::changeItem( *pixmap(i), text, i ); + else + KComboBox::changeItem( text, i ); + } +} + +#include "k3bcutcombobox.moc" diff --git a/libk3b/tools/k3bcutcombobox.h b/libk3b/tools/k3bcutcombobox.h new file mode 100644 index 0000000..1c35e78 --- /dev/null +++ b/libk3b/tools/k3bcutcombobox.h @@ -0,0 +1,92 @@ +/* + * + * $Id: k3bcutcombobox.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_CUT_COMBOBOX_H_ +#define _K3B_CUT_COMBOBOX_H_ + +#include <kcombobox.h> +#include "k3b_export.h" +class QResizeEvent; + + +/** + * Cuts it's text. + * Since it rebuilds the complete list of strings every time + * a new string is added or one gets removed it is not a good + * idea to use this for dynamic lists. + * + * Be aware that currently only insertItem works. + * none of the insertStrList or insertStringList methods are implemeted + * yet and also the removeItem methos does not work. + */ +class LIBK3B_EXPORT K3bCutComboBox : public KComboBox +{ + Q_OBJECT + + public: + K3bCutComboBox( QWidget* parent = 0, const char* name = 0 ); + K3bCutComboBox( int method, QWidget* parent = 0, const char* name = 0 ); + virtual ~K3bCutComboBox(); + + enum Method { + CUT, + SQUEEZE + }; + + /** + * The method to shorten the text + * defaut: CUT + */ + void setMethod( int ); + + /** reimplemeted */ + QSize sizeHint() const; + + /** reimplemeted */ + QSize minimumSizeHint() const; + + /** reimplemeted */ + virtual void setCurrentText( const QString& ); + + void insertStringList( const QStringList &, int index=-1 ); + void insertStrList( const QStrList &, int index=-1 ); + void insertStrList( const QStrList *, int index=-1 ); + void insertStrList( const char **, int numStrings=-1, int index=-1); + + void insertItem( const QString &text, int index=-1 ); + void insertItem( const QPixmap &pixmap, int index=-1 ); + void insertItem( const QPixmap &pixmap, const QString &text, int index=-1 ); + + void removeItem( int index ); + + void changeItem( const QString &text, int index ); + void changeItem( const QPixmap &pixmap, const QString &text, int index ); + + QString text( int ) const; + QString currentText() const; + + void clear(); + + protected: + void resizeEvent( QResizeEvent* e ); + void cutText(); + + private: + class Private; + Private* d; +}; + +#endif diff --git a/libk3b/tools/k3bdevicecombobox.cpp b/libk3b/tools/k3bdevicecombobox.cpp new file mode 100644 index 0000000..165b59d --- /dev/null +++ b/libk3b/tools/k3bdevicecombobox.cpp @@ -0,0 +1,174 @@ +/* + * + * $Id: k3bdevicecombobox.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bdevicecombobox.h" +#include <k3bdevice.h> +#include <k3bdevicemanager.h> +#include <k3bcore.h> + +#include <klocale.h> + +#include <qmap.h> +#include <qptrvector.h> + + +class K3bDeviceComboBox::Private +{ +public: + QMap<QString, int> deviceIndexMap; + QPtrVector<K3bDevice::Device> devices; +}; + + +K3bDeviceComboBox::K3bDeviceComboBox( QWidget* parent, const char* name ) + : KComboBox( parent, name ) +{ + d = new Private(); + connect( this, SIGNAL(activated(int)), + this, SLOT(slotActivated(int)) ); + connect( k3bcore->deviceManager(), SIGNAL(changed(K3bDevice::DeviceManager*)), + this, SLOT(slotDeviceManagerChanged(K3bDevice::DeviceManager*)) ); +} + + +K3bDeviceComboBox::~K3bDeviceComboBox() +{ + delete d; +} + +K3bDevice::Device* K3bDeviceComboBox::selectedDevice() const +{ + if ( count() > 0 ) + return d->devices[currentItem()]; + else + return 0; +} + + +void K3bDeviceComboBox::addDevice( K3bDevice::Device* dev ) +{ + int devIndex = -2; + bool addDevice = false; + for( int i = 0; i < count(); ++i ) { + if( dev->vendor() == d->devices[i]->vendor() && + dev->description() == d->devices[i]->description() ) { + addDevice = true; + if( devIndex < -1 ) // when devIndex == -1 we already found two devices. + devIndex = i; + else + devIndex = -1; // when there are already two or more equal devices they have already been updated + } + } + + // update the existing device item + if( devIndex >= 0 ) { + changeItem( d->devices[devIndex]->vendor() + " " + + d->devices[devIndex]->description() + + " (" + d->devices[devIndex]->blockDeviceName() + ")", + devIndex ); + d->deviceIndexMap[d->devices[devIndex]->devicename()] = devIndex; + } + + // add the new device item + if( addDevice ) + insertItem( dev->vendor() + " " + dev->description() + " (" + dev->blockDeviceName() + ")" ); + else + insertItem( dev->vendor() + " " + dev->description() ); + + d->deviceIndexMap[dev->devicename()] = count()-1; + d->devices.resize( count() ); + d->devices.insert(count()-1, dev); +} + + +void K3bDeviceComboBox::removeDevice( K3bDevice::Device* dev ) +{ + if( dev ) { + if( d->deviceIndexMap.contains(dev->devicename()) ) { + // let's make it easy and recreate the whole list + K3bDevice::Device* selDev = selectedDevice(); + QPtrList<K3bDevice::Device> devices; + for( unsigned int i = 0; i < d->devices.size(); ++i ) + devices.append( d->devices[i] ); + + clear(); + + devices.removeRef( dev ); + + addDevices( devices ); + setSelectedDevice( selDev ); + } + } +} + + +void K3bDeviceComboBox::addDevices( const QPtrList<K3bDevice::Device>& list ) +{ + for( QPtrListIterator<K3bDevice::Device> it( list ); + it.current(); ++it ) + addDevice( it.current() ); +} + + +void K3bDeviceComboBox::refreshDevices( const QPtrList<K3bDevice::Device>& list ) +{ + K3bDevice::Device* selDev = selectedDevice(); + clear(); + if( !list.containsRef( selDev ) ) + selDev = 0; + addDevices( list ); + setSelectedDevice( selDev ); +} + + +void K3bDeviceComboBox::setSelectedDevice( K3bDevice::Device* dev ) +{ + if( dev ) { + if( d->deviceIndexMap.contains(dev->devicename()) ) { + setCurrentItem( d->deviceIndexMap[dev->devicename()] ); + emit selectionChanged( dev ); + } + } +} + + +void K3bDeviceComboBox::clear() +{ + d->deviceIndexMap.clear(); + d->devices.clear(); + KComboBox::clear(); +} + + +void K3bDeviceComboBox::slotActivated( int i ) +{ + emit selectionChanged( d->devices[i] ); +} + + +void K3bDeviceComboBox::slotDeviceManagerChanged( K3bDevice::DeviceManager* dm ) +{ + unsigned int i = 0; + while( i < d->devices.size() ) { + if( !dm->allDevices().containsRef( d->devices[i] ) ) { + removeDevice( d->devices[i] ); + i = 0; + } + else + ++i; + } +} + +#include "k3bdevicecombobox.moc" diff --git a/libk3b/tools/k3bdevicecombobox.h b/libk3b/tools/k3bdevicecombobox.h new file mode 100644 index 0000000..5a9cb85 --- /dev/null +++ b/libk3b/tools/k3bdevicecombobox.h @@ -0,0 +1,67 @@ +/* + * + * $Id: k3bdevicecombobox.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_DEVICE_COMBO_BOX_H_ +#define _K3B_DEVICE_COMBO_BOX_H_ + +#include <kcombobox.h> +#include "k3b_export.h" + +namespace K3bDevice { + class Device; + class DeviceManager; +} + + +/** + * A combobox to select a K3b device. + * + * It automatically removes devices that are removed from the system. + */ +class LIBK3B_EXPORT K3bDeviceComboBox : public KComboBox +{ + Q_OBJECT + + public: + K3bDeviceComboBox( QWidget* parent = 0, const char* name = 0 ); + ~K3bDeviceComboBox(); + + K3bDevice::Device* selectedDevice() const; + + signals: + void selectionChanged( K3bDevice::Device* ); + + public slots: + void addDevice( K3bDevice::Device* ); + void addDevices( const QPtrList<K3bDevice::Device>& ); + /** + * Clears the device combo and tries to keep the current selection + */ + void refreshDevices( const QPtrList<K3bDevice::Device>& ); + void removeDevice( K3bDevice::Device* ); + void setSelectedDevice( K3bDevice::Device* ); + void clear(); + + private slots: + void slotActivated( int ); + void slotDeviceManagerChanged( K3bDevice::DeviceManager* dm ); + + private: + class Private; + Private* d; +}; + +#endif diff --git a/libk3b/tools/k3bdevicehandler.cpp b/libk3b/tools/k3bdevicehandler.cpp new file mode 100644 index 0000000..c77f1e6 --- /dev/null +++ b/libk3b/tools/k3bdevicehandler.cpp @@ -0,0 +1,332 @@ +/* + * + * $Id: k3bdevicehandler.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bdevicehandler.h" +#include <k3bprogressinfoevent.h> +#include <k3bthread.h> +#include <k3bdevice.h> +#include <k3bcdtext.h> + + + +class K3bDevice::DeviceHandler::DeviceHandlerThread : public K3bThread +{ +public: + DeviceHandlerThread() + : K3bThread(), + dev(0) { + } + + + void run() { + success = false; + m_bCanceled = false; + + // clear data + toc.clear(); + ngInfo = DiskInfo(); + cdText.clear(); + cdTextRaw.resize(0); + + if( dev ) { + success = dev->open(); + if( !m_bCanceled && command & DISKINFO ) { + ngInfo = dev->diskInfo(); + if( !m_bCanceled && !ngInfo.empty() ) { + toc = dev->readToc(); + if( toc.contentType() == AUDIO || + toc.contentType() == MIXED ) + cdText = dev->readCdText(); + } + } + + if( !m_bCanceled && command & (NG_DISKINFO| + DISKSIZE| + REMAININGSIZE| + NUMSESSIONS) ) { + ngInfo = dev->diskInfo(); + } + + if( !m_bCanceled && command & (TOC|TOCTYPE) ) { + toc = dev->readToc(); + } + + if( !m_bCanceled && command & CD_TEXT ) { + cdText = dev->readCdText(); + success = (success && !cdText.isEmpty()); + } + + if( !m_bCanceled && command & CD_TEXT_RAW ) { + unsigned char* data = 0; + unsigned int dataLen = 0; + if( dev->readTocPmaAtip( &data, dataLen, 5, false, 0 ) ) { + // we need more than the header and a multiple of 18 bytes to have valid CD-TEXT + if( dataLen > 4 && dataLen%18 == 4 ) { + cdTextRaw.assign( reinterpret_cast<char*>(data), dataLen ); + } + else { + kdDebug() << "(K3bDevice::DeviceHandler) invalid CD-TEXT length: " << dataLen << endl; + delete [] data; + success = false; + } + } + else + success = false; + } + + if( !m_bCanceled && command & BLOCK ) + success = (success && dev->block( true )); + + if( !m_bCanceled && command & UNBLOCK ) + success = (success && dev->block( false )); + + // + // It is important that eject is performed before load + // since the RELOAD command is a combination of both + // + + if( !m_bCanceled && command & EJECT ) + success = (success && dev->eject()); + + if( !m_bCanceled && command & LOAD ) + success = (success && dev->load()); + + if( !m_bCanceled && command & BUFFER_CAPACITY ) + success = dev->readBufferCapacity( bufferCapacity, availableBufferCapacity ); + + dev->close(); + } + + // + // This thread only gets cancelled if a new request was started. + // So we don't emit the finished signal for this (old) request. + // + if( !m_bCanceled ) + emitFinished(success); + } + + void cancel() { + m_bCanceled = true; + } + + + bool success; + int errorCode; + int command; + DiskInfo ngInfo; + Toc toc; + CdText cdText; + QByteArray cdTextRaw; + long long bufferCapacity; + long long availableBufferCapacity; + Device* dev; + +private: + bool m_bCanceled; +}; + + +K3bDevice::DeviceHandler::DeviceHandler( Device* dev, QObject* parent, const char* name ) + : K3bThreadJob( 0, parent, name ), + m_selfDelete(false) +{ + m_thread = new DeviceHandlerThread(); + m_thread->dev = dev; + setThread( m_thread ); +} + + +K3bDevice::DeviceHandler::DeviceHandler( QObject* parent, const char* name ) + : K3bThreadJob( 0, parent, name ), + m_selfDelete(false) +{ + m_thread = new DeviceHandlerThread(); + setThread( m_thread ); +} + + +K3bDevice::DeviceHandler::DeviceHandler( int command, Device* dev, const char* name ) + : K3bThreadJob( 0, 0, name ), + m_selfDelete(true) +{ + m_thread = new DeviceHandlerThread(); + setThread( m_thread ); + m_thread->dev = dev; + sendCommand(command); +} + +K3bDevice::DeviceHandler::~DeviceHandler() +{ + delete m_thread; +} + + +int K3bDevice::DeviceHandler::errorCode() const +{ + return m_thread->errorCode; +} + +bool K3bDevice::DeviceHandler::success() const +{ + return m_thread->success; +} + + +const K3bDevice::DiskInfo& K3bDevice::DeviceHandler::diskInfo() const +{ + return m_thread->ngInfo; +} + + +const K3bDevice::Toc& K3bDevice::DeviceHandler::toc() const +{ + return m_thread->toc; +} + +const K3bDevice::CdText& K3bDevice::DeviceHandler::cdText() const +{ + return m_thread->cdText; +} + + +const QByteArray& K3bDevice::DeviceHandler::cdTextRaw() const +{ + return m_thread->cdTextRaw; +} + + +K3b::Msf K3bDevice::DeviceHandler::diskSize() const +{ + return m_thread->ngInfo.capacity(); +} + +K3b::Msf K3bDevice::DeviceHandler::remainingSize() const +{ + return m_thread->ngInfo.remainingSize(); +} + +int K3bDevice::DeviceHandler::tocType() const +{ + return m_thread->toc.contentType(); +} + +int K3bDevice::DeviceHandler::numSessions() const +{ + return m_thread->ngInfo.numSessions(); +} + +long long K3bDevice::DeviceHandler::bufferCapacity() const +{ + return m_thread->bufferCapacity; +} + +long long K3bDevice::DeviceHandler::availableBufferCapacity() const +{ + return m_thread->availableBufferCapacity; +} + +void K3bDevice::DeviceHandler::setDevice( Device* dev ) +{ + m_thread->dev = dev; +} + + + +void K3bDevice::DeviceHandler::sendCommand( int command ) +{ + // + // We do not want the finished signal emitted in case the devicehandler was cancelled. This is a special case. + // That's why we do not use K3bThreadJob::start() becasue otherwise we would be registered twice. + // + if( m_thread->running() ) { + kdDebug() << "(K3bDevice::DeviceHandler) thread already running. canceling thread..." << endl; + m_thread->cancel(); + m_thread->wait(); + } + else + jobStarted(); + + kdDebug() << "(K3bDevice::DeviceHandler) starting command: " << command << endl; + + m_thread->command = command; + m_thread->start(); +} + +void K3bDevice::DeviceHandler::getToc() +{ + sendCommand(DeviceHandler::TOC); +} + +void K3bDevice::DeviceHandler::getDiskInfo() +{ + sendCommand(DeviceHandler::DISKINFO); +} + +void K3bDevice::DeviceHandler::getDiskSize() +{ + sendCommand(DeviceHandler::DISKSIZE); +} + +void K3bDevice::DeviceHandler::getRemainingSize() +{ + sendCommand(DeviceHandler::REMAININGSIZE); +} + +void K3bDevice::DeviceHandler::getTocType() +{ + sendCommand(DeviceHandler::TOCTYPE); +} + +void K3bDevice::DeviceHandler::getNumSessions() +{ + sendCommand(DeviceHandler::NUMSESSIONS); +} + + +void K3bDevice::DeviceHandler::block( bool b ) +{ + sendCommand(b ? DeviceHandler::BLOCK : DeviceHandler::UNBLOCK); +} + +void K3bDevice::DeviceHandler::eject() +{ + sendCommand(DeviceHandler::EJECT); +} + +K3bDevice::DeviceHandler* K3bDevice::sendCommand( int command, Device* dev ) +{ + return new DeviceHandler( command, dev, "DeviceHandler" ); +} + +void K3bDevice::DeviceHandler::customEvent( QCustomEvent* e ) +{ + K3bThreadJob::customEvent(e); + + if( (int)e->type() == K3bProgressInfoEvent::Finished ) { + emit finished( this ); + if( m_selfDelete ) { + kdDebug() << "(K3bDevice::DeviceHandler) thread emitted finished. Waiting for thread actually finishing" << endl; + kdDebug() << "(K3bDevice::DeviceHandler) success: " << m_thread->success << endl; + // wait for the thread to finish + m_thread->wait(); + kdDebug() << "(K3bDevice::DeviceHandler) deleting thread." << endl; + deleteLater(); + } + } +} + + +#include "k3bdevicehandler.moc" diff --git a/libk3b/tools/k3bdevicehandler.h b/libk3b/tools/k3bdevicehandler.h new file mode 100644 index 0000000..d5159a0 --- /dev/null +++ b/libk3b/tools/k3bdevicehandler.h @@ -0,0 +1,237 @@ +/* + * + * $Id: k3bdevicehandler.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_DEVICE_HANDLER_H_ +#define _K3B_DEVICE_HANDLER_H_ + +#include <k3bthreadjob.h> +#include "k3bdevice.h" +#include "k3bdiskinfo.h" +#include "k3bmsf.h" +#include "k3bcdtext.h" +#include "k3b_export.h" +#include <qcstring.h> + +class QCustomEvent; + + +namespace K3bDevice +{ + class Device; + + + /** + * The K3bDevice::Devicehandler is a threaded wrapper around K3bDevice::Device. + * It allows async access to the time comsuming blocking K3bDevice::Device methods. + * Since it's a K3bJob it is very easy to handle. Just use one of the methods and + * connect to the finished signal. + * Be aware that all methods only return valid values if the corresponding info has + * been successfuly requested. + * + * Be aware that multiple requests in a row (without waiting for the job to finish) will + * only result in one finished() signal answering the last request. + */ + class LIBK3B_EXPORT DeviceHandler : public K3bThreadJob + { + Q_OBJECT + + public: + DeviceHandler( Device*, QObject* parent = 0, const char* name = 0 ); + DeviceHandler( QObject* parent = 0, const char* name = 0 ); + + /** + * This constructor is used by the global "quick" methods and should not be used + * otherwise except for the same usage. + */ + DeviceHandler( int command, Device*, const char* name = 0 ); + + ~DeviceHandler(); + + const DiskInfo& diskInfo() const; + const Toc& toc() const; + const CdText& cdText() const; + const QByteArray& cdTextRaw() const; + K3b::Msf diskSize() const; + K3b::Msf remainingSize() const; + int tocType() const; + int numSessions() const; + long long bufferCapacity() const; + long long availableBufferCapacity() const; + + bool success() const; + + /** + * Use this when the command + * returnes some error code. + */ + int errorCode() const; + + enum Command { + /** + * Always successful, even with an empty or no media at all! + */ + NG_DISKINFO = 1, // TODO: rename this into DISKINFO + /** + * Always successful, even with an empty or no media at all! + */ + TOC = 2, + /** + * Successful if the media contains CD-Text. + */ + CD_TEXT = 4, + /** + * Successful if the media contains CD-Text. + */ + CD_TEXT_RAW = 8, + /** + * Always successful, even with an empty or no media at all! + */ + DISKSIZE = 16, + /** + * Always successful, even with an empty or no media at all! + */ + REMAININGSIZE = 32, + /** + * Always successful, even with an empty or no media at all! + */ + TOCTYPE = 64, + /** + * Always successful, even with an empty or no media at all! + */ + NUMSESSIONS = 128, + /** + * Successful if the drive could be blocked. + */ + BLOCK = 256, + /** + * Successful if the drive could be unblocked. + */ + UNBLOCK = 512, + /** + * Successful if the media was ejected. + */ + EJECT = 1024, + /** + * Successful if the media was loaded + */ + LOAD = 2048, + RELOAD = EJECT|LOAD, + /** + * Retrieves NG_DISKINFO, TOC, and CD-Text in case of an audio or mixed + * mode cd. + * The only difference to NG_DISKINFO|TOC|CD_TEXT is that no CD-Text is not + * considered an error. + * + * Always successful, even with an empty or no media at all! + */ + DISKINFO = 4096, // TODO: rename this in somthing like: DISKINFO_COMPLETE + /** + * Determine the device buffer state. + */ + BUFFER_CAPACITY = 8192 + }; + + signals: + void finished( K3bDevice::DeviceHandler* ); + + public slots: + void setDevice( Device* ); + void sendCommand( int command ); + + void getToc(); + void getDiskInfo(); + void getDiskSize(); + void getRemainingSize(); + void getTocType(); + void getNumSessions(); + void block( bool ); + void eject(); + + protected: + /** + * reimplemented from K3bThreadJob for internal reasons + */ + virtual void customEvent( QCustomEvent* ); + + private: + class DeviceHandlerThread; + DeviceHandlerThread* m_thread; + + bool m_selfDelete; + }; + + /** + * Usage: + * \code + * connect( K3bDevice::sendCommand( K3bDevice::DeviceHandler::MOUNT, dev ), + * SIGNAL(finished(DeviceHandler*)), + * this, SLOT(someSlot(DeviceHandler*)) ); + * + * void someSlot( DeviceHandler* dh ) { + * if( dh->success() ) { + * \endcode + * + * Be aware that the DeviceHandler will get destroyed once the signal has been + * emited. + */ + LIBK3B_EXPORT DeviceHandler* sendCommand( int command, Device* ); + + inline DeviceHandler* diskInfo(Device* dev) { + return sendCommand(DeviceHandler::DISKINFO,dev); + } + + inline DeviceHandler* toc(Device* dev) { + return sendCommand(DeviceHandler::TOC,dev); + } + + inline DeviceHandler* diskSize(Device* dev) { + return sendCommand(DeviceHandler::DISKSIZE,dev); + } + + inline DeviceHandler* remainingSize(Device* dev) { + return sendCommand(DeviceHandler::REMAININGSIZE,dev); + } + + inline DeviceHandler* tocType(Device* dev) { + return sendCommand(DeviceHandler::TOCTYPE,dev); + } + + inline DeviceHandler* numSessions(Device* dev) { + return sendCommand(DeviceHandler::NUMSESSIONS,dev); + } + + inline DeviceHandler* block(Device* dev) { + return sendCommand(DeviceHandler::BLOCK,dev); + } + + inline DeviceHandler* unblock(Device* dev) { + return sendCommand(DeviceHandler::UNBLOCK,dev); + } + + inline DeviceHandler* eject(Device* dev) { + return sendCommand(DeviceHandler::EJECT,dev); + } + + inline DeviceHandler* reload(Device* dev) { + return sendCommand(DeviceHandler::RELOAD,dev); + } + + inline DeviceHandler* load(Device* dev) { + return sendCommand(DeviceHandler::LOAD,dev); + } +} + +#endif diff --git a/libk3b/tools/k3bdeviceselectiondialog.cpp b/libk3b/tools/k3bdeviceselectiondialog.cpp new file mode 100644 index 0000000..d622457 --- /dev/null +++ b/libk3b/tools/k3bdeviceselectiondialog.cpp @@ -0,0 +1,130 @@ +/* + * + * $Id: k3bdeviceselectiondialog.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bdeviceselectiondialog.h" +#include <k3bdevice.h> +#include <k3bdevicecombobox.h> +#include <k3bcore.h> +#include <k3bdevicemanager.h> + +#include <qcombobox.h> +#include <qlayout.h> +#include <qlabel.h> +#include <qstring.h> +#include <qframe.h> + +#include <klocale.h> + + +class K3bDeviceSelectionDialog::Private +{ +public: + K3bDeviceComboBox* comboDevices; +}; + + +K3bDeviceSelectionDialog::K3bDeviceSelectionDialog( QWidget* parent, + const char* name, + const QString& text, + bool modal ) + : KDialogBase( KDialogBase::Plain, + i18n("Device Selection"), + Ok|Cancel, + Ok, + parent, + name, + modal ) +{ + d = new Private(); + + QGridLayout* lay = new QGridLayout( plainPage() ); + + QLabel* label = new QLabel( text.isEmpty() ? i18n("Please select a device:") : text, plainPage() ); + d->comboDevices = new K3bDeviceComboBox( plainPage() ); + + // lay->setMargin( marginHint() ); + lay->setSpacing( spacingHint() ); + lay->addWidget( label, 0, 0 ); + lay->addWidget( d->comboDevices, 1, 0 ); + lay->setRowStretch( 2, 1 ); +} + + +K3bDeviceSelectionDialog::~K3bDeviceSelectionDialog() +{ + delete d; +} + + +void K3bDeviceSelectionDialog::addDevice( K3bDevice::Device* dev ) +{ + d->comboDevices->addDevice( dev ); +} + + +void K3bDeviceSelectionDialog::addDevices( const QPtrList<K3bDevice::Device>& list ) +{ + d->comboDevices->addDevices( list ); +} + + +K3bDevice::Device* K3bDeviceSelectionDialog::selectedDevice() const +{ + return d->comboDevices->selectedDevice(); +} + + +void K3bDeviceSelectionDialog::setSelectedDevice( K3bDevice::Device* dev ) +{ + d->comboDevices->setSelectedDevice( dev ); +} + + +K3bDevice::Device* K3bDeviceSelectionDialog::selectDevice( QWidget* parent, + const QPtrList<K3bDevice::Device>& devices, + const QString& text ) +{ + if( devices.isEmpty() ) + return 0; + if( devices.count() == 1 ) + return devices.getFirst(); + + K3bDeviceSelectionDialog dlg( parent, 0, text ); + dlg.addDevices( devices ); + + if( dlg.exec() == Accepted ) + return dlg.selectedDevice(); + else + return 0; +} + +K3bDevice::Device* K3bDeviceSelectionDialog::selectDevice( QWidget* parent, + const QString& text ) +{ + return selectDevice( parent, k3bcore->deviceManager()->allDevices(), text ); + + +} + + +K3bDevice::Device* K3bDeviceSelectionDialog::selectWriter( QWidget* parent, const QString& text ) +{ + return selectDevice( parent, k3bcore->deviceManager()->burningDevices(), text ); +} + + +#include "k3bdeviceselectiondialog.moc" diff --git a/libk3b/tools/k3bdeviceselectiondialog.h b/libk3b/tools/k3bdeviceselectiondialog.h new file mode 100644 index 0000000..b61ce1e --- /dev/null +++ b/libk3b/tools/k3bdeviceselectiondialog.h @@ -0,0 +1,62 @@ +/* + * + * $Id: k3bdeviceselectiondialog.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_DEVICE_SELECTION_DIALOG_H +#define K3B_DEVICE_SELECTION_DIALOG_H + + +#include <kdialogbase.h> +#include "k3b_export.h" +#include <qptrlist.h> + +namespace K3bDevice { + class Device; +} + + +class LIBK3B_EXPORT K3bDeviceSelectionDialog : public KDialogBase +{ + Q_OBJECT + + public: + K3bDeviceSelectionDialog( QWidget* parent = 0, + const char* name = 0, + const QString& text = QString::null, + bool modal = false ); + ~K3bDeviceSelectionDialog(); + + void addDevice( K3bDevice::Device* ); + void addDevices( const QPtrList<K3bDevice::Device>& ); + + void setSelectedDevice( K3bDevice::Device* ); + + K3bDevice::Device* selectedDevice() const; + + static K3bDevice::Device* selectWriter( QWidget* parent, + const QString& text = QString::null ); + static K3bDevice::Device* selectDevice( QWidget* parent, + const QString& text = QString::null ); + static K3bDevice::Device* selectDevice( QWidget* parent, + const QPtrList<K3bDevice::Device>& devices, + const QString& text = QString::null ); + + private: + class Private; + Private* d; +}; + +#endif diff --git a/libk3b/tools/k3bdirsizejob.cpp b/libk3b/tools/k3bdirsizejob.cpp new file mode 100644 index 0000000..ab9cb8a --- /dev/null +++ b/libk3b/tools/k3bdirsizejob.cpp @@ -0,0 +1,184 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bdirsizejob.h" + +#include <k3bthread.h> +#include <k3bthreadjob.h> +#include <k3bsimplejobhandler.h> +#include <k3bglobals.h> + +#include <kdebug.h> +#include <kglobal.h> + +#include <qfileinfo.h> +#include <qdir.h> + + +class K3bDirSizeJob::WorkThread : public K3bThread +{ +public: + WorkThread() + : K3bThread(), + followSymlinks(false), + totalSize(0), + totalFiles(0), + totalDirs(0), + totalSymlinks(0) { + } + + void init() { + m_canceled = false; + + totalSize = 0; + totalFiles = 0; + totalDirs = 0; + totalSymlinks = 0; + } + + void run() { + emitStarted(); + + QStringList l; + for( KURL::List::const_iterator it = urls.begin(); + it != urls.end(); ++it ) { + const KURL& url = *it; + + if( !url.isLocalFile() ) { + kdDebug() << "(K3bDirSizeJob) no remote support." << endl; + emitFinished( false ); + return; + } + + l.append( url.path() ); + } + + emitFinished( countFiles( l, QString() ) ); + } + + bool countDir( const QString& dir ) { + const QString& dot = KGlobal::staticQString( "." ); + const QString& dotdot = KGlobal::staticQString( ".." ); + QStringList l = QDir(dir).entryList( QDir::All|QDir::Hidden|QDir::System ); + l.remove( dot ); + l.remove( dotdot ); + + return countFiles( l, dir ); + } + + + bool countFiles( const QStringList& l, const QString& dir ) { + for( QStringList::const_iterator it = l.begin(); + it != l.end(); ++it ) { + + if( m_canceled ) + return false; + + k3b_struct_stat s; + if( k3b_lstat( QFile::encodeName( dir + *it ), &s ) ) + return false; + + if( S_ISLNK( s.st_mode ) ) { + ++totalSymlinks; + if( followSymlinks ) { + if( k3b_stat( QFile::encodeName( dir + *it ), &s ) ) + return false; + } + } + + if( S_ISDIR( s.st_mode ) ) { + ++totalDirs; + if( !countDir( dir + *it + '/' ) ) + return false; + } + else if( !S_ISLNK( s.st_mode ) ) { + ++totalFiles; + totalSize += (KIO::filesize_t)s.st_size; + } + } + + return true; + } + + void cancel() { + m_canceled = true; + emitCanceled(); + wait(); + } + + KURL::List urls; + bool followSymlinks; + + KIO::filesize_t totalSize; + KIO::filesize_t totalFiles; + KIO::filesize_t totalDirs; + KIO::filesize_t totalSymlinks; + +private: + bool m_canceled; +}; + + +K3bDirSizeJob::K3bDirSizeJob( QObject* parent ) + : K3bThreadJob( new K3bSimpleJobHandler(), parent ) +{ + d = new WorkThread; + setThread( d ); +} + + +K3bDirSizeJob::~K3bDirSizeJob() +{ + delete d; + delete jobHandler(); +} + + +KIO::filesize_t K3bDirSizeJob::totalSize() const +{ + return d->totalSize; +} + + +KIO::filesize_t K3bDirSizeJob::totalFiles() const +{ + return d->totalFiles; +} + + +KIO::filesize_t K3bDirSizeJob::totalDirs() const +{ + return d->totalDirs; +} + + +KIO::filesize_t K3bDirSizeJob::totalSymlinks() const +{ + return d->totalSymlinks; +} + + +void K3bDirSizeJob::setUrls( const KURL::List& urls ) +{ + d->urls = urls; +} + + +void K3bDirSizeJob::setFollowSymlinks( bool b ) +{ + d->followSymlinks = b; +} + +#include "k3bdirsizejob.moc" diff --git a/libk3b/tools/k3bdirsizejob.h b/libk3b/tools/k3bdirsizejob.h new file mode 100644 index 0000000..d6a3e5a --- /dev/null +++ b/libk3b/tools/k3bdirsizejob.h @@ -0,0 +1,67 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_DIR_SIZE_JOB_H_ +#define _K3B_DIR_SIZE_JOB_H_ + +#include <k3bthreadjob.h> +#include <kio/global.h> + +#include <k3b_export.h> + +/** + * K3bDirSizeJob is a replacement for KDirSize which allows + * a much finer grained control over what is counted and how. + * Additionally it uses threading for enhanced speed. + * + * For now K3bDirSizeJob only works on local urls. + */ +class LIBK3B_EXPORT K3bDirSizeJob : public K3bThreadJob +{ + Q_OBJECT + + public: + K3bDirSizeJob( QObject* parent = 0 ); + ~K3bDirSizeJob(); + + KIO::filesize_t totalSize() const; + + /** + * Does also include symlinks to files, devices, and fifos + */ + KIO::filesize_t totalFiles() const; + + /** + * Total number of counted dirs. This does also + * include the first dirs the job was started with. + * Does also include symlinks to dirs. + */ + KIO::filesize_t totalDirs() const; + + /** + * Includes symlinks to files and folders + */ + KIO::filesize_t totalSymlinks() const; + + public slots: + void setUrls( const KURL::List& urls ); + void setFollowSymlinks( bool ); + + private: + class WorkThread; + WorkThread* d; +}; + +#endif diff --git a/libk3b/tools/k3bexceptions.cpp b/libk3b/tools/k3bexceptions.cpp new file mode 100644 index 0000000..1d8806f --- /dev/null +++ b/libk3b/tools/k3bexceptions.cpp @@ -0,0 +1,43 @@ +/* + * + * $Id: k3bexceptions.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bexceptions.h" +#include <k3bdevice.h> + +bool K3bExceptions::brokenDaoAudio( K3bDevice::Device* dev ) +{ + if( dev->vendor().upper().startsWith("PIONEER") ) + if( dev->description().upper().startsWith("DVR-106D") || + dev->description().upper().startsWith("DVD-RW DVR-K12D") ) + return true; + + if( dev->vendor().upper().startsWith("HL-DT-ST") ) + if( dev->description().upper().startsWith("RW/DVD GCC-4320B") || + dev->description().upper().contains("GCE-8520B") ) + return true; + + if( dev->vendor().upper().startsWith("PHILIPS") && + dev->description().upper().startsWith("CDRWDVD3210") ) + return true; + + if( dev->vendor().upper().startsWith("LITE-ON") ) + if( dev->description().upper().startsWith("LTR-32123S") || + dev->description().upper().startsWith("LTR-40125S") || + dev->description().upper().contains("LTC-48161H") || + dev->description().upper().startsWith("DVDRW LDW-811S") ) + return true; + + return false; +} diff --git a/libk3b/tools/k3bexceptions.h b/libk3b/tools/k3bexceptions.h new file mode 100644 index 0000000..a078992 --- /dev/null +++ b/libk3b/tools/k3bexceptions.h @@ -0,0 +1,35 @@ +/* + * + * $Id: k3bexceptions.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_EXCEPTIONS_H_ +#define _K3B_EXCEPTIONS_H_ + +namespace K3bDevice { + class Device; +} + +class K3bExceptions +{ + public: + /** + * Returns true if the drive's firmware produces broken + * Audio CDs with zero length pregaps. + * + * It simply uses a compiled in table. + */ + static bool brokenDaoAudio( K3bDevice::Device* ); +}; + +#endif diff --git a/libk3b/tools/k3bfilesplitter.cpp b/libk3b/tools/k3bfilesplitter.cpp new file mode 100644 index 0000000..af5b83f --- /dev/null +++ b/libk3b/tools/k3bfilesplitter.cpp @@ -0,0 +1,307 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bfilesplitter.h" +#include "k3bfilesysteminfo.h" + +#include <kdebug.h> + +#include <qfile.h> + + +class K3bFileSplitter::Private +{ +public: + Private( K3bFileSplitter* splitter ) + : m_splitter( splitter ) { + } + + QString filename; + QFile file; + int counter; + + // QIODevice::Offset is too small on most compilations + KIO::filesize_t maxFileSize; + + KIO::filesize_t currentOverallPos; + KIO::filesize_t currentFilePos; + + void determineMaxFileSize() { + if( maxFileSize == 0 ) { + if( K3bFileSystemInfo( filename ).type() == K3bFileSystemInfo::FS_FAT ) + maxFileSize = 1024ULL*1024ULL*1024ULL; // 1GB + else + maxFileSize = 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL; // incredibly big, 1024 TB + } + } + + QString buildFileName( int counter ) { + if( counter > 0 ) + return filename + '.' + QString::number(counter).rightJustify( 3, '0' ); + else + return filename; + } + + QString currentFileName() { + return buildFileName( counter ); + } + + bool openPrevFile() { + return openFile( --counter ); + } + + bool openNextFile() { + return openFile( ++counter ); + } + + bool openFile( int counter ) { + file.close(); + file.setName( buildFileName( counter ) ); + currentFilePos = 0; + if( file.open( m_splitter->mode() ) ) { + m_splitter->setState( IO_Open ); + return true; + } + else { + m_splitter->setState( ~IO_Open ); + return false; + } + } + +private: + K3bFileSplitter* m_splitter; +}; + + +K3bFileSplitter::K3bFileSplitter() +{ + d = new Private( this ); +} + + +K3bFileSplitter::K3bFileSplitter( const QString& filename ) +{ + d = new Private( this ); + setName( filename ); +} + + +K3bFileSplitter::~K3bFileSplitter() +{ + delete d; +} + + +const QString& K3bFileSplitter::name() const +{ + return d->filename; +} + + +void K3bFileSplitter::setName( const QString& filename ) +{ + close(); + d->maxFileSize = 0; + d->filename = filename; +} + + +bool K3bFileSplitter::open( int mode ) +{ + close(); + + d->determineMaxFileSize(); + + d->counter = 0; + d->currentFilePos = 0; + d->currentOverallPos = 0; + setMode( mode ); + + return d->openFile( 0 ); +} + + +void K3bFileSplitter::close() +{ + d->file.close(); + d->counter = 0; + d->currentFilePos = 0; + d->currentOverallPos = 0; +} + + +int K3bFileSplitter::handle() const +{ + // FIXME: use a K3bPipe to simulate this + return -1; +} + + + +void K3bFileSplitter::flush() +{ + d->file.flush(); +} + + +QIODevice::Offset K3bFileSplitter::size() const +{ + // not implemented due to Offset size limitations + return 0; +} + + +QIODevice::Offset K3bFileSplitter::at() const +{ + return d->currentOverallPos; +} + + +bool K3bFileSplitter::at( QIODevice::Offset pos ) +{ + Q_UNUSED( pos ); + // not implemented due to Offset size limitations + return false; +} + + +bool K3bFileSplitter::atEnd() const +{ + return d->file.atEnd() && !QFile::exists( d->buildFileName( d->counter+1 ) ); +} + + +Q_LONG K3bFileSplitter::readBlock( char *data, Q_ULONG maxlen ) +{ + Q_LONG r = d->file.readBlock( data, maxlen ); + if( r == 0 ) { + if( atEnd() ) { + return r; + } + else if( d->openNextFile() ) { + // recursively call us + return readBlock( data, maxlen ); + } + } + else if( r > 0 ) { + d->currentOverallPos += r; + d->currentFilePos += r; + } + + return r; +} + + +Q_LONG K3bFileSplitter::writeBlock( const char *data, Q_ULONG len ) +{ + // We cannot rely on QFile::at since it uses long on most copmpilations + Q_ULONG max = (Q_ULONG)QMIN( (KIO::filesize_t)len, d->maxFileSize - d->currentFilePos ); + + Q_LONG r = d->file.writeBlock( data, max ); + + if( r < 0 ) + return r; + + d->currentOverallPos += r; + d->currentFilePos += r; + + // recursively call us + if( (Q_ULONG)r < len ) { + if( d->openNextFile() ) + return r + writeBlock( data+r, len-r ); + else + return -1; + } + else + return r; +} + + +int K3bFileSplitter::getch() +{ + int r = d->file.getch(); + if( r == -1 ) { + if( !d->file.atEnd() ) { + return -1; + } + else if( !atEnd() ) { + if( !d->openNextFile() ) + return -1; + else + return getch(); + } + } + + d->currentOverallPos++; + d->currentFilePos++; + + return r; +} + + +int K3bFileSplitter::putch( int c ) +{ + if( d->currentFilePos < d->maxFileSize ) { + d->currentOverallPos++; + d->currentFilePos++; + return d->file.putch( c ); + } + else if( d->openNextFile() ) { + // recursively call us + return putch( c ); + } + else + return -1; +} + + +int K3bFileSplitter::ungetch( int c ) +{ + if( d->currentFilePos > 0 ) { + int r = d->file.ungetch( c ); + if( r != -1 ) { + d->currentOverallPos--; + d->currentFilePos--; + } + return r; + } + else if( d->counter > 0 ) { + // open prev file + if( d->openPrevFile() ) { + // seek to the end + d->file.at( d->file.size() ); + d->currentFilePos = d->file.at(); + return getch(); + } + else + return -1; + } + else + return -1; +} + + +void K3bFileSplitter::remove() +{ + close(); + while( QFile::exists( d->buildFileName( d->counter ) ) ) + QFile::remove( d->buildFileName( d->counter++ ) ); +} + + +void K3bFileSplitter::setMaxFileSize( KIO::filesize_t size ) +{ + d->maxFileSize = size; +} diff --git a/libk3b/tools/k3bfilesplitter.h b/libk3b/tools/k3bfilesplitter.h new file mode 100644 index 0000000..22dcad9 --- /dev/null +++ b/libk3b/tools/k3bfilesplitter.h @@ -0,0 +1,108 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_FILE_SPLITTER_H_ +#define _K3B_FILE_SPLITTER_H_ + +#include <qiodevice.h> +#include <qstring.h> + +#include <kio/global.h> + +#include <k3b_export.h> + + +/** + * QFile replacement which splits + * big files according to the underlying file system's + * maximum file size. + * + * The filename will be changed to include a counter + * if the file has to be splitted like so: + * + * <pre> + * filename.iso + * filename.iso.001 + * filename.iso.002 + * ... + * </pre> + */ +class LIBK3B_EXPORT K3bFileSplitter : public QIODevice +{ + public: + K3bFileSplitter(); + K3bFileSplitter( const QString& filename ); + ~K3bFileSplitter(); + + /** + * Set the maximum file size. If this is set to 0 + * (the default) the max filesize is determined based on + * the filesystem type. + * + * Be aware that setName will reset the max file size. + */ + void setMaxFileSize( KIO::filesize_t size ); + + const QString& name() const; + + void setName( const QString& filename ); + + virtual bool open( int mode ); + + virtual void close(); + + /** + * File descriptor to read from and write to. + * Not implemented yet! + */ + int handle() const; + + virtual void flush(); + + /** + * Not implemented + */ + virtual Offset size() const; + + /** + * Not implemented + */ + virtual Offset at() const; + + /** + * Not implemented + */ + virtual bool at( Offset ); + + virtual bool atEnd() const; + virtual Q_LONG readBlock( char *data, Q_ULONG maxlen ); + virtual Q_LONG writeBlock( const char *data, Q_ULONG len ); + virtual int getch(); + virtual int putch( int ); + virtual int ungetch( int ); + + /** + * Deletes all the splitted files. + * Caution: Does remove all files that fit the naming scheme without any + * additional checks. + */ + void remove(); + + private: + class Private; + Private* d; +}; + +#endif diff --git a/libk3b/tools/k3bfilesysteminfo.cpp b/libk3b/tools/k3bfilesysteminfo.cpp new file mode 100644 index 0000000..fe1eaf8 --- /dev/null +++ b/libk3b/tools/k3bfilesysteminfo.cpp @@ -0,0 +1,141 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bfilesysteminfo.h" + +#include <k3bglobals.h> + +#include <qfile.h> +#include <qfileinfo.h> +#include <qregexp.h> + +#include <kdebug.h> + +#ifdef Q_OS_FREEBSD +#include <sys/param.h> +#include <sys/mount.h> +#endif +#ifdef HAVE_SYS_STATVFS_H +# include <sys/statvfs.h> +# if defined(Q_OS_NETBSD) +# include <sys/param.h> +# if __NetBSD_Version__ > 299000000 +# define statfs statvfs +# define f_type f_fsid +# endif +# endif +#endif +#ifdef HAVE_SYS_VFS_H +# include <sys/vfs.h> +#endif + +#include <errno.h> +#include <string.h> + + + +class K3bFileSystemInfo::Private +{ +public: + Private() + : type(FS_UNKNOWN), + statDone(false) { + } + + FileSystemType type; + QString path; + + bool statDone; + + void stat() { + struct statfs fs; + if( !::statfs( QFile::encodeName( QFileInfo(path).dirPath( true ) ), &fs ) ) { + switch( fs.f_type ) { + case 0x4d44: // MS-DOS + type = FS_FAT; + default: + type = FS_UNKNOWN; + } + + statDone = true; + } + else { + kdDebug() << "(K3bFileSystemInfo) statfs failed: " << ::strerror(errno) << endl; + } + } +}; + + +K3bFileSystemInfo::K3bFileSystemInfo() +{ + d = new Private; +} + + +K3bFileSystemInfo::K3bFileSystemInfo( const QString& path ) +{ + d = new Private; + d->path = path; +} + + +K3bFileSystemInfo::K3bFileSystemInfo( const K3bFileSystemInfo& other ) +{ + d = new Private; + d->type = other.d->type; + d->path = other.d->path; + d->statDone = other.d->statDone; +} + + +K3bFileSystemInfo::~K3bFileSystemInfo() +{ + delete d; +} + + +QString K3bFileSystemInfo::path() const +{ + return d->path; +} + + +void K3bFileSystemInfo::setPath( const QString& path ) +{ + if( d->path != path ) { + d->path = path; + d->statDone = false; + } +} + + +K3bFileSystemInfo::FileSystemType K3bFileSystemInfo::type() const +{ + if( !d->statDone ) + d->stat(); + return d->type; +} + + +QString K3bFileSystemInfo::fixupPath( const QString& path ) +{ + QString s = K3b::fixupPath( path ); + if( type() == K3bFileSystemInfo::FS_FAT ) + return s.replace( QRegExp("[\"\\?\\*/\\\\[\\]\\|\\=\\:;]"), "_" ); + else + return s; +} diff --git a/libk3b/tools/k3bfilesysteminfo.h b/libk3b/tools/k3bfilesysteminfo.h new file mode 100644 index 0000000..be9995c --- /dev/null +++ b/libk3b/tools/k3bfilesysteminfo.h @@ -0,0 +1,56 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_FILE_SYSTEM_INFO_H_ +#define _K3B_FILE_SYSTEM_INFO_H_ + +#include <k3b_export.h> + +#include <qstring.h> + +class LIBK3B_EXPORT K3bFileSystemInfo +{ + public: + K3bFileSystemInfo(); + K3bFileSystemInfo( const QString& path ); + K3bFileSystemInfo( const K3bFileSystemInfo& ); + ~K3bFileSystemInfo(); + + QString path() const; + void setPath( const QString& path ); + + enum FileSystemType { + FS_UNKNOWN, + FS_FAT + // FIXME: add way more file system types + }; + + FileSystemType type() const; + + /** + * Ensures that the file path does not contain + * any invalid chars. + * + * For now it only replaces characters like * or [ + * on FAT file systems. + */ + QString fixupPath( const QString& ); + + private: + class Private; + Private* d; +}; + +#endif diff --git a/libk3b/tools/k3bintmapcombobox.cpp b/libk3b/tools/k3bintmapcombobox.cpp new file mode 100644 index 0000000..19ac649 --- /dev/null +++ b/libk3b/tools/k3bintmapcombobox.cpp @@ -0,0 +1,127 @@ +/* + * + * $Id: k3bwritingmodewidget.cpp 554512 2006-06-24 07:25:39Z trueg $ + * Copyright (C) 2006 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bintmapcombobox.h" + +#include <qwhatsthis.h> +#include <qmap.h> +#include <qvaluevector.h> + + +class K3bIntMapComboBox::Private +{ +public: + QMap<int, int> valueIndexMap; + QMap<int, QPair<int, QString> > indexValueDescriptionMap; + + QString topWhatsThis; + QString bottomWhatsThis; +}; + + +K3bIntMapComboBox::K3bIntMapComboBox( QWidget* parent, const char* name ) + : KComboBox( parent, name ) +{ + d = new Private; + connect( this, SIGNAL(highlighted(int)), + this, SLOT(slotItemHighlighted(int)) ); + connect( this, SIGNAL(activated(int)), + this, SLOT(slotItemActivated(int)) ); +} + + +K3bIntMapComboBox::~K3bIntMapComboBox() +{ + delete d; +} + + +int K3bIntMapComboBox::selectedValue() const +{ + if( (int)d->indexValueDescriptionMap.count() > KComboBox::currentItem() ) + return d->indexValueDescriptionMap[KComboBox::currentItem()].first; + else + return 0; +} + + +void K3bIntMapComboBox::setSelectedValue( int value ) +{ + if( d->valueIndexMap.contains( value ) ) + KComboBox::setCurrentItem( d->valueIndexMap[value] ); +} + + +void K3bIntMapComboBox::clear() +{ + d->valueIndexMap.clear(); + d->indexValueDescriptionMap.clear(); + + KComboBox::clear(); +} + + +bool K3bIntMapComboBox::insertItem( int value, const QString& text, const QString& description, int index ) +{ + if( d->valueIndexMap.contains( value ) ) + return false; + + // FIXME: allow inserition at any index + index = KComboBox::count(); + + d->valueIndexMap[value] = index; + d->indexValueDescriptionMap[index] = qMakePair<int, QString>( value, description ); + + KComboBox::insertItem( text ); + + updateWhatsThis(); + + return true; +} + + +void K3bIntMapComboBox::updateWhatsThis() +{ + QString ws( d->topWhatsThis ); + for( unsigned int i = 0; i < d->indexValueDescriptionMap.count(); ++i ) { + ws += "<p><b>" + KComboBox::text( i ) + "</b><br>"; + ws += d->indexValueDescriptionMap[i].second; + } + ws += "<p>" + d->bottomWhatsThis; + + QWhatsThis::add( this, ws ); +} + + +void K3bIntMapComboBox::slotItemHighlighted( int index ) +{ + emit valueHighlighted( d->indexValueDescriptionMap[index].first ); +} + + +void K3bIntMapComboBox::slotItemActivated( int index ) +{ + emit valueChanged( d->indexValueDescriptionMap[index].first ); +} + + +void K3bIntMapComboBox::addGlobalWhatsThisText( const QString& top, const QString& bottom ) +{ + d->topWhatsThis = top; + d->bottomWhatsThis = bottom; + updateWhatsThis(); +} + +#include "k3bintmapcombobox.moc" diff --git a/libk3b/tools/k3bintmapcombobox.h b/libk3b/tools/k3bintmapcombobox.h new file mode 100644 index 0000000..b0ae717 --- /dev/null +++ b/libk3b/tools/k3bintmapcombobox.h @@ -0,0 +1,83 @@ +/* + * + * $Id: k3bwritingmodewidget.cpp 554512 2006-06-24 07:25:39Z trueg $ + * Copyright (C) 2006 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_INT_MAP_COMBOBOX_H_ +#define _K3B_INT_MAP_COMBOBOX_H_ + +#include <kcombobox.h> + +#include "k3b_export.h" + +/** + * The K3bIntMapComboBox allows a simple selection of integer + * values. + * + * The K3bIntMapComboBox will create a WhatsThis help automatically from + * the description texts (if all are set). The ToolTip has to be set manually. + */ +class LIBK3B_EXPORT K3bIntMapComboBox : public KComboBox +{ + Q_OBJECT + + public: + K3bIntMapComboBox( QWidget* parent = 0, const char* name = 0 ); + ~K3bIntMapComboBox(); + + int selectedValue() const; + + signals: + /** + * Emitted if the selected value changes by user interaction. + */ + void valueChanged( int ); + + /** + * Emitted if the current highlighted value changed by user interaction. + */ + void valueHighlighted( int ); + + public slots: + /** + * If \a v has not been added via insertItem the selection will not be changed + */ + void setSelectedValue( int v ); + + void clear(); + + /** + * Insert a new item + * \param value The integer value to insert + * \param text The text to be displayed in the combobox + * \param description The text to be used to describe the item in the whatsthis help + * \param index The position where to inserts the item. The item will be appended if index is negative. + * + * \return true if the item could be inserted. False if the value had already been inserted. + */ + bool insertItem( int value, const QString& text, const QString& description, int index = -1 ); + + void addGlobalWhatsThisText( const QString& top, const QString& bottom ); + + private slots: + void slotItemActivated( int ); + void slotItemHighlighted( int ); + + private: + void updateWhatsThis(); + + class Private; + Private* d; +}; + +#endif diff --git a/libk3b/tools/k3bintvalidator.cpp b/libk3b/tools/k3bintvalidator.cpp new file mode 100644 index 0000000..dabf719 --- /dev/null +++ b/libk3b/tools/k3bintvalidator.cpp @@ -0,0 +1,137 @@ +/* + * + * $Id: k3bintvalidator.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <qwidget.h> +#include <qstring.h> + +#include "k3bintvalidator.h" + +#include <klocale.h> +#include <kglobal.h> +#include <kdebug.h> + + +K3bIntValidator::K3bIntValidator ( QWidget * parent, const char * name ) + : QValidator(parent, name) +{ + m_min = m_max = 0; +} + + +K3bIntValidator::K3bIntValidator ( int bottom, int top, QWidget * parent, const char * name ) + : QValidator(parent, name) +{ + m_min = bottom; + m_max = top; +} + + +K3bIntValidator::~K3bIntValidator () +{ +} + + +QValidator::State K3bIntValidator::validate ( QString &str, int & ) const +{ + bool ok; + int val = 0; + QString newStr; + + newStr = str.stripWhiteSpace(); + newStr = newStr.upper(); + + if( newStr.length() ) { + // check for < 0 + bool minus = newStr.startsWith( "-" ); + if( minus ) + newStr.remove( 0, 1 ); + + // check for hex + bool hex = newStr.startsWith( "0X" ); + + if( hex ) + newStr.remove( 0, 2 ); + + // a special case + if( newStr.isEmpty() ) { + if( minus && m_min && m_min >= 0) + ok = false; + else + return QValidator::Acceptable; + } + + val = newStr.toInt( &ok, hex ? 16 : 10 ); + if( minus ) + val *= -1; + } + else { + val = 0; + ok = true; + } + + if( !ok ) + return QValidator::Invalid; + + if( m_min && val > 0 && val < m_min ) + return QValidator::Acceptable; + + if( m_max && val < 0 && val > m_max ) + return QValidator::Acceptable; + + if( (m_max && val > m_max) || (m_min && val < m_min) ) + return QValidator::Invalid; + + return QValidator::Valid; +} + + +void K3bIntValidator::fixup ( QString& ) const +{ + // TODO: remove preceding zeros +} + + +void K3bIntValidator::setRange ( int bottom, int top ) +{ + m_min = bottom; + m_max = top; + + if( m_max < m_min ) + m_max = m_min; +} + + +int K3bIntValidator::bottom () const +{ + return m_min; +} + + +int K3bIntValidator::top () const +{ + return m_max; +} + + +int K3bIntValidator::toInt( const QString& s, bool* ok ) +{ + if( s.lower().startsWith( "0x" ) ) + return s.right( s.length()-2 ).toInt( ok, 16 ); + else if( s.lower().startsWith( "-0x" ) ) + return -1 * s.right( s.length()-3 ).toInt( ok, 16 ); + else + return s.toInt( ok, 10 ); +} diff --git a/libk3b/tools/k3bintvalidator.h b/libk3b/tools/k3bintvalidator.h new file mode 100644 index 0000000..551d56a --- /dev/null +++ b/libk3b/tools/k3bintvalidator.h @@ -0,0 +1,84 @@ +/* + * + * $Id: k3bintvalidator.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_INT_VALIDATOR_H_ +#define _K3B_INT_VALIDATOR_H_ + +#include <qvalidator.h> +#include "k3b_export.h" +class QWidget; +class QString; + +/** + * QValidator for integers. + * + * It differs from QIntValidator and KIntValidator in the fact that + * it also accepts hex numbers prefixed with 0x. + */ +class LIBK3B_EXPORT K3bIntValidator : public QValidator +{ + public: + /** + * Constuctor. Also sets the base value. + */ + K3bIntValidator ( QWidget * parent, const char * name = 0 ); + + /** + * Constructor. Also sets the minimum, maximum, and numeric base values. + */ + K3bIntValidator ( int bottom, int top, QWidget * parent, const char * name = 0 ); + + /** + * Destructs the validator. + */ + virtual ~K3bIntValidator (); + + /** + * Validates the text, and return the result. Does not modify the parameters. + */ + virtual State validate ( QString &, int & ) const; + + /** + * Fixes the text if possible, providing a valid string. The parameter may be modified. + */ + virtual void fixup ( QString & ) const; + + /** + * Sets the minimum and maximum values allowed. + */ + virtual void setRange ( int bottom, int top ); + + /** + * Returns the current minimum value allowed. + */ + virtual int bottom () const; + + /** + * Returns the current maximum value allowed. + */ + virtual int top () const; + + /** + * If the string starts with 0x it's assumed to be a hex number. + */ + static int toInt( const QString&, bool* ok = 0 ); + + private: + int m_min; + int m_max; +}; + +#endif diff --git a/libk3b/tools/k3biso9660.cpp b/libk3b/tools/k3biso9660.cpp new file mode 100644 index 0000000..84edc4b --- /dev/null +++ b/libk3b/tools/k3biso9660.cpp @@ -0,0 +1,899 @@ +/* + * + * $Id: k3biso9660.cpp 690529 2007-07-21 10:51:47Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3biso9660.h" +#include "k3biso9660backend.h" + +#include <k3bdevice.h> + +#include "libisofs/isofs.h" + +#include <qcstring.h> +#include <qdir.h> +#include <qfile.h> +#include <qptrlist.h> + +#include <kdebug.h> + + +/* callback function for libisofs */ +int K3bIso9660::read_callback( char* buf, sector_t start, int len, void* udata ) +{ + K3bIso9660* isoF = static_cast<K3bIso9660*>(udata); + + return isoF->read( start, buf, len ); +} + +/* callback function for libisofs */ +int K3bIso9660::isofs_callback( struct iso_directory_record *idr, void *udata ) +{ + K3bIso9660 *iso = static_cast<K3bIso9660*> (udata); + QString path, isoPath,user,group,symlink; + int i; + int access; + int time,cdate,adate; + rr_entry rr; + bool special=false; + K3bIso9660Entry *entry=0; + //K3bIso9660Entry *oldentry=0; + char z_algo[2],z_params[2]; + int z_size=0; + + if (isonum_711(idr->name_len)==1) { + switch (idr->name[0]) { + case 0: + path+=("."); + special=true; + break; + case 1: + path+=(".."); + special=true; + break; + } + } + // + // First extract the raw iso9660 name + // + if( !special ) { + for( i = 0; i < isonum_711( idr->name_len ); i++ ) { + if( idr->name[i] ) + isoPath += idr->name[i]; + } + } + else + isoPath = path; + + // + // Now see if we have RockRidge + // + if( !iso->plainIso9660() && ParseRR(idr,&rr) > 0 ) { + iso->m_rr = true; + if (!special) + path = QString::fromLocal8Bit( rr.name ); + symlink=rr.sl; + access=rr.mode; + time=0;//rr.st_mtime; + adate=0;//rr.st_atime; + cdate=0;//rr.st_ctime; + user.setNum(rr.uid); + group.setNum(rr.gid); + z_algo[0]=rr.z_algo[0];z_algo[1]=rr.z_algo[1]; + z_params[0]=rr.z_params[0];z_params[1]=rr.z_params[1]; + z_size=rr.z_size; + } + else { + access=iso->dirent->permissions() & ~S_IFMT; + adate=cdate=time=isodate_915(idr->date,0); + user=iso->dirent->user(); + group=iso->dirent->group(); + if (idr->flags[0] & 2) access |= S_IFDIR; else access |= S_IFREG; + if (!special) { + if( !iso->plainIso9660() && iso->jolietLevel() ) { + for (i=0;i<(isonum_711(idr->name_len)-1);i+=2) { + QChar ch( be2me_16(*((ushort*)&(idr->name[i]))) ); + if (ch==';') break; + path+=ch; + } + } + else { + // no RR, no Joliet, just plain iso9660 + path = isoPath; + + // remove the version field + int pos = path.find( ';' ); + if( pos > 0 ) + path.truncate( pos ); + } + if (path.endsWith(".")) path.setLength(path.length()-1); + } + } + + if( !iso->plainIso9660() ) + FreeRR(&rr); + + if (idr->flags[0] & 2) { + entry = new K3bIso9660Directory( iso, isoPath, path, access | S_IFDIR, time, adate, cdate, + user, group, symlink, + special ? 0 : isonum_733(idr->extent), + special ? 0 : isonum_733(idr->size) ); + } + else { + entry = new K3bIso9660File( iso, isoPath, path, access, time, adate, cdate, + user, group, symlink, isonum_733(idr->extent), isonum_733(idr->size) ); + if (z_size) + (static_cast<K3bIso9660File*>(entry))->setZF( z_algo, z_params, z_size ); + } + iso->dirent->addEntry(entry); + + return 0; +} + + + +K3bIso9660Entry::K3bIso9660Entry( K3bIso9660* archive, + const QString& isoName, + const QString& name, + int access, + int date, + int adate, + int cdate, + const QString& user, + const QString& group, + const QString& symlink ) + : m_adate( adate ), + m_cdate( cdate ), + m_name( name ), + m_isoName( isoName ), + m_date( date ), + m_access( access ), + m_user( user ), + m_group( group ), + m_symlink( symlink ), + m_archive( archive ) +{ +} + + +K3bIso9660Entry::~K3bIso9660Entry() +{ +} + + + + + + +K3bIso9660File::K3bIso9660File( K3bIso9660* archive, + const QString& isoName, + const QString& name, + int access, + int date, + int adate, + int cdate, + const QString& user, + const QString& group, + const QString& symlink, + unsigned int pos, + unsigned int size ) + : K3bIso9660Entry( archive, isoName, name, access, date, adate, cdate, user, group, symlink ), + m_startSector(pos), + m_size(size) +{ + m_algo[0] = 0; + m_algo[1] = 0; + m_parms[0] = 0; + m_parms[1] = 0; + m_realsize = 0; +} + +K3bIso9660File::~K3bIso9660File() +{ +} + +void K3bIso9660File::setZF(char algo[2],char parms[2],int realsize) +{ + m_algo[0]=algo[0];m_algo[1]=algo[1]; + m_parms[0]=parms[0];m_parms[1]=parms[1]; + m_realsize=realsize; +} + + +int K3bIso9660File::read( unsigned int pos, char* data, int maxlen ) const +{ + if( pos >= size() ) + return 0; + + unsigned long startSec = m_startSector + pos/2048; + int startSecOffset = pos%2048; + char* buffer = data; + bool buffered = false; + unsigned long bufferLen = maxlen+startSecOffset; + + // cut to size + if( pos + maxlen > size() ) + bufferLen = size() - pos + startSecOffset; + + // pad to 2048 + if( bufferLen%2048 ) + bufferLen += (2048-(bufferLen%2048)); + + // we need to buffer if we changed the startSec or need a bigger buffer + if( startSecOffset || bufferLen > (unsigned int)maxlen ) { + buffered = true; + buffer = new char[bufferLen]; + } + + int read = archive()->read( startSec, buffer, bufferLen/2048 )*2048; + + if( buffered ) { + if( read > 0 ) { + // cut to requested data + read -= startSecOffset; + if( read + pos > size() ) + read = size() - pos; + if( read > maxlen ) + read = maxlen; + + ::memcpy( data, buffer+startSecOffset, read ); + } + delete [] buffer; + + return read; + } + else { + // cut read data + if( read + pos > size() ) + read = size() - pos; + + return read; + } +} + + +bool K3bIso9660File::copyTo( const QString& url ) const +{ + QFile of( url ); + if( of.open( IO_WriteOnly ) ) { + char buffer[2048*10]; + unsigned int pos = 0; + int r = 0; + while( ( r = read( pos, buffer, 2048*10 ) ) > 0 ) { + of.writeBlock( buffer, r ); + pos += r; + } + + return !r; + } + else { + kdDebug() << "(K3bIso9660File) could not open " << url << " for writing." << endl; + return false; + } +} + + +K3bIso9660Directory::K3bIso9660Directory( K3bIso9660* archive, + const QString& isoName, + const QString& name, + int access, + int date, + int adate, + int cdate, + const QString& user, + const QString& group, + const QString& symlink, + unsigned int pos, + unsigned int size ) + : K3bIso9660Entry( archive, isoName, name, access, date, adate, cdate, user, group, symlink ), + m_bExpanded( size == 0 ), // we can only expand entries that represent an actual directory + m_startSector(pos), + m_size(size) +{ + m_entries.setAutoDelete( true ); +} + +K3bIso9660Directory::~K3bIso9660Directory() +{ +} + + +void K3bIso9660Directory::expand() +{ + if( !m_bExpanded ) { + archive()->dirent = this; + if( ProcessDir( &K3bIso9660::read_callback, m_startSector, m_size, &K3bIso9660::isofs_callback, archive() ) ) + kdDebug() << "(K3bIso9660) failed to expand dir: " << name() << " with size: " << m_size << endl; + + m_bExpanded = true; + } +} + + +QStringList K3bIso9660Directory::entries() const +{ + // create a fake const method to fool the user ;) + const_cast<K3bIso9660Directory*>(this)->expand(); + + QStringList l; + + QDictIterator<K3bIso9660Entry> it( m_entries ); + for( ; it.current(); ++it ) + l.append( it.currentKey() ); + + return l; +} + + +QStringList K3bIso9660Directory::iso9660Entries() const +{ + // create a fake const method to fool the user ;) + const_cast<K3bIso9660Directory*>(this)->expand(); + + QStringList l; + + QDictIterator<K3bIso9660Entry> it( m_iso9660Entries ); + for( ; it.current(); ++it ) + l.append( it.currentKey() ); + + return l; +} + + +K3bIso9660Entry* K3bIso9660Directory::entry( const QString& n ) +{ + if( n.isEmpty() ) + return 0; + + expand(); + + QString name(n); + + // trailing slash ? -> remove + if( name.length() > 1 && name[name.length()-1] == '/' ) { + name.truncate( name.length()-1 ); + } + + int pos = name.find( '/' ); + while( pos == 0 ) { + if( name.length() > 1 ) { + name = name.mid( 1 ); // remove leading slash + pos = name.find( '/' ); // look again + } + else // "/" + return this; + } + + if ( pos != -1 ) { + QString left = name.left( pos ); + QString right = name.mid( pos + 1 ); + + K3bIso9660Entry* e = m_entries[ left ]; + if ( !e || !e->isDirectory() ) + return 0; + return static_cast<K3bIso9660Directory*>(e)->entry( right ); + } + + return m_entries[ name ]; +} + + +K3bIso9660Entry* K3bIso9660Directory::iso9660Entry( const QString& n ) +{ + if( n.isEmpty() ) + return 0; + + expand(); + + QString name(n); + + // trailing slash ? -> remove + if( name.length() > 1 && name[name.length()-1] == '/' ) { + name.truncate( name.length()-1 ); + } + + int pos = name.find( '/' ); + while( pos == 0 ) { + if( name.length() > 1 ) { + name = name.mid( 1 ); // remove leading slash + pos = name.find( '/' ); // look again + } + else // "/" + return this; + } + + if ( pos != -1 ) { + QString left = name.left( pos ); + QString right = name.mid( pos + 1 ); + + K3bIso9660Entry* e = m_iso9660Entries[ left ]; + if ( !e || !e->isDirectory() ) + return 0; + return static_cast<K3bIso9660Directory*>(e)->iso9660Entry( right ); + } + + return m_iso9660Entries[ name ]; +} + + +const K3bIso9660Entry* K3bIso9660Directory::entry( const QString& name ) const +{ + return const_cast<K3bIso9660Directory*>(this)->entry( name ); +} + + +const K3bIso9660Entry* K3bIso9660Directory::iso9660Entry( const QString& name ) const +{ + return const_cast<K3bIso9660Directory*>(this)->iso9660Entry( name ); +} + + +void K3bIso9660Directory::addEntry( K3bIso9660Entry* entry ) +{ + m_entries.insert( entry->name(), entry ); + m_iso9660Entries.insert( entry->isoName(), entry ); +} + + + + + +class K3bIso9660::Private +{ +public: + Private() + : cdDevice(0), + fd(-1), + isOpen(false), + startSector(0), + plainIso9660(false), + backend(0) { + } + + QPtrList<K3bIso9660Directory> elToritoDirs; + QPtrList<K3bIso9660Directory> jolietDirs; + QPtrList<K3bIso9660Directory> isoDirs; + QPtrList<K3bIso9660Directory> rrDirs; // RockRidge + + K3bIso9660SimplePrimaryDescriptor primaryDesc; + + K3bDevice::Device* cdDevice; + int fd; + + bool isOpen; + + // only used for direkt K3bDevice::Device access + unsigned int startSector; + + bool plainIso9660; + + K3bIso9660Backend* backend; +}; + + +K3bIso9660::K3bIso9660( const QString& filename ) + : m_filename( filename ) +{ + d = new Private(); +} + + +K3bIso9660::K3bIso9660( int fd ) +{ + d = new Private(); + d->fd = fd; +} + + +K3bIso9660::K3bIso9660( K3bIso9660Backend* backend ) +{ + d = new Private(); + d->backend = backend; +} + + +K3bIso9660::K3bIso9660( K3bDevice::Device* dev, unsigned int startSector ) +{ + d = new Private(); + d->cdDevice = dev; + d->startSector = startSector; +} + + +K3bIso9660::~K3bIso9660() +{ + close(); + delete d->backend; + delete d; +} + + +void K3bIso9660::setStartSector( unsigned int startSector ) +{ + d->startSector = startSector; +} + + +void K3bIso9660::setPlainIso9660( bool b ) +{ + d->plainIso9660 = b; +} + + +bool K3bIso9660::plainIso9660() const +{ + return d->plainIso9660; +} + + +int K3bIso9660::read( unsigned int sector, char* data, int count ) +{ + if( count == 0 ) + return 0; + else + return d->backend->read( sector, data, count ); +} + + +void K3bIso9660::addBoot(struct el_torito_boot_descriptor* bootdesc) +{ + int i,size; + boot_head boot; + boot_entry *be; + QString path; + K3bIso9660File *entry; + + entry=new K3bIso9660File( this, "Catalog", "Catalog", dirent->permissions() & ~S_IFDIR, + dirent->date(), dirent->adate(), dirent->cdate(), + dirent->user(), dirent->group(), QString::null, + isonum_731(bootdesc->boot_catalog), 2048 ); + dirent->addEntry(entry); + if (!ReadBootTable(&K3bIso9660::read_callback,isonum_731(bootdesc->boot_catalog),&boot,this)) { + i=1; + be=boot.defentry; + while (be) { + size=BootImageSize(&K3bIso9660::read_callback, + isonum_711(((struct default_entry*) be->data)->media), + isonum_731(((struct default_entry*) be->data)->start), + isonum_721(((struct default_entry*) be->data)->seccount), + this); + path="Default Image"; + if (i>1) path += " (" + QString::number(i) + ")"; + entry=new K3bIso9660File( this, path, path, dirent->permissions() & ~S_IFDIR, + dirent->date(), dirent->adate(), dirent->cdate(), + dirent->user(), dirent->group(), QString::null, + isonum_731(((struct default_entry*) be->data)->start), size<<9 ); + dirent->addEntry(entry); + be=be->next; + i++; + } + + FreeBootTable(&boot); + } +} + + +bool K3bIso9660::open() +{ + if( d->isOpen ) + return true; + + if( !d->backend ) { + // create a backend + + if( !m_filename.isEmpty() ) + d->backend = new K3bIso9660FileBackend( m_filename ); + + else if( d->fd > 0 ) + d->backend = new K3bIso9660FileBackend( d->fd ); + + else if( d->cdDevice ) { + // now check if we have a scrambled video dvd + if( d->cdDevice->copyrightProtectionSystemType() == 1 ) { + + kdDebug() << "(K3bIso9660) found encrypted dvd. using libdvdcss." << endl; + + // open the libdvdcss stuff + d->backend = new K3bIso9660LibDvdCssBackend( d->cdDevice ); + if( !d->backend->open() ) { + // fallback to devicebackend + delete d->backend; + d->backend = new K3bIso9660DeviceBackend( d->cdDevice ); + } + } + else + d->backend = new K3bIso9660DeviceBackend( d->cdDevice ); + } + } + + d->isOpen = d->backend->open(); + if( !d->isOpen ) + return false; + + iso_vol_desc *desc; + QString path,tmp,uid,gid; + k3b_struct_stat buf; + int access,c_i,c_j; + struct el_torito_boot_descriptor* bootdesc; + + + /* We'll use the permission and user/group of the 'host' file except + * in Rock Ridge, where the permissions are stored on the file system + */ + if ( k3b_stat( QFile::encodeName(m_filename), &buf ) < 0 ) { + /* defaults, if stat fails */ + memset(&buf,0,sizeof(k3b_struct_stat)); + buf.st_mode=0777; + } + uid.setNum(buf.st_uid); + gid.setNum(buf.st_gid); + access = buf.st_mode & ~S_IFMT; + + + int c_b=1; + c_i=1;c_j=1; + + desc = ReadISO9660( &K3bIso9660::read_callback, d->startSector, this ); + + if (!desc) { + kdDebug() << "K3bIso9660::openArchive no volume descriptors" << endl; + close(); + return false; + } + + while (desc) { + + m_rr = false; + + switch (isonum_711(desc->data.type)) { + case ISO_VD_BOOT: + + bootdesc=(struct el_torito_boot_descriptor*) &(desc->data); + if( !memcmp( EL_TORITO_ID, bootdesc->system_id, ISODCL(8,39) ) ) { + path="El Torito Boot"; + if( c_b > 1 ) + path += " (" + QString::number(c_b) + ")"; + + dirent = new K3bIso9660Directory( this, path, path, access | S_IFDIR, + buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, QString::null ); + d->elToritoDirs.append( dirent ); + + addBoot(bootdesc); + c_b++; + } + break; + + case ISO_VD_PRIMARY: + createSimplePrimaryDesc( (struct iso_primary_descriptor*)&desc->data ); + // fall through + case ISO_VD_SUPPLEMENTARY: + { + struct iso_primary_descriptor* primaryDesc = (struct iso_primary_descriptor*)&desc->data; + struct iso_directory_record* idr = (struct iso_directory_record*)&primaryDesc->root_directory_record; + + m_joliet = JolietLevel(&desc->data); + + // skip joliet in plain iso mode + if( m_joliet && plainIso9660() ) + break; + + if (m_joliet) { + path = "Joliet level " + QString::number(m_joliet); + if( c_j > 1 ) + path += " (" + QString::number(c_j) + ")"; + } + else { + path = QString::fromLocal8Bit( primaryDesc->volume_id, 32 ); + if( c_i > 1 ) + path += " (" + QString::number(c_i) + ")"; + } + + dirent = new K3bIso9660Directory( this, path, path, access | S_IFDIR, + buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, QString::null ); + + // expand the root entry + ProcessDir( &K3bIso9660::read_callback, isonum_733(idr->extent),isonum_733(idr->size),&K3bIso9660::isofs_callback,this); + + if (m_joliet) + c_j++; + else + c_i++; + + if( m_joliet ) + d->jolietDirs.append( dirent ); + else { + if( m_rr ) + d->rrDirs.append( dirent ); + d->isoDirs.append( dirent ); + } + + break; + } + } + desc = desc->next; + } + + FreeISO9660(desc); + + return true; +} + + +bool K3bIso9660::isOpen() const +{ + return d->isOpen; +} + + +void K3bIso9660::createSimplePrimaryDesc( struct iso_primary_descriptor* desc ) +{ + d->primaryDesc.volumeId = QString::fromLocal8Bit( desc->volume_id, 32 ).stripWhiteSpace(); + d->primaryDesc.systemId = QString::fromLocal8Bit( desc->system_id, 32 ).stripWhiteSpace(); + d->primaryDesc.volumeSetId = QString::fromLocal8Bit( desc->volume_set_id, 128 ).stripWhiteSpace(); + d->primaryDesc.publisherId = QString::fromLocal8Bit( desc->publisher_id, 128 ).stripWhiteSpace(); + d->primaryDesc.preparerId = QString::fromLocal8Bit( desc->preparer_id, 128 ).stripWhiteSpace(); + d->primaryDesc.applicationId = QString::fromLocal8Bit( desc->application_id, 128 ).stripWhiteSpace(); + d->primaryDesc.volumeSetSize = isonum_723(desc->volume_set_size); + d->primaryDesc.volumeSetNumber = isonum_723(desc->volume_set_size); + d->primaryDesc.logicalBlockSize = isonum_723(desc->logical_block_size); + d->primaryDesc.volumeSpaceSize = isonum_733(desc->volume_space_size); +} + + +void K3bIso9660::close() +{ + if( d->isOpen ) { + d->backend->close(); + + // Since the first isoDir is the KArchive + // root we must not delete it but all the + // others. + + d->elToritoDirs.setAutoDelete(true); + d->jolietDirs.setAutoDelete(true); + d->isoDirs.setAutoDelete(true); + d->elToritoDirs.clear(); + d->jolietDirs.clear(); + d->isoDirs.clear(); + + d->isOpen = false; + } +} + + +const K3bIso9660Directory* K3bIso9660::firstJolietDirEntry() const +{ + return d->jolietDirs.first(); +} + + +const K3bIso9660Directory* K3bIso9660::firstIsoDirEntry() const +{ + return d->isoDirs.first(); +} + + +const K3bIso9660Directory* K3bIso9660::firstElToritoEntry() const +{ + return d->elToritoDirs.first(); +} + + +const K3bIso9660Directory* K3bIso9660::firstRRDirEntry() const +{ + return d->rrDirs.first(); +} + + +const K3bIso9660SimplePrimaryDescriptor& K3bIso9660::primaryDescriptor() const +{ + return d->primaryDesc; +} + + +void K3bIso9660::debug() const +{ + if( isOpen() ) { + kdDebug() << "System Id: " << primaryDescriptor().systemId << endl; + kdDebug() << "Volume Id: " << primaryDescriptor().volumeId << endl; + kdDebug() << "Volume Set Id: " << primaryDescriptor().volumeSetId << endl; + kdDebug() << "Preparer Id: " << primaryDescriptor().preparerId << endl; + kdDebug() << "Publisher Id: " << primaryDescriptor().publisherId << endl; + kdDebug() << "Application Id: " << primaryDescriptor().applicationId << endl; + kdDebug() << "Volume Set Size: " << primaryDescriptor().volumeSetSize << endl; + kdDebug() << "Volume Set Number: " << primaryDescriptor().volumeSetNumber << endl; + + if( firstIsoDirEntry() ) { + kdDebug() << "First ISO Dir entry:" << endl; + kdDebug() << "----------------------------------------------" << endl; + debugEntry( firstIsoDirEntry(), 0 ); + kdDebug() << "----------------------------------------------" << endl << endl; + } + if( firstRRDirEntry() ) { + kdDebug() << "First RR Dir entry:" << endl; + kdDebug() << "----------------------------------------------" << endl; + debugEntry( firstRRDirEntry(), 0 ); + kdDebug() << "----------------------------------------------" << endl << endl; + } + if( firstJolietDirEntry() ) { + kdDebug() << "First Joliet Dir entry:" << endl; + kdDebug() << "----------------------------------------------" << endl; + debugEntry( firstJolietDirEntry(), 0 ); + kdDebug() << "----------------------------------------------" << endl << endl; + } + } +} + + +void K3bIso9660::debugEntry( const K3bIso9660Entry* entry, int depth ) const +{ + if( !entry ) { + kdDebug() << "(K3bIso9660::debugEntry) null entry." << endl; + return; + } + + QString spacer; + spacer.fill( ' ', depth*3 ); + kdDebug() << spacer << "- " << entry->name() << " (" << entry->isoName() << ")" << endl; + if( entry->isDirectory() ) { + const K3bIso9660Directory* dir = dynamic_cast<const K3bIso9660Directory*>(entry); + QStringList entries = dir->entries(); + for( QStringList::const_iterator it = entries.begin(); it != entries.end(); ++it ) { + debugEntry( dir->entry( *it ), depth+1 ); + } + } +} + + +K3bIso9660SimplePrimaryDescriptor::K3bIso9660SimplePrimaryDescriptor() + : volumeSetSize(0), + volumeSetNumber(0), + logicalBlockSize(0), + volumeSpaceSize(0) +{ +} + + +bool operator==( const K3bIso9660SimplePrimaryDescriptor& d1, + const K3bIso9660SimplePrimaryDescriptor& d2 ) +{ + return( d1.volumeId == d2.volumeId && + d1.systemId == d2.systemId && + d1.volumeSetId == d2.volumeSetId && + d1.publisherId == d2.publisherId && + d1.preparerId == d2.preparerId && + d1.applicationId == d2.applicationId && + d1.volumeSetSize == d2.volumeSetSize && + d1.volumeSetNumber == d2.volumeSetNumber && + d1.logicalBlockSize == d2.logicalBlockSize && + d1.volumeSpaceSize == d2.volumeSpaceSize ); +} + + +bool operator!=( const K3bIso9660SimplePrimaryDescriptor& d1, + const K3bIso9660SimplePrimaryDescriptor& d2 ) +{ + return( d1.volumeId != d2.volumeId || + d1.systemId != d2.systemId || + d1.volumeSetId != d2.volumeSetId || + d1.publisherId != d2.publisherId || + d1.preparerId != d2.preparerId || + d1.applicationId != d2.applicationId || + d1.volumeSetSize != d2.volumeSetSize || + d1.volumeSetNumber != d2.volumeSetNumber || + d1.logicalBlockSize != d2.logicalBlockSize || + d1.volumeSpaceSize != d2.volumeSpaceSize ); +} diff --git a/libk3b/tools/k3biso9660.h b/libk3b/tools/k3biso9660.h new file mode 100644 index 0000000..7fc52d9 --- /dev/null +++ b/libk3b/tools/k3biso9660.h @@ -0,0 +1,453 @@ +/* + * + * $Id: k3biso9660.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_ISO9660_H_ +#define _K3B_ISO9660_H_ + +#include <sys/stat.h> +#include <sys/types.h> + +#include <qdatetime.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qdict.h> + +#include "k3b_export.h" + + +namespace K3bDevice { + class Device; +} + +class K3bIso9660; +class K3bIso9660Backend; +struct iso_directory_record; +struct el_torito_boot_descriptor; +struct iso_primary_descriptor; + +typedef long sector_t; + + + +/** + * Simplyfied primary descriptor which just contains the fields + * used by K3b. + */ +class LIBK3B_EXPORT K3bIso9660SimplePrimaryDescriptor +{ + public: + /** + * Creates an empty descriptor + */ + K3bIso9660SimplePrimaryDescriptor(); + + QString volumeId; + QString systemId; + QString volumeSetId; + QString publisherId; + QString preparerId; + QString applicationId; + int volumeSetSize; + int volumeSetNumber; + long logicalBlockSize; + long long volumeSpaceSize; +}; + + +LIBK3B_EXPORT bool operator==( const K3bIso9660SimplePrimaryDescriptor& d1, + const K3bIso9660SimplePrimaryDescriptor& d2 ); +LIBK3B_EXPORT bool operator!=( const K3bIso9660SimplePrimaryDescriptor& d1, + const K3bIso9660SimplePrimaryDescriptor& d2 ); + + +/** + * Base class for all entries in a K3bIso9660 archive. A lot has been copied + * from KArchive. + */ +class LIBK3B_EXPORT K3bIso9660Entry +{ + public: + K3bIso9660Entry( K3bIso9660* archive, + const QString& isoName, + const QString& name, + int access, + int date, + int adate, + int cdate, + const QString& user, + const QString& group, + const QString& symlink ); + virtual ~K3bIso9660Entry(); + + int adate() const { return m_adate; } + int cdate() const { return m_cdate; } + + /** + * Creation date of the file. + * @return the creation date + */ + QDateTime datetime() const; + + /** + * Creation date of the file. + * @return the creation date in seconds since 1970 + */ + int date() const { return m_date; } + + /** + * Name of the file without path. + * @return The file name without path. + */ + const QString& name() const { return m_name; } + + /** + * \return The raw name as saved in the ISO9660 tree + */ + const QString& isoName() const { return m_isoName; } + + /** + * The permissions and mode flags as returned by the stat() function + * in st_mode. + * @return the permissions + */ + mode_t permissions() const { return m_access; } + + /** + * User who created the file. + * @return the owner of the file + */ + const QString& user() const { return m_user; } + + /** + * Group of the user who created the file. + * @return the group of the file + */ + const QString& group() const { return m_group; } + + /** + * Symlink if there is one. + * @return the symlink, or QString::null + */ + const QString& symlink() const { return m_symlink; } + + /** + * Checks whether the entry is a file. + * @return true if this entry is a file + */ + virtual bool isFile() const { return false; } + + /** + * Checks whether the entry is a directory. + * @return true if this entry is a directory + */ + virtual bool isDirectory() const { return false; } + + K3bIso9660* archive() const { return m_archive; } + + private: + int m_adate; + int m_cdate; + QString m_name; + QString m_isoName; + int m_date; + mode_t m_access; + QString m_user; + QString m_group; + QString m_symlink; + K3bIso9660* m_archive; +}; + + +class LIBK3B_EXPORT K3bIso9660Directory : public K3bIso9660Entry +{ + public: + K3bIso9660Directory( K3bIso9660* archive, + const QString& isoName, + const QString& name, + int access, + int date, + int adate, + int cdate, + const QString& user, + const QString& group, + const QString& symlink, + unsigned int pos = 0, + unsigned int size = 0 ); + ~K3bIso9660Directory(); + + /** + * Returns a list of sub-entries. + * @return the names of all entries in this directory (filenames, no path). + */ + QStringList entries() const; + + /** + * Returns the entry with the given name. + * @param name may be "test1", "mydir/test3", "mydir/mysubdir/test3", etc. + * @return a pointer to the entry in the directory. + */ + K3bIso9660Entry* entry( const QString& name ); + + /** + * Returns the entry with the given name. + * @param name may be "test1", "mydir/test3", "mydir/mysubdir/test3", etc. + * @return a pointer to the entry in the directory. + */ + const K3bIso9660Entry* entry( const QString& name ) const; + + /** + * Returns a list of sub-entries. + * Searches for Iso9660 names. + * @return the names of all entries in this directory (filenames, no path). + */ + QStringList iso9660Entries() const; + + /** + * Returns the entry with the given name. + * Searches for Iso9660 names. + * @param name may be "test1", "mydir/test3", "mydir/mysubdir/test3", etc. + * @return a pointer to the entry in the directory. + */ + K3bIso9660Entry* iso9660Entry( const QString& name ); + + /** + * Returns the entry with the given name. + * Searches for Iso9660 names. + * @param name may be "test1", "mydir/test3", "mydir/mysubdir/test3", etc. + * @return a pointer to the entry in the directory. + */ + const K3bIso9660Entry* iso9660Entry( const QString& name ) const; + + /** + * @internal + * Adds a new entry to the directory. + */ + void addEntry( K3bIso9660Entry* ); + + /** + * Checks whether this entry is a directory. + * @return true, since this entry is a directory + */ + bool isDirectory() const { return true; } + + private: + void expand(); + + QDict<K3bIso9660Entry> m_entries; + QDict<K3bIso9660Entry> m_iso9660Entries; + + bool m_bExpanded; + unsigned int m_startSector; + unsigned int m_size; +}; + + +class LIBK3B_EXPORT K3bIso9660File : public K3bIso9660Entry +{ + public: + /** + * @param pos start sector + */ + K3bIso9660File( K3bIso9660* archive, + const QString& isoName, + const QString& name, + int access, + int date, + int adate, + int cdate, + const QString& user, + const QString& group, + const QString& symlink, + unsigned int pos, + unsigned int size ); + ~K3bIso9660File(); + + bool isFile() const { return true; } + + void setZF( char algo[2], char parms[2], int realsize ); + int realsize() const { return m_realsize; } + + /** + * @return size in bytes. + */ + unsigned int size() const { return m_size; } + + /** + * Returnes the startSector of the file. + */ + unsigned int startSector() const { return m_startSector; } + + /** + * Returnes the startOffset of the file in bytes. + */ + unsigned long long startPostion() const { return (unsigned long long)m_startSector * 2048; } + + /** + * @param pos offset in bytes + * @param len max number of bytes to read + */ + int read( unsigned int pos, char* data, int len ) const; + + /** + * Copy this file to a url. + */ + bool copyTo( const QString& url ) const; + + private: + char m_algo[2]; + char m_parms[2]; + int m_realsize; + + unsigned int m_curpos; + unsigned int m_startSector; + unsigned int m_size; +}; + + +/** + * This class is based on the KIso class by + * Gy�gy Szombathelyi <[email protected]>. + * A lot has been changed and bugfixed. + * The API has been improved to be useful. + * + * Due to the stupid Qt which does not support large files as default + * we cannot use QIODevice with DVDs! That's why we have our own + * reading code which is not allowed by KArchive (which is limited to int + * by the way... who the hell designed this?) + * I also removed the KArchive inheritance because of the named reasons. + * So this stuff contains a lot KArchive code which has been made usable. + * + * That does not mean that this class is well designed. No, it's not. :) + * + * Opening a K3bIso9660 object should be fast since creation of the directory + * and file entries is not done until a call to K3bIso9660Directory::entries. +*/ +class LIBK3B_EXPORT K3bIso9660 +{ + public: + /** + * Creates an instance that operates on the given filename. + * using the compression filter associated to given mimetype. + * + * @param filename is a local path (e.g. "/home/weis/myfile.tgz") + */ + K3bIso9660( const QString& filename ); + + /** + * Special case which always reads the TOC from the specified sector + * thus supporting multisession CDs. + */ + K3bIso9660( K3bDevice::Device* dev, unsigned int startSector = 0 ); + + /** + * @param fd open file descriptor + */ + K3bIso9660( int fd ); + + /** + * Directly specify the backend to read from. + * K3bIso9660 will take ownership of the backend and delete it. + */ + K3bIso9660( K3bIso9660Backend* ); + + /** + * If the .iso is still opened, then it will be + * closed automatically by the destructor. + */ + virtual ~K3bIso9660(); + + /** + * Set where to start reading in the source. + */ + void setStartSector( unsigned int startSector ); + + /** + * If set to true before opening K3bIso9660 will ignore RR and joliet extensions + * and only create plain iso9660 names. + */ + void setPlainIso9660( bool ); + + bool plainIso9660() const; + + /** + * Opens the archive for reading. + * Parses the directory listing of the archive + * and creates the K3bIso9660Directory/K3bIso9660File entries. + */ + bool open(); + + bool isOpen() const; + + /** + * Closes everything. + * This is also called in the destructor + */ + void close(); + + /** + * @param sector startsector + * @param len number of sectors + * @return number of sectors read or -1 on error + */ + int read( unsigned int sector, char* data, int len ); + + /** + * The name of the os file, as passed to the constructor + * Null if you did not use the QString constructor. + */ + const QString& fileName() { return m_filename; } + + const K3bIso9660Directory* firstJolietDirEntry() const; + const K3bIso9660Directory* firstRRDirEntry() const; + const K3bIso9660Directory* firstIsoDirEntry() const; + const K3bIso9660Directory* firstElToritoEntry() const; + + /** + * @returns 0 if no joliet desc could be found + * the joliet level (1-3) otherwise + */ + int jolietLevel() const { return m_joliet; } + + const K3bIso9660SimplePrimaryDescriptor& primaryDescriptor() const; + + void debug() const; + + private: + /** + * @internal + */ + void addBoot( struct el_torito_boot_descriptor* bootdesc ); + void createSimplePrimaryDesc( struct iso_primary_descriptor* desc ); + + void debugEntry( const K3bIso9660Entry*, int depth ) const; + + int m_joliet; + + // only used for creation + static int read_callback( char* buf, sector_t start, int len, void* udata ); + static int isofs_callback( struct iso_directory_record* idr, void *udata ); + K3bIso9660Directory *dirent; + bool m_rr; + friend class K3bIso9660Directory; + + private: + QString m_filename; + + class Private; + Private * d; +}; + +#endif diff --git a/libk3b/tools/k3biso9660backend.cpp b/libk3b/tools/k3biso9660backend.cpp new file mode 100644 index 0000000..aacc079 --- /dev/null +++ b/libk3b/tools/k3biso9660backend.cpp @@ -0,0 +1,239 @@ +/* + * + * $Id: sourceheader 380067 2005-01-19 13:03:46Z trueg $ + * Copyright (C) 2005 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3biso9660backend.h" +#include "k3blibdvdcss.h" + +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <qfile.h> + +#include <k3bdevice.h> + + +// +// K3bIso9660DeviceBackend ----------------------------------- +// + +K3bIso9660DeviceBackend::K3bIso9660DeviceBackend( K3bDevice::Device* dev ) + : m_device( dev ), + m_isOpen(false) +{ +} + + +K3bIso9660DeviceBackend::~K3bIso9660DeviceBackend() +{ + close(); +} + + +bool K3bIso9660DeviceBackend::open() +{ + if( m_isOpen ) + return true; + else if( m_device->open() ) { + // set optimal reading speed + m_device->setSpeed( 0xffff, 0xffff ); + m_isOpen = true; + return true; + } + else + return false; +} + + +void K3bIso9660DeviceBackend::close() +{ + if( m_isOpen ) { + m_isOpen = false; + m_device->close(); + } +} + + +int K3bIso9660DeviceBackend::read( unsigned int sector, char* data, int len ) +{ + if( isOpen() ) { + // + // split the number of sectors to be read + // FIXME: use a "real" value, not some arbitrary one + // + static const int maxReadSectors = 20; + int sectorsRead = 0; + int retries = 10; // TODO: no fixed value + while( retries ) { + int read = QMIN(len-sectorsRead, maxReadSectors); + if( !m_device->read10( (unsigned char*)(data+sectorsRead*2048), + read*2048, + sector+sectorsRead, + read ) ) { + retries--; + } + else { + sectorsRead += read; + retries = 10; // new retires for every read part + if( sectorsRead == len ) + return len; + } + } + } + + return -1; +} + + +// +// K3bIso9660FileBackend ----------------------------------- +// + +K3bIso9660FileBackend::K3bIso9660FileBackend( const QString& filename ) + : m_filename( filename ), + m_fd( -1 ), + m_closeFd( true ) +{ +} + + +K3bIso9660FileBackend::K3bIso9660FileBackend( int fd ) + : m_fd( fd ), + m_closeFd( false ) +{ +} + + +K3bIso9660FileBackend::~K3bIso9660FileBackend() +{ + close(); +} + + +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + +bool K3bIso9660FileBackend::open() +{ + if( m_fd > 0 ) + return true; + else { + m_fd = ::open( QFile::encodeName( m_filename ), O_RDONLY|O_LARGEFILE ); + return ( m_fd > 0 ); + } +} + + +void K3bIso9660FileBackend::close() +{ + if( m_closeFd && m_fd > 0 ) { + ::close( m_fd ); + m_fd = -1; + } +} + + + +bool K3bIso9660FileBackend::isOpen() const +{ + return ( m_fd > 0 ); +} + + +int K3bIso9660FileBackend::read( unsigned int sector, char* data, int len ) +{ + int read = 0; + if( ::lseek( m_fd, static_cast<unsigned long long>(sector)*2048, SEEK_SET ) != -1 ) + if( (read = ::read( m_fd, data, len*2048 )) != -1 ) + return read / 2048; + + return -1; +} + + + +// +// K3bIso9660LibDvdCssBackend ----------------------------------- +// + +K3bIso9660LibDvdCssBackend::K3bIso9660LibDvdCssBackend( K3bDevice::Device* dev ) + : m_device( dev ), + m_libDvdCss( 0 ) +{ +} + + +K3bIso9660LibDvdCssBackend::~K3bIso9660LibDvdCssBackend() +{ + close(); +} + + +bool K3bIso9660LibDvdCssBackend::open() +{ + if( !m_libDvdCss ) { + // open the libdvdcss stuff + m_libDvdCss = K3bLibDvdCss::create(); + + if( m_libDvdCss ) { + + if( !m_libDvdCss->open( m_device ) || + !m_libDvdCss->crackAllKeys() ) { + kdDebug() << "(K3bIso9660LibDvdCssBackend) Failed to retrieve all CSS keys." << endl; + close(); + } + } + else + kdDebug() << "(K3bIso9660LibDvdCssBackend) failed to open libdvdcss." << endl; + } + + return ( m_libDvdCss != 0 ); +} + + +void K3bIso9660LibDvdCssBackend::close() +{ + delete m_libDvdCss; + m_libDvdCss = 0; +} + + + +bool K3bIso9660LibDvdCssBackend::isOpen() const +{ + return ( m_libDvdCss != 0 ); +} + + +int K3bIso9660LibDvdCssBackend::read( unsigned int sector, char* data, int len ) +{ + int read = -1; + + if( isOpen() ) { + int retries = 10; // TODO: no fixed value + while( retries && !m_libDvdCss->readWrapped( reinterpret_cast<void*>(data), + sector, + len ) ) + retries--; + + if( retries > 0 ) + read = len; + } + + return read; +} + diff --git a/libk3b/tools/k3biso9660backend.h b/libk3b/tools/k3biso9660backend.h new file mode 100644 index 0000000..78937e5 --- /dev/null +++ b/libk3b/tools/k3biso9660backend.h @@ -0,0 +1,95 @@ +/* + * + * $Id: sourceheader 380067 2005-01-19 13:03:46Z trueg $ + * Copyright (C) 2005 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_ISO9660_BACKEND_H_ +#define _K3B_ISO9660_BACKEND_H_ + +#include <qstring.h> + +#include "k3b_export.h" + +namespace K3bDevice { + class Device; +} + +class K3bLibDvdCss; + + +class K3bIso9660Backend +{ + public: + K3bIso9660Backend() {} + virtual ~K3bIso9660Backend() {} + + virtual bool open() = 0; + virtual void close() = 0; + virtual bool isOpen() const = 0; + virtual int read( unsigned int sector, char* data, int len ) = 0; +}; + + +class K3bIso9660DeviceBackend : public K3bIso9660Backend +{ + public: + LIBK3B_EXPORT K3bIso9660DeviceBackend( K3bDevice::Device* dev ); + ~K3bIso9660DeviceBackend(); + + bool open(); + void close(); + bool isOpen() const { return m_isOpen; } + int read( unsigned int sector, char* data, int len ); + + private: + K3bDevice::Device* m_device; + bool m_isOpen; +}; + + +class K3bIso9660FileBackend : public K3bIso9660Backend +{ + public: + LIBK3B_EXPORT K3bIso9660FileBackend( const QString& filename ); + K3bIso9660FileBackend( int fd ); + ~K3bIso9660FileBackend(); + + bool open(); + void close(); + bool isOpen() const; + int read( unsigned int sector, char* data, int len ); + + private: + QString m_filename; + int m_fd; + bool m_closeFd; +}; + + +class K3bIso9660LibDvdCssBackend : public K3bIso9660Backend +{ + public: + LIBK3B_EXPORT K3bIso9660LibDvdCssBackend( K3bDevice::Device* ); + ~K3bIso9660LibDvdCssBackend(); + + bool open(); + void close(); + bool isOpen() const; + int read( unsigned int sector, char* data, int len ); + + private: + K3bDevice::Device* m_device; + K3bLibDvdCss* m_libDvdCss; +}; + +#endif diff --git a/libk3b/tools/k3blibdvdcss.cpp b/libk3b/tools/k3blibdvdcss.cpp new file mode 100644 index 0000000..0a8d1f0 --- /dev/null +++ b/libk3b/tools/k3blibdvdcss.cpp @@ -0,0 +1,307 @@ +/* + * + * $Id: k3blibdvdcss.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3blibdvdcss.h" + +#include <k3bdevice.h> +#include <k3biso9660.h> +#include <k3biso9660backend.h> + +#include <qfile.h> +#include <qcstring.h> +#include <qvaluevector.h> +#include <qpair.h> + +#include <dlfcn.h> + + +void* K3bLibDvdCss::s_libDvdCss = 0; +int K3bLibDvdCss::s_counter = 0; + + +extern "C" { + struct dvdcss_s; + typedef struct dvdcss_s* dvdcss_t; + + dvdcss_t (*k3b_dvdcss_open)(char*); + int (*k3b_dvdcss_close)( dvdcss_t ); + int (*k3b_dvdcss_seek)( dvdcss_t, int, int ); + int (*k3b_dvdcss_read)( dvdcss_t, void*, int, int ); +} + + + +class K3bLibDvdCss::Private +{ +public: + Private() + :dvd(0) { + } + + dvdcss_t dvd; + K3bDevice::Device* device; + QValueVector< QPair<int,int> > titleOffsets; + int currentSector; + bool currentSectorInTitle; +}; + +K3bLibDvdCss::K3bLibDvdCss() +{ + d = new Private(); + s_counter++; +} + + +K3bLibDvdCss::~K3bLibDvdCss() +{ + close(); + delete d; + s_counter--; + if( s_counter == 0 ) { + dlclose( s_libDvdCss ); + s_libDvdCss = 0; + } +} + + +bool K3bLibDvdCss::open( K3bDevice::Device* dev ) +{ + d->device = dev; + dev->close(); + d->dvd = k3b_dvdcss_open( const_cast<char*>( QFile::encodeName(dev->blockDeviceName()).data() ) ); + d->currentSector = 0; + d->currentSectorInTitle = false; + return ( d->dvd != 0 ); +} + + +void K3bLibDvdCss::close() +{ + if( d->dvd ) + k3b_dvdcss_close( d->dvd ); + d->dvd = 0; +} + + +int K3bLibDvdCss::seek( int sector, int flags ) +{ + return k3b_dvdcss_seek( d->dvd, sector, flags ); +} + + +int K3bLibDvdCss::read( void* buffer, int sectors, int flags ) +{ + return k3b_dvdcss_read( d->dvd, buffer, sectors, flags ); +} + + +int K3bLibDvdCss::readWrapped( void* buffer, int firstSector, int sectors ) +{ + // 1. are we in a title? + // 2. does a new title start in the read sector area? + // - see below, set title if firstSector is the first sector of a new title + // 3. does a title end in the read sector area? + // 3.1 does a previous title end + // 3.2 does the title from 2. already end + + // we need to seek to the first sector. Otherwise we get faulty data. + bool needToSeek = ( firstSector != d->currentSector || firstSector == 0 ); + bool inTitle = false; + bool startOfTitle = false; + + // + // Make sure we never read encrypted and unencrypted data at once since libdvdcss + // only decrypts the whole area of read sectors or nothing at all. + // + for( unsigned int i = 0; i < d->titleOffsets.count(); ++i ) { + int titleStart = d->titleOffsets[i].first; + int titleEnd = titleStart + d->titleOffsets[i].second - 1; + + // update key when entrering a new title + // FIXME: we also need this if we seek into a new title (not only the start of the title) + if( titleStart == firstSector ) + startOfTitle = needToSeek = inTitle = true; + + // check if a new title or non-title area starts inside the read sector range + if( firstSector < titleStart && firstSector+sectors > titleStart ) { + kdDebug() << "(K3bLibDvdCss) title start inside of sector range (" + << firstSector << "-" << (firstSector+sectors-1) + << "). only reading " << (titleStart - firstSector) << " sectors up to title offset " + << (titleStart-1) << endl; + sectors = titleStart - firstSector; + } + + if( firstSector < titleEnd && firstSector+sectors > titleEnd ) { + kdDebug() << "(K3bLibDvdCss) title end inside of sector range (" + << firstSector << "-" << (firstSector+sectors-1) + << "). only reading " << (titleEnd - firstSector + 1) << " sectors up to title offset " + << titleEnd << endl; + sectors = titleEnd - firstSector + 1; + inTitle = true; + } + + // is our read range part of one title + if( firstSector >= titleStart && firstSector+sectors-1 <= titleEnd ) + inTitle = true; + } + + if( needToSeek ) { + int flags = DVDCSS_NOFLAGS; + if( startOfTitle ) + flags = DVDCSS_SEEK_KEY; + else if( inTitle ) + flags = DVDCSS_SEEK_MPEG; + + kdDebug() << "(K3bLibDvdCss) need to seek from " << d->currentSector << " to " << firstSector << " with " << flags << endl; + + d->currentSector = seek( firstSector, flags ); + if( d->currentSector != firstSector ) { + kdDebug() << "(K3bLibDvdCss) seek failed: " << d->currentSector << endl; + return -1; + } + + kdDebug() << "(K3bLibDvdCss) seek done: " << d->currentSector << endl; + } + + + int flags = DVDCSS_NOFLAGS; + if( inTitle ) + flags = DVDCSS_READ_DECRYPT; + + int ret = read( buffer, sectors, flags ); + if( ret >= 0 ) + d->currentSector += ret; + else + d->currentSector = 0; // force a seek the next time + + return ret; +} + + +bool K3bLibDvdCss::crackAllKeys() +{ + // + // Loop over all titles and crack the keys (inspired by libdvdread) + // + kdDebug() << "(K3bLibDvdCss) cracking all keys." << endl; + + d->titleOffsets.clear(); + + K3bIso9660 iso( new K3bIso9660DeviceBackend( d->device ) ); + iso.setPlainIso9660( true ); + if( !iso.open() ) { + kdDebug() << "(K3bLibDvdCss) could not open iso9660 fs." << endl; + return false; + } + +#ifdef K3B_DEBUG + iso.debug(); +#endif + + const K3bIso9660Directory* dir = iso.firstIsoDirEntry(); + + int title = 0; + for( ; title < 100; ++title ) { + QString filename; + + // first we get the menu vob + if( title == 0 ) + filename.sprintf( "VIDEO_TS/VIDEO_TS.VOB" ); + else + filename.sprintf( "VIDEO_TS/VTS_%02d_%d.VOB", title, 0 ); + + const K3bIso9660File* file = dynamic_cast<const K3bIso9660File*>( dir->entry( filename ) ); + if( file && file->size() > 0 ) { + d->titleOffsets.append( qMakePair( (int)file->startSector(), (int)(file->size() / 2048U) ) ); + kdDebug() << "(K3bLibDvdCss) Get key for /" << filename << " at " << file->startSector() << endl; + if( seek( (int)file->startSector(), DVDCSS_SEEK_KEY ) < 0 ) { + kdDebug() << "(K3bLibDvdCss) unable to seek to " << file->startSector() << endl; + return false; + } + } + + if( title > 0 ) { + QPair<int,int> p; + int vob = 1; + for( ; vob < 100; ++vob ) { + filename.sprintf( "VIDEO_TS/VTS_%02d_%d.VOB", title, vob ); + file = dynamic_cast<const K3bIso9660File*>( dir->entry( filename ) ); + if( file ) { + if( file->size() % 2048 ) + kdError() << "(K3bLibDvdCss) FILESIZE % 2048 != 0!!!" << endl; + if( vob == 1 ) { + p.first = file->startSector(); + p.second = file->size() / 2048; + kdDebug() << "(K3bLibDvdCss) Get key for /" << filename << " at " << file->startSector() << endl; + if( seek( (int)file->startSector(), DVDCSS_SEEK_KEY ) < 0 ) { + kdDebug() << "(K3bLibDvdCss) unable to seek to " << file->startSector() << endl; + return false; + } + } + else { + p.second += file->size() / 2048; + } + } + else { + // last vob + break; + } + } + --vob; + + // last title + if( vob == 0 ) + break; + + kdDebug() << "(K3bLibDvdCss) Title " << title << " " << vob << " vobs with length " << p.second << endl; + d->titleOffsets.append( p ); + } + } + + --title; + + kdDebug() << "(K3bLibDvdCss) found " << title << " titles." << endl; + + return (title > 0); +} + + +K3bLibDvdCss* K3bLibDvdCss::create() +{ + if( s_libDvdCss == 0 ) { + s_libDvdCss = dlopen( "libdvdcss.so.2", RTLD_LAZY|RTLD_GLOBAL ); + if( s_libDvdCss ) { + k3b_dvdcss_open = (dvdcss_t (*)(char*))dlsym( s_libDvdCss, "dvdcss_open" ); + k3b_dvdcss_close = (int (*)( dvdcss_t ))dlsym( s_libDvdCss, "dvdcss_close" ); + k3b_dvdcss_seek = (int (*)( dvdcss_t, int, int ))dlsym( s_libDvdCss, "dvdcss_seek" ); + k3b_dvdcss_read = (int (*)( dvdcss_t, void*, int, int ))dlsym( s_libDvdCss, "dvdcss_read" ); + + if( !k3b_dvdcss_open || !k3b_dvdcss_close || !k3b_dvdcss_seek || !k3b_dvdcss_read ) { + kdDebug() << "(K3bLibDvdCss) unable to resolve libdvdcss." << endl; + dlclose( s_libDvdCss ); + s_libDvdCss = 0; + } + } + else + kdDebug() << "(K3bLibDvdCss) unable to load libdvdcss." << endl; + } + + if( s_libDvdCss ) + return new K3bLibDvdCss(); + else + return 0; +} diff --git a/libk3b/tools/k3blibdvdcss.h b/libk3b/tools/k3blibdvdcss.h new file mode 100644 index 0000000..308296f --- /dev/null +++ b/libk3b/tools/k3blibdvdcss.h @@ -0,0 +1,84 @@ +/* + * + * $Id: k3blibdvdcss.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_LIBDVDCSS_H_ +#define _K3B_LIBDVDCSS_H_ + +#include "k3b_export.h" + +namespace K3bDevice { + class Device; +} + + +/** + * Wrapper class for libdvdcss. dynamically openes the library if it + * is available on the system. + */ +class LIBK3B_EXPORT K3bLibDvdCss +{ + public: + ~K3bLibDvdCss(); + + static const int DVDCSS_BLOCK_SIZE = 2048; + static const int DVDCSS_NOFLAGS = 0; + static const int DVDCSS_READ_DECRYPT = (1 << 0); + static const int DVDCSS_SEEK_MPEG = (1 << 0); + static const int DVDCSS_SEEK_KEY = (1 << 1); + + /** + * Try to open a Video DVD and authenticate it. + * @return true if the Video DVD could be authenticated successfully, false otherwise. + */ + bool open( K3bDevice::Device* dev ); + void close(); + + int seek( int sector, int flags ); + int read( void* buffer, int sectors, int flags ); + + /** + * This method optimized the seek calls to maximize reading performance. + * It also makes sure we never read unscrambled and scrambled data at the same time. + * + * You have to call crackAllKeys() before using this. Do never call this in combination + * with seek or read! + */ + int readWrapped( void* buffer, int firstSector, int sectors ); + + /** + * Cache all CSS keys to guarantee smooth reading further on. + * This method also creates a title offset list which is needed by readWrapped. + */ + bool crackAllKeys(); + + /** + * returns 0 if the libdvdcss could not + * be found on the system. + * Otherwise you have to take care of + * deleting. + */ + static K3bLibDvdCss* create(); + + private: + class Private; + Private* d; + + K3bLibDvdCss(); + + static void* s_libDvdCss; + static int s_counter; +}; + +#endif diff --git a/libk3b/tools/k3blistview.cpp b/libk3b/tools/k3blistview.cpp new file mode 100644 index 0000000..34a3aa0 --- /dev/null +++ b/libk3b/tools/k3blistview.cpp @@ -0,0 +1,1290 @@ +/* + * + * $Id: k3blistview.cpp 768493 2008-01-30 08:44:05Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3blistview.h" + +#include "k3bmsfedit.h" + +#include <qstringlist.h> +#include <qfontmetrics.h> +#include <qpainter.h> +#include <qheader.h> +#include <qrect.h> +#include <qpushbutton.h> +#include <qiconset.h> +#include <qcombobox.h> +#include <qspinbox.h> +#include <qlineedit.h> +#include <qlistbox.h> +#include <qevent.h> +#include <qvalidator.h> +#include <qfont.h> +#include <qpalette.h> +#include <qstyle.h> +#include <qapplication.h> +#include <qprogressbar.h> +#include <qimage.h> + +#include <kpixmapeffect.h> + +#include <limits.h> + + + +// /////////////////////////////////////////////// +// +// K3BLISTVIEWITEM +// +// /////////////////////////////////////////////// + + +class K3bListViewItem::ColumnInfo +{ +public: + ColumnInfo() + : showProgress(false), + progressValue(0), + totalProgressSteps(100), + margin(0), + validator(0) { + editorType = NONE; + button = false; + comboEditable = false; + next = 0; + fontSet = false; + backgroundColorSet = false; + foregroundColorSet = false; + } + + ~ColumnInfo() { + if( next ) + delete next; + } + + bool button; + int editorType; + QStringList comboItems; + bool comboEditable; + bool fontSet; + bool backgroundColorSet; + bool foregroundColorSet; + QFont font; + QColor backgroundColor; + QColor foregroundColor; + ColumnInfo* next; + + bool showProgress; + int progressValue; + int totalProgressSteps; + int margin; + + QValidator* validator; +}; + + + +K3bListViewItem::K3bListViewItem(QListView *parent) + : KListViewItem( parent ) +{ + init(); +} + +K3bListViewItem::K3bListViewItem(QListViewItem *parent) + : KListViewItem( parent ) +{ + init(); +} + +K3bListViewItem::K3bListViewItem(QListView *parent, QListViewItem *after) + : KListViewItem( parent, after ) +{ + init(); +} + +K3bListViewItem::K3bListViewItem(QListViewItem *parent, QListViewItem *after) + : KListViewItem( parent, after ) +{ + init(); +} + + +K3bListViewItem::K3bListViewItem(QListView *parent, + const QString& s1, const QString& s2, + const QString& s3, const QString& s4, + const QString& s5, const QString& s6, + const QString& s7, const QString& s8) + : KListViewItem( parent, s1, s2, s3, s4, s5, s6, s7, s8 ) +{ + init(); +} + + +K3bListViewItem::K3bListViewItem(QListViewItem *parent, + const QString& s1, const QString& s2, + const QString& s3, const QString& s4, + const QString& s5, const QString& s6, + const QString& s7, const QString& s8) + : KListViewItem( parent, s1, s2, s3, s4, s5, s6, s7, s8 ) +{ + init(); +} + + +K3bListViewItem::K3bListViewItem(QListView *parent, QListViewItem *after, + const QString& s1, const QString& s2, + const QString& s3, const QString& s4, + const QString& s5, const QString& s6, + const QString& s7, const QString& s8) + : KListViewItem( parent, after, s1, s2, s3, s4, s5, s6, s7, s8 ) +{ + init(); +} + + +K3bListViewItem::K3bListViewItem(QListViewItem *parent, QListViewItem *after, + const QString& s1, const QString& s2, + const QString& s3, const QString& s4, + const QString& s5, const QString& s6, + const QString& s7, const QString& s8) + : KListViewItem( parent, after, s1, s2, s3, s4, s5, s6, s7, s8 ) +{ + init(); +} + + +K3bListViewItem::~K3bListViewItem() +{ + if( K3bListView* lv = dynamic_cast<K3bListView*>(listView()) ) + if( lv->currentlyEditedItem() == this ) + lv->hideEditor(); + + if( m_columns ) + delete m_columns; +} + + +void K3bListViewItem::init() +{ + m_columns = 0; + m_vMargin = 0; +} + + +int K3bListViewItem::width( const QFontMetrics& fm, const QListView* lv, int c ) const +{ + return KListViewItem::width( fm, lv, c ) + getColumnInfo(c)->margin*2; +} + + +void K3bListViewItem::setEditor( int column, int editor, const QStringList& cs ) +{ + ColumnInfo* colInfo = getColumnInfo(column); + + colInfo->editorType = editor; + if( !cs.isEmpty() ) + colInfo->comboItems = cs; +} + + +void K3bListViewItem::setValidator( int column, QValidator* v ) +{ + getColumnInfo(column)->validator = v; +} + + +QValidator* K3bListViewItem::validator( int col ) const +{ + return getColumnInfo(col)->validator; +} + + +void K3bListViewItem::setButton( int column, bool on ) +{ + ColumnInfo* colInfo = getColumnInfo(column); + + colInfo->button = on; +} + + +K3bListViewItem::ColumnInfo* K3bListViewItem::getColumnInfo( int col ) const +{ + if( !m_columns ) + m_columns = new ColumnInfo(); + + ColumnInfo* info = m_columns; + int i = 0; + while( i < col ) { + if( !info->next ) + info->next = new ColumnInfo(); + info = info->next; + ++i; + } + + return info; +} + + +int K3bListViewItem::editorType( int col ) const +{ + ColumnInfo* info = getColumnInfo( col ); + return info->editorType; +} + + +bool K3bListViewItem::needButton( int col ) const +{ + ColumnInfo* info = getColumnInfo( col ); + return info->button; +} + + +const QStringList& K3bListViewItem::comboStrings( int col ) const +{ + ColumnInfo* info = getColumnInfo( col ); + return info->comboItems; +} + + +void K3bListViewItem::setFont( int col, const QFont& f ) +{ + ColumnInfo* info = getColumnInfo( col ); + info->fontSet = true; + info->font = f; +} + + +void K3bListViewItem::setBackgroundColor( int col, const QColor& c ) +{ + ColumnInfo* info = getColumnInfo( col ); + info->backgroundColorSet = true; + info->backgroundColor = c; + repaint(); +} + + +void K3bListViewItem::setForegroundColor( int col, const QColor& c ) +{ + ColumnInfo* info = getColumnInfo( col ); + info->foregroundColorSet = true; + info->foregroundColor = c; + repaint(); +} + + +void K3bListViewItem::setDisplayProgressBar( int col, bool displ ) +{ + ColumnInfo* info = getColumnInfo( col ); + info->showProgress = displ; +} + + +void K3bListViewItem::setProgress( int col, int p ) +{ + ColumnInfo* info = getColumnInfo( col ); + if( !info->showProgress ) + setDisplayProgressBar( col, true ); + if( info->progressValue != p ) { + info->progressValue = p; + repaint(); + } +} + + +void K3bListViewItem::setTotalSteps( int col, int steps ) +{ + ColumnInfo* info = getColumnInfo( col ); + info->totalProgressSteps = steps; + + repaint(); +} + + +void K3bListViewItem::setMarginHorizontal( int col, int margin ) +{ + ColumnInfo* info = getColumnInfo( col ); + info->margin = margin; + + repaint(); +} + + +void K3bListViewItem::setMarginVertical( int margin ) +{ + m_vMargin = margin; + repaint(); +} + + +int K3bListViewItem::marginHorizontal( int col ) const +{ + return getColumnInfo( col )->margin; +} + + +int K3bListViewItem::marginVertical() const +{ + return m_vMargin; +} + + +void K3bListViewItem::setup() +{ + KListViewItem::setup(); + + setHeight( height() + 2*m_vMargin ); +} + + +void K3bListViewItem::paintCell( QPainter* p, const QColorGroup& cg, int col, int width, int align ) +{ + ColumnInfo* info = getColumnInfo( col ); + + p->save(); + + QFont oldFont( p->font() ); + QFont newFont = info->fontSet ? info->font : oldFont; + p->setFont( newFont ); + QColorGroup cgh(cg); + if( info->foregroundColorSet ) + cgh.setColor( QColorGroup::Text, info->foregroundColor ); + if( info->backgroundColorSet ) + cgh.setColor( QColorGroup::Base, info->backgroundColor ); + + // in case this is the selected row has a margin we need to repaint the selection bar + if( isSelected() && + (col == 0 || listView()->allColumnsShowFocus()) && + info->margin > 0 ) { + + p->fillRect( 0, 0, info->margin, height(), + cgh.brush( QColorGroup::Highlight ) ); + p->fillRect( width-info->margin, 0, info->margin, height(), + cgh.brush( QColorGroup::Highlight ) ); + } + else { // in case we use the KListView alternate color stuff + p->fillRect( 0, 0, info->margin, height(), + cgh.brush( QColorGroup::Base ) ); + p->fillRect( width-info->margin, 0, info->margin, height(), + cgh.brush( QColorGroup::Base ) ); + } + + // FIXME: the margin (we can only translate horizontally since height() is used for painting) + p->translate( info->margin, 0 ); + + if( info->showProgress ) { + paintProgressBar( p, cgh, col, width-2*info->margin ); + } + else { + paintK3bCell( p, cgh, col, width-2*info->margin, align ); + } + + p->restore(); +} + + +void K3bListViewItem::paintK3bCell( QPainter* p, const QColorGroup& cg, int col, int width, int align ) +{ + QListViewItem::paintCell( p, cg, col, width, align ); +} + + +void K3bListViewItem::paintProgressBar( QPainter* p, const QColorGroup& cgh, int col, int width ) +{ + ColumnInfo* info = getColumnInfo( col ); + + QStyle::SFlags flags = QStyle::Style_Default; + if( listView()->isEnabled() ) + flags |= QStyle::Style_Enabled; + if( listView()->hasFocus() ) + flags |= QStyle::Style_HasFocus; + + // FIXME: the QPainter is translated so 0, m_vMargin is the upper left of our paint rect + QRect r( 0, m_vMargin, width, height()-2*m_vMargin ); + + // create the double buffer pixmap + static QPixmap *doubleBuffer = 0; + if( !doubleBuffer ) + doubleBuffer = new QPixmap; + doubleBuffer->resize( width, height() ); + + QPainter dbPainter( doubleBuffer ); + + // clear the background (we cannot use paintEmptyArea since it's protected in QListView) + if( K3bListView* lv = dynamic_cast<K3bListView*>(listView()) ) + lv->paintEmptyArea( &dbPainter, r ); + else + dbPainter.fillRect( 0, 0, width, height(), + cgh.brush( QPalette::backgroundRoleFromMode(listView()->viewport()->backgroundMode()) ) ); + + // we want a little additional margin + r.setLeft( r.left()+1 ); + r.setWidth( r.width()-2 ); + r.setTop( r.top()+1 ); + r.setHeight( r.height()-2 ); + + // this might be a stupid hack but most styles do not reimplement drawPrimitive PE_ProgressBarChunk + // so this way the user is happy.... + static QProgressBar* s_dummyProgressBar = 0; + if( !s_dummyProgressBar ) { + s_dummyProgressBar = new QProgressBar(); + } + + s_dummyProgressBar->setTotalSteps( info->totalProgressSteps ); + s_dummyProgressBar->setProgress( info->progressValue ); + + // some styles use the widget's geometry + s_dummyProgressBar->setGeometry( r ); + + listView()->style().drawControl(QStyle::CE_ProgressBarContents, &dbPainter, s_dummyProgressBar, r, cgh, flags ); + listView()->style().drawControl(QStyle::CE_ProgressBarLabel, &dbPainter, s_dummyProgressBar, r, cgh, flags ); + + // now we really paint the progress in the listview + p->drawPixmap( 0, 0, *doubleBuffer ); +} + + + + + + + +K3bCheckListViewItem::K3bCheckListViewItem(QListView *parent) + : K3bListViewItem( parent ), + m_checked(false) +{ +} + + +K3bCheckListViewItem::K3bCheckListViewItem(QListViewItem *parent) + : K3bListViewItem( parent ), + m_checked(false) +{ +} + + +K3bCheckListViewItem::K3bCheckListViewItem(QListView *parent, QListViewItem *after) + : K3bListViewItem( parent, after ), + m_checked(false) +{ +} + + +K3bCheckListViewItem::K3bCheckListViewItem(QListViewItem *parent, QListViewItem *after) + : K3bListViewItem( parent, after ), + m_checked(false) +{ +} + + +bool K3bCheckListViewItem::isChecked() const +{ + return m_checked; +} + + +void K3bCheckListViewItem::setChecked( bool checked ) +{ + m_checked = checked; + repaint(); +} + + +void K3bCheckListViewItem::paintK3bCell( QPainter* p, const QColorGroup& cg, int col, int width, int align ) +{ + K3bListViewItem::paintK3bCell( p, cg, col, width, align ); + + if( col == 0 ) { + if( m_checked ) { + QRect r( 0, marginVertical(), width, /*listView()->style().pixelMetric( QStyle::PM_CheckListButtonSize )*/height()-2*marginVertical() ); + + QStyle::SFlags flags = QStyle::Style_Default; + if( listView()->isEnabled() ) + flags |= QStyle::Style_Enabled; + if( listView()->hasFocus() ) + flags |= QStyle::Style_HasFocus; + if( isChecked() ) + flags |= QStyle::Style_On; + else + flags |= QStyle::Style_Off; + + listView()->style().drawPrimitive( QStyle::PE_CheckMark, p, r, cg, flags ); + } + } +} + + + + + + +// /////////////////////////////////////////////// +// +// K3BLISTVIEW +// +// /////////////////////////////////////////////// + + +class K3bListView::Private +{ +public: + QLineEdit* spinBoxLineEdit; + QLineEdit* msfEditLineEdit; +}; + + +K3bListView::K3bListView( QWidget* parent, const char* name ) + : KListView( parent, name ), + m_noItemVMargin( 20 ), + m_noItemHMargin( 20 ) +{ + d = new Private; + + connect( header(), SIGNAL( sizeChange( int, int, int ) ), + this, SLOT( updateEditorSize() ) ); + + m_editorButton = 0; + m_editorComboBox = 0; + m_editorSpinBox = 0; + m_editorLineEdit = 0; + m_editorMsfEdit = 0; + m_currentEditItem = 0; + m_currentEditColumn = 0; + m_doubleClickForEdit = true; + m_lastClickedItem = 0; +} + +K3bListView::~K3bListView() +{ + delete d; +} + + +QWidget* K3bListView::editor( K3bListViewItem::EditorType t ) const +{ + switch( t ) { + case K3bListViewItem::COMBO: + return m_editorComboBox; + case K3bListViewItem::LINE: + return m_editorLineEdit; + case K3bListViewItem::SPIN: + return m_editorSpinBox; + case K3bListViewItem::MSF: + return m_editorMsfEdit; + default: + return 0; + } +} + + +void K3bListView::clear() +{ + hideEditor(); + KListView::clear(); +} + + +void K3bListView::editItem( K3bListViewItem* item, int col ) +{ + if( item == 0 ) + hideEditor(); + else if( item->isEnabled() ) { + showEditor( item, col ); + } +} + + +void K3bListView::hideEditor() +{ + m_lastClickedItem = 0; + m_currentEditItem = 0; + m_currentEditColumn = 0; + + if( m_editorSpinBox ) + m_editorSpinBox->hide(); + if( m_editorLineEdit ) + m_editorLineEdit->hide(); + if( m_editorComboBox ) + m_editorComboBox->hide(); + if( m_editorButton ) + m_editorButton->hide(); + if( m_editorMsfEdit ) + m_editorMsfEdit->hide(); +} + +void K3bListView::showEditor( K3bListViewItem* item, int col ) +{ + if( !item ) + return; + + if( item->needButton( col ) || item->editorType(col) != K3bListViewItem::NONE ) { + m_currentEditColumn = col; + m_currentEditItem = item; + } + + placeEditor( item, col ); + if( item->needButton( col ) ) { + m_editorButton->show(); + } + switch( item->editorType(col) ) { + case K3bListViewItem::COMBO: + m_editorComboBox->show(); + m_editorComboBox->setFocus(); + m_editorComboBox->setValidator( item->validator(col) ); + break; + case K3bListViewItem::LINE: + m_editorLineEdit->show(); + m_editorLineEdit->setFocus(); + m_editorLineEdit->setValidator( item->validator(col) ); + break; + case K3bListViewItem::SPIN: + m_editorSpinBox->show(); + m_editorSpinBox->setFocus(); + break; + case K3bListViewItem::MSF: + m_editorMsfEdit->show(); + m_editorMsfEdit->setFocus(); + break; + default: + break; + } +} + + +void K3bListView::placeEditor( K3bListViewItem* item, int col ) +{ + ensureItemVisible( item ); + QRect r = itemRect( item ); + + r.setX( contentsToViewport( QPoint(header()->sectionPos( col ), 0) ).x() ); + r.setWidth( header()->sectionSize( col ) - 1 ); + + // check if the column is fully visible + if( visibleWidth() < r.right() ) + r.setRight(visibleWidth()); + + r = QRect( viewportToContents( r.topLeft() ), r.size() ); + + if( item->pixmap( col ) ) { + r.setX( r.x() + item->pixmap(col)->width() ); + } + + // the tree-stuff is painted in the first column + if( col == 0 ) { + r.setX( r.x() + item->depth() * treeStepSize() ); + if( rootIsDecorated() ) + r.setX( r.x() + treeStepSize() ); + } + + if( item->needButton(col) ) { + prepareButton( item, col ); + m_editorButton->setFixedHeight( r.height() ); + // for now we make a square button + m_editorButton->setFixedWidth( m_editorButton->height() ); + r.setWidth( r.width() - m_editorButton->width() ); + moveChild( m_editorButton, r.right(), r.y() ); + } + + if( QWidget* editor = prepareEditor( item, col ) ) { + editor->resize( r.size() ); + // editor->resize( QSize( r.width(), editor->minimumSizeHint().height() ) ); + moveChild( editor, r.x(), r.y() ); + } +} + + +void K3bListView::prepareButton( K3bListViewItem*, int ) +{ + if( !m_editorButton ) { + m_editorButton = new QPushButton( viewport() ); + connect( m_editorButton, SIGNAL(clicked()), + this, SLOT(slotEditorButtonClicked()) ); + } + + // TODO: do some useful things + m_editorButton->setText( "..." ); +} + + +QWidget* K3bListView::prepareEditor( K3bListViewItem* item, int col ) +{ + switch( item->editorType(col) ) { + case K3bListViewItem::COMBO: + if( !m_editorComboBox ) { + m_editorComboBox = new QComboBox( viewport() ); + connect( m_editorComboBox, SIGNAL(activated(const QString&)), + this, SLOT(slotEditorComboBoxActivated(const QString&)) ); + m_editorComboBox->installEventFilter( this ); + } + m_editorComboBox->clear(); + if( item->comboStrings( col ).isEmpty() ) { + m_editorComboBox->insertItem( item->text( col ) ); + } + else { + m_editorComboBox->insertStringList( item->comboStrings(col) ); + int current = item->comboStrings(col).findIndex( item->text(col) ); + if( current != -1 ) + m_editorComboBox->setCurrentItem( current ); + } + return m_editorComboBox; + + case K3bListViewItem::LINE: { + if( !m_editorLineEdit ) { + m_editorLineEdit = new QLineEdit( viewport() ); + m_editorLineEdit->setFrameStyle( QFrame::Box | QFrame::Plain ); + m_editorLineEdit->setLineWidth(1); + m_editorLineEdit->installEventFilter( this ); + } + + QString txt = item->text( col ); + m_editorLineEdit->setText( txt ); + + // select the edit text (handle extensions while doing so) + int pos = txt.findRev( '.' ); + if( pos > 0 ) + m_editorLineEdit->setSelection( 0, pos ); + else + m_editorLineEdit->setSelection( 0, txt.length() ); + + return m_editorLineEdit; + } + + // + // A QSpinBox (and thus also a K3bMsfEdit) uses a QLineEdit), thus + // we have to use this QLineEdit as the actual object to dead with + // + + case K3bListViewItem::SPIN: + if( !m_editorSpinBox ) { + m_editorSpinBox = new QSpinBox( viewport() ); + d->spinBoxLineEdit = static_cast<QLineEdit*>( m_editorSpinBox->child( 0, "QLineEdit" ) ); + connect( m_editorSpinBox, SIGNAL(valueChanged(int)), + this, SLOT(slotEditorSpinBoxValueChanged(int)) ); + // m_editorSpinBox->installEventFilter( this ); + d->spinBoxLineEdit->installEventFilter( this ); + } + // set the range + m_editorSpinBox->setValue( item->text(col).toInt() ); + return m_editorSpinBox; + + case K3bListViewItem::MSF: + if( !m_editorMsfEdit ) { + m_editorMsfEdit = new K3bMsfEdit( viewport() ); + d->msfEditLineEdit = static_cast<QLineEdit*>( m_editorMsfEdit->child( 0, "QLineEdit" ) ); + connect( m_editorMsfEdit, SIGNAL(valueChanged(int)), + this, SLOT(slotEditorMsfEditValueChanged(int)) ); + // m_editorMsfEdit->installEventFilter( this ); + d->msfEditLineEdit->installEventFilter( this ); + } + m_editorMsfEdit->setText( item->text(col) ); + return m_editorMsfEdit; + + default: + return 0; + } +} + +void K3bListView::setCurrentItem( QListViewItem* i ) +{ + if( !i || i == currentItem() ) + return; + + // I cannot remember why I did this here exactly. However, it resets the + // m_lastClickedItem and thus invalidates the editing. +// doRename(); +// hideEditor(); +// m_currentEditItem = 0; + KListView::setCurrentItem( i ); +} + + +void K3bListView::setNoItemText( const QString& text ) +{ + m_noItemText = text; + triggerUpdate(); +} + + +void K3bListView::viewportPaintEvent( QPaintEvent* e ) +{ + KListView::viewportPaintEvent( e ); +} + + +// FIXME: move this to viewportPaintEvent +void K3bListView::drawContentsOffset( QPainter * p, int ox, int oy, int cx, int cy, int cw, int ch ) +{ + KListView::drawContentsOffset( p, ox, oy, cx, cy, cw, ch ); + + if( childCount() == 0 && !m_noItemText.isEmpty()) { + + p->setPen( Qt::darkGray ); + + QStringList lines = QStringList::split( "\n", m_noItemText ); + int xpos = m_noItemHMargin; + int ypos = m_noItemVMargin + p->fontMetrics().height(); + + QStringList::Iterator end ( lines.end() ); + for( QStringList::Iterator str = lines.begin(); str != end; ++str ) { + p->drawText( xpos, ypos, *str ); + ypos += p->fontMetrics().lineSpacing(); + } + } +} + +void K3bListView::paintEmptyArea( QPainter* p, const QRect& rect ) +{ + KListView::paintEmptyArea( p, rect ); + +// if( childCount() == 0 && !m_noItemText.isEmpty()) { + +// QPainter pp( viewport() ); +// pp.fillRect( viewport()->rect(), viewport()->paletteBackgroundColor() ); +// pp.end(); + +// p->setPen( Qt::darkGray ); + +// QStringList lines = QStringList::split( "\n", m_noItemText ); +// int xpos = m_noItemHMargin; +// int ypos = m_noItemVMargin + p->fontMetrics().height(); + +// for( QStringList::Iterator str = lines.begin(); str != lines.end(); str++ ) { +// p->drawText( xpos, ypos, *str ); +// ypos += p->fontMetrics().lineSpacing(); +// } +// } +} + +void K3bListView::resizeEvent( QResizeEvent* e ) +{ + KListView::resizeEvent( e ); + updateEditorSize(); +} + + +void K3bListView::updateEditorSize() +{ + if( m_currentEditItem ) + placeEditor( m_currentEditItem, m_currentEditColumn ); +} + + +void K3bListView::slotEditorLineEditReturnPressed() +{ + if( doRename() ) { + // edit the next line + // TODO: add config for this + if( K3bListViewItem* nextItem = dynamic_cast<K3bListViewItem*>( m_currentEditItem->nextSibling() ) ) + editItem( nextItem, currentEditColumn() ); + else { + hideEditor(); + + // keep the focus here + viewport()->setFocus(); + } + } +} + + +void K3bListView::slotEditorComboBoxActivated( const QString& ) +{ + doRename(); +// if( renameItem( m_currentEditItem, m_currentEditColumn, str ) ) { +// m_currentEditItem->setText( m_currentEditColumn, str ); +// emit itemRenamed( m_currentEditItem, str, m_currentEditColumn ); +// } +// else { +// for( int i = 0; i < m_editorComboBox->count(); ++i ) { +// if( m_editorComboBox->text(i) == m_currentEditItem->text(m_currentEditColumn) ) { +// m_editorComboBox->setCurrentItem( i ); +// break; +// } +// } +// } +} + + +void K3bListView::slotEditorSpinBoxValueChanged( int ) +{ +// if( renameItem( m_currentEditItem, m_currentEditColumn, QString::number(value) ) ) { +// m_currentEditItem->setText( m_currentEditColumn, QString::number(value) ); +// emit itemRenamed( m_currentEditItem, QString::number(value), m_currentEditColumn ); +// } +// else +// m_editorSpinBox->setValue( m_currentEditItem->text( m_currentEditColumn ).toInt() ); +} + + +void K3bListView::slotEditorMsfEditValueChanged( int ) +{ + // FIXME: do we always need to update the value. Isn't it enough to do it at the end? +// if( renameItem( m_currentEditItem, m_currentEditColumn, QString::number(value) ) ) { +// m_currentEditItem->setText( m_currentEditColumn, QString::number(value) ); +// emit itemRenamed( m_currentEditItem, QString::number(value), m_currentEditColumn ); +// } +// else +// m_editorMsfEdit->setText( m_currentEditItem->text( m_currentEditColumn ) ); +} + + +bool K3bListView::doRename() +{ + if( m_currentEditItem ) { + QString newValue; + switch( m_currentEditItem->editorType( m_currentEditColumn ) ) { + case K3bListViewItem::COMBO: + newValue = m_editorComboBox->currentText(); + break; + case K3bListViewItem::LINE: + newValue = m_editorLineEdit->text(); + break; + case K3bListViewItem::SPIN: + newValue = QString::number(m_editorSpinBox->value()); + break; + case K3bListViewItem::MSF: + newValue = QString::number(m_editorMsfEdit->value()); + break; + } + + if( renameItem( m_currentEditItem, m_currentEditColumn, newValue ) ) { + m_currentEditItem->setText( m_currentEditColumn, newValue ); + emit itemRenamed( m_currentEditItem, newValue, m_currentEditColumn ); + return true; + } + else { + switch( m_currentEditItem->editorType( m_currentEditColumn ) ) { + case K3bListViewItem::COMBO: + for( int i = 0; i < m_editorComboBox->count(); ++i ) { + if( m_editorComboBox->text(i) == m_currentEditItem->text(m_currentEditColumn) ) { + m_editorComboBox->setCurrentItem( i ); + break; + } + } + break; + case K3bListViewItem::LINE: + m_editorLineEdit->setText( m_currentEditItem->text( m_currentEditColumn ) ); + break; + case K3bListViewItem::SPIN: + m_editorSpinBox->setValue( m_currentEditItem->text( m_currentEditColumn ).toInt() ); + break; + case K3bListViewItem::MSF: + m_editorMsfEdit->setText( m_currentEditItem->text( m_currentEditColumn ) ); + break; + } + } + } + + + return false; +} + + +void K3bListView::slotEditorButtonClicked() +{ + slotEditorButtonClicked( m_currentEditItem, m_currentEditColumn ); +} + + +bool K3bListView::renameItem( K3bListViewItem* item, int col, const QString& text ) +{ + Q_UNUSED(item); + Q_UNUSED(col); + Q_UNUSED(text); + return true; +} + + +void K3bListView::slotEditorButtonClicked( K3bListViewItem* item, int col ) +{ + emit editorButtonClicked( item, col ); +} + + +bool K3bListView::eventFilter( QObject* o, QEvent* e ) +{ + if( e->type() == QEvent::KeyPress ) { + QKeyEvent* ke = static_cast<QKeyEvent*>(e); + if( ke->key() == Key_Tab ) { + if( o == m_editorLineEdit || + o == d->msfEditLineEdit || + o == d->spinBoxLineEdit ) { + K3bListViewItem* lastEditItem = m_currentEditItem; + + doRename(); + + if( lastEditItem ) { + // can we rename one of the other columns? + int col = currentEditColumn()+1; + while( col < columns() && lastEditItem->editorType( col ) == K3bListViewItem::NONE ) + ++col; + if( col < columns() ) + editItem( lastEditItem, col ); + else { + hideEditor(); + + // keep the focus here + viewport()->setFocus(); + + // search for the next editable item + while( K3bListViewItem* nextItem = + dynamic_cast<K3bListViewItem*>( lastEditItem->nextSibling() ) ) { + // edit first column + col = 0; + while( col < columns() && nextItem->editorType( col ) == K3bListViewItem::NONE ) + ++col; + if( col < columns() ) { + editItem( nextItem, col ); + break; + } + + lastEditItem = nextItem; + } + } + } + + return true; + } + } + if( ke->key() == Key_Return || + ke->key() == Key_Enter ) { + if( o == m_editorLineEdit || + o == d->msfEditLineEdit || + o == d->spinBoxLineEdit ) { + K3bListViewItem* lastEditItem = m_currentEditItem; + doRename(); + + if( K3bListViewItem* nextItem = + dynamic_cast<K3bListViewItem*>( lastEditItem->nextSibling() ) ) + editItem( nextItem, currentEditColumn() ); + else { + hideEditor(); + + // keep the focus here + viewport()->setFocus(); + } + + return true; + } + } + else if( ke->key() == Key_Escape ) { + if( o == m_editorLineEdit || + o == d->msfEditLineEdit || + o == d->spinBoxLineEdit ) { + hideEditor(); + + // keep the focus here + viewport()->setFocus(); + + return true; + } + } + } + + else if( e->type() == QEvent::MouseButtonPress && o == viewport() ) { + + // first let's grab the focus + viewport()->setFocus(); + + QMouseEvent* me = static_cast<QMouseEvent*>( e ); + QListViewItem* item = itemAt( me->pos() ); + int col = header()->sectionAt( me->pos().x() ); + if( K3bCheckListViewItem* ci = dynamic_cast<K3bCheckListViewItem*>( item ) ) { + if( col == 0 ) { + // FIXME: improve this click area! + ci->setChecked( !ci->isChecked() ); + return true; + } + } + + if( me->button() == QMouseEvent::LeftButton ) { + if( item != m_currentEditItem || m_currentEditColumn != col ) { + doRename(); + if( K3bListViewItem* k3bItem = dynamic_cast<K3bListViewItem*>(item) ) { + if( me->pos().x() > item->depth()*treeStepSize() && + item->isEnabled() && + (m_lastClickedItem == item || !m_doubleClickForEdit) ) + showEditor( k3bItem, col ); + else { + hideEditor(); + + // keep the focus here + viewport()->setFocus(); + } + } + else { + hideEditor(); + + // keep the focus here + viewport()->setFocus(); + } + + // do not count clicks in the item tree for editing + if( item && me->pos().x() > item->depth()*treeStepSize() ) + m_lastClickedItem = item; + } + } + } + + else if( e->type() == QEvent::FocusOut ) { + if( o == m_editorLineEdit || + o == d->msfEditLineEdit || + o == d->spinBoxLineEdit || + o == m_editorComboBox ) { + // make sure we did not lose the focus to one of the edit widgets' children + if( !qApp->focusWidget() || qApp->focusWidget()->parentWidget() != o ) { + doRename(); + hideEditor(); + } + } + } + + return KListView::eventFilter( o, e ); +} + + +void K3bListView::setK3bBackgroundPixmap( const QPixmap& pix, int pos ) +{ + m_backgroundPixmap = pix; + m_backgroundPixmapPosition = pos; +} + + +void K3bListView::viewportResizeEvent( QResizeEvent* e ) +{ + if( !m_backgroundPixmap.isNull() ) { + + QSize size = viewport()->size().expandedTo( QSize( contentsWidth(), contentsHeight() ) ); + + QPixmap bgPix( size ); + + // FIXME: let the user specify the color + bgPix.fill( colorGroup().base() ); + + if( bgPix.width() < m_backgroundPixmap.width() || + bgPix.height() < m_backgroundPixmap.height() ) { + QPixmap newBgPix( m_backgroundPixmap.convertToImage().scale( bgPix.size(), QImage::ScaleMin ) ); + if( m_backgroundPixmapPosition == TOP_LEFT ) + bitBlt( &bgPix, 0, 0, + &newBgPix, 0, 0, + newBgPix.width(), newBgPix.height() ); + else { + int dx = bgPix.width() / 2 - m_backgroundPixmap.width() /2; + int dy = bgPix.height() / 2 - m_backgroundPixmap.height() /2; + bitBlt( &bgPix, dx, dy, &newBgPix, 0, 0, + newBgPix.width(), newBgPix.height() ); + } + } + else { + if( m_backgroundPixmapPosition == TOP_LEFT ) + bitBlt( &bgPix, 0, 0, + &m_backgroundPixmap, 0, 0, + m_backgroundPixmap.width(), m_backgroundPixmap.height() ); + else { + int dx = bgPix.width() / 2 - m_backgroundPixmap.width() /2; + int dy = bgPix.height() / 2 - m_backgroundPixmap.height() /2; + bitBlt( &bgPix, dx, dy, &m_backgroundPixmap, 0, 0, + m_backgroundPixmap.width(), m_backgroundPixmap.height() ); + } + } + + viewport()->setPaletteBackgroundPixmap( bgPix ); + } + + KListView::viewportResizeEvent( e ); +} + + +QListViewItem* K3bListView::parentItem( QListViewItem* item ) +{ + if( !item ) + return 0; + if( item->parent() ) + return item->parent(); + else + return K3bListView::parentItem( item->itemAbove() ); +} + + +KPixmap K3bListView::createDragPixmap( const QPtrList<QListViewItem>& items ) +{ + // + // Create drag pixmap. + // If there are too many items fade the pixmap using the mask + // always fade invisible items + // + int width = header()->width(); + int height = 0; + for( QPtrListIterator<QListViewItem> it( items ); *it; ++it ) { + QRect r = itemRect( *it ); + + if( r.isValid() ) { + height += ( *it )->height(); + } + } + + // now we should have a range top->bottom which includes all visible items + + // there are two situations in which we fade the pixmap on the top or the bottom: + // 1. there are invisible items above (below) the visible + // 2. the range is way too big + + // FIXME: how do we determine if there are invisible items outside our range? + + KPixmap pix; + pix.resize( width, height ); + pix.fill( Qt::white ); + // QBitmap mask( width, bottom-top ); + + // now paint all the visible items into the pixmap + // FIXME: only paint the visible items + QPainter p( &pix ); + for( QListViewItemIterator it( this ); *it; ++it ) { + QListViewItem* item = *it; + + // FIXME: items on other than the top level have a smaller first column + // the same goes for all items if root is decorated + bool alreadyDrawing = false; + QRect r = itemRect( item ); + if( r.isValid() ) { + if( items.containsRef( item ) ) { + // paint all columns + int x = 0; + for( int i = 0; i < columns(); ++i ) { + item->paintCell( &p, colorGroup(), i, columnWidth( i ), columnAlignment( i ) ); + p.translate( columnWidth( i ), 0 ); + x += columnWidth( i ); + } + + p.translate( -x, item->height() ); + + alreadyDrawing = true; + } + else if( alreadyDrawing ) + p.translate( 0, item->height() ); + + if( p.worldMatrix().dy() >= pix.height() ) + break; + } + } + + // make it a little lighter + KPixmapEffect::fade( pix, 0.3, Qt::white ); + + // FIXME: fade the pixmap at the right side if the items are longer than width + + return pix; +} + +#include "k3blistview.moc" diff --git a/libk3b/tools/k3blistview.h b/libk3b/tools/k3blistview.h new file mode 100644 index 0000000..f8eb2ad --- /dev/null +++ b/libk3b/tools/k3blistview.h @@ -0,0 +1,296 @@ +/* + * + * $Id: k3blistview.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 K3BLISTVIEW_H +#define K3BLISTVIEW_H + + +#include <klistview.h> +#include "k3b_export.h" +#include <qptrvector.h> +#include <qptrlist.h> +#include <qstringlist.h> +#include <kpixmap.h> + +class QPainter; +class QPushButton; +class QIconSet; +class QResizeEvent; +class QComboBox; +class QSpinBox; +class QLineEdit; +class QEvent; +class QValidator; +class K3bMsfEdit; + +class K3bListView; + + +class LIBK3B_EXPORT K3bListViewItem : public KListViewItem +{ + public: + K3bListViewItem(QListView *parent); + K3bListViewItem(QListViewItem *parent); + K3bListViewItem(QListView *parent, QListViewItem *after); + K3bListViewItem(QListViewItem *parent, QListViewItem *after); + + K3bListViewItem(QListView *parent, + const QString&, const QString& = QString::null, + const QString& = QString::null, const QString& = QString::null, + const QString& = QString::null, const QString& = QString::null, + const QString& = QString::null, const QString& = QString::null); + + K3bListViewItem(QListViewItem *parent, + const QString&, const QString& = QString::null, + const QString& = QString::null, const QString& = QString::null, + const QString& = QString::null, const QString& = QString::null, + const QString& = QString::null, const QString& = QString::null); + + K3bListViewItem(QListView *parent, QListViewItem *after, + const QString&, const QString& = QString::null, + const QString& = QString::null, const QString& = QString::null, + const QString& = QString::null, const QString& = QString::null, + const QString& = QString::null, const QString& = QString::null); + + K3bListViewItem(QListViewItem *parent, QListViewItem *after, + const QString&, const QString& = QString::null, + const QString& = QString::null, const QString& = QString::null, + const QString& = QString::null, const QString& = QString::null, + const QString& = QString::null, const QString& = QString::null); + + virtual ~K3bListViewItem(); + + /** + * reimplemented from KListViewItem + */ + void setup(); + + virtual int width( const QFontMetrics& fm, const QListView* lv, int c ) const; + + void setEditor( int col, int type, const QStringList& = QStringList() ); + void setButton( int col, bool ); + + void setValidator( int col, QValidator* v ); + QValidator* validator( int col ) const; + + int editorType( int col ) const; + bool needButton( int col ) const; + const QStringList& comboStrings( int col ) const; + + enum EditorType { NONE, COMBO, LINE, SPIN, MSF }; + + void setFont( int col, const QFont& f ); + void setBackgroundColor( int col, const QColor& ); + void setForegroundColor( int col, const QColor& ); + + void setDisplayProgressBar( int col, bool ); + void setProgress( int, int ); + void setTotalSteps( int col, int steps ); + + /** + * The margin left and right of the cell + */ + void setMarginHorizontal( int col, int margin ); + + /** + * The top and button margin of the cell + */ + void setMarginVertical( int margin ); + + int marginHorizontal( int col ) const; + int marginVertical() const; + + /** + * Do not reimplement this but paintK3bCell to use the margin and background stuff. + */ + virtual void paintCell( QPainter* p, const QColorGroup& cg, int col, int width, int align ); + + virtual void paintK3bCell( QPainter* p, const QColorGroup& cg, int col, int width, int align ); + + private: + void paintProgressBar( QPainter* p, const QColorGroup& cgh, int col, int width ); + + class ColumnInfo; + mutable ColumnInfo* m_columns; + + ColumnInfo* getColumnInfo( int ) const; + void init(); + + int m_vMargin; +}; + + +class LIBK3B_EXPORT K3bCheckListViewItem : public K3bListViewItem +{ + public: + K3bCheckListViewItem(QListView *parent); + K3bCheckListViewItem(QListViewItem *parent); + K3bCheckListViewItem(QListView *parent, QListViewItem *after); + K3bCheckListViewItem(QListViewItem *parent, QListViewItem *after); + + virtual bool isChecked() const; + virtual void setChecked( bool checked ); + + protected: + virtual void paintK3bCell( QPainter* p, const QColorGroup& cg, int col, int width, int align ); + + private: + bool m_checked; +}; + + + +class LIBK3B_EXPORT K3bListView : public KListView +{ + friend class K3bListViewItem; + + Q_OBJECT + + public: + K3bListView (QWidget *parent = 0, const char *name = 0); + virtual ~K3bListView(); + + virtual void setCurrentItem( QListViewItem* ); + + K3bListViewItem* currentlyEditedItem() const { return m_currentEditItem; } + + QWidget* editor( K3bListViewItem::EditorType ) const; + + enum BgPixPosition { + TOP_LEFT, + CENTER + }; + + /** + * This will set a background pixmap which is not tiled. + * @param pos position on the viewport. + */ + void setK3bBackgroundPixmap( const QPixmap&, int pos = CENTER ); + + /** + * Create a faded pixmap showing the items. + */ + KPixmap createDragPixmap( const QPtrList<QListViewItem>& items ); + + /** + * Searches for the first item above @p i which is one level higher. + * For 1st level items this will always be the listview's root item. + */ + static QListViewItem* parentItem( QListViewItem* i ); + + signals: + void editorButtonClicked( K3bListViewItem*, int ); + + public slots: + void setNoItemText( const QString& ); + // void setNoItemPixmap( const QPixmap& ); + void setNoItemVerticalMargin( int i ) { m_noItemVMargin = i; } + void setNoItemHorizontalMargin( int i ) { m_noItemHMargin = i; } + void setDoubleClickForEdit( bool b ) { m_doubleClickForEdit = b; } + void hideEditor(); + void editItem( K3bListViewItem*, int ); + + virtual void clear(); + + private slots: + void updateEditorSize(); + virtual void slotEditorLineEditReturnPressed(); + virtual void slotEditorComboBoxActivated( const QString& ); + virtual void slotEditorSpinBoxValueChanged( int ); + virtual void slotEditorMsfEditValueChanged( int ); + virtual void slotEditorButtonClicked(); + + protected slots: + void showEditor( K3bListViewItem*, int col ); + void placeEditor( K3bListViewItem*, int col ); + + /** + * This is called whenever one of the editor's contents changes + * the default implementation just returnes true + * + * FIXME: should be called something like mayRename + */ + virtual bool renameItem( K3bListViewItem*, int, const QString& ); + + /** + * default impl just emits signal + * editorButtonClicked(...) + */ + virtual void slotEditorButtonClicked( K3bListViewItem*, int ); + + protected: + /** + * calls KListView::drawContentsOffset + * and paints a the noItemText if no item is in the list + */ + virtual void drawContentsOffset ( QPainter * p, int ox, int oy, int cx, int cy, int cw, int ch ); + virtual void resizeEvent( QResizeEvent* ); + virtual void paintEmptyArea( QPainter*, const QRect& rect ); + + /** + * Reimplemented for internal reasons. + * + * Further reimplementations should call this function or else some features may not work correctly. + * + * The API is unaffected. + */ + virtual void viewportResizeEvent( QResizeEvent* ); + + /** + * Reimplemented for internal reasons. + * Further reimplementations should call this function or else + * some features may not work correctly. + * + * The API is unaffected. + */ + virtual void viewportPaintEvent(QPaintEvent*); + + virtual bool eventFilter( QObject*, QEvent* ); + + K3bListViewItem* currentEditItem() const { return m_currentEditItem; } + int currentEditColumn() const { return m_currentEditColumn; } + + private: + QWidget* prepareEditor( K3bListViewItem* item, int col ); + void prepareButton( K3bListViewItem* item, int col ); + bool doRename(); + + QString m_noItemText; + // QPixmap m_noItemPixmap; + int m_noItemVMargin; + int m_noItemHMargin; + + K3bListViewItem* m_currentEditItem; + int m_currentEditColumn; + + bool m_doubleClickForEdit; + QListViewItem* m_lastClickedItem; + + QPushButton* m_editorButton; + QComboBox* m_editorComboBox; + QSpinBox* m_editorSpinBox; + QLineEdit* m_editorLineEdit; + K3bMsfEdit* m_editorMsfEdit; + + QPixmap m_backgroundPixmap; + int m_backgroundPixmapPosition; + + class Private; + Private* d; +}; + + +#endif diff --git a/libk3b/tools/k3blistviewitemanimator.cpp b/libk3b/tools/k3blistviewitemanimator.cpp new file mode 100644 index 0000000..1c729b6 --- /dev/null +++ b/libk3b/tools/k3blistviewitemanimator.cpp @@ -0,0 +1,137 @@ +/* + * + * $Id: k3blistviewitemanimator.cpp 689561 2007-07-18 15:19:38Z trueg $ + * Copyright (C) 2004 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3blistviewitemanimator.h" + +#include <qtimer.h> +#include <qlistview.h> + +#include <kpixmap.h> +#include <kpixmapeffect.h> + + +K3bListViewItemAnimator::K3bListViewItemAnimator( QObject* parent, const char* name ) + : QObject( parent, name ) +{ + init(); +} + + +K3bListViewItemAnimator::K3bListViewItemAnimator( QListViewItem* item, int col, QObject* parent, const char* name ) + : QObject( parent, name ) +{ + init(); + setItem( item, col ); +} + + +K3bListViewItemAnimator::~K3bListViewItemAnimator() +{ +} + + +QListViewItem* K3bListViewItemAnimator::item() const +{ + return m_item; +} + + +void K3bListViewItemAnimator::init() +{ + m_item = 0; + m_column = 0; + m_timer = new QTimer( this ); + connect( m_timer, SIGNAL(timeout()), this, SLOT(slotAnimate()) ); +} + + +void K3bListViewItemAnimator::start() +{ + if( m_item && !m_pixmap.isNull() ) { + m_animationStep = 0; + m_animationBack = false; + m_timer->start( 150 ); + } + else + stop(); +} + + +void K3bListViewItemAnimator::stop() +{ + m_timer->stop(); +} + + +void K3bListViewItemAnimator::setItem( QListViewItem* item, int col ) +{ + m_item = item; + m_column = col; + m_pixmap = *item->pixmap(col); + m_fadeColor = item->listView()->colorGroup().base(); + start(); +} + + +void K3bListViewItemAnimator::setPixmap( const QPixmap& p ) +{ + m_pixmap = p; + start(); +} + + +void K3bListViewItemAnimator::setColumn( int col ) +{ + m_column = col; + start(); +} + + +void K3bListViewItemAnimator::setFadeColor( const QColor& c ) +{ + m_fadeColor = c; + start(); +} + + +void K3bListViewItemAnimator::slotAnimate() +{ + if( m_item->isVisible() ) { + double val = (double)m_animationStep; + val /= 10.0; + // we need a temp pixmap since KPixmapEffect changes our pixmap + KPixmap pix( m_pixmap ); + m_item->setPixmap( m_column, KPixmapEffect::fade( pix, val, m_fadeColor ) );; + } + + if( m_animationBack ) { + --m_animationStep; + if( m_animationStep < 0 ) { + // two steps full + m_animationStep = 0; + m_animationBack = false; + } + } + else { + ++m_animationStep; + // do not fade it completely + if( m_animationStep > 9 ) { + m_animationStep = 8; + m_animationBack = true; + } + } +} + +#include "k3blistviewitemanimator.moc" diff --git a/libk3b/tools/k3blistviewitemanimator.h b/libk3b/tools/k3blistviewitemanimator.h new file mode 100644 index 0000000..200079a --- /dev/null +++ b/libk3b/tools/k3blistviewitemanimator.h @@ -0,0 +1,78 @@ +/* + * + * $Id: k3blistviewitemanimator.h 689561 2007-07-18 15:19:38Z trueg $ + * Copyright (C) 2004 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_LISTVIEWITEM_ANIMATOR_H_ +#define _K3B_LISTVIEWITEM_ANIMATOR_H_ + +#include <qobject.h> +#include <qpixmap.h> +#include "k3b_export.h" + +class QListViewItem; +class QTimer; + + +/** + * Fades an icon on a listview item in and out. + */ +class LIBK3B_EXPORT K3bListViewItemAnimator : public QObject +{ + Q_OBJECT + + public: + K3bListViewItemAnimator( QObject* parent = 0, const char* name = 0 ); + /** + * Will use the items pixmap. + */ + K3bListViewItemAnimator( QListViewItem* item, int col, QObject* parent = 0, const char* name = 0 ); + ~K3bListViewItemAnimator(); + + QListViewItem* item() const; + + public slots: + void start(); + void stop(); + + void setItem( QListViewItem*, int col ); + + /** + * Default is the pixmap from the item. + */ + void setPixmap( const QPixmap& ); + + void setColumn( int col ); + + /** + * Default is the base color of the listview. + */ + void setFadeColor( const QColor& ); + + private slots: + void slotAnimate(); + + private: + void init(); + + int m_animationStep; + bool m_animationBack; + QPixmap m_pixmap; + QColor m_fadeColor; + QListViewItem* m_item; + int m_column; + + QTimer* m_timer; +}; + +#endif diff --git a/libk3b/tools/k3bmd5job.cpp b/libk3b/tools/k3bmd5job.cpp new file mode 100644 index 0000000..2679f33 --- /dev/null +++ b/libk3b/tools/k3bmd5job.cpp @@ -0,0 +1,322 @@ +/* + * + * $Id: k3bmd5job.cpp 633751 2007-02-15 08:22:49Z trueg $ + * Copyright (C) 2003-2007 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bmd5job.h" +#include <k3biso9660.h> +#include <k3bglobals.h> +#include <k3bdevice.h> +#include <k3bfilesplitter.h> + +#include <kmdcodec.h> +#include <klocale.h> +#include <kdebug.h> +#include <kio/netaccess.h> + +#include <qtimer.h> +#include <qcstring.h> +#include <qsocketnotifier.h> + +#include <unistd.h> + + +class K3bMd5Job::K3bMd5JobPrivate +{ +public: + K3bMd5JobPrivate() + : fileDes(-1), + fdNotifier(0), + finished(true), + data(0), + isoFile(0), + maxSize(0), + lastProgress(0) { + } + + KMD5 md5; + K3bFileSplitter file; + QTimer timer; + QString filename; + int fileDes; + K3bDevice::Device* device; + QSocketNotifier* fdNotifier; + + bool finished; + char* data; + const K3bIso9660File* isoFile; + + unsigned long long maxSize; + unsigned long long readData; + + int lastProgress; + + KIO::filesize_t imageSize; + + static const int BUFFERSIZE = 2048*10; +}; + + +K3bMd5Job::K3bMd5Job( K3bJobHandler* jh, QObject* parent, const char* name ) + : K3bJob( jh, parent, name ) +{ + d = new K3bMd5JobPrivate; + d->data = new char[K3bMd5JobPrivate::BUFFERSIZE]; + connect( &d->timer, SIGNAL(timeout()), + this, SLOT(slotUpdate()) ); +} + + +K3bMd5Job::~K3bMd5Job() +{ + delete [] d->data; + delete d; +} + + +void K3bMd5Job::start() +{ + cancel(); + + jobStarted(); + d->readData = 0; + + if( d->isoFile ) { + d->imageSize = d->isoFile->size(); + } + else if( !d->filename.isEmpty() ) { + if( !QFile::exists( d->filename ) ) { + emit infoMessage( i18n("Could not find file %1").arg(d->filename), ERROR ); + jobFinished(false); + return; + } + + d->file.setName( d->filename ); + if( !d->file.open( IO_ReadOnly ) ) { + emit infoMessage( i18n("Could not open file %1").arg(d->filename), ERROR ); + jobFinished(false); + return; + } + + d->imageSize = K3b::filesize( KURL::fromPathOrURL(d->filename) ); + } + else + d->imageSize = 0; + + if( d->device ) { + // + // Let the drive determine the optimal reading speed + // + d->device->setSpeed( 0xffff, 0xffff ); + } + + d->md5.reset(); + d->finished = false; + if( d->fileDes != -1 ) + setupFdNotifier(); + else + d->timer.start(0); +} + + +void K3bMd5Job::setupFdNotifier() +{ + // the QSocketNotifier will fire once the fd is closed + delete d->fdNotifier; + d->fdNotifier = new QSocketNotifier( d->fileDes, QSocketNotifier::Read, this ); + connect( d->fdNotifier, SIGNAL(activated(int)), this, SLOT(slotUpdate()) ); + d->fdNotifier->setEnabled( true ); +} + + +void K3bMd5Job::cancel() +{ + if( !d->finished ) { + stopAll(); + + emit canceled(); + jobFinished( false ); + } +} + + +void K3bMd5Job::setFile( const QString& filename ) +{ + d->filename = filename; + d->isoFile = 0; + d->fileDes = -1; + d->device = 0; +} + + +void K3bMd5Job::setFile( const K3bIso9660File* file ) +{ + d->isoFile = file; + d->fileDes = -1; + d->filename.truncate(0); + d->device = 0; +} + + +void K3bMd5Job::setFd( int fd ) +{ + d->fileDes = fd; + d->filename.truncate(0); + d->isoFile = 0; + d->device = 0; +} + + +void K3bMd5Job::setDevice( K3bDevice::Device* dev ) +{ + d->device = dev; + d->fileDes = -1; + d->filename.truncate(0); + d->isoFile = 0; +} + + +void K3bMd5Job::setMaxReadSize( unsigned long long size ) +{ + d->maxSize = size; +} + + +void K3bMd5Job::slotUpdate() +{ + if( !d->finished ) { + + // determine bytes to read + unsigned int readSize = K3bMd5JobPrivate::BUFFERSIZE; + if( d->maxSize > 0 ) + readSize = QMIN( readSize, d->maxSize - d->readData ); + + if( readSize <= 0 ) { + // kdDebug() << "(K3bMd5Job) reached max size of " << d->maxSize << ". Stopping." << endl; + emit debuggingOutput( "K3bMd5Job", QString("Reached max read of %1. Stopping after %2 bytes.").arg(d->maxSize).arg(d->readData) ); + stopAll(); + emit percent( 100 ); + jobFinished(true); + } + else { + int read = 0; + + // + // read from the iso9660 file + // + if( d->isoFile ) { + read = d->isoFile->read( d->readData, d->data, readSize ); + } + + // + // read from the device + // + else if( d->device ) { + // + // when reading from a device we always read multiples of 2048 bytes. + // Only the last sector may not be used completely. + // + unsigned long sector = d->readData/2048; + unsigned int sectorCnt = QMAX( readSize/2048, 1 ); + read = -1; + if( d->device->read10( reinterpret_cast<unsigned char*>(d->data), + sectorCnt*2048, + sector, + sectorCnt ) ) + read = QMIN( readSize, sectorCnt*2048 ); + } + + // + // read from the file + // + else if( d->fileDes < 0 ) { + read = d->file.readBlock( d->data, readSize ); + } + + // + // reading from the file descriptor + // + else { + read = ::read( d->fileDes, d->data, readSize ); + } + + if( read < 0 ) { + emit infoMessage( i18n("Error while reading from file %1").arg(d->filename), ERROR ); + stopAll(); + jobFinished(false); + } + else if( read == 0 ) { + // kdDebug() << "(K3bMd5Job) read all data. Total size: " << d->readData << ". Stopping." << endl; + emit debuggingOutput( "K3bMd5Job", QString("All data read. Stopping after %1 bytes.").arg(d->readData) ); + stopAll(); + emit percent( 100 ); + jobFinished(true); + } + else { + d->readData += read; + d->md5.update( d->data, read ); + int progress = 0; + if( d->isoFile || !d->filename.isEmpty() ) + progress = (int)((double)d->readData * 100.0 / (double)d->imageSize); + else if( d->maxSize > 0 ) + progress = (int)((double)d->readData * 100.0 / (double)d->maxSize); + + if( progress != d->lastProgress ) { + d->lastProgress = progress; + emit percent( progress ); + } + } + } + } +} + + +QCString K3bMd5Job::hexDigest() +{ + if( d->finished ) + return d->md5.hexDigest(); + else + return ""; +} + + +QCString K3bMd5Job::base64Digest() +{ + if( d->finished ) + return d->md5.base64Digest(); + else + return ""; + +} + + +void K3bMd5Job::stop() +{ + emit debuggingOutput( "K3bMd5Job", QString("Stopped manually after %1 bytes.").arg(d->readData) ); + stopAll(); + jobFinished( true ); +} + + +void K3bMd5Job::stopAll() +{ + if( d->fdNotifier ) + d->fdNotifier->setEnabled( false ); + if( d->file.isOpen() ) + d->file.close(); + d->timer.stop(); + d->finished = true; +} + +#include "k3bmd5job.moc" diff --git a/libk3b/tools/k3bmd5job.h b/libk3b/tools/k3bmd5job.h new file mode 100644 index 0000000..cd64795 --- /dev/null +++ b/libk3b/tools/k3bmd5job.h @@ -0,0 +1,92 @@ +/* + * + * $Id: k3bmd5job.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_MD5_JOB_H_ +#define _K3B_MD5_JOB_H_ + +#include <k3bjob.h> +#include <qcstring.h> +#include "k3b_export.h" + +namespace K3bDevice { + class Device; +} + +class K3bIso9660File; + + +class LIBK3B_EXPORT K3bMd5Job : public K3bJob +{ + Q_OBJECT + + public: + K3bMd5Job( K3bJobHandler* jh , QObject* parent = 0, const char* name = 0 ); + ~K3bMd5Job(); + + QCString hexDigest(); + QCString base64Digest(); + + public slots: + void start(); + void stop(); + void cancel(); + + // FIXME: read from QIODevice and thus add K3bFileSplitter support + + /** + * read from a file. + * + * Be aware that the K3bMd5Job uses K3bFileSplitter to read splitted + * images. In the future this will be changed with the introduction + * of a setIODevice method. + */ + void setFile( const QString& filename ); + + /** + * read from an iso9660 file + */ + void setFile( const K3bIso9660File* ); + + /** + * read from a device + * This should be used in combination with setMaxReadSize + */ + void setDevice( K3bDevice::Device* dev ); + + /** + * read from the opened file descriptor. + * One needs to set the max read length or call stop() + * to finish calculation. + */ + void setFd( int fd ); + + /** + * Set the maximum bytes to read. + */ + void setMaxReadSize( unsigned long long ); + + private slots: + void slotUpdate(); + + private: + void setupFdNotifier(); + void stopAll(); + + class K3bMd5JobPrivate; + K3bMd5JobPrivate* d; +}; + +#endif diff --git a/libk3b/tools/k3bmsfedit.cpp b/libk3b/tools/k3bmsfedit.cpp new file mode 100644 index 0000000..13ad0a3 --- /dev/null +++ b/libk3b/tools/k3bmsfedit.cpp @@ -0,0 +1,153 @@ +/* + * + * $Id: k3bmsfedit.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bmsfedit.h" +#include "k3bglobals.h" + +#include <qstringlist.h> +#include <qlineedit.h> +#include <qstyle.h> +#include <qfontmetrics.h> +#include <qapplication.h> + + + +K3bMsfValidator::K3bMsfValidator( QObject* parent, const char* name ) + : QRegExpValidator( K3b::Msf::regExp(), parent, name ) +{ +} + + + +K3bMsfEdit::K3bMsfEdit( QWidget* parent, const char* name ) + : QSpinBox( parent, name ) +{ + setValidator( new K3bMsfValidator( this ) ); + setMinValue( 0 ); + // some very high value (10000 minutes) + setMaxValue( 10000*60*75 ); + + connect( this, SIGNAL(valueChanged(int)), + this, SLOT(slotValueChanged(int)) ); +} + + +K3bMsfEdit::~K3bMsfEdit() +{} + + +QSize K3bMsfEdit::sizeHint() const +{ + // more or less copied from QSpinBox + constPolish(); + QSize sz = editor()->sizeHint(); + int h = sz.height(); + QFontMetrics fm( font() ); + int w = fm.width( "00:00:00" ); + int wx = fm.width( ' ' )*2; + int frame = style().pixelMetric( QStyle::PM_SpinBoxFrameWidth ); + return style().sizeFromContents(QStyle::CT_SpinBox, this, + QSize( w + wx + downRect().width() + frame*2, + h + frame*2). + expandedTo( QApplication::globalStrut() )); +} + + +QString K3bMsfEdit::mapValueToText( int value ) +{ + return K3b::framesToString( value, true ); +} + + +K3b::Msf K3bMsfEdit::msfValue() const +{ + return K3b::Msf(value()); +} + + +void K3bMsfEdit::setMsfValue( const K3b::Msf& msf ) +{ + setValue( msf.totalFrames() ); +} + + +int K3bMsfEdit::mapTextToValue( bool* ok ) +{ + return K3b::Msf::fromString( text(), ok ).lba(); +} + + +void K3bMsfEdit::setText( const QString& str ) +{ + bool ok; + editor()->setText( str ); + setValue( mapTextToValue( &ok) ); +} + + +void K3bMsfEdit::setFrameStyle( int style ) +{ + editor()->setFrameStyle( style ); +} + +void K3bMsfEdit::setLineWidth( int v ) +{ + editor()->setLineWidth( v ); +} + +void K3bMsfEdit::setValue( int v ) +{ + int i = editor()->cursorPosition(); + QSpinBox::setValue( v ); + editor()->setCursorPosition( i ); +} + +void K3bMsfEdit::stepUp() +{ + setValue( value() + currentStepValue() ); +} + +void K3bMsfEdit::stepDown() +{ + setValue( value() - currentStepValue() ); +} + +int K3bMsfEdit::currentStepValue() const +{ + int val = 1; + + // look if we are currently editing minutes or seconds + QString text = editor()->text(); + if( text.length() == 8 ) { + text = text.mid( editor()->cursorPosition() ); + int num = text.contains( ':' ); + if( num == 1 ) + val = 75; + else if( num == 2 ) + val = 60*75; + } + + return val; +} + + +void K3bMsfEdit::slotValueChanged( int v ) +{ + emit valueChanged( K3b::Msf(v) ); +} + +#include "k3bmsfedit.moc" diff --git a/libk3b/tools/k3bmsfedit.h b/libk3b/tools/k3bmsfedit.h new file mode 100644 index 0000000..c573910 --- /dev/null +++ b/libk3b/tools/k3bmsfedit.h @@ -0,0 +1,70 @@ +/* + * + * $Id: k3bmsfedit.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_MSF_EDIT_H +#define K3B_MSF_EDIT_H + + +#include <qspinbox.h> +#include <qstring.h> +#include <qvalidator.h> + +#include <k3bmsf.h> +#include "k3b_export.h" + +class K3bMsfValidator : public QRegExpValidator +{ + public: + K3bMsfValidator( QObject* parent = 0, const char* name = 0 ); +}; + + +class LIBK3B_EXPORT K3bMsfEdit : public QSpinBox +{ + Q_OBJECT + + public: + K3bMsfEdit( QWidget* parent = 0, const char* name = 0 ); + ~K3bMsfEdit(); + + QSize sizeHint() const; + + void setFrameStyle( int style ); + void setLineWidth(int); + + K3b::Msf msfValue() const; + + signals: + void valueChanged( const K3b::Msf& ); + + public slots: + void setValue( int v ); + void setText( const QString& ); + void setMsfValue( const K3b::Msf& ); + void stepUp(); + void stepDown(); + + protected: + QString mapValueToText( int ); + int mapTextToValue( bool* ok ); + int currentStepValue() const; + + private slots: + void slotValueChanged( int ); +}; + + +#endif diff --git a/libk3b/tools/k3bmultichoicedialog.cpp b/libk3b/tools/k3bmultichoicedialog.cpp new file mode 100644 index 0000000..4c12554 --- /dev/null +++ b/libk3b/tools/k3bmultichoicedialog.cpp @@ -0,0 +1,191 @@ +/* + * + * $Id: k3bmultichoicedialog.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bmultichoicedialog.h" +#include "k3bstdguiitems.h" +#include <k3brichtextlabel.h> + +#include <kpushbutton.h> +#include <kapplication.h> +#include <kiconloader.h> + +#include <qlayout.h> +#include <qsignalmapper.h> +#include <qptrlist.h> +#include <qlabel.h> +#include <qhbox.h> +#include <qmessagebox.h> + + +class K3bMultiChoiceDialog::Private +{ +public: + Private() + : mapper(0), + buttonLayout(0) { + } + + QSignalMapper* mapper; + QPtrList<KPushButton> buttons; + QHBoxLayout* buttonLayout; + + bool buttonClicked; +}; + + +// from kmessagebox.cpp +static QPixmap themedMessageBoxIcon(QMessageBox::Icon icon) +{ + QString icon_name; + + switch(icon) { + case QMessageBox::NoIcon: + return QPixmap(); + break; + case QMessageBox::Information: + icon_name = "messagebox_info"; + break; + case QMessageBox::Warning: + icon_name = "messagebox_warning"; + break; + case QMessageBox::Critical: + icon_name = "messagebox_critical"; + break; + default: + break; + } + + QPixmap ret = KApplication::kApplication()->iconLoader()->loadIcon(icon_name, KIcon::NoGroup, KIcon::SizeMedium, KIcon::DefaultState, 0, true); + + if (ret.isNull()) + return QMessageBox::standardIcon(icon); + else + return ret; +} + +K3bMultiChoiceDialog::K3bMultiChoiceDialog( const QString& caption, + const QString& text, + QMessageBox::Icon icon, + QWidget* parent, const char* name ) + : KDialog( parent, name ) +{ + d = new Private(); + d->mapper = new QSignalMapper( this ); + connect( d->mapper, SIGNAL(mapped(int)), this, SLOT(done(int)) ); + + setCaption( caption ); + + QGridLayout* mainGrid = new QGridLayout( this ); + mainGrid->setSpacing( spacingHint() ); + mainGrid->setMargin( marginHint() ); + + QHBox* contents = new QHBox( this ); + contents->setSpacing( KDialog::spacingHint()*2 ); + contents->setMargin( 0 ); + + QLabel* pixLabel = new QLabel( contents ); + pixLabel->setPixmap( themedMessageBoxIcon( icon ) ); + pixLabel->setScaledContents( false ); + QLabel* label = new K3bRichTextLabel( text, contents ); + contents->setStretchFactor( label, 1 ); + + d->buttonLayout = new QHBoxLayout; + d->buttonLayout->setSpacing( spacingHint() ); + d->buttonLayout->setMargin( 0 ); + + mainGrid->addMultiCellWidget( contents, 0, 0, 0, 2 ); + mainGrid->addMultiCellWidget( K3bStdGuiItems::horizontalLine( this ), 1, 1, 0, 2 ); + mainGrid->addLayout( d->buttonLayout, 2, 1 ); + + mainGrid->setColStretch( 0, 1 ); + mainGrid->setColStretch( 2, 1 ); + mainGrid->setRowStretch( 0, 1 ); +} + + +K3bMultiChoiceDialog::~K3bMultiChoiceDialog() +{ + delete d; +} + + +int K3bMultiChoiceDialog::addButton( const KGuiItem& b ) +{ + KPushButton* button = new KPushButton( b, this ); + d->buttonLayout->add( button ); + d->buttons.append(button); + d->mapper->setMapping( button, d->buttons.count() ); + connect( button, SIGNAL(clicked()), d->mapper, SLOT(map()) ); + return d->buttons.count(); +} + + +void K3bMultiChoiceDialog::slotButtonClicked( int code ) +{ + d->buttonClicked = true; + done( code ); +} + + +int K3bMultiChoiceDialog::exec() +{ + d->buttonClicked = false; + return KDialog::exec(); +} + + +void K3bMultiChoiceDialog::closeEvent( QCloseEvent* e ) +{ + // make sure the dialog can only be closed by the buttons + // otherwise we may get an undefined return value in exec + + if( d->buttonClicked ) + KDialog::closeEvent( e ); + else + e->ignore(); +} + + +int K3bMultiChoiceDialog::choose( const QString& caption, + const QString& text, + QMessageBox::Icon icon, + QWidget* parent, + const char* name, + int buttonCount, + const KGuiItem& b1, + const KGuiItem& b2, + const KGuiItem& b3, + const KGuiItem& b4, + const KGuiItem& b5, + const KGuiItem& b6 ) +{ + K3bMultiChoiceDialog dlg( caption, text, icon, parent, name ); + dlg.addButton( b1 ); + if( buttonCount > 1 ) + dlg.addButton( b2 ); + if( buttonCount > 2 ) + dlg.addButton( b3 ); + if( buttonCount > 3 ) + dlg.addButton( b4 ); + if( buttonCount > 4 ) + dlg.addButton( b5 ); + if( buttonCount > 5 ) + dlg.addButton( b6 ); + + return dlg.exec(); +} + + +#include "k3bmultichoicedialog.moc" diff --git a/libk3b/tools/k3bmultichoicedialog.h b/libk3b/tools/k3bmultichoicedialog.h new file mode 100644 index 0000000..f293fef --- /dev/null +++ b/libk3b/tools/k3bmultichoicedialog.h @@ -0,0 +1,73 @@ +/* + * + * $Id: k3bmultichoicedialog.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_MULTI_CHOICE_DIALOG_H_ +#define _K3B_MULTI_CHOICE_DIALOG_H_ + +#include <kdialog.h> +#include <kstdguiitem.h> +#include "k3b_export.h" + +#include <qmessagebox.h> + + +class QCloseEvent; + +class LIBK3B_EXPORT K3bMultiChoiceDialog : public KDialog +{ + Q_OBJECT + + public: + K3bMultiChoiceDialog( const QString& caption, + const QString& text, + QMessageBox::Icon = QMessageBox::Information, + QWidget* parent = 0, const char* name = 0 ); + ~K3bMultiChoiceDialog(); + + /** + * Adds a new button. returns it's number starting at 1. + */ + int addButton( const KGuiItem& ); + + static int choose( const QString& caption, + const QString& text, + QMessageBox::Icon = QMessageBox::Information, + QWidget* parent = 0, + const char* name = 0, + int buttonCount = 2, + const KGuiItem& b1 = KStdGuiItem::yes(), + const KGuiItem& b2 = KStdGuiItem::no(), + const KGuiItem& b3 = KStdGuiItem::no(), + const KGuiItem& b4 = KStdGuiItem::no(), + const KGuiItem& b5 = KStdGuiItem::no(), + const KGuiItem& b6 = KStdGuiItem::no() ); + + public slots: + /** + * returnes the number of the clicked button starting at 1. + */ + int exec(); + + private slots: + void slotButtonClicked( int ); + + private: + void closeEvent( QCloseEvent* ); + + class Private; + Private* d; +}; + +#endif diff --git a/libk3b/tools/k3bpipe.cpp b/libk3b/tools/k3bpipe.cpp new file mode 100644 index 0000000..6f15915 --- /dev/null +++ b/libk3b/tools/k3bpipe.cpp @@ -0,0 +1,79 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bpipe.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> + + +K3bPipe::K3bPipe() +{ + m_fd[0] = m_fd[1] = -1; +} + + +K3bPipe::~K3bPipe() +{ + close(); +} + + +bool K3bPipe::open() +{ + close(); + + if( ::socketpair( AF_UNIX, SOCK_STREAM, 0, m_fd ) == 0 ) { + fcntl( m_fd[0], F_SETFD, FD_CLOEXEC ); + fcntl( m_fd[1], F_SETFD, FD_CLOEXEC ); + return true; + } + else { + kdDebug() << "(K3bPipe) failed to setup socket pair." << endl; + return false; + } +} + + +void K3bPipe::closeIn() +{ + if( m_fd[1] != -1 ) { + ::close( m_fd[1] ); + m_fd[1] = -1; + } +} + + +void K3bPipe::closeOut() +{ + if( m_fd[0] != -1 ) { + ::close( m_fd[0] ); + m_fd[0] = -1; + } +} + + +void K3bPipe::close() +{ + closeIn(); + closeOut(); +} diff --git a/libk3b/tools/k3bpipe.h b/libk3b/tools/k3bpipe.h new file mode 100644 index 0000000..fba3455 --- /dev/null +++ b/libk3b/tools/k3bpipe.h @@ -0,0 +1,60 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_PIPE_H_ +#define _K3B_PIPE_H_ + +#include "k3b_export.h" + +/** + * The K3bPipe class represents a file descriptor pair + * which can for example be used to connect two processes + */ +class LIBK3B_EXPORT K3bPipe +{ + public: + /** + * Creates a closed pipe object + */ + K3bPipe(); + + /** + * The destructor takes care of closing the pipe + */ + ~K3bPipe(); + + /** + * Open the pipe. This creates a file descriptor pair + * which can be accessed using the in() and out() + * methods. + */ + bool open(); + + /** + * Calls both closeIn() and closeOut() + */ + void close(); + + void closeIn(); + void closeOut(); + + int in() const { return m_fd[1]; } + int out() const { return m_fd[0]; } + + private: + int m_fd[2]; +}; + +#endif diff --git a/libk3b/tools/k3bprogressdialog.cpp b/libk3b/tools/k3bprogressdialog.cpp new file mode 100644 index 0000000..69a81f5 --- /dev/null +++ b/libk3b/tools/k3bprogressdialog.cpp @@ -0,0 +1,107 @@ +/* + * + * $Id: k3bprogressdialog.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bprogressdialog.h" + +#include <k3bbusywidget.h> + +#include <qlabel.h> +#include <qpushbutton.h> +#include <qlayout.h> +#include <qframe.h> +#include <qwidgetstack.h> + +#include <klocale.h> +#include <kprogress.h> + + +K3bProgressDialog::K3bProgressDialog( const QString& text, + QWidget* parent, + const QString& caption, + const char* name ) + : KDialogBase( parent, name, true, caption, Cancel|Ok, Ok, true ) +{ + QFrame* main = makeMainWidget(); + QGridLayout* mainLayout = new QGridLayout( main ); + mainLayout->setMargin( marginHint() ); + mainLayout->setSpacing( spacingHint() ); + + m_label = new QLabel( text, main ); + m_stack = new QWidgetStack( main ); + m_progressBar = new KProgress( m_stack ); + m_busyWidget = new K3bBusyWidget( m_stack ); + m_stack->addWidget( m_progressBar ); + m_stack->addWidget( m_busyWidget ); + + mainLayout->addWidget( m_label, 0, 0 ); + mainLayout->addWidget( m_stack, 1, 0 ); + + showButtonOK( false ); +} + + +K3bProgressDialog::~K3bProgressDialog() +{} + + +int K3bProgressDialog::exec( bool progress ) +{ + if( progress ) + m_stack->raiseWidget( m_progressBar ); + else + m_stack->raiseWidget( m_busyWidget ); + + m_busyWidget->showBusy( !progress ); + + actionButton( Cancel )->setEnabled(true); + + return KDialogBase::exec(); +} + + +void K3bProgressDialog::setText( const QString& text ) +{ + m_label->setText( text ); +} + + +void K3bProgressDialog::slotFinished( bool success ) +{ + m_busyWidget->showBusy( false ); + + showButtonOK( true ); + showButtonCancel( false ); + + if( success ) + m_label->setText( i18n("Disk successfully erased. Please reload the disk.") ); + else + m_label->setText( i18n("K3b was unable to erase the disk.") ); +} + + +void K3bProgressDialog::slotCancel() +{ + emit cancelClicked(); + // we simply forbid to click cancel twice + actionButton( Cancel )->setEnabled(false); +} + + +void K3bProgressDialog::setProgress( int p ) +{ + m_progressBar->setProgress( p ); +} + +#include "k3bprogressdialog.moc" diff --git a/libk3b/tools/k3bprogressdialog.h b/libk3b/tools/k3bprogressdialog.h new file mode 100644 index 0000000..32a0544 --- /dev/null +++ b/libk3b/tools/k3bprogressdialog.h @@ -0,0 +1,63 @@ +/* + * + * $Id: k3bprogressdialog.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_DIALOG_H_ +#define _K3B_PROGRESS_DIALOG_H_ + +#include <kdialogbase.h> +#include "k3b_export.h" + +class K3bBusyWidget; +class QLabel; +class KProgress; +class QWidgetStack; + + +/** + * A progressdialog which displays a line of text and a progress + * bar or a moving dot for tasks that do not provide any progress + * information. + */ +class LIBK3B_EXPORT K3bProgressDialog : public KDialogBase +{ + Q_OBJECT + + public: + K3bProgressDialog( const QString& text = QString::null, + QWidget* parent = 0, + const QString& caption = QString::null, + const char* name = 0 ); + ~K3bProgressDialog(); + + int exec( bool showProgress ); + + public slots: + void setText( const QString& ); + void slotFinished( bool success ); + void setProgress( int p ); + + private slots: + void slotCancel(); + + private: + QLabel* m_label; + QWidgetStack* m_stack; + K3bBusyWidget* m_busyWidget; + KProgress* m_progressBar; +}; + + +#endif diff --git a/libk3b/tools/k3bpushbutton.cpp b/libk3b/tools/k3bpushbutton.cpp new file mode 100644 index 0000000..11425ad --- /dev/null +++ b/libk3b/tools/k3bpushbutton.cpp @@ -0,0 +1,136 @@ +/* + * + * $Id: k3bpushbutton.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bpushbutton.h" + +#include <qtimer.h> +#include <qpopupmenu.h> +#include <qevent.h> + +#include <kglobalsettings.h> +#include <kapplication.h> + + + +class K3bPushButton::Private +{ +public: + Private() + : popupTimer(0) { + } + + QTimer* popupTimer; + QPoint mousePressPos; +}; + + + +K3bPushButton::K3bPushButton( QWidget* parent, const char* name ) + : KPushButton( parent, name ) +{ + d = new Private(); + installEventFilter(this); +} + + +K3bPushButton::K3bPushButton( const QString& text, QWidget* parent, const char* name ) + : KPushButton( text, parent, name ) +{ + d = new Private(); + installEventFilter(this); +} + + +K3bPushButton::K3bPushButton( const QIconSet& icon, const QString& text, + QWidget* parent, const char* name ) + : KPushButton( icon, text, parent, name ) +{ + d = new Private(); + installEventFilter(this); +} + + +K3bPushButton::K3bPushButton( const KGuiItem& item, QWidget* parent, const char* name ) + : KPushButton( item, parent, name ) +{ + d = new Private(); + installEventFilter(this); +} + + +K3bPushButton::~K3bPushButton() +{ + delete d; +} + + +void K3bPushButton::setDelayedPopupMenu( QPopupMenu* popup ) +{ + if( !d->popupTimer ) { + d->popupTimer = new QTimer( this ); + connect( d->popupTimer, SIGNAL(timeout()), this, SLOT(slotDelayedPopup()) ); + } + + setPopup( popup ); + + // we need to do the popup handling ourselves so we cheat a little + // QPushButton connects a popup slot to the pressed signal which we disconnect here + disconnect( this ); +} + + +bool K3bPushButton::eventFilter( QObject* o, QEvent* ev ) +{ + if( dynamic_cast<K3bPushButton*>(o) == this ) { + + // Popup the menu when the left mousebutton is pressed and the mouse + // is moved by a small distance. + if( popup() ) { + if( ev->type() == QEvent::MouseButtonPress ) { + QMouseEvent* mev = static_cast<QMouseEvent*>(ev); + d->mousePressPos = mev->pos(); + d->popupTimer->start( QApplication::startDragTime() ); + } + else if( ev->type() == QEvent::MouseMove ) { + QMouseEvent* mev = static_cast<QMouseEvent*>(ev); + if( ( mev->pos() - d->mousePressPos).manhattanLength() > KGlobalSettings::dndEventDelay() ) { + d->popupTimer->stop(); + slotDelayedPopup(); + return true; + } + } + } + } + + return KPushButton::eventFilter( o, ev ); +} + + +void K3bPushButton::slotDelayedPopup() +{ + d->popupTimer->stop(); + + if( isDown() ) { + // popup the menu. + // this has been taken from the QPushButton code + if( mapToGlobal( QPoint( 0, rect().bottom() ) ).y() + popup()->sizeHint().height() <= qApp->desktop()->height() ) + popup()->exec( mapToGlobal( rect().bottomLeft() ) ); + else + popup()->exec( mapToGlobal( rect().topLeft() - QPoint( 0, popup()->sizeHint().height() ) ) ); + setDown( false ); + } +} + +#include "k3bpushbutton.moc" diff --git a/libk3b/tools/k3bpushbutton.h b/libk3b/tools/k3bpushbutton.h new file mode 100644 index 0000000..1369af3 --- /dev/null +++ b/libk3b/tools/k3bpushbutton.h @@ -0,0 +1,75 @@ +/* + * + * $Id: k3bpushbutton.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004-2007 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_PUSH_BUTTON_H_ +#define _K3B_PUSH_BUTTON_H_ + + +#include <kpushbutton.h> +#include "k3b_export.h" + +/** + * A pushbutton with delayed popu pmenu support just like the KToolBarButton + */ +class LIBK3B_EXPORT K3bPushButton : public KPushButton +{ + Q_OBJECT + + public: + /** + * Default constructor. + */ + K3bPushButton( QWidget* parent = 0, const char* name = 0 ); + + /** + * Constructor, that sets the button-text to @p text + */ + K3bPushButton( const QString& text, QWidget* parent = 0, const char* name = 0 ); + + /** + * Constructor, that sets an icon and the button-text to @p text + */ + K3bPushButton( const QIconSet& icon, const QString& text, + QWidget* parent = 0, const char* name = 0 ); + + /** + * Constructor that takes a KGuiItem for the text, the icon, the tooltip + * and the what's this help + */ + K3bPushButton( const KGuiItem& item, QWidget* parent = 0, const char* name = 0 ); + + /** + * Destructs the button. + */ + ~K3bPushButton(); + + /** + * The popup menu will show if the button is pressed down for about half a second + * or if the mouse is moved while pressed just like the KToolBarButton. + */ + void setDelayedPopupMenu( QPopupMenu* ); + + protected: + virtual bool eventFilter( QObject*, QEvent* ); + + private slots: + void slotDelayedPopup(); + + private: + class Private; + Private* d; +}; + +#endif diff --git a/libk3b/tools/k3bradioaction.cpp b/libk3b/tools/k3bradioaction.cpp new file mode 100644 index 0000000..08fbece --- /dev/null +++ b/libk3b/tools/k3bradioaction.cpp @@ -0,0 +1,94 @@ +/* + * + * $Id: k3bradioaction.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2005 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bradioaction.h" + +#include <ktoolbarbutton.h> + +K3bRadioAction::K3bRadioAction( const QString& text, const KShortcut& cut, + QObject* parent, const char* name ) + : KToggleAction( text, cut, parent, name ), + m_alwaysEmit(false) +{ +} + +K3bRadioAction::K3bRadioAction( const QString& text, const KShortcut& cut, + const QObject* receiver, const char* slot, + QObject* parent, const char* name ) + : KToggleAction( text, cut, receiver, slot, parent, name ), + m_alwaysEmit(false) +{ +} + +K3bRadioAction::K3bRadioAction( const QString& text, const QIconSet& pix, + const KShortcut& cut, + QObject* parent, const char* name ) + : KToggleAction( text, pix, cut, parent, name ), + m_alwaysEmit(false) +{ +} + +K3bRadioAction::K3bRadioAction( const QString& text, const QString& pix, + const KShortcut& cut, + QObject* parent, const char* name ) + : KToggleAction( text, pix, cut, parent, name ), + m_alwaysEmit(false) +{ +} + +K3bRadioAction::K3bRadioAction( const QString& text, const QIconSet& pix, + const KShortcut& cut, + const QObject* receiver, const char* slot, + QObject* parent, const char* name ) + : KToggleAction( text, pix, cut, receiver, slot, parent, name ), + m_alwaysEmit(false) +{ +} + +K3bRadioAction::K3bRadioAction( const QString& text, const QString& pix, + const KShortcut& cut, + const QObject* receiver, const char* slot, + QObject* parent, const char* name ) + : KToggleAction( text, pix, cut, receiver, slot, parent, name ), + m_alwaysEmit(false) +{ +} + +K3bRadioAction::K3bRadioAction( QObject* parent, const char* name ) + : KToggleAction( parent, name ), + m_alwaysEmit(false) +{ +} + +void K3bRadioAction::slotActivated() +{ + if( isChecked() ) { + if( m_alwaysEmit ) + emit activated(); + + const QObject *senderObj = sender(); + + if ( !senderObj || !::qt_cast<const KToolBarButton *>( senderObj ) ) + return; + + const_cast<KToolBarButton *>( static_cast<const KToolBarButton *>( senderObj ) )->on( true ); + + return; + } + + KToggleAction::slotActivated(); +} + +#include "k3bradioaction.moc" diff --git a/libk3b/tools/k3bradioaction.h b/libk3b/tools/k3bradioaction.h new file mode 100644 index 0000000..104b598 --- /dev/null +++ b/libk3b/tools/k3bradioaction.h @@ -0,0 +1,122 @@ +/* + * + * $Id: k3bradioaction.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2005 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_RADIO_ACTION_H_ +#define _K3B_RADIO_ACTION_H_ + +#include <kactionclasses.h> +#include "k3b_export.h" + +/** + * This differs from KRadioAction only in the boolean + * flag which says if it should always emit the signals + * even if it was checked twice. + * + * Docu copied from kdelibs + */ +class LIBK3B_EXPORT K3bRadioAction : public KToggleAction +{ + Q_OBJECT + + public: + /** + * Constructs a radio action with text and potential keyboard + * accelerator but nothing else. Use this only if you really + * know what you are doing. + * + * @param text The text that will be displayed. + * @param cut The corresponding keyboard accelerator (shortcut). + * @param parent This action's parent. + * @param name An internal name for this action. + */ + K3bRadioAction( const QString& text, const KShortcut& cut = KShortcut(), QObject* parent = 0, const char* name = 0 ); + + /** + * @param text The text that will be displayed. + * @param cut The corresponding keyboard accelerator (shortcut). + * @param receiver The SLOT's parent. + * @param slot The SLOT to invoke to execute this action. + * @param parent This action's parent. + * @param name An internal name for this action. + */ + K3bRadioAction( const QString& text, const KShortcut& cut, + const QObject* receiver, const char* slot, QObject* parent, const char* name = 0 ); + + /** + * @param text The text that will be displayed. + * @param pix The icons that go with this action. + * @param cut The corresponding keyboard accelerator (shortcut). + * @param parent This action's parent. + * @param name An internal name for this action. + */ + K3bRadioAction( const QString& text, const QIconSet& pix, const KShortcut& cut = KShortcut(), + QObject* parent = 0, const char* name = 0 ); + + /** + * @param text The text that will be displayed. + * @param pix The dynamically loaded icon that goes with this action. + * @param cut The corresponding keyboard accelerator (shortcut). + * @param parent This action's parent. + * @param name An internal name for this action. + */ + K3bRadioAction( const QString& text, const QString& pix, const KShortcut& cut = KShortcut(), + QObject* parent = 0, const char* name = 0 ); + + /** + * @param text The text that will be displayed. + * @param pix The icons that go with this action. + * @param cut The corresponding keyboard accelerator (shortcut). + * @param receiver The SLOT's parent. + * @param slot The SLOT to invoke to execute this action. + * @param parent This action's parent. + * @param name An internal name for this action. + */ + K3bRadioAction( const QString& text, const QIconSet& pix, const KShortcut& cut, + const QObject* receiver, const char* slot, QObject* parent, const char* name = 0 ); + + /** + * @param text The text that will be displayed. + * @param pix The dynamically loaded icon that goes with this action. + * @param cut The corresponding keyboard accelerator (shortcut). + * @param receiver The SLOT's parent. + * @param slot The SLOT to invoke to execute this action. + * @param parent This action's parent. + * @param name An internal name for this action. + */ + K3bRadioAction( const QString& text, const QString& pix, const KShortcut& cut, + const QObject* receiver, const char* slot, + QObject* parent, const char* name = 0 ); + + /** + * @param parent This action's parent. + * @param name An internal name for this action. + */ + K3bRadioAction( QObject* parent = 0, const char* name = 0 ); + + /** + * @param b if true the action will always emit the activated signal + * even if the toggled state did not change. The default is false. + * which is the same behaviour as KRadioAction + */ + void setAlwaysEmitActivated( bool b ) { m_alwaysEmit = b; } + + protected: + virtual void slotActivated(); + + private: + bool m_alwaysEmit; +}; + +#endif diff --git a/libk3b/tools/k3brichtextlabel.cpp b/libk3b/tools/k3brichtextlabel.cpp new file mode 100644 index 0000000..747bba1 --- /dev/null +++ b/libk3b/tools/k3brichtextlabel.cpp @@ -0,0 +1,109 @@ +/* + * + * $Id: k3brichtextlabel.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2005 Waldo Bastian <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3brichtextlabel.h" + +#include <qtooltip.h> +#include <qstylesheet.h> +#include <qsimplerichtext.h> + +#include <kglobalsettings.h> + +static QString qrichtextify( const QString& text ) +{ + if ( text.isEmpty() || text[0] == '<' ) + return text; + + QStringList lines = QStringList::split('\n', text); + for(QStringList::Iterator it = lines.begin(); it != lines.end(); ++it) + { + *it = QStyleSheet::convertFromPlainText( *it, QStyleSheetItem::WhiteSpaceNormal ); + } + + return lines.join(QString::null); +} + +K3bRichTextLabel::K3bRichTextLabel( const QString &text , QWidget *parent, const char *name ) + : QLabel ( parent, name ) { + m_defaultWidth = QMIN(400, KGlobalSettings::desktopGeometry(this).width()*2/5); + setAlignment( Qt::WordBreak ); + setText(text); +} + +K3bRichTextLabel::K3bRichTextLabel( QWidget *parent, const char *name ) + : QLabel ( parent, name ) { + m_defaultWidth = QMIN(400, KGlobalSettings::desktopGeometry(this).width()*2/5); + setAlignment( Qt::WordBreak ); +} + +void K3bRichTextLabel::setDefaultWidth(int defaultWidth) +{ + m_defaultWidth = defaultWidth; + updateGeometry(); +} + +QSizePolicy K3bRichTextLabel::sizePolicy() const +{ + return QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum, false); +} + +QSize K3bRichTextLabel::minimumSizeHint() const +{ + QString qt_text = qrichtextify( text() ); + int pref_width = 0; + int pref_height = 0; + QSimpleRichText rt(qt_text, font()); + pref_width = m_defaultWidth; + rt.setWidth(pref_width); + int used_width = rt.widthUsed(); + if (used_width <= pref_width) + { + while(true) + { + int new_width = (used_width * 9) / 10; + rt.setWidth(new_width); + int new_height = rt.height(); + if (new_height > pref_height) + break; + used_width = rt.widthUsed(); + if (used_width > new_width) + break; + } + pref_width = used_width; + } + else + { + if (used_width > (pref_width *2)) + pref_width = pref_width *2; + else + pref_width = used_width; + } + + return QSize(pref_width, rt.height()); +} + +QSize K3bRichTextLabel::sizeHint() const +{ + return minimumSizeHint(); +} + +void K3bRichTextLabel::setText( const QString &text ) { + QLabel::setText(text); +} + +void K3bRichTextLabel::virtual_hook( int, void* ) +{ /*BASE::virtual_hook( id, data );*/ } + +#include "k3brichtextlabel.moc" diff --git a/libk3b/tools/k3brichtextlabel.h b/libk3b/tools/k3brichtextlabel.h new file mode 100644 index 0000000..0a25395 --- /dev/null +++ b/libk3b/tools/k3brichtextlabel.h @@ -0,0 +1,62 @@ +/* + * + * $Id: k3brichtextlabel.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2005 Waldo Bastian <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 K3BRICHTEXTLABEL_H +#define K3BRICHTEXTLABEL_H + +#include <qlabel.h> + +#include <k3b_export.h> + +/** + * @short A replacement for QLabel that supports richtext and proper layout management + * + * @author Waldo Bastian <[email protected]> + */ + +/* + * QLabel + */ +class LIBK3B_EXPORT K3bRichTextLabel : public QLabel { + Q_OBJECT + +public: + /** + * Default constructor. + */ + K3bRichTextLabel( QWidget *parent, const char *name = 0 ); + K3bRichTextLabel( const QString &text, QWidget *parent, const char *name = 0 ); + + int defaultWidth() const { return m_defaultWidth; } + void setDefaultWidth(int defaultWidth); + + virtual QSize minimumSizeHint() const; + virtual QSize sizeHint() const; + QSizePolicy sizePolicy() const; + +public slots: + void setText( const QString & ); + +protected: + int m_defaultWidth; + +protected: + virtual void virtual_hook( int id, void* data ); +private: + class K3bRichTextLabelPrivate; + K3bRichTextLabelPrivate *d; +}; + +#endif // K3BRICHTEXTLABEL_H diff --git a/libk3b/tools/k3bsignalwaiter.cpp b/libk3b/tools/k3bsignalwaiter.cpp new file mode 100644 index 0000000..fcee2e0 --- /dev/null +++ b/libk3b/tools/k3bsignalwaiter.cpp @@ -0,0 +1,62 @@ +/* + * + * $Id: sourceheader 380067 2005-01-19 13:03:46Z trueg $ + * Copyright (C) 2005 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bsignalwaiter.h" +#include "k3bjob.h" + +#include <qeventloop.h> +#include <qapplication.h> + + +K3bSignalWaiter::K3bSignalWaiter() + : QObject(), + m_inLoop(true) +{ +} + + +K3bSignalWaiter::~K3bSignalWaiter() +{ +} + + +void K3bSignalWaiter::waitForSignal( QObject* o, const char* signal ) +{ + K3bSignalWaiter w; + connect( o, signal, + &w, SLOT(slotSignal()) ); + + QApplication::eventLoop()->enterLoop(); +} + + +void K3bSignalWaiter::waitForJob( K3bJob* job ) +{ + if( !job->active() ) + return; + + waitForSignal( job, SIGNAL(finished(bool)) ); +} + + +void K3bSignalWaiter::slotSignal() +{ + if( m_inLoop ) { + m_inLoop = false; + QApplication::eventLoop()->exitLoop(); + } +} + +#include "k3bsignalwaiter.moc" diff --git a/libk3b/tools/k3bsignalwaiter.h b/libk3b/tools/k3bsignalwaiter.h new file mode 100644 index 0000000..19876b4 --- /dev/null +++ b/libk3b/tools/k3bsignalwaiter.h @@ -0,0 +1,51 @@ +/* + * + * $Id: sourceheader 380067 2005-01-19 13:03:46Z trueg $ + * Copyright (C) 2005 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_SIGNAL_WAITER_H_ +#define _K3B_SIGNAL_WAITER_H_ + +#include <qobject.h> + +#include <k3b_export.h> + +class K3bJob; + +class K3bSignalWaiter : public QObject +{ + Q_OBJECT + + public: + /** + * Use this to syncroneously wait for a signal. + */ + LIBK3B_EXPORT static void waitForSignal( QObject* o, const char* signal ); + + /** + * Use this to syncroneously wait for a job to finish. + * If the job is not running at all this returns immedeately. + */ + LIBK3B_EXPORT static void waitForJob( K3bJob* job ); + + private slots: + void slotSignal(); + + private: + K3bSignalWaiter(); + ~K3bSignalWaiter(); + + bool m_inLoop; +}; + +#endif diff --git a/libk3b/tools/k3bstdguiitems.cpp b/libk3b/tools/k3bstdguiitems.cpp new file mode 100644 index 0000000..8a6ab00 --- /dev/null +++ b/libk3b/tools/k3bstdguiitems.cpp @@ -0,0 +1,215 @@ +/* + * + * $Id: k3bstdguiitems.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bstdguiitems.h" + +#include <qcheckbox.h> +#include <qtooltip.h> +#include <qwhatsthis.h> +#include <qcombobox.h> +#include <qframe.h> +#include <qpalette.h> + +#include <klocale.h> + + +QCheckBox* K3bStdGuiItems::simulateCheckbox( QWidget* parent, const char* name ) +{ + QCheckBox* c = new QCheckBox( i18n("Simulate"), parent, name ); + QWhatsThis::add( c, i18n("<p>If this option is checked K3b will perform all writing steps with the " + "laser turned off." + "<p>This is useful, for example, to test a higher writing speed " + "or whether your system is able to write on-the-fly." + "<p><b>Caution:</b> DVD+R(W) does not support simulated writing.") ); + QToolTip::add( c, i18n("Only simulate the writing process") ); + return c; +} + +QCheckBox* K3bStdGuiItems::daoCheckbox( QWidget* parent, const char* name ) +{ + QCheckBox* c = new QCheckBox( i18n("Disk at once"), parent, name ); + QWhatsThis::add( c, i18n("<p>If this option is checked, K3b will write the CD in 'disk at once' mode as " + "compared to 'track at once' (TAO)." + "<p>It is always recommended to use DAO where possible." + "<p><b>Caution:</b> Track pregaps with a length other than 2 seconds are only supported " + "in DAO mode.") ); + QToolTip::add( c, i18n("Write in disk at once mode") ); + return c; +} + +QCheckBox* K3bStdGuiItems::burnproofCheckbox( QWidget* parent, const char* name ) +{ + QCheckBox* c = new QCheckBox( i18n("Use Burnfree"), parent, name ); + QToolTip::add( c, i18n("Enable Burnfree (or Just Link) to avoid buffer underruns") ); + QWhatsThis::add( c, i18n("<p>If this option is checked, K3b enables <em>Burnfree</em> " + "(or <em>Just Link</em>). This is " + "a feature of the CD writer which avoids buffer underruns." + "<p>Without <em>burnfree</em>, if the writer cannot get any more " + "data a buffer underrun would occurs, since the writer needs " + "a constant stream of data to write the CD." + "<p>With <em>burnfree</em> the writer can <em>mark</em> the current " + "position of the laser and get back to it when the buffer is filled again;" + "but, since this means having little data gaps on the CD, <b>it is " + "highly recommended to always choose an appropriate writing " + "speed to prevent the usage of burnfree, especially for audio CDs</b> " + "(in the worst case one would hear the gap)." + "<p><em>Burnfree</em> was formerly known as <em>Burnproof</em>, " + "but has since been renamed when it became part of the MMC standard.") ); + return c; +} + +QCheckBox* K3bStdGuiItems::onlyCreateImagesCheckbox( QWidget* parent, const char* name ) +{ + QCheckBox* c = new QCheckBox( i18n("Only create image"), parent, name ); + QWhatsThis::add( c, i18n("<p>If this option is checked, K3b will only create an " + "image and not do any actual writing." + "<p>The image can later be written to a CD/DVD with most current writing " + "programs (including K3b of course).") ); + QToolTip::add( c, i18n("Only create an image") ); + return c; +} + +QCheckBox* K3bStdGuiItems::createCacheImageCheckbox( QWidget* parent, const char* name ) +{ + QCheckBox* c = new QCheckBox( i18n("Create image"), parent, name ); + QWhatsThis::add( c, i18n("<p>If this option is checked, K3b will create an image before writing " + "the files to the CD/DVD. Otherwise the data will be written <em>on-the-fly</em>, " + "i.e. no intermediate image will be created." + "<p><b>Caution:</b> Although writing on-the-fly should work on most systems, make sure " + "the data is sent to the writer fast enough.") + + i18n("<p>It is recommended to try a simulation first.") ); + QToolTip::add( c, i18n("Cache the data to be written on the harddisk") ); + return c; +} + +QCheckBox* K3bStdGuiItems::removeImagesCheckbox( QWidget* parent, const char* name ) +{ + QCheckBox* c = new QCheckBox( i18n("Remove image"), parent, name ); + QWhatsThis::add( c, i18n("<p>If this option is checked, K3b will remove any created images after the " + "writing has finished." + "<p>Uncheck this if you want to keep the images.") ); + QToolTip::add( c, i18n("Remove images from disk when finished") ); + return c; +} + +QCheckBox* K3bStdGuiItems::onTheFlyCheckbox( QWidget* parent, const char* name ) +{ + QCheckBox* c = new QCheckBox( i18n("On the fly"), parent, name ); + QWhatsThis::add( c, i18n("<p>If this option is checked, K3b will not create an image first but write " + "the files directly to the CD/DVD." + "<p><b>Caution:</b> Although this should work on most systems, make sure " + "the data is sent to the writer fast enough.") + + i18n("<p>It is recommended to try a simulation first.") ); + QToolTip::add( c, i18n("Write files directly to CD/DVD without creating an image") ); + return c; +} + +QCheckBox* K3bStdGuiItems::cdTextCheckbox( QWidget* parent, const char* name ) +{ + QCheckBox* c = new QCheckBox( i18n("Write CD-TEXT"), parent, name ); + QToolTip::add( c, i18n("Create CD-TEXT entries") ); + QWhatsThis::add( c, i18n("<p>If this option is checked K3b uses some otherwise-unused space on the audio " + "CD to store additional information, like the artist or the CD title." + "<p>CD-TEXT is an extension to the audio CD standard introduced by Sony." + "<p>CD-TEXT will only be usable on CD players that support this extension " + "(mostly car CD players)." + "<p>Since a CD-TEXT-enhanced CDs will work in any CD player it is never a bad " + "idea to enable this (if you specify CD-TEXT data).") ); + return c; +} + + +QComboBox* K3bStdGuiItems::paranoiaModeComboBox( QWidget* parent, const char* name ) +{ + QComboBox* c = new QComboBox( parent, name ); + c->insertItem( "0" ); + c->insertItem( "1" ); + c->insertItem( "2" ); + c->insertItem( "3" ); + c->setCurrentItem( 3 ); + QToolTip::add( c, i18n("Set the paranoia level for reading audio CDs") ); + QWhatsThis::add( c, i18n("<p>Sets the correction mode for digital audio extraction." + "<ul><li>0: No checking, data is copied directly from the drive. " + "<li>1: Perform overlapped reading to avoid jitter.</li>" + "<li>2: Like 1 but with additional checks of the read audio data.</li>" + "<li>3: Like 2 but with additional scratch detection and repair.</li></ul>" + "<p><b>The extraction speed reduces from 0 to 3.</b>") ); + return c; +} + + +QCheckBox* K3bStdGuiItems::startMultisessionCheckBox( QWidget* parent, const char* name ) +{ + QCheckBox* c = new QCheckBox( i18n("Start multisession CD"), parent, name ); + QToolTip::add( c, i18n("Do not close the disk to allow additional sessions to be added later") ); + QWhatsThis::add( c, i18n("<p>If this option is checked K3b will not close the CD, and will write " + "a temporary table of contents.</p>" + "<p>This allows further sessions to be appended to the CD later.</p>") ); + return c; +} + + +QCheckBox* K3bStdGuiItems::normalizeCheckBox( QWidget* parent, const char* name ) +{ + QCheckBox* c = new QCheckBox( i18n("Normalize volume levels"), parent, name ); + QToolTip::add( c, i18n("Adjust the volume levels of all tracks") ); + QWhatsThis::add( c, i18n("<p>If this option is checked K3b will adjust the volume of all tracks " + "to a standard level. This is useful for things like creating mixes, " + "where different recording levels on different albums can cause the volume " + "to vary greatly from song to song." + "<p><b>Be aware that K3b currently does not support normalizing when writing " + "on the fly.</b>") ); + return c; +} + + +QCheckBox* K3bStdGuiItems::verifyCheckBox( QWidget* parent, const char* name ) +{ + QCheckBox* c = new QCheckBox( i18n("Verify written data"), parent, name ); + QToolTip::add( c, i18n("Compare original with written data") ); + QWhatsThis::add( c, i18n("<p>If this option is checked, then after successfully " + "writing the disk K3b will compare the original source data " + "with the written data to verify that the disk has been written " + "correctly.") ); + return c; +} + + +QCheckBox* K3bStdGuiItems::ignoreAudioReadErrorsCheckBox( QWidget* parent, const char* name ) +{ + QCheckBox* c = new QCheckBox( i18n("Ignore read errors"), parent, name ); + QToolTip::add( c, i18n("Skip unreadable audio sectors") ); + QWhatsThis::add( c, i18n("<p>If this option is checked and K3b is not able to read an " + "audio sector from the source CD it will be replaced with zeros " + "on the resulting copy." + "<p>Since audio CD Player are able to interpolate small errors " + "in the data it is no problem to let K3b skip unreadable sectors.") ); + return c; +} + + +QFrame* K3bStdGuiItems::horizontalLine( QWidget* parent, const char* name ) +{ + QFrame* line = new QFrame( parent, name ); + line->setFrameStyle( QFrame::HLine | QFrame::Sunken ); + return line; +} + +QFrame* K3bStdGuiItems::verticalLine( QWidget* parent, const char* name ) +{ + QFrame* line = new QFrame( parent, name ); + line->setFrameStyle( QFrame::VLine | QFrame::Sunken ); + return line; +} diff --git a/libk3b/tools/k3bstdguiitems.h b/libk3b/tools/k3bstdguiitems.h new file mode 100644 index 0000000..e118b6a --- /dev/null +++ b/libk3b/tools/k3bstdguiitems.h @@ -0,0 +1,45 @@ +/* + * + * $Id: k3bstdguiitems.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_STD_GUIITEMS_H +#define K3B_STD_GUIITEMS_H +#include "k3b_export.h" + +class QWidget; +class QCheckBox; +class QComboBox; +class QFrame; + + +namespace K3bStdGuiItems +{ + LIBK3B_EXPORT QCheckBox* simulateCheckbox( QWidget* parent = 0, const char* name = 0 ); + LIBK3B_EXPORT QCheckBox* daoCheckbox( QWidget* parent = 0, const char* name = 0 ); + LIBK3B_EXPORT QCheckBox* burnproofCheckbox( QWidget* parent = 0, const char* name = 0 ); + LIBK3B_EXPORT QCheckBox* onlyCreateImagesCheckbox( QWidget* parent = 0, const char* name = 0 ); + LIBK3B_EXPORT QCheckBox* createCacheImageCheckbox( QWidget* parent = 0, const char* name = 0 ); + LIBK3B_EXPORT QCheckBox* removeImagesCheckbox( QWidget* parent = 0, const char* name = 0 ); + LIBK3B_EXPORT QCheckBox* onTheFlyCheckbox( QWidget* parent = 0, const char* name = 0 ); + LIBK3B_EXPORT QCheckBox* cdTextCheckbox( QWidget* parent = 0, const char* name = 0); + LIBK3B_EXPORT QComboBox* paranoiaModeComboBox( QWidget* parent = 0, const char* name = 0 ); + LIBK3B_EXPORT QCheckBox* startMultisessionCheckBox( QWidget* parent = 0, const char* name = 0 ); + LIBK3B_EXPORT QCheckBox* normalizeCheckBox( QWidget* parent = 0, const char* name = 0 ); + LIBK3B_EXPORT QCheckBox* verifyCheckBox( QWidget* parent = 0, const char* name = 0 ); + LIBK3B_EXPORT QCheckBox* ignoreAudioReadErrorsCheckBox( QWidget* parent = 0, const char* name = 0 ); + LIBK3B_EXPORT QFrame* horizontalLine( QWidget* parent = 0, const char* name = 0 ); + LIBK3B_EXPORT QFrame* verticalLine( QWidget* parent = 0, const char* name = 0 ); +} + +#endif diff --git a/libk3b/tools/k3bstringutils.cpp b/libk3b/tools/k3bstringutils.cpp new file mode 100644 index 0000000..198bb9e --- /dev/null +++ b/libk3b/tools/k3bstringutils.cpp @@ -0,0 +1,111 @@ +/* + * + * $Id: k3bstringutils.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bstringutils.h" + +#include <qfontmetrics.h> + +#include <kdebug.h> + + +QString K3b::cutToWidth( const QFontMetrics& fm, const QString& fullText, int cutWidth ) +{ + QString squeezedText = "..."; + int squeezedWidth = fm.width(squeezedText); + int textWidth = fm.width(fullText); + + if( textWidth <= cutWidth ) { + return fullText; + } + + if( fm.width(fullText.right(1) + "..." ) > cutWidth ) { + kdDebug() << "(K3b::cutToWidth) not able to cut text to " << cutWidth << "!" << endl; + return fullText.right(1) + "..."; + } + + // estimate how many letters we can add to the dots + int letters = fullText.length() * (cutWidth - squeezedWidth) / textWidth; + squeezedText = fullText.left(letters) + "..."; + squeezedWidth = fm.width(squeezedText); + + if (squeezedWidth < cutWidth) { + // we estimated too short + // add letters while text < label + do { + letters++; + squeezedText = fullText.left(letters) + "..."; + squeezedWidth = fm.width(squeezedText); + } while (squeezedWidth < cutWidth); + letters--; + squeezedText = fullText.left(letters) + "..."; + } else if (squeezedWidth > cutWidth) { + // we estimated too long + // remove letters while text > label + do { + letters--; + squeezedText = fullText.left(letters) + "..."; + squeezedWidth = fm.width(squeezedText); + } while (squeezedWidth > cutWidth); + } + + return squeezedText; +} + + +// from KSqueezedTextLabel +QString K3b::squeezeTextToWidth( const QFontMetrics& fm, const QString& fullText, int cutWidth ) +{ + int textWidth = fm.width(fullText); + if (textWidth > cutWidth) { + // start with the dots only + QString squeezedText = "..."; + int squeezedWidth = fm.width(squeezedText); + + // estimate how many letters we can add to the dots on both sides + int letters = fullText.length() * (cutWidth - squeezedWidth) / textWidth / 2; + if (cutWidth < squeezedWidth) letters=1; + squeezedText = fullText.left(letters) + "..." + fullText.right(letters); + squeezedWidth = fm.width(squeezedText); + + if (squeezedWidth < cutWidth) { + // we estimated too short + // add letters while text < label + do { + letters++; + squeezedText = fullText.left(letters) + "..." + fullText.right(letters); + squeezedWidth = fm.width(squeezedText); + } while (squeezedWidth < cutWidth); + letters--; + squeezedText = fullText.left(letters) + "..." + fullText.right(letters); + } + else if (squeezedWidth > cutWidth) { + // we estimated too long + // remove letters while text > label + do { + letters--; + squeezedText = fullText.left(letters) + "..." + fullText.right(letters); + squeezedWidth = fm.width(squeezedText); + } while (letters > 2 && squeezedWidth > cutWidth); + } + + if (letters == 2) + kdDebug() << "(K3b::squeezeTextToWidth) WARNING: unable to squeeze text to width " + << cutWidth << endl; + + return squeezedText; + } + else + return fullText; +} diff --git a/libk3b/tools/k3bstringutils.h b/libk3b/tools/k3bstringutils.h new file mode 100644 index 0000000..05cfd32 --- /dev/null +++ b/libk3b/tools/k3bstringutils.h @@ -0,0 +1,39 @@ +/* + * + * $Id: k3bstringutils.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_STRING_UTILS_H_ +#define _K3B_STRING_UTILS_H_ + +#include <qstring.h> + +class QFontMetrics; + +namespace K3b +{ + /** + * Cuts the text at the end. + * Example: "some long text" -> "some lo..." + */ + QString cutToWidth( const QFontMetrics&, const QString&, int ); + + /** + * squeezes the text. + * Example: "some long text" -> "some...ext" + */ + QString squeezeTextToWidth( const QFontMetrics& fm, const QString& fullText, int cutWidth ); +} + +#endif diff --git a/libk3b/tools/k3btempfile.cpp b/libk3b/tools/k3btempfile.cpp new file mode 100644 index 0000000..635be40 --- /dev/null +++ b/libk3b/tools/k3btempfile.cpp @@ -0,0 +1,51 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3btempfile.h" + +#include <kglobal.h> +#include <kstandarddirs.h> + + +static inline QString defaultTempDir() +{ + // we need a world-readable temp dir + + // FIXME: check if the default is world-readable +// QStringList dirs = KGlobal::dirs()->resourceDirs( "tmp" ); +// for( QStringList::const_iterator it = dirs.begin(); +// it != dirs.end(); ++it ) { +// const QString& dir = *it; + +// } + + // fallback to /tmp + return "/tmp/k3b"; +} + + +K3bTempFile::K3bTempFile( const QString& filePrefix, + const QString& fileExtension, + int mode ) + : KTempFile( filePrefix.isEmpty() ? defaultTempDir() : filePrefix, + fileExtension, + mode ) +{ +} + + +K3bTempFile::~K3bTempFile() +{ +} diff --git a/libk3b/tools/k3btempfile.h b/libk3b/tools/k3btempfile.h new file mode 100644 index 0000000..41a8756 --- /dev/null +++ b/libk3b/tools/k3btempfile.h @@ -0,0 +1,43 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_TEMP_FILE_H_ +#define _K3B_TEMP_FILE_H_ + +#include <ktempfile.h> + +#include <k3b_export.h> + +/** + * K3bTempFile does only change one thing over KTempFile: + * It tries to use a default temp dir which is always world-readable. + * + * This is important in the following situation: + * + * cdrecord often runs suid root. Some distributions like Mandriva + * set the default KDE temp dir to $HOME/tmp-$USER. Thus, if the home + * dir is mounted via NFS root has no read permissions htere and cdrecord + * fails to read the temp files. + */ +class LIBK3B_EXPORT K3bTempFile : public KTempFile +{ + public: + K3bTempFile( const QString& filePrefix = QString::null, + const QString& fileExtension = QString::null, + int mode = 0600 ); + ~K3bTempFile(); +}; + +#endif diff --git a/libk3b/tools/k3bthreadwidget.cpp b/libk3b/tools/k3bthreadwidget.cpp new file mode 100644 index 0000000..ea82101 --- /dev/null +++ b/libk3b/tools/k3bthreadwidget.cpp @@ -0,0 +1,142 @@ +/* + * + * $Id: k3bthreadwidget.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2005 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bthreadwidget.h" +#include "k3bdeviceselectiondialog.h" +#include <k3bdevice.h> + +#include <qevent.h> +#include <qapplication.h> +#include <qwaitcondition.h> + + +class K3bThreadWidget::Data +{ +public: + int id; + void* data; + QWaitCondition con; +}; + + +class K3bThreadWidget::DeviceSelectionEvent : public QCustomEvent +{ +public: + DeviceSelectionEvent( QWidget* parent, const QString& text, int id ) + : QCustomEvent( QEvent::User + 22 ), + m_parent(parent), + m_text(text), + m_id(id) { + } + + QWidget* parent() const { return m_parent; } + const QString& text() const { return m_text; } + int id() const { return m_id; } + +private: + QWidget* m_parent; + QString m_text; + int m_id; +}; + + +K3bThreadWidget* K3bThreadWidget::s_instance = 0; + + +K3bThreadWidget::K3bThreadWidget() + : QObject(), + m_idCounter(1) +{ + m_dataMap.setAutoDelete(true); + s_instance = this; +} + + +K3bThreadWidget::~K3bThreadWidget() +{ + s_instance = 0; +} + + +int K3bThreadWidget::getNewId() +{ + // create new data + Data* data = new Data; + data->id = m_idCounter++; + data->data = 0; + m_dataMap.insert( data->id, data ); + return data->id; +} + + +void K3bThreadWidget::clearId( int id ) +{ + m_dataMap.remove( id ); +} + + +K3bThreadWidget::Data* K3bThreadWidget::data( int id ) +{ + return m_dataMap[id]; +} + + +K3bThreadWidget* K3bThreadWidget::instance() +{ + if( !s_instance ) + s_instance = new K3bThreadWidget(); + return s_instance; +} + + +// static +K3bDevice::Device* K3bThreadWidget::selectDevice( QWidget* parent, + const QString& text ) +{ + // request a new data set + Data* data = K3bThreadWidget::instance()->data( K3bThreadWidget::instance()->getNewId() ); + + // inform the instance about the request + QApplication::postEvent( K3bThreadWidget::instance(), + new K3bThreadWidget::DeviceSelectionEvent( parent, text, data->id ) ); + + // wait for the result to be ready + data->con.wait(); + + K3bDevice::Device* dev = static_cast<K3bDevice::Device*>( data->data ); + + // delete the no longer needed data + K3bThreadWidget::instance()->clearId( data->id ); + + return dev; +} + + +void K3bThreadWidget::customEvent( QCustomEvent* e ) +{ + if( DeviceSelectionEvent* dse = dynamic_cast<DeviceSelectionEvent*>(e) ) { + // create dialog + K3bDevice::Device* dev = K3bDeviceSelectionDialog::selectDevice( dse->parent(), dse->text() ); + + // return it to the thread + Data* dat = data( dse->id() ); + dat->data = static_cast<void*>( dev ); + + // wake the thread + dat->con.wakeAll(); + } +} + +#include "k3bthreadwidget.moc" diff --git a/libk3b/tools/k3bthreadwidget.h b/libk3b/tools/k3bthreadwidget.h new file mode 100644 index 0000000..b3fedb2 --- /dev/null +++ b/libk3b/tools/k3bthreadwidget.h @@ -0,0 +1,78 @@ +/* + * + * $Id: k3bthreadwidget.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2005 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_WIDGET_H_ +#define _K3B_THREAD_WIDGET_H_ + +#include <qobject.h> +#include <qintdict.h> + + +class QCustomEvent; +namespace K3bDevice { + class Device; +} + +/** + * This class allows a thread other than the GUI thread to perform simple GUI + * operations. Mainly creating some simple K3b Dialogs like Device selection. + * + * Since the calling thread cannot create the K3bThreadWidget by himself there exists + * exactly one instance created by K3bCore which is used by all threads. + */ +class K3bThreadWidget : public QObject +{ + Q_OBJECT + + public: + ~K3bThreadWidget(); + + static K3bThreadWidget* instance(); + + /** + * Call this from a thread to show a device selection dialog. + */ + static K3bDevice::Device* selectDevice( QWidget* parent, + const QString& text = QString::null ); + + protected: + /** + * communication between the threads + */ + void customEvent( QCustomEvent* ); + + private: + /** + * used internally + */ + class DeviceSelectionEvent; + class Data; + + K3bThreadWidget(); + + /** + * Get unique id + */ + int getNewId(); + void clearId( int id ); + Data* data( int id ); + + int m_idCounter; + QIntDict<Data> m_dataMap; + + static K3bThreadWidget* s_instance; +}; + +#endif diff --git a/libk3b/tools/k3bthroughputestimator.cpp b/libk3b/tools/k3bthroughputestimator.cpp new file mode 100644 index 0000000..aa52478 --- /dev/null +++ b/libk3b/tools/k3bthroughputestimator.cpp @@ -0,0 +1,98 @@ +/* + * + * $Id: k3bthroughputestimator.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bthroughputestimator.h" + +#include <qdatetime.h> +#include <kdebug.h> + + +class K3bThroughputEstimator::Private +{ +public: + Private() + : started(false) { + } + + QTime firstDataTime; + unsigned long firstData; + QTime lastDataTime; + unsigned long lastData; + + int lastThroughput; + + bool started; +}; + + +K3bThroughputEstimator::K3bThroughputEstimator( QObject* parent, const char* name ) + : QObject( parent, name ) +{ + d = new Private(); +} + + +K3bThroughputEstimator::~K3bThroughputEstimator() +{ + delete d; +} + + +int K3bThroughputEstimator::average() const +{ + int msecs = d->firstDataTime.msecsTo( d->lastDataTime ); + if( msecs > 0 ) + return (int)( 1000.0*(double)(d->lastData - d->firstData)/(double)msecs); + else + return 0; +} + + +void K3bThroughputEstimator::reset() +{ + d->started = false; +} + + +void K3bThroughputEstimator::dataWritten( unsigned long data ) +{ + if( !d->started ) { + d->started = true; + d->firstData = d->lastData = data; + d->firstDataTime.start(); + d->lastDataTime.start(); + d->lastThroughput = 0; + } + else if( data > d->lastData ) { + unsigned long diff = data - d->lastData; + int msecs = d->lastDataTime.elapsed(); + + //if( msecs > 0 ) { + // down the update sequence a little bit + if( msecs > 500 ) { + d->lastData = data; + d->lastDataTime.start(); + int t = (int)(1000.0*(double)diff/(double)msecs); + if( t != d->lastThroughput ) { + d->lastThroughput = t; + emit throughput( t ); + } + } + } +} + + +#include "k3bthroughputestimator.moc" diff --git a/libk3b/tools/k3bthroughputestimator.h b/libk3b/tools/k3bthroughputestimator.h new file mode 100644 index 0000000..aed71e0 --- /dev/null +++ b/libk3b/tools/k3bthroughputestimator.h @@ -0,0 +1,57 @@ +/* + * + * $Id: k3bthroughputestimator.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_THROUGHPUT_ESTIMATOR_H_ +#define _K3B_THROUGHPUT_ESTIMATOR_H_ + +#include <qobject.h> + + +/** + * Little helper class that allows an estimation of the current writing + * speed. Just init with @p reset() then always call @p dataWritten with + * the already written data in KB. The class will emit throughput signals + * whenever the throughput changes. + */ +class K3bThroughputEstimator : public QObject +{ + Q_OBJECT + + public: + K3bThroughputEstimator( QObject* parent = 0, const char* name = 0 ); + ~K3bThroughputEstimator(); + + int average() const; + + signals: + /** + * kb/s if differs from previous + */ + void throughput( int ); + + public slots: + void reset(); + + /** + * @param data written kb + */ + void dataWritten( unsigned long data ); + + private: + class Private; + Private* d; +}; + +#endif diff --git a/libk3b/tools/k3btitlelabel.cpp b/libk3b/tools/k3btitlelabel.cpp new file mode 100644 index 0000000..9e1f18b --- /dev/null +++ b/libk3b/tools/k3btitlelabel.cpp @@ -0,0 +1,266 @@ +/* + * + * $Id: k3btitlelabel.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3btitlelabel.h" + +#include <k3bstringutils.h> + +#include <qpainter.h> +#include <qevent.h> +#include <qfontmetrics.h> +#include <qfont.h> +#include <qtooltip.h> + + +class K3bTitleLabel::Private +{ +public: + Private() { + titleLength = subTitleLength = 0; + margin = 2; + alignment = Qt::AlignLeft; + cachedMinimumWidth = 0; + titleBaseLine = 0; + } + + QString title; + QString subTitle; + + QString displayTitle; + QString displaySubTitle; + + int alignment; + + int titleLength; + int subTitleLength; + int displayTitleLength; + int displaySubTitleLength; + int titleBaseLine; + int subTitleBaseLine; + int margin; + + int cachedMinimumWidth; +}; + + +class K3bTitleLabel::ToolTip : public QToolTip +{ +public: + ToolTip( K3bTitleLabel* label ) + : QToolTip( label ), + m_label(label) { + } + + void maybeTip( const QPoint &pos ) { + QRect r = m_label->contentsRect(); + + int neededWidth = m_label->d->displayTitleLength; + if( !m_label->d->displaySubTitle.isEmpty() ) + neededWidth += m_label->d->displaySubTitleLength + 5; + + int startPos = 0; + if( m_label->d->alignment & Qt::AlignHCenter ) + startPos = r.left() + ( r.width() - 2*m_label->d->margin - neededWidth ) / 2; + else if( m_label->d->alignment & Qt::AlignRight ) + startPos = r.right() - m_label->d->margin - neededWidth; + else + startPos = r.left() + m_label->d->margin; + + QRect titleTipRect( startPos, 0, m_label->d->displayTitleLength, m_label->height() ); + QRect subTitleTipRect( startPos + m_label->d->displayTitleLength, 0, m_label->d->displaySubTitleLength, m_label->height() ); + + if( titleTipRect.contains( pos ) && + m_label->d->displayTitle != m_label->d->title ) + tip( titleTipRect, m_label->d->title ); + else if( subTitleTipRect.contains( pos ) && + m_label->d->displaySubTitle != m_label->d->subTitle ) + tip( subTitleTipRect, m_label->d->subTitle ); + } + + K3bTitleLabel* m_label; +}; + + + +K3bTitleLabel::K3bTitleLabel( QWidget* parent, const char* name ) + : QFrame( parent, name ) +{ + d = new Private(); + m_toolTip = new ToolTip( this ); +} + + +K3bTitleLabel::~K3bTitleLabel() +{ + delete m_toolTip; + delete d; +} + + +void K3bTitleLabel::setTitle( const QString& title, const QString& subTitle ) +{ + d->title = title; + d->subTitle = subTitle; + updatePositioning(); + update(); +} + + +void K3bTitleLabel::setSubTitle( const QString& subTitle ) +{ + d->subTitle = subTitle; + updatePositioning(); + update(); +} + + +void K3bTitleLabel::setAlignment( int align ) +{ + d->alignment = align; + update(); +} + + +QSize K3bTitleLabel::sizeHint() const +{ + return QSize( d->titleLength + d->subTitleLength + 2*d->margin, d->titleBaseLine ); +} + +QSize K3bTitleLabel::minimumSizeHint() const +{ + return QSize( d->cachedMinimumWidth, d->titleBaseLine ); +} + +void K3bTitleLabel::resizeEvent( QResizeEvent* e ) +{ + QFrame::resizeEvent( e ); + updatePositioning(); + update(); +} + +void K3bTitleLabel::drawContents( QPainter* p ) +{ + p->save(); + + QRect r = contentsRect(); + p->eraseRect( r ); + + QFont f(font()); + f.setBold(true); + f.setPointSize( f.pointSize() + 2 ); + + p->setFont(f); + + int neededWidth = d->displayTitleLength; + if( !d->displaySubTitle.isEmpty() ) + neededWidth += d->displaySubTitleLength + 5; + + int startPos = 0; + if( d->alignment & Qt::AlignHCenter ) + startPos = r.left() + ( r.width() - 2*d->margin - neededWidth ) / 2; + else if( d->alignment & Qt::AlignRight ) + startPos = r.right() - d->margin - neededWidth; + else + startPos = r.left() + d->margin; + + // paint title + p->drawText( startPos, r.top() + d->titleBaseLine, d->displayTitle ); + + if( !d->subTitle.isEmpty() ) { + f.setBold(false); + f.setPointSize( f.pointSize() - 4 ); + p->setFont(f); + p->drawText( startPos + d->displayTitleLength + 5, r.top() + d->subTitleBaseLine, d->displaySubTitle ); + } + + p->restore(); +} + + +void K3bTitleLabel::setMargin( int m ) +{ + d->margin = m; + updatePositioning(); + update(); +} + + +void K3bTitleLabel::updatePositioning() +{ + QFont f(font()); + f.setBold(true); + f.setPointSize( f.pointSize() + 2 ); + QFontMetrics titleFm(f); + + f.setBold(false); + f.setPointSize( f.pointSize() - 4 ); + QFontMetrics subTitleFm(f); + + d->titleBaseLine = contentsRect().height()/2 + titleFm.height()/2 - titleFm.descent(); + d->titleLength = titleFm.width( d->title ); + + d->subTitleBaseLine = d->titleBaseLine - titleFm.underlinePos() + subTitleFm.underlinePos(); + + d->subTitleLength = ( d->subTitle.isEmpty() ? 0 : subTitleFm.width( d->subTitle ) ); + + // cut the text to window width + d->displayTitle = d->title; + d->displaySubTitle = d->subTitle; + int widthAvail = contentsRect().width() - 2*margin(); + + // 5 pix spacing between title and subtitle + if( !d->subTitle.isEmpty() ) + widthAvail -= 5; + + if( d->titleLength > widthAvail/2 ) { + if( d->subTitleLength <= widthAvail/2 ) + d->displayTitle = K3b::cutToWidth( titleFm, d->title, widthAvail - d->subTitleLength ); + else + d->displayTitle = K3b::cutToWidth( titleFm, d->title, widthAvail/2 ); + } + if( d->subTitleLength > widthAvail/2 ) { + if( d->titleLength <= widthAvail/2 ) + d->displaySubTitle = K3b::cutToWidth( subTitleFm, d->subTitle, widthAvail - d->titleLength ); + else + d->displaySubTitle = K3b::cutToWidth( subTitleFm, d->subTitle, widthAvail/2 ); + } + + d->displayTitleLength = titleFm.width( d->displayTitle ); + d->displaySubTitleLength = subTitleFm.width( d->displaySubTitle ); + + + // + // determine the minimum width for the minumum size hint + // + d->cachedMinimumWidth = 2*d->margin; + + QString cutTitle = d->title; + if( cutTitle.length() > 2 ) { + cutTitle.truncate( 2 ); + cutTitle += "..."; + } + QString cutSubTitle = d->subTitle; + if( cutSubTitle.length() > 2 ) { + cutSubTitle.truncate( 2 ); + cutSubTitle += "..."; + } + + d->cachedMinimumWidth += titleFm.width( cutTitle ) + subTitleFm.width( cutSubTitle ); + // 5 pix spacing between title and subtitle + if( !d->subTitle.isEmpty() ) + d->cachedMinimumWidth += 5; +} + +#include "k3btitlelabel.moc" diff --git a/libk3b/tools/k3btitlelabel.h b/libk3b/tools/k3btitlelabel.h new file mode 100644 index 0000000..ba1044d --- /dev/null +++ b/libk3b/tools/k3btitlelabel.h @@ -0,0 +1,68 @@ +/* + * + * $Id: k3btitlelabel.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_TITLE_LABEL_H_ +#define _K3B_TITLE_LABEL_H_ + +#include <qframe.h> +#include "k3b_export.h" +class QPainter; +class QResizeEvent; + + +class LIBK3B_EXPORT K3bTitleLabel : public QFrame +{ + Q_OBJECT + + public: + K3bTitleLabel( QWidget* parent = 0, const char* name = 0 ); + ~K3bTitleLabel(); + + QSize sizeHint() const; + QSize minimumSizeHint() const; + + public slots: + /** + * default: 2 + */ + void setMargin( int ); + + void setTitle( const QString& title, const QString& subTitle = QString::null ); + void setSubTitle( const QString& subTitle ); + + /** + * The title label only supports alignments left, hcenter, and right + * + * Default alignment is left. + */ + // FIXME: honor right-to-left languages + void setAlignment( int align ); + + protected: + void resizeEvent( QResizeEvent* ); + void drawContents( QPainter* p ); + + private: + void updatePositioning(); + + class ToolTip; + ToolTip* m_toolTip; + + class Private; + Private* d; +}; + +#endif diff --git a/libk3b/tools/k3btoolbox.cpp b/libk3b/tools/k3btoolbox.cpp new file mode 100644 index 0000000..b08dfc8 --- /dev/null +++ b/libk3b/tools/k3btoolbox.cpp @@ -0,0 +1,293 @@ +/* + * + * $Id$ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3btoolbox.h" + +#include <kaction.h> +#include <kpopupmenu.h> +#include <ktoolbarbutton.h> +#include <kiconloader.h> + +#include <qtoolbutton.h> +#include <qsizepolicy.h> +#include <qlayout.h> +#include <qwhatsthis.h> +#include <qtooltip.h> +#include <qlabel.h> +#include <qvbox.h> +#include <qstyle.h> +#include <qpainter.h> +#include <qevent.h> +#include <qobjectlist.h> + + +/** + * internal class. Do not use! + */ +class K3bToolBoxSeparator : public QWidget +{ + // Q_OBJECT + + public: + K3bToolBoxSeparator( K3bToolBox* parent ); + + QSize sizeHint() const; + + protected: + void paintEvent( QPaintEvent * ); +}; + + +K3bToolBoxSeparator::K3bToolBoxSeparator( K3bToolBox* parent ) + : QWidget( parent ) +{ + setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Minimum ) ); +} + + +QSize K3bToolBoxSeparator::sizeHint() const +{ + int extent = style().pixelMetric( QStyle::PM_DockWindowSeparatorExtent, + this ); + return QSize( extent, 0 ); +} + + +void K3bToolBoxSeparator::paintEvent( QPaintEvent* ) +{ + QPainter p( this ); + QStyle::SFlags flags = QStyle::Style_Default|QStyle::Style_Horizontal; + + style().drawPrimitive( QStyle::PE_DockWindowSeparator, &p, rect(), + colorGroup(), flags ); +} + + + +K3bToolBoxButton::K3bToolBoxButton( KAction* action, QWidget* parent ) + : QToolButton( parent ), + m_popupMenu(0) +{ + setSizePolicy( QSizePolicy(QSizePolicy::Fixed, sizePolicy().verData()) ); + setAutoRaise( true ); + + setIconSet( action->iconSet() ); + setTextLabel( action->text() ); + setEnabled( action->isEnabled() ); + + QWhatsThis::add( this, action->whatsThis() ); + if( action->toolTip().isEmpty() ) + QToolTip::add( this, action->text() ); + else + QToolTip::add( this, action->toolTip() ); + +// if( KToggleAction* ta = dynamic_cast<KToggleAction*>( action ) ) { +// setToggleButton( true ); + +// // initial state +// if( ta->isChecked() ) +// toggle(); + +// connect( ta, SIGNAL(toggled(bool)), this, SLOT(toggle()) ); +// connect( this, SIGNAL(toggled(bool)), ta, SLOT(setChecked(bool)) ); +// } + +// else + if( KActionMenu* am = dynamic_cast<KActionMenu*>( action ) ) { + m_popupMenu = am->popupMenu(); + connect( this, SIGNAL(pressed()), this, SLOT(slotPopupActivated()) ); + setPopup( m_popupMenu ); + } + + else { + connect( this, SIGNAL(clicked()), action, SLOT(activate()) ); + } + + connect( action, SIGNAL(enabled(bool)), this, SLOT(setEnabled(bool)) ); +} + + +K3bToolBoxButton::K3bToolBoxButton( const QString& text, const QString& icon, + const QString& tooltip, const QString& whatsthis, + QObject* receiver, const char* slot, + QWidget* parent ) + : QToolButton( parent ), + m_popupMenu(0) +{ + setSizePolicy( QSizePolicy(QSizePolicy::Fixed, sizePolicy().verData()) ); + setAutoRaise( true ); + + setTextLabel( text ); + + if( icon.isEmpty() ) + setUsesTextLabel( true ); + else + setIconSet( SmallIconSet( icon ) ); + + QWhatsThis::add( this, whatsthis ); + QToolTip::add( this, tooltip ); + + if( receiver && slot ) + connect( this, SIGNAL(clicked()), receiver, slot ); +} + + +void K3bToolBoxButton::slotPopupActivated() +{ + // force the toolbutton to open the popupmenu instantly + openPopup(); +} + + +void K3bToolBoxButton::resizeEvent( QResizeEvent* e ) +{ + QToolButton::resizeEvent( e ); + + // force icon-only buttons to be square + if( e->oldSize().height() != e->size().height() && + !usesTextLabel() ) + setFixedWidth( e->size().height() ); +} + + + + + + + +K3bToolBox::K3bToolBox( QWidget* parent, const char* name ) + : QFrame( parent, name ) +{ + setSizePolicy( QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed) ); + + m_mainLayout = new QGridLayout( this ); + m_mainLayout->setMargin( 1 ); + m_mainLayout->setSpacing( 0 ); +} + + +K3bToolBox::~K3bToolBox() +{ + clear(); +} + + +K3bToolBoxButton* K3bToolBox::addButton( KAction* action, bool forceText ) +{ + if( action ) { + K3bToolBoxButton* b = new K3bToolBoxButton( action, this ); + if( forceText ) { + b->setUsesTextLabel( true ); + b->setTextPosition( QToolButton::BesideIcon ); + } + addWidget( b ); + return b; + } + else + return 0; +} + + +K3bToolBoxButton* K3bToolBox::addButton( const QString& text, const QString& icon, + const QString& tooltip, const QString& whatsthis, + QObject* receiver, const char* slot, + bool forceText ) +{ + K3bToolBoxButton* b = new K3bToolBoxButton( text, icon, tooltip, whatsthis, receiver, slot, this ); + if( forceText ) { + b->setUsesTextLabel( true ); + b->setTextPosition( QToolButton::BesideIcon ); + } + addWidget( b ); + return b; +} + + +void K3bToolBox::addSpacing() +{ + int lastStretch = m_mainLayout->colStretch( m_mainLayout->numCols()-1 ); + m_mainLayout->setColStretch( m_mainLayout->numCols()-1, 0 ); + m_mainLayout->addColSpacing( m_mainLayout->numCols()-1, 8 ); + m_mainLayout->setColStretch( m_mainLayout->numCols(), lastStretch ); +} + + +void K3bToolBox::addSeparator() +{ + K3bToolBoxSeparator* s = new K3bToolBoxSeparator( this ); + addWidget( s ); +} + + +void K3bToolBox::addStretch() +{ + // add an empty widget + addWidget( new QWidget( this ) ); + m_mainLayout->setColStretch( m_mainLayout->numCols(), 1 ); +} + + +void K3bToolBox::addLabel( const QString& text ) +{ + QLabel* label = new QLabel( text, this ); + label->setSizePolicy( QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed) ); + + addWidget( label ); +} + + +void K3bToolBox::addWidget( QWidget* w ) +{ + w->reparent( this, QPoint() ); + + m_mainLayout->setColStretch( m_mainLayout->numCols()-1, 0 ); + + m_mainLayout->addWidget( w, 0, m_mainLayout->numCols()-1 ); + + if( w->sizePolicy().horData() == QSizePolicy::Fixed || w->sizePolicy().horData() == QSizePolicy::Maximum ) + m_mainLayout->setColStretch( m_mainLayout->numCols(), 1 ); + else { + m_mainLayout->setColStretch( m_mainLayout->numCols()-1, 1 ); + m_mainLayout->setColStretch( m_mainLayout->numCols(), 0 ); + } +} + + +K3bToolBoxButton* K3bToolBox::addToggleButton( KToggleAction* action ) +{ + return addButton( action ); +} + + +void K3bToolBox::addWidgetAction( KWidgetAction* action ) +{ + addWidget( action->widget() ); + m_doNotDeleteWidgets.append( action->widget() ); +} + + +void K3bToolBox::clear() +{ + // we do not want to delete the widgets from the widgetactions becasue they + // might be used afterwards + for( QPtrListIterator<QWidget> it( m_doNotDeleteWidgets ); it.current(); ++it ) + it.current()->reparent( 0L, QPoint() ); + + for( QObjectListIterator it2( *children() ); it2.current(); ++it2 ) + if( it2.current()->isWidgetType() ) + delete it2.current(); +} + +#include "k3btoolbox.moc" diff --git a/libk3b/tools/k3btoolbox.h b/libk3b/tools/k3btoolbox.h new file mode 100644 index 0000000..828fca1 --- /dev/null +++ b/libk3b/tools/k3btoolbox.h @@ -0,0 +1,93 @@ +/* + * + * $Id$ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_TOOLBOX_H +#define K3B_TOOLBOX_H + +#include <qframe.h> +#include <qstring.h> +#include <qtoolbutton.h> +#include <qptrlist.h> +#include "k3b_export.h" + +class KAction; +class KToggleAction; +class KWidgetAction; +class QGridLayout; +class QPopupMenu; +class QResizeEvent; + + +/** + * internal class. Do not use! + */ +class LIBK3B_EXPORT K3bToolBoxButton : public QToolButton +{ + Q_OBJECT + + public: + K3bToolBoxButton( KAction*, QWidget* parent ); + K3bToolBoxButton( const QString& text, const QString& icon, + const QString& tooltip, const QString& whatsthis, + QObject* receiver, const char* slot, + QWidget* parent ); + + private slots: + void slotPopupActivated(); + + protected: + void resizeEvent( QResizeEvent* ); + + private: + QPopupMenu* m_popupMenu; +}; + + +class LIBK3B_EXPORT K3bToolBox : public QFrame +{ + Q_OBJECT + + public: + K3bToolBox( QWidget* parent = 0, const char* name = 0 ); + ~K3bToolBox(); + + K3bToolBoxButton* addButton( const QString& text, const QString& icon, + const QString& tooltip = QString::null, const QString& whatsthis = QString::null, + QObject* receiver = 0, const char* slot = 0, + bool forceTextLabel = false ); + K3bToolBoxButton* addButton( KAction*, bool forceTextLabel = false ); + K3bToolBoxButton* addToggleButton( KToggleAction* ); + void addWidgetAction( KWidgetAction* ); + + /** + * Be aware that the toolbox will take ownership of the widget + * and destroy it on destruction. Becasue of this it is not fitted + * for WidgetActions. + */ + void addWidget( QWidget* ); + void addLabel( const QString& ); + void addSpacing(); + void addSeparator(); + void addStretch(); + + void clear(); + + protected: + QGridLayout* m_mainLayout; + QPtrList<QWidget> m_doNotDeleteWidgets; +}; + + +#endif diff --git a/libk3b/tools/k3btoolbutton.cpp b/libk3b/tools/k3btoolbutton.cpp new file mode 100644 index 0000000..2e3e468 --- /dev/null +++ b/libk3b/tools/k3btoolbutton.cpp @@ -0,0 +1,109 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3btoolbutton.h" + +#include <qstyle.h> +#include <qpainter.h> +#include <qevent.h> + +#include <kglobalsettings.h> +#include <kapplication.h> + + +class K3bToolButton::Private +{ +public: + QPoint mousePressPos; + bool instantMenu; +}; + + +K3bToolButton::K3bToolButton( QWidget* parent ) + : QToolButton( parent ) +{ + d = new Private; + d->instantMenu = false; + installEventFilter(this); +} + + +K3bToolButton::~K3bToolButton() +{ + delete d; +} + + +void K3bToolButton::setInstantMenu( bool b ) +{ + d->instantMenu = b; +} + + +void K3bToolButton::drawButton( QPainter* p ) +{ + QToolButton::drawButton( p ); + + // + // code below comes from ktoolbarbutton.cpp from the kdelibs sources + // see the file for copyright information + // + if( QToolButton::popup() ) { + QStyle::SFlags arrowFlags = QStyle::Style_Default; + + if( isDown() ) + arrowFlags |= QStyle::Style_Down; + if( isEnabled() ) + arrowFlags |= QStyle::Style_Enabled; + + style().drawPrimitive(QStyle::PE_ArrowDown, p, + QRect(width()-7, height()-7, 7, 7), colorGroup(), + arrowFlags, QStyleOption() ); + } +} + + +bool K3bToolButton::eventFilter( QObject* o, QEvent* ev ) +{ + if( dynamic_cast<K3bToolButton*>(o) == this ) { + + // Popup the menu when the left mousebutton is pressed and the mouse + // is moved by a small distance. + if( QToolButton::popup() ) { + if( ev->type() == QEvent::MouseButtonPress ) { + QMouseEvent* mev = static_cast<QMouseEvent*>(ev); + + if( d->instantMenu ) { + setDown(true); + openPopup(); + return true; + } + else { + d->mousePressPos = mev->pos(); + } + } + else if( ev->type() == QEvent::MouseMove ) { + QMouseEvent* mev = static_cast<QMouseEvent*>(ev); + if( !d->instantMenu && + ( mev->pos() - d->mousePressPos).manhattanLength() > KGlobalSettings::dndEventDelay() ) { + openPopup(); + return true; + } + } + } + } + + return QToolButton::eventFilter( o, ev ); +} diff --git a/libk3b/tools/k3btoolbutton.h b/libk3b/tools/k3btoolbutton.h new file mode 100644 index 0000000..fe96e4c --- /dev/null +++ b/libk3b/tools/k3btoolbutton.h @@ -0,0 +1,50 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_TOOL_BUTTON_H_ +#define _K3B_TOOL_BUTTON_H_ + +#include "k3b_export.h" + +#include <qtoolbutton.h> + +class QPainter; +class QEvent; + + +/** + * the K3bToolButton is an enhanced QToolButton which adds two functionalities: + * <li>A delayed popup menu is shown immiadetely once the mouse is dragged downwards + * much like the KToolBarButton + * <li>If a popup menu is set a little arrow indicates this. + */ +class LIBK3B_EXPORT K3bToolButton : public QToolButton +{ + public: + K3bToolButton( QWidget* parent = 0 ); + ~K3bToolButton(); + + void setInstantMenu( bool ); + + protected: + virtual void drawButton( QPainter* ); + virtual bool eventFilter( QObject*, QEvent* ); + + private: + class Private; + Private* d; +}; + +#endif diff --git a/libk3b/tools/k3bvalidators.cpp b/libk3b/tools/k3bvalidators.cpp new file mode 100644 index 0000000..9252fdd --- /dev/null +++ b/libk3b/tools/k3bvalidators.cpp @@ -0,0 +1,154 @@ +/* + * + * $Id: k3bvalidators.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003-2007 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bvalidators.h" + +#include <ctype.h> + + +K3bCharValidator::K3bCharValidator( QObject* parent, const char* name ) + : QValidator( parent, name ), + m_replaceChar( '_' ) +{ +} + + +QValidator::State K3bCharValidator::validate( QString& s, int& pos ) const +{ + Q_UNUSED(pos); + + for( unsigned int i = 0; i < s.length(); ++i ) { + State r = validateChar( s[i] ); + if( r != Acceptable ) + return r; + } + + return Acceptable; +} + + +void K3bCharValidator::fixup( QString& s ) const +{ + for( unsigned int i = 0; i < s.length(); ++i ) { + if( validateChar( s[i] ) != Acceptable ) + s[i] = m_replaceChar; + } +} + + +K3bLatin1Validator::K3bLatin1Validator( QObject* parent, const char* name ) + : K3bCharValidator( parent, name ) +{ +} + + +QValidator::State K3bLatin1Validator::validateChar( const QChar& c ) const +{ + if( !c.latin1() ) + return Invalid; + else + return Acceptable; +} + + +K3bAsciiValidator::K3bAsciiValidator( QObject* parent, const char* name ) + : K3bLatin1Validator( parent, name ) +{ +} + + +QValidator::State K3bAsciiValidator::validateChar( const QChar& c ) const +{ + if( K3bLatin1Validator::validateChar( c ) == Invalid ) + return Invalid; + else if( !isascii( c.latin1() ) ) + return Invalid; + else + return Acceptable; +} + + + +K3bValidator::K3bValidator( QObject* parent, const char* name ) + : QRegExpValidator( parent, name ), + m_replaceChar('_') +{ +} + + +K3bValidator::K3bValidator( const QRegExp& rx, QObject* parent, const char* name ) + : QRegExpValidator( rx, parent, name ), + m_replaceChar('_') +{ +} + + +void K3bValidator::fixup( QString& input ) const +{ + for( unsigned int i = 0; i < input.length(); ++i ) + if( !regExp().exactMatch( input.mid(i, 1) ) ) + input[i] = m_replaceChar; +} + + +QString K3bValidators::fixup( const QString& input, const QRegExp& rx, const QChar& replaceChar ) +{ + QString s; + for( unsigned int i = 0; i < input.length(); ++i ) + if( rx.exactMatch( input.mid(i, 1) ) ) + s += input[i]; + else + s += replaceChar; + return s; +} + + +K3bValidator* K3bValidators::isrcValidator( QObject* parent, const char* name ) +{ + return new K3bValidator( QRegExp("^[A-Z\\d]{2,2}-[A-Z\\d]{3,3}-\\d{2,2}-\\d{5,5}$"), parent, name ); +} + + +K3bValidator* K3bValidators::iso9660Validator( bool allowEmpty, QObject* parent, const char* name ) +{ + if( allowEmpty ) + return new K3bValidator( QRegExp( "[^/]*" ), parent, name ); + else + return new K3bValidator( QRegExp( "[^/]+" ), parent, name ); +} + + +K3bValidator* K3bValidators::iso646Validator( int type, bool AllowLowerCase, QObject* parent, const char* name ) +{ + QRegExp rx; + switch ( type ) { + case Iso646_d: + if ( AllowLowerCase ) + rx = QRegExp( "[a-zA-Z0-9_]*" ); + else + rx = QRegExp( "[A-Z0-9_]*" ); + break; + case Iso646_a: + default: + if ( AllowLowerCase ) + rx = QRegExp( "[a-zA-Z0-9!\"\\s%&'\\(\\)\\*\\+,\\-\\./:;<=>\\?_]*" ); + else + rx = QRegExp( "[A-Z0-9!\"\\s%&'\\(\\)\\*\\+,\\-\\./:;<=>\\?_]*" ); + break; + } + + return new K3bValidator( rx, parent, name ); +} diff --git a/libk3b/tools/k3bvalidators.h b/libk3b/tools/k3bvalidators.h new file mode 100644 index 0000000..89c6397 --- /dev/null +++ b/libk3b/tools/k3bvalidators.h @@ -0,0 +1,131 @@ +/* + * + * $Id: k3bvalidators.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003-2007 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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_VALIDATORS_H_ +#define _K3B_VALIDATORS_H_ + +#include <qvalidator.h> +#include "k3b_export.h" + + +/** + * Simple validator that validates a string char by char + */ +class LIBK3B_EXPORT K3bCharValidator : public QValidator +{ + public: + K3bCharValidator( QObject* parent = 0, const char* name = 0 ); + + virtual State validateChar( const QChar& ) const = 0; + + virtual State validate( QString& s, int& pos ) const; + + /** + * Replaces all invalid chars with the repplace char + */ + virtual void fixup( QString& ) const; + + /** + * Default to '_' + */ + void setReplaceChar( const QChar& c ) { m_replaceChar = c; } + + private: + QChar m_replaceChar; +}; + + +class LIBK3B_EXPORT K3bLatin1Validator : public K3bCharValidator +{ + public: + K3bLatin1Validator( QObject* parent = 0, const char* name = 0 ); + + virtual State validateChar( const QChar& ) const; +}; + + +class LIBK3B_EXPORT K3bAsciiValidator : public K3bLatin1Validator +{ + public: + K3bAsciiValidator( QObject* parent = 0, const char* name = 0 ); + + virtual State validateChar( const QChar& ) const; +}; + + +/** + * The K3bValidator extends QRegExpValidator with a fixup method + * that just replaces all characters that are not allowed with the + * replace character. It only makes sense for QRegExps that simply + * allow or forbid some characters. + */ +class LIBK3B_EXPORT K3bValidator : public QRegExpValidator +{ + public: + K3bValidator( QObject* parent, const char * name = 0 ); + K3bValidator( const QRegExp& rx, QObject* parent, const char* name = 0 ); + + void setReplaceChar( const QChar& s ) { m_replaceChar = s; } + const QChar& replaceChar() const { return m_replaceChar; } + + virtual void fixup( QString& ) const; + + private: + QChar m_replaceChar; +}; + + +namespace K3bValidators +{ + /** + * just replaces all characters that are not allowed with the + * replace character. It only makes sense for QRegExps that simply + * allow or forbid some characters. + */ + LIBK3B_EXPORT QString fixup( const QString&, const QRegExp&, const QChar& replaceChar = '_' ); + + /** + * Validates an ISRC code of the form "CCOOOYYSSSSS" where: + * <ul> + * <li>C: country code (upper case letters or digits)</li> + * <li>O: owner code (upper case letters or digits)</li> + * <li>Y: year (digits)</li> + * <li>S: serial number (digits)</li> + * </ul> + */ + LIBK3B_EXPORT K3bValidator* isrcValidator( QObject* parent = 0, const char* name = 0 ); + + /** + * This needs to be replaced by something better in the future... + * Even the name sucks! + */ + LIBK3B_EXPORT K3bValidator* iso9660Validator( bool allowEmpty = true, QObject* parent = 0, const char* name = 0 ); + + /** + * (1) d-characters are: A-Z, 0-9, _ (see ISO-9660:1988, Annex A, Table 15) + * (2) a-characters are: A-Z, 0-9, _, space, !, ", %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ? + * (see ISO-9660:1988, Annex A, Table 14) + */ + enum Iso646Type { + Iso646_a, + Iso646_d + }; + + LIBK3B_EXPORT K3bValidator* iso646Validator( int type = Iso646_a, + bool AllowLowerCase = false, + QObject* parent = 0, const char* name = 0 ); +} + +#endif diff --git a/libk3b/tools/k3bwavefilewriter.cpp b/libk3b/tools/k3bwavefilewriter.cpp new file mode 100644 index 0000000..36267c2 --- /dev/null +++ b/libk3b/tools/k3bwavefilewriter.cpp @@ -0,0 +1,186 @@ +/* + * + * $Id: k3bwavefilewriter.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "k3bwavefilewriter.h" +#include <kdebug.h> + +K3bWaveFileWriter::K3bWaveFileWriter() + : m_outputStream( &m_outputFile ) +{ +} + + +K3bWaveFileWriter::~K3bWaveFileWriter() +{ + close(); +} + + +bool K3bWaveFileWriter::open( const QString& filename ) +{ + close(); + + m_outputFile.setName( filename ); + + if( m_outputFile.open( IO_ReadWrite ) ) { + m_filename = filename; + + writeEmptyHeader(); + + return true; + } + else { + return false; + } +} + + +void K3bWaveFileWriter::close() +{ + if( isOpen() ) { + if( m_outputFile.at() > 0 ) { + padTo2352(); + + // update wave header + updateHeader(); + + m_outputFile.close(); + } + else { + m_outputFile.close(); + m_outputFile.remove(); + } + } + + m_filename = QString::null; +} + + +bool K3bWaveFileWriter::isOpen() +{ + return m_outputFile.isOpen(); +} + + +const QString& K3bWaveFileWriter::filename() const +{ + return m_filename; +} + + +void K3bWaveFileWriter::write( const char* data, int len, Endianess e ) +{ + if( isOpen() ) { + if( e == LittleEndian ) { + m_outputStream.writeRawBytes( data, len ); + } + else { + if( len % 2 > 0 ) { + kdDebug() << "(K3bWaveFileWriter) data length (" + << len << ") is not a multiple of 2! Cannot swap bytes." << endl; + return; + } + + // we need to swap the bytes + char* buffer = new char[len]; + for( int i = 0; i < len-1; i+=2 ) { + buffer[i] = data[i+1]; + buffer[i+1] = data[i]; + } + m_outputStream.writeRawBytes( buffer, len ); + + delete [] buffer; + } + } +} + + +void K3bWaveFileWriter::writeEmptyHeader() +{ + static const char riffHeader[] = + { + 0x52, 0x49, 0x46, 0x46, // 0 "RIFF" + 0x00, 0x00, 0x00, 0x00, // 4 wavSize + 0x57, 0x41, 0x56, 0x45, // 8 "WAVE" + 0x66, 0x6d, 0x74, 0x20, // 12 "fmt " + 0x10, 0x00, 0x00, 0x00, // 16 + 0x01, 0x00, 0x02, 0x00, // 20 + 0x44, 0xac, 0x00, 0x00, // 24 + 0x10, 0xb1, 0x02, 0x00, // 28 + 0x04, 0x00, 0x10, 0x00, // 32 + 0x64, 0x61, 0x74, 0x61, // 36 "data" + 0x00, 0x00, 0x00, 0x00 // 40 byteCount + }; + + m_outputStream.writeRawBytes( riffHeader, 44 ); +} + + +void K3bWaveFileWriter::updateHeader() +{ + if( isOpen() ) { + + m_outputFile.flush(); + + Q_INT32 dataSize( m_outputFile.at() - 44 ); + Q_INT32 wavSize(dataSize + 44 - 8); + char c[4]; + + // jump to the wavSize position in the header + if( m_outputFile.at( 4 ) ) { + c[0] = (wavSize >> 0 ) & 0xff; + c[1] = (wavSize >> 8 ) & 0xff; + c[2] = (wavSize >> 16) & 0xff; + c[3] = (wavSize >> 24) & 0xff; + m_outputStream.writeRawBytes( c, 4 ); + } + else + kdDebug() << "(K3bWaveFileWriter) unable to seek in file: " << m_outputFile.name() << endl; + + if( m_outputFile.at( 40 ) ) { + c[0] = (dataSize >> 0 ) & 0xff; + c[1] = (dataSize >> 8 ) & 0xff; + c[2] = (dataSize >> 16) & 0xff; + c[3] = (dataSize >> 24) & 0xff; + m_outputStream.writeRawBytes( c, 4 ); + } + else + kdDebug() << "(K3bWaveFileWriter) unable to seek in file: " << m_outputFile.name() << endl; + + // jump back to the end + m_outputFile.at( m_outputFile.size() ); + } +} + + +void K3bWaveFileWriter::padTo2352() +{ + int bytesToPad = ( m_outputFile.at() - 44 ) % 2352; + if( bytesToPad > 0 ) { + kdDebug() << "(K3bWaveFileWriter) padding wave file with " << bytesToPad << " bytes." << endl; + + char* c = new char[bytesToPad]; + memset( c, 0, bytesToPad ); + m_outputStream.writeRawBytes( c, bytesToPad ); + delete [] c; + } +} + + +int K3bWaveFileWriter::fd() const +{ + return m_outputFile.handle(); +} diff --git a/libk3b/tools/k3bwavefilewriter.h b/libk3b/tools/k3bwavefilewriter.h new file mode 100644 index 0000000..e5394a5 --- /dev/null +++ b/libk3b/tools/k3bwavefilewriter.h @@ -0,0 +1,78 @@ +/* + * + * $Id: k3bwavefilewriter.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 K3BWAVEFILEWRITER_H +#define K3BWAVEFILEWRITER_H + +#include <qstring.h> +#include <qfile.h> +#include <qdatastream.h> +#include "k3b_export.h" +/** + * @author Sebastian Trueg + * Creates wave files from 16bit stereo little or big endian + * sound samples + */ +class LIBK3B_EXPORT K3bWaveFileWriter +{ + public: + + enum Endianess { BigEndian, LittleEndian }; + + K3bWaveFileWriter(); + ~K3bWaveFileWriter(); + + /** + * open a new wave file. + * closes any opened file. + */ + bool open( const QString& filename ); + + bool isOpen(); + const QString& filename() const; + + /** + * closes the file. + * Length of the wave file will be written into the header. + * If no data has been written to the file except the header + * it will be removed. + */ + void close(); + + /** + * write 16bit samples to the file. + * @param e the endianess of the data + * (it will be swapped to little endian byte order if necessary) + */ + void write( const char* data, int len, Endianess e = BigEndian ); + + /** + * returnes a filedescriptor with the already opened file + * or -1 if isOpen() is false + */ + int fd() const; + + private: + void writeEmptyHeader(); + void updateHeader(); + void padTo2352(); + + QFile m_outputFile; + QDataStream m_outputStream; + QString m_filename; +}; + +#endif diff --git a/libk3b/tools/kcutlabel.cpp b/libk3b/tools/kcutlabel.cpp new file mode 100644 index 0000000..accbe17 --- /dev/null +++ b/libk3b/tools/kcutlabel.cpp @@ -0,0 +1,115 @@ +/* + * + * $Id: kcutlabel.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 "kcutlabel.h" + +#include <k3bstringutils.h> + +#include <qtooltip.h> +#include <qstringlist.h> +#include <kdebug.h> + + +KCutLabel::KCutLabel( const QString &text , QWidget *parent, const char *name ) + : QLabel ( parent, name ), + m_minChars(1) { + QSizePolicy myLabelSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); + setSizePolicy(myLabelSizePolicy); + m_fullText = text; + cutTextToLabel(); +} + +KCutLabel::KCutLabel( QWidget *parent, const char *name ) + : QLabel ( parent, name ), + m_minChars(1) { + QSizePolicy myLabelSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); + setSizePolicy(myLabelSizePolicy); +} + +QSize KCutLabel::minimumSizeHint() const +{ + QSize sh = QLabel::minimumSizeHint(); + if( m_minChars == 0 ) + sh.setWidth(-1); + else if( m_minChars < (int)m_fullText.length() ) + sh.setWidth( QMIN( fontMetrics().width( m_fullText.left(m_minChars) + "..." ), + fontMetrics().width( m_fullText ) ) ); + + return sh; +} + + +void KCutLabel::setMinimumVisibleText( int i ) +{ + m_minChars = i; + cutTextToLabel(); +} + + +void KCutLabel::resizeEvent( QResizeEvent * ) +{ + cutTextToLabel(); +} + +void KCutLabel::setText( const QString &text ) +{ + m_fullText = text; + cutTextToLabel(); +} + + +const QString& KCutLabel::fullText() const +{ + return m_fullText; +} + + +void KCutLabel::cutTextToLabel() +{ + QToolTip::remove( this ); + QToolTip::hide(); + + if( m_fullText.contains( "\n" ) ) { + QString newText; + QStringList lines = QStringList::split( "\n", m_fullText ); + for( QStringList::Iterator it = lines.begin(); it != lines.end(); ++it ) { + QString squeezedText = K3b::cutToWidth( fontMetrics(), + *it, + QMAX( size().width(), + QMIN( fontMetrics().width( m_fullText.left(m_minChars) + "..." ), + fontMetrics().width( m_fullText ) ) ) ); + newText += squeezedText; + newText += "\n"; + if( squeezedText != *it ) + QToolTip::add( this, m_fullText ); + } + newText.truncate( newText.length() - 1 ); // get rid of the last newline + + QLabel::setText( newText ); + } + else { + QString squeezedText = K3b::cutToWidth( fontMetrics(), + m_fullText, + QMAX( size().width(), + QMIN( fontMetrics().width( m_fullText.left(m_minChars) + "..." ), + fontMetrics().width( m_fullText ) ) ) ); + QLabel::setText( squeezedText ); + if( squeezedText != m_fullText ) + QToolTip::add( this, m_fullText ); + } +} + +#include "kcutlabel.moc" diff --git a/libk3b/tools/kcutlabel.h b/libk3b/tools/kcutlabel.h new file mode 100644 index 0000000..6cf459c --- /dev/null +++ b/libk3b/tools/kcutlabel.h @@ -0,0 +1,68 @@ +/* + * + * $Id: kcutlabel.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 KCUTLABEL_H +#define KCUTLABEL_H + +#include <qlabel.h> +#include "k3b_export.h" + + +/* + * @ref QLabel + */ +class LIBK3B_EXPORT KCutLabel : public QLabel +{ + Q_OBJECT + + public: + /** + * Default constructor. + */ + KCutLabel( QWidget *parent = 0, const char *name = 0); + KCutLabel( const QString &text, QWidget *parent = 0, const char *name = 0 ); + + virtual QSize minimumSizeHint() const; + + /** + * \return the full text while text() returns the cut text + */ + const QString& fullText() const; + + public slots: + void setText( const QString & ); + + /** + * \param i the number of characters that have to be visible. Default is 1. + */ + void setMinimumVisibleText( int i ); + + protected: + /** + * used when widget is resized + */ + void resizeEvent( QResizeEvent * ); + /** + * does the dirty work + */ + void cutTextToLabel(); + + private: + QString m_fullText; + int m_minChars; +}; + +#endif // KCUTLABEL_H diff --git a/libk3b/tools/libisofs/COPYING b/libk3b/tools/libisofs/COPYING new file mode 100644 index 0000000..9fe1a71 --- /dev/null +++ b/libk3b/tools/libisofs/COPYING @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Steet, Fifth Floor, Cambridge, MA 02110-1301, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/libk3b/tools/libisofs/ChangeLog b/libk3b/tools/libisofs/ChangeLog new file mode 100644 index 0000000..78d7e66 --- /dev/null +++ b/libk3b/tools/libisofs/ChangeLog @@ -0,0 +1,9 @@ +K3b Changes +- renamed the st_xxx time fileds in rr_entry to rr_st_xxx to make it compile + +0.1 -> 0.2 + +- Critical directory parsing bug fixed +- Call backs only if some sanity checks on the directory entry succeeds + (length checks to avoid buffer overrun if received corrupt data) +- Preliminary El Torito boot specification support (No multiple boot entries yet) diff --git a/libk3b/tools/libisofs/Makefile.am b/libk3b/tools/libisofs/Makefile.am new file mode 100644 index 0000000..cd5594c --- /dev/null +++ b/libk3b/tools/libisofs/Makefile.am @@ -0,0 +1,5 @@ +AM_CPPFLAGS= $(all_includes) + +noinst_LTLIBRARIES = libisofs.la + +libisofs_la_SOURCES = isofs.cpp diff --git a/libk3b/tools/libisofs/README b/libk3b/tools/libisofs/README new file mode 100644 index 0000000..3657861 --- /dev/null +++ b/libk3b/tools/libisofs/README @@ -0,0 +1,24 @@ +This is the 0.2 release of libisofs. For changes, see the ChangeLog. + +Libisofs implements the reading of the famous ISO-9660 (ECMA-167) file system, +found on CD-ROM media. It also supports the Rock Ridge Interchange Protocol and +Microsoft Joliet extensions. It allows user-mode programs to query the +filesystem volume descriptors and traverse through the directory structure. +Preliminary support for El-Torito boot CDs are added in version 0.2. + +To use it in your project, I recommend to copy bswap.h, isofs.h, iso_fs.h, +el_torito.h rock.h and isofs.c to your sources, and include isofs.h in the +appropriate places. + +Currently only the directory tables are parsed, the path tables are not. +(The path tables contain redundant information.) + +Also a sample program can be compiled with the supplied Makefile. Simply +execute 'make', it should create the executable file isofs. + +On big-endian systems, you need to define WORDS_BIGENDIAN (either in the +compiler command-line, or if you defined HAVE_CONFIG_H, in config.h) + + +Gy�rgy Szombathelyi <[email protected]> +http://libcdrom.sourceforge.net/libisofs.html diff --git a/libk3b/tools/libisofs/bswap.h b/libk3b/tools/libisofs/bswap.h new file mode 100644 index 0000000..96bd588 --- /dev/null +++ b/libk3b/tools/libisofs/bswap.h @@ -0,0 +1,94 @@ +/* From the mplayer project (www.mplayerhq.hu) */ + +#ifndef __BSWAP_H__ +#define __BSWAP_H__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_BYTESWAP_H +#include <byteswap.h> +#else + +#ifdef ARCH_X86 +inline static unsigned short ByteSwap16(unsigned short x) +{ + __asm("xchgb %b0,%h0" : + "=q" (x) : + "0" (x)); + return x; +} +#define bswap_16(x) ByteSwap16(x) + +inline static unsigned int ByteSwap32(unsigned int x) +{ +#if defined(__CPU__) && (__CPU__ > 386) + __asm("bswap %0": + "=r" (x) : +#else + __asm("xchgb %b0,%h0\n" + " rorl $16,%0\n" + " xchgb %b0,%h0": + "=q" (x) : +#endif + "0" (x)); + return x; +} +#define bswap_32(x) ByteSwap32(x) + +inline static unsigned long long int ByteSwap64(unsigned long long int x) +{ + register union { __extension__ unsigned long long int __ll; + unsigned int __l[2]; } __x; + asm("xchgl %0,%1": + "=r"(__x.__l[0]),"=r"(__x.__l[1]): + "0"(bswap_32((unsigned long)x)),"1"(bswap_32((unsigned long)(x>>32)))); + return __x.__ll; +} +#define bswap_64(x) ByteSwap64(x) + +#else + +#define bswap_16(x) (((x) & 0x00ff) << 8 | ((x) & 0xff00) >> 8) + + +/* code from bits/byteswap.h (C) 1997, 1998 Free Software Foundation, Inc. */ +#define bswap_32(x) \ + ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ + (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) + +#define bswap_64(x) \ + (__extension__ \ + ({ union { __extension__ unsigned long long int __ll; \ + unsigned int __l[2]; } __w, __r; \ + __w.__ll = (x); \ + __r.__l[0] = bswap_32 (__w.__l[1]); \ + __r.__l[1] = bswap_32 (__w.__l[0]); \ + __r.__ll; })) +#endif /* !ARCH_X86 */ + +#endif /* !HAVE_BYTESWAP_H */ + +/* + be2me ... BigEndian to MachineEndian + le2me ... LittleEndian to MachineEndian +*/ + +#ifdef WORDS_BIGENDIAN +#define be2me_16(x) (x) +#define be2me_32(x) (x) +#define be2me_64(x) (x) +#define le2me_16(x) bswap_16(x) +#define le2me_32(x) bswap_32(x) +#define le2me_64(x) bswap_64(x) +#else +#define be2me_16(x) bswap_16(x) +#define be2me_32(x) bswap_32(x) +#define be2me_64(x) bswap_64(x) +#define le2me_16(x) (x) +#define le2me_32(x) (x) +#define le2me_64(x) (x) +#endif + +#endif diff --git a/libk3b/tools/libisofs/el_torito.h b/libk3b/tools/libisofs/el_torito.h new file mode 100644 index 0000000..cba83f7 --- /dev/null +++ b/libk3b/tools/libisofs/el_torito.h @@ -0,0 +1,63 @@ +#ifndef ELTORITO_H +#define ELTORITO_H 1 + +#include "iso_fs.h" + +#define EL_TORITO_ID "EL TORITO SPECIFICATION\0\0\0\0\0\0\0\0\0" + +struct el_torito_boot_descriptor { + char type [ISODCL ( 1, 1)]; /* 711 */ + char id [ISODCL ( 2, 6)]; + char version [ISODCL ( 7, 7)]; /* 711 */ + char system_id [ISODCL ( 8, 39)]; /* achars */ + char unused [ISODCL ( 40, 71)]; + char boot_catalog [ISODCL ( 72, 75)]; /* 731 */ +}; + +struct validation_entry { + char type [ISODCL ( 1, 1)]; /* 1 */ + char platform [ISODCL ( 2, 2)]; + char unused [ISODCL ( 3, 4)]; + char id [ISODCL ( 5, 28)]; + char cheksum [ISODCL ( 29, 30)]; + char key [ISODCL ( 31, 31)]; /* 0x55 */ + char key2 [ISODCL ( 32, 32)]; /* 0xaa */ +}; + +struct default_entry { + char bootid [ISODCL ( 1, 1)]; + char media [ISODCL ( 2, 2)]; + char loadseg [ISODCL ( 3, 4)]; + char systype [ISODCL ( 5, 5)]; + char unused [ISODCL ( 6, 6)]; + char seccount [ISODCL ( 7, 8)]; + char start [ISODCL ( 9, 12)]; + char unused2 [ISODCL ( 13, 32)]; +}; + +struct section_header { + char headerid [ISODCL ( 1, 1)]; + char platform [ISODCL ( 2, 2)]; + char entries [ISODCL ( 3, 4)]; + char id [ISODCL ( 5, 32)]; +}; + +struct section_entry { + char bootid [ISODCL ( 1, 1)]; + char media [ISODCL ( 2, 2)]; + char loadseg [ISODCL ( 3, 4)]; + char systype [ISODCL ( 5, 5)]; + char unused [ISODCL ( 6, 6)]; + char seccount [ISODCL ( 7, 8)]; + char start [ISODCL ( 9, 12)]; + char selcrit [ISODCL ( 13, 13)]; + char vendor_selcrit [ISODCL ( 14, 32)]; +}; + +struct section_entry_ext { + char extid [ISODCL ( 1, 1)]; + char extrec [ISODCL ( 2, 2)]; + char vendor_selcrit [ISODCL ( 3, 32)]; +}; + +#endif diff --git a/libk3b/tools/libisofs/iso_fs.h b/libk3b/tools/libisofs/iso_fs.h new file mode 100644 index 0000000..43353b0 --- /dev/null +++ b/libk3b/tools/libisofs/iso_fs.h @@ -0,0 +1,219 @@ +/* From the linux kernel */ + +#ifndef _ISO_FS_H +#define _ISO_FS_H 1 + +#include "bswap.h" + +/* + * The isofs filesystem constants/structures + */ + +/* This part borrowed from the bsd386 isofs */ +#define ISODCL(from, to) (to - from + 1) + +struct iso_volume_descriptor { + char type[ISODCL(1,1)]; /* 711 */ + char id[ISODCL(2,6)]; + char version[ISODCL(7,7)]; + char data[ISODCL(8,2048)]; +}; + +/* volume descriptor types */ +#define ISO_VD_BOOT 0 +#define ISO_VD_PRIMARY 1 +#define ISO_VD_SUPPLEMENTARY 2 +#define ISO_VD_END 255 + +#define ISO_STANDARD_ID "CD001" + +struct iso_primary_descriptor { + char type [ISODCL ( 1, 1)]; /* 711 */ + char id [ISODCL ( 2, 6)]; + char version [ISODCL ( 7, 7)]; /* 711 */ + char unused1 [ISODCL ( 8, 8)]; + char system_id [ISODCL ( 9, 40)]; /* achars */ + char volume_id [ISODCL ( 41, 72)]; /* dchars */ + char unused2 [ISODCL ( 73, 80)]; + char volume_space_size [ISODCL ( 81, 88)]; /* 733 */ + char unused3 [ISODCL ( 89, 120)]; + char volume_set_size [ISODCL (121, 124)]; /* 723 */ + char volume_sequence_number [ISODCL (125, 128)]; /* 723 */ + char logical_block_size [ISODCL (129, 132)]; /* 723 */ + char path_table_size [ISODCL (133, 140)]; /* 733 */ + char type_l_path_table [ISODCL (141, 144)]; /* 731 */ + char opt_type_l_path_table [ISODCL (145, 148)]; /* 731 */ + char type_m_path_table [ISODCL (149, 152)]; /* 732 */ + char opt_type_m_path_table [ISODCL (153, 156)]; /* 732 */ + char root_directory_record [ISODCL (157, 190)]; /* 9.1 */ + char volume_set_id [ISODCL (191, 318)]; /* dchars */ + char publisher_id [ISODCL (319, 446)]; /* achars */ + char preparer_id [ISODCL (447, 574)]; /* achars */ + char application_id [ISODCL (575, 702)]; /* achars */ + char copyright_file_id [ISODCL (703, 739)]; /* 7.5 dchars */ + char abstract_file_id [ISODCL (740, 776)]; /* 7.5 dchars */ + char bibliographic_file_id [ISODCL (777, 813)]; /* 7.5 dchars */ + char creation_date [ISODCL (814, 830)]; /* 8.4.26.1 */ + char modification_date [ISODCL (831, 847)]; /* 8.4.26.1 */ + char expiration_date [ISODCL (848, 864)]; /* 8.4.26.1 */ + char effective_date [ISODCL (865, 881)]; /* 8.4.26.1 */ + char file_structure_version [ISODCL (882, 882)]; /* 711 */ + char unused4 [ISODCL (883, 883)]; + char application_data [ISODCL (884, 1395)]; + char unused5 [ISODCL (1396, 2048)]; +}; + +/* Almost the same as the primary descriptor but two fields are specified */ +struct iso_supplementary_descriptor { + char type [ISODCL ( 1, 1)]; /* 711 */ + char id [ISODCL ( 2, 6)]; + char version [ISODCL ( 7, 7)]; /* 711 */ + char flags [ISODCL ( 8, 8)]; /* 853 */ + char system_id [ISODCL ( 9, 40)]; /* achars */ + char volume_id [ISODCL ( 41, 72)]; /* dchars */ + char unused2 [ISODCL ( 73, 80)]; + char volume_space_size [ISODCL ( 81, 88)]; /* 733 */ + char escape [ISODCL ( 89, 120)]; /* 856 */ + char volume_set_size [ISODCL (121, 124)]; /* 723 */ + char volume_sequence_number [ISODCL (125, 128)]; /* 723 */ + char logical_block_size [ISODCL (129, 132)]; /* 723 */ + char path_table_size [ISODCL (133, 140)]; /* 733 */ + char type_l_path_table [ISODCL (141, 144)]; /* 731 */ + char opt_type_l_path_table [ISODCL (145, 148)]; /* 731 */ + char type_m_path_table [ISODCL (149, 152)]; /* 732 */ + char opt_type_m_path_table [ISODCL (153, 156)]; /* 732 */ + char root_directory_record [ISODCL (157, 190)]; /* 9.1 */ + char volume_set_id [ISODCL (191, 318)]; /* dchars */ + char publisher_id [ISODCL (319, 446)]; /* achars */ + char preparer_id [ISODCL (447, 574)]; /* achars */ + char application_id [ISODCL (575, 702)]; /* achars */ + char copyright_file_id [ISODCL (703, 739)]; /* 7.5 dchars */ + char abstract_file_id [ISODCL (740, 776)]; /* 7.5 dchars */ + char bibliographic_file_id [ISODCL (777, 813)]; /* 7.5 dchars */ + char creation_date [ISODCL (814, 830)]; /* 8.4.26.1 */ + char modification_date [ISODCL (831, 847)]; /* 8.4.26.1 */ + char expiration_date [ISODCL (848, 864)]; /* 8.4.26.1 */ + char effective_date [ISODCL (865, 881)]; /* 8.4.26.1 */ + char file_structure_version [ISODCL (882, 882)]; /* 711 */ + char unused4 [ISODCL (883, 883)]; + char application_data [ISODCL (884, 1395)]; + char unused5 [ISODCL (1396, 2048)]; +}; + +#define HS_STANDARD_ID "CDROM" + +struct hs_volume_descriptor { + char foo [ISODCL ( 1, 8)]; /* 733 */ + char type [ISODCL ( 9, 9)]; /* 711 */ + char id [ISODCL ( 10, 14)]; + char version [ISODCL ( 15, 15)]; /* 711 */ + char data[ISODCL(16,2048)]; +}; + + +struct hs_primary_descriptor { + char foo [ISODCL ( 1, 8)]; /* 733 */ + char type [ISODCL ( 9, 9)]; /* 711 */ + char id [ISODCL ( 10, 14)]; + char version [ISODCL ( 15, 15)]; /* 711 */ + char unused1 [ISODCL ( 16, 16)]; /* 711 */ + char system_id [ISODCL ( 17, 48)]; /* achars */ + char volume_id [ISODCL ( 49, 80)]; /* dchars */ + char unused2 [ISODCL ( 81, 88)]; /* 733 */ + char volume_space_size [ISODCL ( 89, 96)]; /* 733 */ + char unused3 [ISODCL ( 97, 128)]; /* 733 */ + char volume_set_size [ISODCL (129, 132)]; /* 723 */ + char volume_sequence_number [ISODCL (133, 136)]; /* 723 */ + char logical_block_size [ISODCL (137, 140)]; /* 723 */ + char path_table_size [ISODCL (141, 148)]; /* 733 */ + char type_l_path_table [ISODCL (149, 152)]; /* 731 */ + char unused4 [ISODCL (153, 180)]; /* 733 */ + char root_directory_record [ISODCL (181, 214)]; /* 9.1 */ +}; + +/* We use this to help us look up the parent inode numbers. */ + +struct iso_path_table{ + char name_len[1]; /* 711 */ + char ext_attr_length[1]; /* 711 */ + char extent[4]; /* 731 */ + char parent[2]; /* 721 */ + char name[1]; +}; + +/* high sierra is identical to iso, except that the date is only 6 bytes, and + there is an extra reserved byte after the flags */ + +struct iso_directory_record { + char length [ISODCL (1, 1)]; /* 711 */ + char ext_attr_length [ISODCL (2, 2)]; /* 711 */ + char extent [ISODCL (3, 10)]; /* 733 */ + char size [ISODCL (11, 18)]; /* 733 */ + char date [ISODCL (19, 25)]; /* 7 by 711 */ + char flags [ISODCL (26, 26)]; + char file_unit_size [ISODCL (27, 27)]; /* 711 */ + char interleave [ISODCL (28, 28)]; /* 711 */ + char volume_sequence_number [ISODCL (29, 32)]; /* 723 */ + char name_len [ISODCL (33, 33)]; /* 711 */ + char name [1]; +}; + +/* 8 bit numbers */ +__inline unsigned char isonum_711(char *p); +__inline char isonum_712(char *p); + +/* 16 bit numbers */ +__inline unsigned short isonum_721(char *p); +__inline unsigned short isonum_722(char *p); +__inline unsigned short isonum_723(char *p); + +/* 32 bit numbers */ +__inline unsigned int isonum_731(char *p); +__inline unsigned int isonum_732(char *p); +__inline unsigned int isonum_733(char *p); + + +/* 8 bit numbers */ +__inline unsigned char isonum_711(char *p) +{ + return *(unsigned char *)p; +} +__inline char isonum_712(char *p) +{ + return *p; +} + +/* 16 bit numbers */ +__inline unsigned short isonum_721(char *p) +{ + return le2me_16(*(unsigned short *)p); +} +__inline unsigned short isonum_722(char *p) +{ + return be2me_16(*(unsigned short *)p); +} +__inline unsigned short isonum_723(char *p) +{ + /* Ignore bigendian datum due to broken mastering programs */ + return le2me_16(*(unsigned short *)p); +} + +/* 32 bit numbers */ +__inline unsigned int isonum_731(char *p) +{ + return le2me_32(*(unsigned int *)p); +} + +__inline unsigned int isonum_732(char *p) +{ + return be2me_32(*(unsigned int *)p); +} + +__inline unsigned int isonum_733(char *p) +{ + /* Ignore bigendian datum due to broken mastering programs */ + return le2me_32(*(unsigned int *)p); +} + +#endif /*_ISOFS_H*/ + diff --git a/libk3b/tools/libisofs/isofs.cpp b/libk3b/tools/libisofs/isofs.cpp new file mode 100644 index 0000000..e5c871b --- /dev/null +++ b/libk3b/tools/libisofs/isofs.cpp @@ -0,0 +1,878 @@ +/*************************************************************************** + isofs.c - libisofs implementation + ------------------- + begin : Oct 25 2002 + copyright : (C) 2002 by Szombathelyi Gy�rgy + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "isofs.h" +#include "rock.h" + + + +/* internal function from the linux kernel (isofs fs) */ +static time_t getisotime(int year,int month,int day,int hour, + int minute,int second,int tz) { + + int days, i; + time_t crtime; + + year-=1970; + + if (year < 0) { + crtime = 0; + } else { + int monlen[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; + + days = year * 365; + if (year > 2) + days += (year+1) / 4; + for (i = 1; i < month; i++) + days += monlen[i-1]; + if (((year+2) % 4) == 0 && month > 2) + days++; + days += day - 1; + crtime = ((((days * 24) + hour) * 60 + minute) * 60) + + second; + + /* sign extend */ + if (tz & 0x80) + tz |= (-1 << 8); + + /* + * The timezone offset is unreliable on some disks, + * so we make a sanity check. In no case is it ever + * more than 13 hours from GMT, which is 52*15min. + * The time is always stored in localtime with the + * timezone offset being what get added to GMT to + * get to localtime. Thus we need to subtract the offset + * to get to true GMT, which is what we store the time + * as internally. On the local system, the user may set + * their timezone any way they wish, of course, so GMT + * gets converted back to localtime on the receiving + * system. + * + * NOTE: mkisofs in versions prior to mkisofs-1.10 had + * the sign wrong on the timezone offset. This has now + * been corrected there too, but if you are getting screwy + * results this may be the explanation. If enough people + * complain, a user configuration option could be added + * to add the timezone offset in with the wrong sign + * for 'compatibility' with older discs, but I cannot see how + * it will matter that much. + * + * Thanks to [email protected] (Volker Kuhlmann) + * for pointing out the sign error. + */ + if (-52 <= tz && tz <= 52) + crtime -= tz * 15 * 60; + } + return crtime; + +} + +/** + * Returns the Unix from the ISO9660 9.1.5 time format + */ +time_t isodate_915(char * p, int hs) { + + return getisotime(1900+p[0],p[1],p[2],p[3],p[4],p[5],hs==0 ? p[6] : 0); +} + +/** + * Returns the Unix from the ISO9660 8.4.26.1 time format + * BUG: hundredth of seconds are ignored, because Unix time_t has one second + * resolution (I think it's no problem at all) + */ +time_t isodate_84261(char * p, int hs) { + int year,month,day,hour,minute,second; + year=(p[0]-'0')*1000 + (p[1]-'0')*100 + (p[2]-'0')*10 + p[3]-'0'; + month=(p[4]-'0')*10 + (p[5]-'0'); + day=(p[6]-'0')*10 + (p[7]-'0'); + hour=(p[8]-'0')*10 + (p[9]-'0'); + minute=(p[10]-'0')*10 + (p[11]-'0'); + second=(p[12]-'0')*10 + (p[13]-'0'); + return getisotime(year,month,day,hour,minute,second,hs==0 ? p[16] : 0); +} + +void FreeBootTable(boot_head *boot) { + boot_entry *be,*next; + + be=boot->defentry; + while (be) { + next=be->next; + free(be); + be=next; + } + boot->defentry=NULL; +} + +int BootImageSize(readfunc* read,int media,sector_t start,int len,void* udata) { + int ret; + + switch(media & 0xf) { + case 0: + ret=len; /* No emulation */ + break; + case 1: + ret=80*2*15; /* 1.2 MB */ + break; + case 2: + ret=80*2*18; /* 1.44 MB */ + break; + case 3: + ret=80*2*36; /* 2.88 MB */ + break; + case 4: + /* FIXME!!! */ + ret=len; /* Hard Disk */ + break; + default: + ret=len; + } + return ret; +} + +static boot_entry *CreateBootEntry(char *be) { + boot_entry *entry; + + entry = (boot_entry*) malloc(sizeof(boot_entry)); + if (!entry) return NULL; + memset(entry, 0, sizeof(boot_entry)); + memcpy(entry->data,be,0x20); + return entry; +} + +int ReadBootTable(readfunc *read,sector_t sector, boot_head *head, void *udata) { + + char buf[2048], *c, *be; + int i,end=0; + unsigned short sum; + boot_entry *defcur=NULL,*deflast=NULL; + register struct validation_entry *ventry=NULL; + register struct default_entry *dentry=NULL; + register struct section_header *sheader=NULL; + register struct section_entry *sentry=NULL; + register struct section_entry_ext *extsentry=NULL; + + head->sections=NULL; + head->defentry=NULL; + while (1) { + be = (char*) &buf; + if ( read(be, sector, 1, udata) != 1 ) goto err; + + /* first entry needs to be a validation entry */ + if (!ventry) { + ventry=(struct validation_entry *) be; + if ( isonum_711(ventry->type) !=1 ) goto err; + sum=0; + c = (char*) ventry; + for (i=0;i<16;i++) { sum += isonum_721(c); c+=2; } + if (sum) goto err; + memcpy(&head->ventry,be,0x20); + be += 0x20; + } + + while (!end && (be < (buf+1))) { + switch (isonum_711(be)) { + case 0x88: + defcur=CreateBootEntry(be); + if (!defcur) goto err; + if (deflast) + deflast->next=defcur; + else + head->defentry=defcur; + defcur->prev=deflast; + deflast=defcur; + break; + case 0x90: + case 0x91: + break; + default: + end=1; + break; + } + be += 0x20; + } + if (end) break; + + sector ++; + } + + return 0; + +err: + FreeBootTable(head); + return -1; +} + + +/** + * Creates the linked list of the volume descriptors + */ +iso_vol_desc *ReadISO9660(readfunc *read,sector_t sector,void *udata) { + + int i; + struct iso_volume_descriptor buf; + iso_vol_desc *first=NULL,*current=NULL,*prev=NULL; + + for (i=0;i<100;i++) { + if (read( (char*) &buf, sector+i+16, 1, udata) != 1 ) { + FreeISO9660(first); + return NULL; + } + if (!memcmp(ISO_STANDARD_ID,&buf.id,5)) { + switch ( isonum_711(&buf.type[0]) ) { + + case ISO_VD_BOOT: + case ISO_VD_PRIMARY: + case ISO_VD_SUPPLEMENTARY: + current=(iso_vol_desc*) malloc(sizeof(iso_vol_desc)); + if (!current) { + FreeISO9660(first); + return NULL; + } + current->prev=prev; + current->next=NULL; + if (prev) prev->next=current; + memcpy(&(current->data),&buf,2048); + if (!first) first=current; + prev=current; + break; + + case ISO_VD_END: + return first; + break; + } + } else if (!memcmp(HS_STANDARD_ID,(struct hs_volume_descriptor*) &buf,5)) { + /* High Sierra format not supported (yet) */ + } + } + + return first; +} + +/** + * Frees the linked list of volume descriptors + */ +void FreeISO9660(iso_vol_desc *data) { + + iso_vol_desc *current; + + + while (data) { + current=data; + data=current->next; + free(current); + } +} + +/** + * Frees the strings in 'rrentry' + */ +void FreeRR(rr_entry *rrentry) { + if (rrentry->name) { + free(rrentry->name); + rrentry->name=NULL; + } + if (rrentry->sl) { + free(rrentry->sl); + rrentry->name=NULL; + } +} + +static int str_nappend(char **d,char *s,int n) { + int i=0; + char *c; + +/* i=strnlen(s,n)+1; */ + while (i<n && s[i]) i++; + i++; + if (*d) i+=(strlen(*d)+1); + c=(char*) malloc(i); + if (!c) return -ENOMEM; + if (*d) { + strcpy(c,*d); + strncat(c,s,n); + + free(*d); + } else + strncpy(c,s,n); + c[i-1]=0; + *d=c; + return 0; +} + +static int str_append(char **d, const char *s) { + int i; + char *c; + + i=strlen(s)+1; + if (*d) i+=(strlen(*d)+1); + c=(char*) malloc(i); + if (!c) return -ENOMEM; + if (*d) { + strcpy(c,*d); + strcat(c,s); + free(*d); + } else + strcpy(c,s); + c[i-1]=0; + *d=c; + return 0; +} + +#define rrtlen(c) (((unsigned char) c & 0x80) ? 17 : 7) +#define rrctime(f,c) ((unsigned char) f & 0x80) ? isodate_84261(c,0) : isodate_915(c,0) +/** + * Parses the System Use area and fills rr_entry with values + */ +int ParseRR(struct iso_directory_record *idr, rr_entry *rrentry) { + + int suspoffs,susplen,i,f,ret=0; + char *r, *c; + struct rock_ridge *rr; + + suspoffs=33+isonum_711(idr->name_len); + if (!(isonum_711(idr->name_len) & 1)) suspoffs++; + susplen=isonum_711(idr->length)-suspoffs; + r= & (((char*) idr)[suspoffs]); + rr = (struct rock_ridge*) r; + + memset(rrentry,0,sizeof(rr_entry)); + rrentry->len = sizeof(rr_entry); + + while (susplen > 0) { + if (isonum_711(&rr->len) > susplen || rr->len == 0) break; + if (rr->signature[0]=='N' && rr->signature[1]=='M') { + if (!(rr->u.NM.flags & 0x26) && rr->len>5 && !rrentry->name) { + + if (str_nappend(&rrentry->name,rr->u.NM.name,isonum_711(&rr->len)-5)) { + FreeRR(rrentry); return -ENOMEM; + } + ret++; + } + } else if (rr->signature[0]=='P' && rr->signature[1]=='X' && + (isonum_711(&rr->len)==44 || isonum_711(&rr->len)==36)) { + rrentry->mode=isonum_733(rr->u.PX.mode); + rrentry->nlink=isonum_733(rr->u.PX.n_links); + rrentry->uid=isonum_733(rr->u.PX.uid); + rrentry->gid=isonum_733(rr->u.PX.gid); + if (isonum_711(&rr->len)==44) rrentry->serno=isonum_733(rr->u.PX.serno); + ret++; + } else if (rr->signature[0]=='P' && rr->signature[1]=='N' && + isonum_711(&rr->len)==20) { + rrentry->dev_major=isonum_733(rr->u.PN.dev_high); + rrentry->dev_minor=isonum_733(rr->u.PN.dev_low); + ret++; + } else if (rr->signature[0]=='P' && rr->signature[1]=='L' && + isonum_711(&rr->len)==12) { + rrentry->pl=isonum_733(rr->u.PL.location); + ret++; + } else if (rr->signature[0]=='C' && rr->signature[1]=='L' && + isonum_711(&rr->len)==12) { + rrentry->cl=isonum_733(rr->u.CL.location); + ret++; + } else if (rr->signature[0]=='R' && rr->signature[1]=='E' && + isonum_711(&rr->len)==4) { + rrentry->re=1; + ret++; + } else if (rr->signature[0]=='S' && rr->signature[1]=='L' && + isonum_711(&rr->len)>7) { + i = isonum_711(&rr->len)-5; + c = (char*) rr; + c += 5; + while (i>0) { + switch(c[0] & ~1) { + case 0x2: + if (str_append(&rrentry->sl,".")) { + FreeRR(rrentry); return -ENOMEM; + } + break; + case 0x4: + if (str_append(&rrentry->sl,"..")) { + FreeRR(rrentry); return -ENOMEM; + } + break; + } + if ( (c[0] & 0x08) == 0x08 || (c[1] && rrentry->sl && + strlen(rrentry->sl)>1) ) { + if (str_append(&rrentry->sl,"/")) { + FreeRR(rrentry); return -ENOMEM; + } + } + + if ((unsigned char)c[1]>0) { + if (str_nappend(&rrentry->sl,c+2,(unsigned char)c[1])) { + FreeRR(rrentry); return -ENOMEM; + } + } + i -= ((unsigned char)c[1] + 2); + c += ((unsigned char)c[1] + 2); + } + ret++; + } else if (rr->signature[0]=='T' && rr->signature[1]=='F' && + isonum_711(&rr->len)>5) { + + i = isonum_711(&rr->len)-5; + f = rr->u.TF.flags; + c = (char*) rr; + c += 5; + + while (i >= rrtlen(f)) { + if (f & 1) { + rrentry->t_creat=rrctime(f,c); + f &= ~1; + } else if (f & 2) { + rrentry->rr_st_mtime=rrctime(f,c); + f &= ~2; + } else if (f & 4) { + rrentry->rr_st_atime=rrctime(f,c); + f &= ~4; + } else if (f & 8) { + rrentry->rr_st_ctime=rrctime(f,c); + f &= ~8; + } else if (f & 16) { + rrentry->t_backup=rrctime(f,c); + f &= ~16; + } else if (f & 32) { + rrentry->t_expire=rrctime(f,c); + f &= ~32; + } else if (f & 64) { + rrentry->t_effect=rrctime(f,c); + f &= ~64; + } + + i -= rrtlen(f); + c += rrtlen(f); + } + ret++; + + } else if (rr->signature[0]=='Z' && rr->signature[1]=='F' && + isonum_711(&rr->len)==16) { + /* Linux-specific extension: transparent decompression */ + rrentry->z_algo[0]=rr->u.ZF.algorithm[0]; + rrentry->z_algo[1]=rr->u.ZF.algorithm[1]; + rrentry->z_params[0]=rr->u.ZF.parms[0]; + rrentry->z_params[1]=rr->u.ZF.parms[1]; + rrentry->z_size=isonum_733(rr->u.ZF.real_size); + ret++; + } else { +/* printf("SUSP sign: %c%c\n",rr->signature[0],rr->signature[1]); */ + } + + susplen -= isonum_711(&rr->len); + r += isonum_711(&rr->len); + rr = (struct rock_ridge*) r; + } + + return ret; +} + +/** + * Iterates over the directory entries. The directory is in 'buf', + * the size of the directory is 'size'. 'callback' is called for each + * directory entry with the parameter 'udata'. + */ +int ProcessDir(readfunc *read,int extent,int size,dircallback *callback,void *udata) { + + int pos=0,ret=0,siz; + char *buf; + struct iso_directory_record *idr; + + if (size & 2047) { + siz=((size>>11)+1)<<11; + } else { + siz=size; + } + + buf=(char*) malloc(siz); + if (!buf) return -ENOMEM; + if (read(buf,extent,siz>>11,udata)!=siz>>11) { + free(buf); + return -EIO; + } + + while (size>0) { + idr=(struct iso_directory_record*) &buf[pos]; + if (isonum_711(idr->length)==0) { + size-=(2048 - (pos & 0x7ff)); + if (size<=2) break; + pos+=0x800; + pos&=0xfffff800; + idr=(struct iso_directory_record*) &buf[pos]; + } + pos+=isonum_711(idr->length); + pos+=isonum_711(idr->ext_attr_length); + size-=isonum_711(idr->length); + size-=isonum_711(idr->ext_attr_length); + if (size<0) break; + + if (isonum_711(idr->length) +<33 || + isonum_711(idr->length)<33+isonum_711(idr->name_len)) { + /* Invalid directory entry */ + continue; + } + if ((ret=callback(idr,udata))) break; + } + + free(buf); + return ret; +} + +/** + * returns the joliet level from the volume descriptor + */ +int JolietLevel(struct iso_volume_descriptor *ivd) { + int ret=0; + register struct iso_supplementary_descriptor *isd; + + isd = (struct iso_supplementary_descriptor *) ivd; + + if (isonum_711(ivd->type)==ISO_VD_SUPPLEMENTARY) { + if (isd->escape[0]==0x25 && + isd->escape[1]==0x2f) { + + switch (isd->escape[2]) { + case 0x40: + ret=1; + break; + case 0x43: + ret=2; + break; + case 0x45: + ret=3; + break; + } + } + } + return ret; +} + +/********************************************************************/ +#if 0 + +#include <time.h> +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <iconv.h> + +int level=0,joliet=0,dirs,files; +iconv_t iconv_d; +int fd; + +int readf(char *buf, int start, int len,void *udata) { + int ret; + + if ((ret=lseek(fd, start << 11, SEEK_SET))<0) return ret; + ret=read(fd, buf, len << 11); + if (ret<0) return ret; + return (ret >> 11); +} + +void dumpchars(char *c,int len) { + while (len>0) { + printf("%c",*c); + len--; + c++; + } +} + +void sp(int num) { + int i; + for (i=0;i<num*5;i++) { printf(" "); }; +} + +void dumpflags(char flags) { + if (flags & 1) printf("HIDDEN "); + if (flags & 2) printf("DIR "); + if (flags & 4) printf("ASF "); +} + +void dumpjoliet(char *c,int len) { + + char outbuf[255]; + size_t out; + int ret; + char *outptr; + + outptr=(char*) &outbuf; + out=255; + if ((iconv(iconv_d,&c,&len,&outptr,&out))<0) { + printf("conversion error=%d",errno); + return; + } + ret=255-out; + dumpchars((char*) &outbuf,ret); +} + +void dumpchardesc(char *c,int len) { + + if (joliet) + dumpjoliet(c,len); + else { + dumpchars(c,len); + } +} + +void dumpiso915time(char *t, int hs) { + + time_t time; + char *c; + + time=isodate_915(t,hs); + c=(char*) ctime(&time); + if (c && c[strlen(c)-1]==0x0a) c[strlen(c)-1]=0; + if (c) printf("%s",c); +} + +void dumpiso84261time(char *t, int hs) { + + time_t time; + char *c; + + time=isodate_84261(t,hs); + c=(char*) ctime(&time); + if (c && c[strlen(c)-1]==0x0a) c[strlen(c)-1]=0; + if (c) printf("%s",c); +} + +void dumpdirrec(struct iso_directory_record *dir) { + + if (isonum_711(dir->name_len)==1) { + switch (dir->name[0]) { + case 0: + printf("."); + break; + case 1: + printf(".."); + break; + default: + printf("%c",dir->name[0]); + break; + } + } + dumpchardesc(dir->name,isonum_711(dir->name_len)); + printf(" size=%d",isonum_733(dir->size)); + printf(" extent=%d ",isonum_733(dir->extent)); + dumpflags(isonum_711(dir->flags)); + dumpiso915time((char*) &(dir->date),0); +} + +void dumprrentry(rr_entry *rr) { + printf(" NM=[%s] uid=%d gid=%d nlink=%d mode=%o ", + rr->name,rr->uid,rr->gid,rr->nlink,rr->mode); + if (S_ISCHR(rr->mode) || S_ISBLK(rr->mode)) + printf("major=%d minor=%d ",rr->dev_major,rr->dev_minor); + if (rr->mode & S_IFLNK && rr->sl) printf("slink=%s ",rr->sl); +/* + printf("\n"); + if (rr->t_creat) printf("t_creat: %s",ctime(&rr->t_creat)); + if (rr->rr_st_mtime) printf("rr_st_mtime: %s",ctime(&rr->rr_st_mtime)); + if (rr->rr_st_atime) printf("rr_st_atime: %s",ctime(&rr->rr_st_atime)); + if (rr->rr_st_ctime) printf("rr_st_ctime: %s",ctime(&rr->rr_st_ctime)); + if (rr->t_backup) printf("t_backup: %s",ctime(&rr->t_backup)); + if (rr->t_expire) printf("t_expire: %s",ctime(&rr->t_expire)); + if (rr->t_effect) printf("t_effect: %s",ctime(&rr->t_effect)); +*/ +} + +void dumpsusp(char *c, int len) { + dumpchars(c,len); +} + +void dumpboot(struct el_torito_boot_descriptor *ebd) { + printf("version: %d\n",isonum_711(ebd->version)); + printf("system id: ");dumpchars(ebd->system_id,ISODCL(8,39));printf("\n"); + printf("boot catalog start: %d\n",isonum_731(ebd->boot_catalog)); +} + +void dumpdefentry(struct default_entry *de) { + printf("Default entry: \n"); + printf(" bootid=%x\n",isonum_711(de->bootid)); + printf(" media emulation=%d (",isonum_711(de->media)); + switch(isonum_711(de->media) & 0xf) { + case 0: + printf("No emulation"); + break; + case 1: + printf("1.2 Mb floppy"); + break; + case 2: + printf("1.44 Mb floppy"); + break; + case 3: + printf("2.88 Mb floppy"); + break; + case 4: + printf("Hard Disk"); + break; + default: + printf("Unknown/Invalid"); + break; + } + printf(")\n"); + printf(" loadseg=%d\n",isonum_721(de->loadseg)); + printf(" systype=%d\n",isonum_711(de->systype)); + printf(" start lba=%d count=%d\n",isonum_731(de->start), + isonum_721(de->seccount)); +} + +void dumpbootcat(boot_head *bh) { + boot_entry *be; + + printf("System id: ");dumpchars(bh->ventry.id,ISODCL(28,5));printf("\n"); + be=bh->defentry; + while (be) { + dumpdefentry(be->data); + be=be->next; + } +} + +void dumpdesc(struct iso_primary_descriptor *ipd) { + + printf("system id: ");dumpchardesc(ipd->system_id,ISODCL(9,40));printf("\n"); + printf("volume id: ");dumpchardesc(ipd->volume_id,ISODCL(41,72));printf("\n"); + printf("volume space size: %d\n",isonum_733(ipd->volume_space_size)); + printf("volume set size: %d\n",isonum_723(ipd->volume_set_size)); + printf("volume seq num: %d\n",isonum_723(ipd->volume_set_size)); + printf("logical block size: %d\n",isonum_723(ipd->logical_block_size)); + printf("path table size: %d\n",isonum_733(ipd->path_table_size)); + printf("location of type_l path table: %d\n",isonum_731(ipd->type_l_path_table)); + printf("location of optional type_l path table: %d\n",isonum_731(ipd->opt_type_l_path_table)); + printf("location of type_m path table: %d\n",isonum_732(ipd->type_m_path_table)); + printf("location of optional type_m path table: %d\n",isonum_732(ipd->opt_type_m_path_table)); +/* + printf("Root dir record:\n");dumpdirrec((struct iso_directory_record*) &ipd->root_directory_record); +*/ + printf("Volume set id: ");dumpchardesc(ipd->volume_set_id,ISODCL(191,318));printf("\n"); + printf("Publisher id: ");dumpchardesc(ipd->publisher_id,ISODCL(319,446));printf("\n"); + printf("Preparer id: ");dumpchardesc(ipd->preparer_id,ISODCL(447,574));printf("\n"); + printf("Application id: ");dumpchardesc(ipd->application_id,ISODCL(575,702));printf("\n"); + printf("Copyright id: ");dumpchardesc(ipd->copyright_file_id,ISODCL(703,739));printf("\n"); + printf("Abstract file id: ");dumpchardesc(ipd->abstract_file_id,ISODCL(740,776));printf("\n"); + printf("Bibliographic file id: ");dumpchardesc(ipd->bibliographic_file_id,ISODCL(777,813));printf("\n"); + printf("Volume creation date: ");dumpiso84261time(ipd->creation_date,0);printf("\n"); + printf("Volume modification date: ");dumpiso84261time(ipd->modification_date,0);printf("\n"); + printf("Volume expiration date: ");dumpiso84261time(ipd->expiration_date,0);printf("\n"); + printf("Volume effective date: ");dumpiso84261time(ipd->effective_date,0);printf("\n"); + printf("File structure version: %d\n",isonum_711(ipd->file_structure_version)); +} + +int mycallb(struct iso_directory_record *idr,void *udata) { + rr_entry rrentry; + + sp(level);dumpdirrec(idr); + if (level==0) printf(" (Root directory) "); + printf("\n"); + + if (ParseRR(idr,&rrentry)>0) { + sp(level);printf(" ");dumprrentry(&rrentry);printf("\n"); + } + FreeRR(&rrentry); + if ( !(idr->flags[0] & 2) ) files++; + if ( (idr->flags[0] & 2) && (level==0 || isonum_711(idr->name_len)>1) ) { + level++; + dirs++; + ProcessDir(&readf,isonum_733(idr->extent),isonum_733(idr->size),&mycallb,udata); + level--; + } + return 0; +} + +/************************************************/ + +int main(int argc, char *argv[]) { + + int i=1,sector=0; + iso_vol_desc *desc; + boot_head boot; + + if (argc<2) { + fprintf(stderr,"\nUsage: %s iso-file-name or device [starting sector]\n\n",argv[0]); + return 0; + } + if (argc>=3) { + sector=atoi(argv[2]); + printf("Using starting sector number %d\n",sector); + } + fd=open(argv[1],O_RDONLY); + if (fd<0) { + fprintf(stderr,"open error\n"); + return -1; + } + iconv_d=iconv_open("ISO8859-2","UTF16BE"); + if (iconv_d==0) { + fprintf(stderr,"iconv open error\n"); + return -1; + } + + desc=ReadISO9660(&readf,sector,NULL); + if (!desc) { + printf("No volume descriptors\n"); + return -1; + } + while (desc) { + + printf("\n\n--------------- Volume descriptor (%d.) type %d: ---------------\n\n", + i,isonum_711(desc->data.type)); + switch (isonum_711(desc->data.type)) { + case ISO_VD_BOOT: { + + struct el_torito_boot_descriptor* bootdesc; + bootdesc=&(desc->data); + dumpboot(bootdesc); + if ( !memcmp(EL_TORITO_ID,bootdesc->system_id,ISODCL(8,39)) ) { + + if (ReadBootTable(&readf,isonum_731(bootdesc->boot_catalog),&boot,NULL)) { + printf("Boot Catalog Error\n"); + } else { + dumpbootcat(&boot); + FreeBootTable(&boot); + } + } + } + break; + + case ISO_VD_PRIMARY: + case ISO_VD_SUPPLEMENTARY: + joliet=0; + joliet = JolietLevel(&desc->data); + printf("Joliet level: %d\n",joliet); + dumpdesc((struct iso_primary_descriptor*) &desc->data); + printf("\n\n--------------- Directory structure: -------------------\n\n"); + dirs=0;files=0; + mycallb( &( ((struct iso_primary_descriptor*) &desc->data)->root_directory_record), NULL ); + printf("\nnumber of directories: %d\n",dirs); + printf("\nnumber of files: %d\n",files); + break; + + } + desc=desc->next; + i++; + } + iconv_close(iconv_d); + close(fd); + FreeISO9660(desc); + return 0; +} + +#endif diff --git a/libk3b/tools/libisofs/isofs.h b/libk3b/tools/libisofs/isofs.h new file mode 100644 index 0000000..f284903 --- /dev/null +++ b/libk3b/tools/libisofs/isofs.h @@ -0,0 +1,151 @@ +/*************************************************************************** + isofs.h - include this file to use libisofs + ------------------- + begin : Oct 25 2002 + copyright : (C) 2002 by Szombathelyi Gy�rgy + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef ISOFS_H +#define ISOFS_H + +#include <sys/time.h> + +#include "iso_fs.h" +#include "el_torito.h" + +typedef long sector_t; + +typedef struct _rr_entry { + int len; /* length of structure */ + char *name; /* Name from 'NM' */ + char *sl; /* symbolic link data */ + time_t t_creat; + time_t rr_st_mtime; + time_t rr_st_atime; + time_t rr_st_ctime; + time_t t_backup; + time_t t_expire; + time_t t_effect; + int mode; /* POSIX file modes */ + int nlink; + int uid; + int gid; + int serno; + int dev_major; + int dev_minor; + int pl; /* parent location */ + int cl; /* child location */ + int re; /* relocated */ + char z_algo[2]; /* zizofs algorithm */ + char z_params[2]; /* zizofs parameters */ + int z_size; /* zizofs real_size */ +} rr_entry; + +typedef struct _iso_vol_desc { + struct _iso_vol_desc *next; + struct _iso_vol_desc *prev; + struct iso_volume_descriptor data; +} iso_vol_desc; + +typedef struct _boot_entry { + struct _boot_entry *next; + struct _boot_entry *prev; + struct _boot_entry *parent; + struct _boot_entry *child; + char data[32]; +} boot_entry; + +typedef struct _boot_head { + struct validation_entry ventry; + struct _boot_entry *defentry; + struct _boot_entry *sections; +} boot_head; + +/** + * this callback function needs to read 'len' sectors from 'start' into 'buf' + */ +typedef int readfunc(char *buf,sector_t start, int len,void *); + +/** + * ProcessDir uses this callback + */ +typedef int dircallback(struct iso_directory_record *,void *); + +/** + * Returns the Unix from the ISO9660 9.1.5 (7 bytes) time format + * This function is from the linux kernel. + * Set 'hs' to non-zero if it's a HighSierra volume + */ +time_t isodate_915(char * p, int hs); + +/** + * Returns the Unix time from the ISO9660 8.4.26.1 (17 bytes) time format + * BUG: hundredth of seconds are ignored, because time_t has one second + * resolution (I think it's no problem at all) + * Set 'hs' to non-zero if it's a HighSierra volume + */ +time_t isodate_84261(char * p, int hs); + +/** + * Creates the linked list of the volume descriptors + * 'sector' is the starting sector number of where the filesystem start + * (starting sector of a session on a CD-ROM) + * If the function fails, returns NULL + * Don't forget to call FreeISO9660 after using the volume descriptor list! + */ +iso_vol_desc *ReadISO9660(readfunc *read,sector_t sector,void *udata); + +/** + * Frees the linked list of volume descriptors. + */ +void FreeISO9660(iso_vol_desc *data); + +/** + * Iterates over the directory entries. The directory is in 'buf', + * the size of the directory is 'size'. 'callback' is called for each + * directory entry with the parameter 'udata'. + */ +int ProcessDir(readfunc *read,int extent,int size,dircallback *callback,void *udata); + +/** + * Parses the System Use area and fills rr_entry with values + */ +int ParseRR(struct iso_directory_record *idr, rr_entry *rrentry); + +/** + * Frees the strings in 'rrentry' + */ +void FreeRR(rr_entry *rrentry); + +/** + * returns the joliet level from the volume descriptor + */ +int JolietLevel(struct iso_volume_descriptor *ivd); + +/** + * Returns the size of the boot image (in 512 byte sectors) + */ +int BootImageSize(readfunc *read,int media,sector_t start,int len,void *udata); + +/** + * Frees the boot catalog entries in 'boot'. If you ever called ReadBootTable, + * then don't forget to call FreeBootTable! + */ +void FreeBootTable(boot_head *boot); + +/** + * Reads the boot catalog into 'head'. Don't forget to call FreeBootTable! + */ +int ReadBootTable(readfunc *read,sector_t sector, boot_head *head, void *udata); + +#endif diff --git a/libk3b/tools/libisofs/rock.h b/libk3b/tools/libisofs/rock.h new file mode 100644 index 0000000..e859192 --- /dev/null +++ b/libk3b/tools/libisofs/rock.h @@ -0,0 +1,127 @@ +/* this header is from the linux kernel */ + +#ifndef ROCK_H +#define ROCK_H 1 + +/* These structs are used by the system-use-sharing protocol, in which the + Rock Ridge extensions are embedded. It is quite possible that other + extensions are present on the disk, and this is fine as long as they + all use SUSP */ + +struct SU_SP{ + unsigned char magic[2]; + unsigned char skip; +}; + +struct SU_CE{ + char extent[8]; + char offset[8]; + char size[8]; +}; + +struct SU_ER{ + unsigned char len_id; + unsigned char len_des; + unsigned char len_src; + unsigned char ext_ver; + char data[1]; +}; + +struct RR_RR{ + char flags[1]; +}; + +struct RR_PX{ + char mode[8]; + char n_links[8]; + char uid[8]; + char gid[8]; + char serno[8]; +}; + +struct RR_PN{ + char dev_high[8]; + char dev_low[8]; +}; + + +struct SL_component{ + unsigned char flags; + unsigned char len; + char text[1]; +}; + +struct RR_SL{ + unsigned char flags; + struct SL_component link; +}; + +struct RR_NM{ + unsigned char flags; + char name[1]; +}; + +struct RR_CL{ + char location[8]; +}; + +struct RR_PL{ + char location[8]; +}; + +struct stamp{ + char time[7]; +}; + +struct RR_TF{ + char flags; + struct stamp times[1]; /* Variable number of these beasts */ +}; + +/* Linux-specific extension for transparent decompression */ +struct RR_ZF{ + char algorithm[2]; + char parms[2]; + char real_size[8]; +}; + +/* These are the bits and their meanings for flags in the TF structure. */ +#define TF_CREATE 1 +#define TF_MODIFY 2 +#define TF_ACCESS 4 +#define TF_ATTRIBUTES 8 +#define TF_BACKUP 16 +#define TF_EXPIRATION 32 +#define TF_EFFECTIVE 64 +#define TF_LONG_FORM 128 + +struct rock_ridge{ + char signature[2]; + char len; /* 711 */ + char version; /* 711 */ + union{ + struct SU_SP SP; + struct SU_CE CE; + struct SU_ER ER; + struct RR_RR RR; + struct RR_PX PX; + struct RR_PN PN; + struct RR_SL SL; + struct RR_NM NM; + struct RR_CL CL; + struct RR_PL PL; + struct RR_TF TF; + struct RR_ZF ZF; + } u; +}; + +#define RR_PX 1 /* POSIX attributes */ +#define RR_PN 2 /* POSIX devices */ +#define RR_SL 4 /* Symbolic link */ +#define RR_NM 8 /* Alternate Name */ +#define RR_CL 16 /* Child link */ +#define RR_PL 32 /* Parent link */ +#define RR_RE 64 /* Relocation directory */ +#define RR_TF 128 /* Timestamps */ + +#endif /* ROCK_H */ |