1 /*
2     SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org>
3     SPDX-License-Identifier: GPL-2.0-or-later
4 */
5 
6 #include "k3bdatamultisessionimportdialog.h"
7 #include "k3bcore.h"
8 #include "k3bdatadoc.h"
9 #include "k3btoc.h"
10 #include "k3bdevice.h"
11 #include "k3bdevicemanager.h"
12 #include "k3bdiskinfo.h"
13 #include "k3biso9660.h"
14 #include "k3bmedium.h"
15 #include "k3bmediacache.h"
16 
17 #include "../k3bapplication.h"
18 #include "../k3b.h"
19 
20 #include <KIconLoader>
21 #include <KLocalizedString>
22 #include <KMessageBox>
23 
24 #include <QMap>
25 #include <QCursor>
26 #include <QFont>
27 #include <QDialogButtonBox>
28 #include <QLabel>
29 #include <QLayout>
30 #include <QPushButton>
31 #include <QTreeWidget>
32 #include <QVBoxLayout>
33 
34 namespace {
35     class SessionInfo
36     {
37     public:
SessionInfo()38         SessionInfo()
39             : sessionNumber( 0 ),
40               device( 0 ) {}
41 
SessionInfo(int num,K3b::Device::Device * dev)42         SessionInfo( int num, K3b::Device::Device* dev )
43             : sessionNumber( num ),
44               device( dev ) {}
45 
46         int sessionNumber;
47         K3b::Device::Device* device;
48     };
49 
50     typedef QMap<QTreeWidgetItem*, SessionInfo> Sessions;
51 }
52 
53 
54 class K3b::DataMultisessionImportDialog::Private
55 {
56 public:
57     K3b::DataDoc* doc;
58     QTreeWidget* sessionView;
59     QPushButton* okButton;
60 
61     Sessions sessions;
62 };
63 
64 
importSession(K3b::DataDoc * doc,QWidget * parent)65 K3b::DataDoc* K3b::DataMultisessionImportDialog::importSession( K3b::DataDoc* doc, QWidget* parent )
66 {
67     K3b::DataMultisessionImportDialog dlg( parent );
68     dlg.importSession( doc );
69     dlg.exec();
70     return dlg.d->doc;
71 }
72 
73 
slotOk()74 void K3b::DataMultisessionImportDialog::slotOk()
75 {
76     Sessions::const_iterator session = d->sessions.constFind( d->sessionView->currentItem() );
77     if ( session != d->sessions.constEnd() ) {
78         QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) );
79 
80         K3b::Device::Device* dev = session->device;
81 
82         //
83         // Mkisofs does not properly import joliet filenames from an old session
84         //
85         // See bug 79215 for details
86         //
87         K3b::Iso9660 iso( dev );
88         if( iso.open() ) {
89             if( iso.firstRRDirEntry() == 0 && iso.jolietLevel() > 0 )
90                 KMessageBox::sorry( this,
91                                     i18n("<p>K3b found session containing Joliet information for long filenames "
92                                          "but no Rock Ridge extensions."
93                                          "<p>The filenames in the imported session will be converted to a restricted "
94                                          "character set in the new session. This character set is based on the ISO 9660 "
95                                          "settings in the K3b project. K3b is not able to display these converted filenames yet."),
96                                     i18n("Session Import Warning") );
97             iso.close();
98         }
99 
100         if( !d->doc ) {
101             d->doc = static_cast<K3b::DataDoc*>( k3bappcore->k3bMainWindow()->slotNewDataDoc() );
102         }
103 
104         d->doc->setBurner( dev );
105         d->doc->importSession( dev, session->sessionNumber );
106 
107         QApplication::restoreOverrideCursor();
108 
109         done( 0 );
110     }
111 }
112 
113 
slotCancel()114 void K3b::DataMultisessionImportDialog::slotCancel()
115 {
116     reject();
117 }
118 
119 
importSession(K3b::DataDoc * doc)120 void K3b::DataMultisessionImportDialog::importSession( K3b::DataDoc* doc )
121 {
122     d->doc = doc;
123     updateMedia();
124     slotSelectionChanged();
125 }
126 
127 
updateMedia()128 void K3b::DataMultisessionImportDialog::updateMedia()
129 {
130     d->sessionView->clear();
131     d->sessions.clear();
132 
133     QList<K3b::Device::Device*> devices = k3bcore->deviceManager()->allDevices();
134 
135     bool haveMedium = false;
136     for( QList<K3b::Device::Device *>::const_iterator it = devices.constBegin();
137          it != devices.constEnd(); ++it ) {
138         K3b::Medium medium = k3bappcore->mediaCache()->medium( *it );
139 
140         if ( medium.diskInfo().mediaType() & K3b::Device::MEDIA_WRITABLE &&
141              medium.diskInfo().diskState() == K3b::Device::STATE_INCOMPLETE ) {
142             addMedium( medium );
143             haveMedium = true;
144         }
145         else if ( !medium.diskInfo().empty() &&
146                   medium.diskInfo().mediaType() & ( K3b::Device::MEDIA_DVD_PLUS_RW|K3b::Device::MEDIA_DVD_RW_OVWR ) ) {
147             addMedium( medium );
148             haveMedium = true;
149         }
150     }
151 
152     if ( !haveMedium ) {
153         QTreeWidgetItem* noMediaItem = new QTreeWidgetItem( d->sessionView );
154         QFont fnt( noMediaItem->font(0) );
155         fnt.setItalic( true );
156         noMediaItem->setText( 0, i18n( "Please insert an appendable medium" ) );
157         noMediaItem->setFont( 0, fnt );
158     }
159     else if( QTreeWidgetItem* firstMedium = d->sessionView->topLevelItem(0) ) {
160         if( firstMedium->childCount() > 0 )
161             d->sessionView->setCurrentItem( firstMedium->child( firstMedium->childCount()-1 ) );
162         else
163             d->sessionView->setCurrentItem( firstMedium );
164     }
165 
166     d->sessionView->setEnabled( haveMedium );
167 }
168 
169 
addMedium(const K3b::Medium & medium)170 void K3b::DataMultisessionImportDialog::addMedium( const K3b::Medium& medium )
171 {
172     QTreeWidgetItem* mediumItem = new QTreeWidgetItem( d->sessionView );
173     QFont fnt( mediumItem->font(0) );
174     fnt.setBold( true );
175     mediumItem->setText( 0, medium.shortString() );
176     mediumItem->setFont( 0, fnt );
177     mediumItem->setIcon( 0, QIcon::fromTheme("media-optical-recordable") );
178 
179     const K3b::Device::Toc& toc = medium.toc();
180     QTreeWidgetItem* sessionItem = 0;
181     int lastSession = 0;
182     for ( K3b::Device::Toc::const_iterator it = toc.begin(); it != toc.end(); ++it ) {
183         const K3b::Device::Track& track = *it;
184 
185         if( track.session() != lastSession ) {
186             lastSession = track.session();
187             QString sessionInfo;
188             if ( track.type() == K3b::Device::Track::TYPE_DATA ) {
189                 K3b::Iso9660 iso( medium.device(), track.firstSector().lba() );
190                 if ( iso.open() ) {
191                     sessionInfo = iso.primaryDescriptor().volumeId;
192                 }
193             }
194             else {
195                 int numAudioTracks = 1;
196                 while ( it != toc.end()
197                         && ( *it ).type() == K3b::Device::Track::TYPE_AUDIO
198                         && ( *it ).session() == lastSession ) {
199                     ++it;
200                     ++numAudioTracks;
201                 }
202                 --it;
203                 sessionInfo = i18np("1 audio track", "%1 audio tracks", numAudioTracks );
204             }
205 
206             sessionItem = new QTreeWidgetItem( mediumItem, sessionItem );
207             sessionItem->setText( 0, i18n( "Session %1", lastSession )
208                                      + ( sessionInfo.isEmpty() ? QString() : " (" + sessionInfo + ')' ) );
209             if ( track.type() == K3b::Device::Track::TYPE_AUDIO )
210                 sessionItem->setIcon( 0, QIcon::fromTheme( "audio-x-generic" ) );
211             else
212                 sessionItem->setIcon( 0, QIcon::fromTheme( "application-x-tar" ) );
213 
214             d->sessions.insert( sessionItem, SessionInfo( lastSession, medium.device() ) );
215         }
216     }
217 
218     if( 0 == lastSession ) {
219         // the medium item in case we have no session info (will always use the last session)
220         d->sessions.insert( mediumItem, SessionInfo( 0, medium.device() ) );
221     }
222     else {
223         // we have a session item, there is no need to select the medium as a whole
224         mediumItem->setFlags( mediumItem->flags() ^ Qt::ItemIsSelectable );
225     }
226 
227     mediumItem->setExpanded( true );
228 }
229 
230 
slotSelectionChanged()231 void K3b::DataMultisessionImportDialog::slotSelectionChanged()
232 {
233     Sessions::const_iterator session = d->sessions.constFind( d->sessionView->currentItem() );
234     if ( session != d->sessions.constEnd() ) {
235         d->okButton->setEnabled( true );
236     }
237     else {
238         d->okButton->setEnabled( false );
239     }
240 }
241 
242 
DataMultisessionImportDialog(QWidget * parent)243 K3b::DataMultisessionImportDialog::DataMultisessionImportDialog( QWidget* parent )
244     : QDialog( parent),
245       d( new Private() )
246 {
247     setModal(true);
248     setWindowTitle(i18n("Session Import"));
249     QVBoxLayout* layout = new QVBoxLayout( this );
250 
251     QLabel* label = new QLabel( i18n( "Please select a session to import." ), this );
252     d->sessionView = new QTreeWidget( this );
253     d->sessionView->setHeaderHidden( true );
254     d->sessionView->setItemsExpandable( false );
255     d->sessionView->setRootIsDecorated( false );
256 
257     QDialogButtonBox* buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this );
258     d->okButton = buttonBox->button( QDialogButtonBox::Ok );
259     connect( buttonBox, SIGNAL(accepted()), SLOT(accept()) );
260     connect( buttonBox, SIGNAL(rejected()), SLOT(reject()) );
261 
262     layout->addWidget( label );
263     layout->addWidget( d->sessionView );
264     layout->addWidget( buttonBox );
265 
266     connect( k3bappcore->mediaCache(), SIGNAL(mediumChanged(K3b::Device::Device*)),
267              this, SLOT(updateMedia()) );
268     connect( d->sessionView, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
269              this, SLOT(slotSelectionChanged()) );
270     connect(d->sessionView, SIGNAL(itemActivated(QTreeWidgetItem*,int)), SLOT(slotOk()) );
271     connect(d->okButton, SIGNAL(clicked()), this, SLOT(slotOk()));
272     connect(buttonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(slotCancel()));
273 }
274 
275 
~DataMultisessionImportDialog()276 K3b::DataMultisessionImportDialog::~DataMultisessionImportDialog()
277 {
278     delete d;
279 }
280 
281 
282