File indexing completed on 2024-05-12 04:51:52

0001 /*
0002     SPDX-FileCopyrightText: 2009-2011 Michal Malek <michalm@jabster.pl>
0003     SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "k3bvideodvdrippingview.h"
0009 #include "k3bvideodvd.h"
0010 #include "k3bvideodvdrippingdialog.h"
0011 #include "k3bvideodvdtitletranscodingjob.h"
0012 #include "k3bvideodvdtitledelegate.h"
0013 #include "k3bvideodvdtitlemodel.h"
0014 
0015 #include "k3bthememanager.h"
0016 #include "k3bglobals.h"
0017 #include "k3blibdvdcss.h"
0018 #include "k3bcore.h"
0019 #include "k3bexternalbinmanager.h"
0020 #include "k3bmediacache.h"
0021 #include "k3bmedium.h"
0022 #include "k3bmodelutils.h"
0023 
0024 #include <KConfig>
0025 #include <KLocalizedString>
0026 #include <KMessageBox>
0027 #include <KToolBarSpacerAction>
0028 #include <KUrlLabel>
0029 #include <KActionCollection>
0030 #include <KToolBar>
0031 
0032 #include <QCursor>
0033 #include <QGuiApplication>
0034 #include <QDesktopServices>
0035 #include <QKeyEvent>
0036 #include <QAction>
0037 #include <QVBoxLayout>
0038 #include <QHeaderView>
0039 #include <QLabel>
0040 #include <QLayout>
0041 #include <QMenu>
0042 #include <QStyle>
0043 #include <QTreeView>
0044 
0045 
0046 namespace mu = K3b::ModelUtils;
0047 
0048 
0049 class K3b::VideoDVDRippingView::Private
0050 {
0051 public:
0052     KActionCollection* actionCollection;
0053     QMenu* popupMenu;
0054 
0055     KToolBar* toolBox;
0056     QLabel* labelLength;
0057     VideoDVDTitleDelegate* delegate;
0058     VideoDVDTitleModel* model;
0059     QTreeView* view;
0060 
0061     VideoDVD::VideoDVD dvd;
0062 };
0063 
0064 K3b::VideoDVDRippingView::VideoDVDRippingView( QWidget* parent )
0065     : K3b::MediaContentsView( true,
0066                             K3b::Medium::ContentVideoDVD,
0067                             K3b::Device::MEDIA_DVD_ALL,
0068                             K3b::Device::STATE_INCOMPLETE|K3b::Device::STATE_COMPLETE,
0069                             parent ),
0070       d( new Private )
0071 {
0072     // toolbox
0073     // ----------------------------------------------------------------------------------
0074     d->toolBox = new KToolBar( mainWidget() );
0075 
0076     KUrlLabel* showFilesLabel = new KUrlLabel( d->toolBox );
0077     showFilesLabel->setContentsMargins( style()->pixelMetric( QStyle::PM_LayoutLeftMargin ), 0,
0078                                         style()->pixelMetric( QStyle::PM_LayoutRightMargin ), 0 );
0079     showFilesLabel->setText( i18n("Show files") );
0080     showFilesLabel->setWhatsThis( i18n("Shows plain Video DVD vob files from the DVD "
0081                                        "(including decryption) for further processing with another application") );
0082     connect( showFilesLabel, SIGNAL(leftClickedUrl()), this, SLOT(slotShowFiles()) );
0083 
0084     d->labelLength = new QLabel( d->toolBox );
0085     d->labelLength->setAlignment( Qt::AlignVCenter | Qt::AlignRight );
0086     d->labelLength->setContentsMargins( style()->pixelMetric( QStyle::PM_LayoutLeftMargin ), 0,
0087                                         style()->pixelMetric( QStyle::PM_LayoutRightMargin ), 0 );
0088 
0089     d->delegate = new VideoDVDTitleDelegate( this );
0090     d->model = new VideoDVDTitleModel( this );
0091 
0092     // the title view
0093     // ----------------------------------------------------------------------------------
0094     d->view = new QTreeView( mainWidget() );
0095     d->view->setItemDelegate( d->delegate );
0096     d->view->setSelectionMode( QAbstractItemView::ExtendedSelection );
0097     d->view->setModel( d->model );
0098     d->view->setRootIsDecorated( false );
0099     d->view->header()->setSectionResizeMode( QHeaderView::ResizeToContents );
0100     d->view->setContextMenuPolicy( Qt::CustomContextMenu );
0101     d->view->installEventFilter( this );
0102     connect( d->view, SIGNAL(customContextMenuRequested(QPoint)),
0103              this, SLOT(slotContextMenu(QPoint)) );
0104 
0105     // general layout
0106     // ----------------------------------------------------------------------------------
0107     QVBoxLayout* mainGrid = new QVBoxLayout( mainWidget() );
0108     mainGrid->addWidget( d->toolBox );
0109     mainGrid->addWidget( d->view );
0110     mainGrid->setContentsMargins( 0, 0, 0, 0 );
0111     mainGrid->setSpacing( 0 );
0112 
0113     setLeftPixmap( K3b::Theme::MEDIA_LEFT );
0114     setRightPixmap( K3b::Theme::MEDIA_VIDEO );
0115 
0116     initActions();
0117 
0118     d->toolBox->addAction( actionCollection()->action("start_rip") );
0119     d->toolBox->addSeparator();
0120     d->toolBox->addWidget( showFilesLabel );
0121     d->toolBox->addAction( new KToolBarSpacerAction( d->toolBox ) );
0122     d->toolBox->addWidget( d->labelLength );
0123 }
0124 
0125 
0126 K3b::VideoDVDRippingView::~VideoDVDRippingView()
0127 {
0128     delete d;
0129 }
0130 
0131 
0132 KActionCollection* K3b::VideoDVDRippingView::actionCollection() const
0133 {
0134     return d->actionCollection;
0135 }
0136 
0137 
0138 bool K3b::VideoDVDRippingView::eventFilter( QObject* obj, QEvent* event )
0139 {
0140     if( event->type() == QEvent::KeyPress ) {
0141         // Due to limitation of default implementation of QTreeView
0142         // checking items with Space key doesn't work for columns other than first.
0143         // Using below code a user can do that.
0144         QKeyEvent* keyEvent = static_cast<QKeyEvent*>( event );
0145         if( keyEvent->key() == Qt::Key_Space ) {
0146             if( keyEvent->modifiers().testFlag( Qt::ControlModifier ) ) {
0147                 QItemSelectionModel* selectionModel = d->view->selectionModel();
0148                 QModelIndex current = d->view->currentIndex();
0149                 selectionModel->select( current, QItemSelectionModel::Toggle | QItemSelectionModel::Rows );
0150             } else {
0151                 slotToggle();
0152             }
0153             return true;
0154         }
0155     }
0156     return MediaContentsView::eventFilter( obj, event );
0157 }
0158 
0159 
0160 void K3b::VideoDVDRippingView::slotStartRipping()
0161 {
0162     QList<int> titles = d->model->selectedTitles();
0163 
0164     if( titles.isEmpty() ) {
0165         KMessageBox::error( this, i18n("Please select the titles to rip."),
0166                             i18n("No Titles Selected") );
0167     }
0168     else {
0169         K3b::VideoDVDRippingDialog dlg( d->dvd, titles, this );
0170         dlg.exec();
0171     }
0172 }
0173 
0174 
0175 void K3b::VideoDVDRippingView::slotContextMenu( const QPoint& pos )
0176 {
0177     d->popupMenu->popup( d->view->viewport()->mapToGlobal( pos ) );
0178 }
0179 
0180 
0181 void K3b::VideoDVDRippingView::slotContextMenuAboutToShow()
0182 {
0183     QAction *actionCheckTracks = actionCollection()->action("check_tracks");
0184     QAction *actionUncheckTracks = actionCollection()->action("uncheck_tracks");
0185     const QModelIndexList selectedRows = d->view->selectionModel()->selectedRows();
0186 
0187     if ( !selectedRows.empty() ) {
0188         const Qt::CheckState commonState = mu::commonCheckState( selectedRows );
0189         actionCheckTracks->setVisible( commonState != Qt::Checked );
0190         actionCheckTracks->setText( selectedRows.count() == 1 ? i18n("Check Track") : i18n("Check Tracks") );
0191         actionUncheckTracks->setVisible( commonState != Qt::Unchecked );
0192         actionUncheckTracks->setText( selectedRows.count() == 1 ? i18n("Uncheck Track") : i18n("Uncheck Tracks") );
0193     } else {
0194         actionCheckTracks->setVisible( false );
0195         actionUncheckTracks->setVisible( false );
0196     }
0197 }
0198 
0199 
0200 void K3b::VideoDVDRippingView::slotCheck()
0201 {
0202     Q_FOREACH( const QModelIndex& index, d->view->selectionModel()->selectedRows() )
0203     {
0204         d->model->setData( index, Qt::Checked, Qt::CheckStateRole );
0205     }
0206 }
0207 
0208 
0209 void K3b::VideoDVDRippingView::slotUncheck()
0210 {
0211     Q_FOREACH( const QModelIndex& index, d->view->selectionModel()->selectedRows() )
0212     {
0213         d->model->setData( index, Qt::Unchecked, Qt::CheckStateRole );
0214     }
0215 }
0216 
0217 
0218 void K3b::VideoDVDRippingView::slotToggle()
0219 {
0220     mu::toggleCommonCheckState( d->model, d->view->selectionModel()->selectedRows() );
0221 }
0222 
0223 
0224 void K3b::VideoDVDRippingView::slotShowFiles()
0225 {
0226     QUrl url;
0227     url.setScheme( "videodvd" );
0228     if( d->dvd.valid() ) {
0229         url.setPath( '/' + d->dvd.volumeIdentifier() );
0230     }
0231     QDesktopServices::openUrl( url );
0232 }
0233 
0234 
0235 void K3b::VideoDVDRippingView::reloadMedium()
0236 {
0237     //
0238     // For VideoDVD reading it is important that the DVD is not mounted
0239     //
0240     if( K3b::isMounted( device() ) && !K3b::unmount( device() ) ) {
0241         KMessageBox::error( this,
0242                             i18n("K3b was unable to unmount device '%1' containing medium '%2'. "
0243                                  "Video DVD ripping will not work if the device is mounted. "
0244                                  "Please unmount manually.",
0245                             device()->blockDeviceName(),
0246                             k3bcore->mediaCache()->medium( device() ).shortString() ),
0247                             i18n("Unmounting failed") );
0248     }
0249 
0250     //
0251     // K3b::VideoDVD::open does not necessarily fail on encrypted DVDs if dvdcss is not
0252     // available. Thus, we test the availability of libdvdcss here
0253     //
0254     if( device()->copyrightProtectionSystemType() == K3b::Device::COPYRIGHT_PROTECTION_CSS ) {
0255         K3b::LibDvdCss* css = K3b::LibDvdCss::create();
0256         if( !css ) {
0257             KMessageBox::error( this, i18n("<p>Unable to read Video DVD contents: Found encrypted Video DVD."
0258                                            "<p>Install <i>libdvdcss</i> to get Video DVD decryption support.") );
0259             return;
0260         }
0261         else
0262             delete css;
0263     }
0264 
0265     QGuiApplication::setOverrideCursor( QCursor(Qt::WaitCursor) );
0266 
0267     if( d->dvd.open( device() ) ) {
0268         setTitle( i18n( "%1 (Video DVD)", medium().beautifiedVolumeId() ) );
0269         d->labelLength->setText( i18np("%1 title", "%1 titles", d->dvd.numTitles() ) );
0270         d->model->setVideoDVD( d->dvd );
0271         QGuiApplication::restoreOverrideCursor();
0272 
0273         bool transcodeUsable = true;
0274 
0275         if( !k3bcore ->externalBinManager() ->foundBin( "transcode" ) ) {
0276             KMessageBox::error( this,
0277                                 i18n("K3b uses transcode to rip Video DVDs. "
0278                                      "Please make sure it is installed.") );
0279             transcodeUsable = false;
0280         }
0281         else {
0282             int vc = 0, ac = 0;
0283             for( int i = 0; i < K3b::VideoDVDTitleTranscodingJob::VIDEO_CODEC_NUM_ENTRIES; ++i )
0284                 if( K3b::VideoDVDTitleTranscodingJob::transcodeBinaryHasSupportFor( (K3b::VideoDVDTitleTranscodingJob::VideoCodec)i ) )
0285                     ++vc;
0286             for( int i = 0; i < K3b::VideoDVDTitleTranscodingJob::AUDIO_CODEC_NUM_ENTRIES; ++i )
0287                 if( K3b::VideoDVDTitleTranscodingJob::transcodeBinaryHasSupportFor( (K3b::VideoDVDTitleTranscodingJob::AudioCodec)i ) )
0288                     ++ac;
0289             if( !ac || !vc ) {
0290                 KMessageBox::error( this,
0291                                     i18n("<p>K3b uses transcode to rip Video DVDs. "
0292                                          "Your installation of transcode lacks support for any of the "
0293                                          "codecs supported by K3b."
0294                                          "<p>Please make sure it is installed properly.") );
0295                 transcodeUsable = false;
0296             }
0297         }
0298 
0299         actionCollection()->action("start_rip")->setEnabled( transcodeUsable );
0300     }
0301     else {
0302         QGuiApplication::restoreOverrideCursor();
0303 
0304         KMessageBox::error( this, i18n("Unable to read Video DVD contents.") );
0305     }
0306 }
0307 
0308 
0309 void K3b::VideoDVDRippingView::enableInteraction( bool enable )
0310 {
0311     actionCollection()->action( "start_rip" )->setEnabled( enable );
0312 }
0313 
0314 
0315 void K3b::VideoDVDRippingView::activate( bool active )
0316 {
0317     if( !active )
0318     {
0319         //
0320         // For now we do it the easy way: just stop the preview generation
0321         // once this view is no longer selected one
0322         //
0323         d->model->stopPreviewGen();
0324     }
0325 
0326     MediaContentsView::activate( active );
0327 }
0328 
0329 
0330 void K3b::VideoDVDRippingView::initActions()
0331 {
0332     d->actionCollection = new KActionCollection( this );
0333 
0334     QAction* actionCheck = new QAction( this );
0335     connect( actionCheck, SIGNAL(triggered()), this, SLOT(slotCheck()) );
0336     actionCollection()->addAction( "check_tracks", actionCheck );
0337 
0338     QAction* actionUncheck = new QAction( this );
0339     connect( actionUncheck, SIGNAL(triggered()), this, SLOT(slotUncheck()) );
0340     actionCollection()->addAction( "uncheck_tracks", actionUncheck );
0341 
0342     QAction* actionStartRip = new QAction( QIcon::fromTheme( "tools-rip-video-dvd" ), i18n("Start Ripping"), this );
0343     actionStartRip->setToolTip( i18n("Open the Video DVD ripping dialog") );
0344     actionStartRip->setStatusTip(actionStartRip->toolTip());
0345     actionStartRip->setWhatsThis( i18n("<p>Rips single titles from a video DVD "
0346                                        "into a compressed format such as XviD. Menu structures are completely ignored."
0347                                        "<p>If you intend to copy the plain Video DVD vob files from the DVD "
0348                                        "(including decryption) for further processing with another application, "
0349                                        "please use \"Show files\" button."
0350                                        "<p>If you intend to make a copy of the entire Video DVD including all menus "
0351                                        "and extras it is recommended to use the K3b Copy tool.") );
0352     connect( actionStartRip, SIGNAL(triggered()), this, SLOT(slotStartRipping()) );
0353     actionCollection()->addAction( "start_rip", actionStartRip );
0354     
0355     QAction* actionSelectAll = KStandardAction::selectAll( d->view, SLOT(selectAll()), actionCollection() );
0356 
0357     // setup the popup menu
0358     d->popupMenu = new QMenu( this );
0359     d->popupMenu->addAction( actionCheck );
0360     d->popupMenu->addAction( actionUncheck );
0361     d->popupMenu->addSeparator();
0362     d->popupMenu->addAction( actionSelectAll );
0363     d->popupMenu->addSeparator();
0364     d->popupMenu->addAction( actionStartRip );
0365     connect( d->popupMenu, SIGNAL(aboutToShow()), this, SLOT(slotContextMenuAboutToShow()) );
0366 }
0367 
0368 #include "moc_k3bvideodvdrippingview.cpp"