1 /*
2 SPDX-FileCopyrightText: 2010 Michal Malek <michalm@jabster.pl>
3 SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org>
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "k3bdataurladdingdialog.h"
8 #include "k3bencodingconverter.h"
9
10 #include "k3bdatadoc.h"
11 #include "k3bdiritem.h"
12 #include "k3bcore.h"
13 #include "k3bfileitem.h"
14 #include "k3bmultichoicedialog.h"
15 #include "k3bvalidators.h"
16 #include "k3bglobals.h"
17 #include "k3bisooptions.h"
18 #include "k3b.h"
19 #include "k3bapplication.h"
20 #include "k3biso9660.h"
21 #include "k3bdirsizejob.h"
22 #include "k3binteractiondialog.h"
23 #include "k3bthread.h"
24 #include "k3bsignalwaiter.h"
25 #include "k3bexternalbinmanager.h"
26
27 #include <KConfig>
28 #include <KSqueezedTextLabel>
29 #include <KIconLoader>
30 #include <KLocalizedString>
31 #include <KMessageBox>
32 #include <KStandardGuiItem>
33 #include <kio/listjob.h>
34
35 #include <QDir>
36 #include <QFileInfo>
37 #include <QList>
38 #include <QUrl>
39 #include <QDialogButtonBox>
40 #include <QGridLayout>
41 #include <QProgressBar>
42 #include <QLabel>
43 #include <QLayout>
44 #include <QInputDialog>
45
46 #include <unistd.h>
47
48
DataUrlAddingDialog(const QList<QUrl> & urls,DirItem * dir,QWidget * parent)49 K3b::DataUrlAddingDialog::DataUrlAddingDialog( const QList<QUrl>& urls, DirItem* dir, QWidget* parent )
50 : DataUrlAddingDialog( dir, parent )
51 {
52 m_urls = urls;
53 for( QList<QUrl>::ConstIterator it = urls.begin(); it != urls.end(); ++it )
54 m_urlQueue.append( qMakePair( K3b::convertToLocalUrl(*it), dir ) );
55 }
56
57
DataUrlAddingDialog(const QList<DataItem * > & items,DirItem * dir,bool copy,QWidget * parent)58 K3b::DataUrlAddingDialog::DataUrlAddingDialog( const QList<DataItem*>& items, DirItem* dir, bool copy, QWidget* parent )
59 : DataUrlAddingDialog( dir, parent )
60 {
61 m_infoLabel->setText( i18n("Moving files to project \"%1\"...", dir->getDoc()->URL().fileName()) );
62 m_copyItems = copy;
63
64 for( QList<K3b::DataItem*>::const_iterator it = items.begin(); it != items.end(); ++it ) {
65 m_items.append( qMakePair( *it, dir ) );
66 ++m_totalFiles;
67 if( (*it)->isDir() ) {
68 m_totalFiles += static_cast<K3b::DirItem*>( *it )->numFiles();
69 m_totalFiles += static_cast<K3b::DirItem*>( *it )->numDirs();
70 }
71 }
72 }
73
74
DataUrlAddingDialog(DirItem * dir,QWidget * parent)75 K3b::DataUrlAddingDialog::DataUrlAddingDialog( DirItem* dir, QWidget* parent )
76 : QDialog( parent),
77 m_doc( dir->getDoc() ),
78 m_bExistingItemsReplaceAll(false),
79 m_bExistingItemsIgnoreAll(false),
80 m_bFolderLinksFollowAll(false),
81 m_bFolderLinksAddAll(false),
82 m_iAddHiddenFiles(0),
83 m_iAddSystemFiles(0),
84 m_bCanceled(false),
85 m_copyItems(false),
86 m_totalFiles(0),
87 m_filesHandled(0),
88 m_lastProgress(0)
89 {
90 m_encodingConverter = new K3b::EncodingConverter();
91
92 setWindowTitle(i18n("Adding files to project '%1'",m_doc->URL().fileName()));
93 setAttribute( Qt::WA_DeleteOnClose );
94 QGridLayout* grid = new QGridLayout( this );
95 grid->setContentsMargins( 0, 0, 0, 0 );
96
97 m_counterLabel = new QLabel( this );
98 m_infoLabel = new KSqueezedTextLabel( i18n("Adding files to project '%1'..."
99 ,m_doc->URL().fileName()), this );
100 m_progressWidget = new QProgressBar( this );
101
102 QDialogButtonBox* buttonBox = new QDialogButtonBox( QDialogButtonBox::Cancel, this );
103 connect( buttonBox, SIGNAL(rejected()), SLOT(reject()) );
104
105 grid->addWidget( m_counterLabel, 0, 1 );
106 grid->addWidget( m_infoLabel, 0, 0 );
107 grid->addWidget( m_progressWidget, 1, 0, 1, 2 );
108 grid->addWidget( buttonBox, 2, 0, 1, 2 );
109
110 m_dirSizeJob = new K3b::DirSizeJob( this );
111 connect( m_dirSizeJob, SIGNAL(finished(bool)),
112 this, SLOT(slotDirSizeDone(bool)) );
113
114 // try to start with a reasonable size
115 resize( (int)( fontMetrics().width( windowTitle() ) * 1.5 ), sizeHint().height() );
116 }
117
118
~DataUrlAddingDialog()119 K3b::DataUrlAddingDialog::~DataUrlAddingDialog()
120 {
121 // make sure the dir size job is finished
122 m_dirSizeJob->cancel();
123 K3b::SignalWaiter::waitForJob( m_dirSizeJob );
124
125 QString message = resultMessage();
126 if( !message.isEmpty() )
127 KMessageBox::detailedSorry( parentWidget(), i18n("Problems while adding files to the project."), message );
128
129 delete m_encodingConverter;
130 }
131
132
addUrls(const QList<QUrl> & urls,K3b::DirItem * dir,QWidget * parent)133 void K3b::DataUrlAddingDialog::addUrls( const QList<QUrl>& urls,
134 K3b::DirItem* dir,
135 QWidget* parent )
136 {
137 if( !urls.isEmpty() ) {
138 auto *dlg = new DataUrlAddingDialog( urls, dir, parent );
139 dlg->setAttribute(Qt::WA_DeleteOnClose);
140 QMetaObject::invokeMethod( dlg, "slotStartAddUrls", Qt::QueuedConnection );
141 }
142 }
143
144
moveItems(const QList<K3b::DataItem * > & items,K3b::DirItem * dir,QWidget * parent)145 void K3b::DataUrlAddingDialog::moveItems( const QList<K3b::DataItem*>& items,
146 K3b::DirItem* dir,
147 QWidget* parent )
148 {
149 if( !items.isEmpty() ) {
150 auto *dlg = new DataUrlAddingDialog( items, dir, false, parent );
151 dlg->setAttribute(Qt::WA_DeleteOnClose);
152 QMetaObject::invokeMethod( dlg, "slotStartCopyMoveItems", Qt::QueuedConnection );
153 }
154 }
155
156
copyItems(const QList<K3b::DataItem * > & items,K3b::DirItem * dir,QWidget * parent)157 void K3b::DataUrlAddingDialog::copyItems( const QList<K3b::DataItem*>& items,
158 K3b::DirItem* dir,
159 QWidget* parent )
160 {
161 if( !items.isEmpty() ) {
162 auto *dlg = new DataUrlAddingDialog( items, dir, true, parent );
163 dlg->setAttribute(Qt::WA_DeleteOnClose);
164 QMetaObject::invokeMethod( dlg, "slotStartCopyMoveItems", Qt::QueuedConnection );
165 }
166 }
167
168
slotStartAddUrls()169 void K3b::DataUrlAddingDialog::slotStartAddUrls()
170 {
171 //
172 // A common mistake by beginners is to try to burn an iso image
173 // with a data project. Let's warn them
174 //
175 if( m_urls.count() == 1 ) {
176 K3b::Iso9660 isoF( m_urls.first().toLocalFile() );
177 if( isoF.open() ) {
178 if( KMessageBox::warningYesNo( parentWidget(),
179 i18n("<p>The file you are about to add to the project is an ISO 9660 image. As such "
180 "it can be burned to a medium directly since it already contains a file "
181 "system.<br>"
182 "Are you sure you want to add this file to the project?"),
183 i18n("Adding image file to project"),
184 KGuiItem(i18n("Add the file to the project"),"list-add"),
185 KGuiItem(i18n("Burn the image directly"),"tools-media-optical-burn") ) == KMessageBox::No ) {
186 k3bappcore->k3bMainWindow()->slotWriteImage( m_urls.first() );
187 reject();
188 return;
189 }
190 }
191 }
192
193 slotAddUrls();
194 if( !m_urlQueue.isEmpty() ) {
195 m_dirSizeJob->setUrls( m_urls );
196 m_dirSizeJob->setFollowSymlinks( m_doc->isoOptions().followSymbolicLinks() );
197 m_dirSizeJob->start();
198 exec();
199 }
200 }
201
202
slotStartCopyMoveItems()203 void K3b::DataUrlAddingDialog::slotStartCopyMoveItems()
204 {
205 slotCopyMoveItems();
206 if( !m_items.isEmpty() ) {
207 m_progressWidget->setMaximum( m_totalFiles );
208 exec();
209 }
210 }
211
212
slotAddUrls()213 void K3b::DataUrlAddingDialog::slotAddUrls()
214 {
215 if( m_bCanceled )
216 return;
217
218 // add next url
219 QUrl url = m_urlQueue.first().first;
220 K3b::DirItem* dir = m_urlQueue.first().second;
221 m_urlQueue.erase( m_urlQueue.begin() );
222 //
223 // HINT:
224 // we only use QFileInfo::absoluteFilePath() and QFileInfo::isHidden()
225 // both do not cause QFileInfo to stat, thus no speed improvement
226 // can come from removing QFileInfo usage here.
227 //
228 QFileInfo info(url.toLocalFile());
229 QString absoluteFilePath( info.absoluteFilePath() );
230 QString resolved( absoluteFilePath );
231
232 bool valid = true;
233 k3b_struct_stat statBuf, resolvedStatBuf;
234 bool isSymLink = false;
235 bool isDir = false;
236 bool isFile = false;
237
238 ++m_filesHandled;
239
240 m_infoLabel->setText( url.toLocalFile() );
241 if( m_totalFiles == 0 )
242 m_counterLabel->setText( QString("(%1)").arg(m_filesHandled) );
243 else
244 m_counterLabel->setText( QString("(%1/%2)").arg(m_filesHandled).arg(m_totalFiles) );
245
246 //
247 // 1. Check if we want and can add the url
248 //
249
250 if( !url.isLocalFile() ) {
251 valid = false;
252 m_nonLocalFiles.append( url.toLocalFile() );
253 }
254
255 else if( k3b_lstat( QFile::encodeName(absoluteFilePath), &statBuf ) != 0 ) {
256 valid = false;
257 m_notFoundFiles.append( url.toLocalFile() );
258 }
259
260 else if( !m_encodingConverter->encodedLocally( QFile::encodeName( url.toLocalFile() ) ) ) {
261 valid = false;
262 m_invalidFilenameEncodingFiles.append( url.toLocalFile() );
263 }
264
265 else {
266 isSymLink = S_ISLNK(statBuf.st_mode);
267 isFile = S_ISREG(statBuf.st_mode);
268 isDir = S_ISDIR(statBuf.st_mode);
269
270 // symlinks are always readable and can always be added to a project
271 // but we need to know if the symlink points to a directory
272 if( isSymLink ) {
273 resolved = K3b::resolveLink( absoluteFilePath );
274 k3b_stat( QFile::encodeName(resolved), &resolvedStatBuf );
275 isDir = S_ISDIR(resolvedStatBuf.st_mode);
276 }
277
278 else {
279 if( ::access( QFile::encodeName( absoluteFilePath ), R_OK ) != 0 ) {
280 valid = false;
281 m_unreadableFiles.append( url.toLocalFile() );
282 }
283 else if( isFile && (unsigned long long)statBuf.st_size >= 0xFFFFFFFFULL ) {
284 const K3b::ExternalBin *mkisofsBin = k3bcore->externalBinManager()->binObject( "mkisofs" );
285 if ( !mkisofsBin || !mkisofsBin->hasFeature( "no-4gb-limit" ) ) {
286 valid = false;
287 m_tooBigFiles.append( url.toLocalFile() );
288 }
289 }
290 }
291
292 // FIXME: if we do not add hidden dirs the progress gets messed up!
293
294 //
295 // check for hidden and system files
296 //
297 if( valid ) {
298 if( info.isHidden() && !addHiddenFiles() )
299 valid = false;
300 if( S_ISCHR(statBuf.st_mode) ||
301 S_ISBLK(statBuf.st_mode) ||
302 S_ISFIFO(statBuf.st_mode) ||
303 S_ISSOCK(statBuf.st_mode) )
304 if( !addSystemFiles() )
305 valid = false;
306 if( isSymLink )
307 if( S_ISCHR(resolvedStatBuf.st_mode) ||
308 S_ISBLK(resolvedStatBuf.st_mode) ||
309 S_ISFIFO(resolvedStatBuf.st_mode) ||
310 S_ISSOCK(resolvedStatBuf.st_mode) )
311 if( !addSystemFiles() )
312 valid = false;
313 }
314 }
315
316
317 //
318 // 2. Handle the url
319 //
320
321 QString newName = url.fileName();
322
323 // filenames cannot end in backslashes (mkisofs problem. See comments in k3bisoimager.cpp (escapeGraftPoint()))
324 bool bsAtEnd = false;
325 while (!newName.isEmpty() && newName[newName.length() - 1] == '\\') {
326 newName.truncate( newName.length()-1 );
327 bsAtEnd = true;
328 }
329 if( bsAtEnd )
330 m_mkisofsLimitationRenamedFiles.append( url.toLocalFile() + " -> " + newName );
331
332 // backup dummy name
333 if( newName.isEmpty() )
334 newName = '1';
335
336 K3b::DirItem* newDirItem = 0;
337
338 //
339 // The source is valid. Now check if the project already contains a file with that name
340 // and if so handle it properly
341 //
342 if( valid ) {
343 if( K3b::DataItem* oldItem = dir->find( newName ) ) {
344 //
345 // reuse an existing dir
346 //
347 if( oldItem->isDir() && isDir )
348 newDirItem = dynamic_cast<K3b::DirItem*>(oldItem);
349
350 //
351 // we cannot replace files in the old session with dirs and vice versa (I think)
352 // files are handled in K3b::FileItem constructor and dirs handled above
353 //
354 else if( oldItem->isFromOldSession() &&
355 isDir != oldItem->isDir() ) {
356 if( !getNewName( newName, dir, newName ) )
357 valid = false;
358 }
359
360 else if( m_bExistingItemsIgnoreAll )
361 valid = false;
362
363 else if( oldItem->localPath() == resolved ) {
364 //
365 // Just ignore if the same file is added again
366 //
367 valid = false;
368 }
369
370 else if( m_bExistingItemsReplaceAll ) {
371 // if we replace an item from an old session the K3b::FileItem constructor takes care
372 // of replacing the item
373 if( !oldItem->isFromOldSession() )
374 delete oldItem;
375 }
376
377 //
378 // Let the user choose
379 //
380 else {
381 switch( K3b::MultiChoiceDialog::choose( i18n("File already exists"),
382 i18n("<p>File <em>%1</em> already exists in "
383 "project folder <em>%2</em>.",
384 newName,
385 QString( '/' + dir->k3bPath()) ),
386 QMessageBox::Warning,
387 this,
388 6,
389 KGuiItem( i18n("Replace"),
390 QString(),
391 i18n("Replace the existing file") ),
392 KGuiItem( i18n("Replace All"),
393 QString(),
394 i18n("Always replace existing files") ),
395 KGuiItem( i18n("Ignore"),
396 QString(),
397 i18n("Keep the existing file") ),
398 KGuiItem( i18n("Ignore All"),
399 QString(),
400 i18n("Always keep the existing file") ),
401 KGuiItem( i18n("Rename"),
402 QString(),
403 i18n("Rename the new file") ),
404 KStandardGuiItem::cancel() ) ) {
405 case 2: // replace all
406 m_bExistingItemsReplaceAll = true;
407 // fallthrough
408 case 1: // replace
409 // if we replace an item from an old session the K3b::FileItem constructor takes care
410 // of replacing the item
411 if( !oldItem->isFromOldSession() )
412 delete oldItem;
413 break;
414 case 4: // ignore all
415 m_bExistingItemsIgnoreAll = true;
416 // fallthrough
417 case 3: // ignore
418 valid = false;
419 break;
420 case 5: // rename
421 if( !getNewName( newName, dir, newName ) )
422 valid = false;
423 break;
424 case 6: // cancel
425 reject();
426 return;
427 }
428 }
429 }
430 }
431
432
433 //
434 // One more thing to warn the user about: We cannot follow links to folders since that
435 // would change the doc. So we simply ask the user what to do with a link to a folder
436 //
437 if( valid ) {
438 // let's see if this link starts a loop
439 // that means if it points to some folder above this one
440 // if so we cannot follow it anyway
441 if( isDir && isSymLink && !absoluteFilePath.startsWith( resolved ) ) {
442 bool followLink = dir->getDoc()->isoOptions().followSymbolicLinks() || m_bFolderLinksFollowAll;
443 if( !followLink && !m_bFolderLinksAddAll ) {
444 switch( K3b::MultiChoiceDialog::choose( i18n("Adding link to folder"),
445 i18n("<p>'%1' is a symbolic link to folder '%2'."
446 "<p>If you intend to make K3b follow symbolic links you should consider letting K3b do this now "
447 "since K3b will not be able to do so afterwards because symbolic links to folders inside a "
448 "K3b project cannot be resolved."
449 "<p><b>If you do not intend to enable the option <em>follow symbolic links</em> you may safely "
450 "ignore this warning and choose to add the link to the project.</b>",
451 absoluteFilePath,
452 resolved ),
453 QMessageBox::Warning,
454 this,
455 5,
456 KGuiItem(i18n("Follow link now")),
457 KGuiItem(i18n("Always follow links")),
458 KGuiItem(i18n("Add link to project")),
459 KGuiItem(i18n("Always add links")),
460 KStandardGuiItem::cancel()) ) {
461 case 2:
462 m_bFolderLinksFollowAll = true;
463 Q_FALLTHROUGH();
464 case 1:
465 followLink = true;
466 break;
467 case 4:
468 m_bFolderLinksAddAll = true;
469 Q_FALLTHROUGH();
470 case 3:
471 followLink = false;
472 break;
473 case 5:
474 reject();
475 return;
476 }
477 }
478
479 if( followLink ) {
480 absoluteFilePath = resolved;
481 isSymLink = false;
482
483 // count the files in the followed dir
484 if( m_dirSizeJob->active() )
485 m_dirSizeQueue.append( QUrl::fromLocalFile(absoluteFilePath) );
486 else {
487 m_progressWidget->setMaximum( 0 );
488 m_dirSizeJob->setUrls( QList<QUrl>() << QUrl::fromLocalFile(absoluteFilePath) );
489 m_dirSizeJob->start();
490 }
491 }
492 }
493 }
494
495
496 //
497 // Project valid also (we overwrite or renamed)
498 // now create the new item
499 //
500 if( valid ) {
501 //
502 // Set the volume id from the first added url
503 // only if the doc was not changed yet
504 //
505 if( m_urls.count() == 1 &&
506 !dir->getDoc()->isModified() &&
507 !dir->getDoc()->isSaved() ) {
508 dir->getDoc()->setVolumeID( K3b::removeFilenameExtension( newName ) );
509 }
510
511 if( isDir && !isSymLink ) {
512 if( !newDirItem ) { // maybe we reuse an already existing dir
513 newDirItem = new K3b::DirItem( newName );
514 newDirItem->setLocalPath( url.toLocalFile() ); // HACK: see k3bdiritem.h
515 dir->addDataItem( newDirItem );
516 }
517
518 KIO::ListJob *lj = KIO::listDir(QUrl::fromLocalFile(absoluteFilePath), KIO::HideProgressInfo);
519 KIO::UDSEntryList list;
520 connect(lj, &KIO::ListJob::entries, this, [&](KIO::Job *, const KIO::UDSEntryList &l) { list.append(l); });
521 lj->exec();
522 foreach( const KIO::UDSEntry& entry, list ) {
523 const QString fileName = entry.stringValue( KIO::UDSEntry::UDS_NAME );
524 if (fileName != QStringLiteral(".") && fileName != QStringLiteral("..")) {
525 m_urlQueue.append( qMakePair( QUrl::fromLocalFile(absoluteFilePath + '/' + fileName ), newDirItem ) );
526 }
527 }
528 }
529 else {
530 m_newItems[ dir ].append( new K3b::FileItem( &statBuf, &resolvedStatBuf, url.toLocalFile(), *dir->getDoc(), newName ) );
531 }
532 }
533
534 if( m_urlQueue.isEmpty() ) {
535 Q_FOREACH( DirItem* dir, m_newItems.keys() ) {
536 dir->addDataItems( m_newItems[ dir ] );
537 }
538 m_dirSizeJob->cancel();
539 m_progressWidget->setMaximum( 100 );
540 accept();
541 }
542 else {
543 updateProgress();
544 QMetaObject::invokeMethod( this, "slotAddUrls", Qt::QueuedConnection );
545 }
546 }
547
548
slotCopyMoveItems()549 void K3b::DataUrlAddingDialog::slotCopyMoveItems()
550 {
551 if( m_bCanceled )
552 return;
553
554 //
555 // Pop first item from the item list
556 //
557 K3b::DataItem* item = m_items.first().first;
558 K3b::DirItem* dir = m_items.first().second;
559 m_items.erase( m_items.begin() );
560
561 ++m_filesHandled;
562 m_infoLabel->setText( item->k3bPath() );
563 if( m_totalFiles == 0 )
564 m_counterLabel->setText( QString("(%1)").arg(m_filesHandled) );
565 else
566 m_counterLabel->setText( QString("(%1/%2)").arg(m_filesHandled).arg(m_totalFiles) );
567
568
569 if( dir == item->parent() ) {
570 qDebug() << "(K3b::DataUrlAddingDialog) trying to move an item into its own parent dir.";
571 }
572 else if( dir == item ) {
573 qDebug() << "(K3b::DataUrlAddingDialog) trying to move an item into itselft.";
574 }
575 else {
576 //
577 // Let's see if an item with that name already exists
578 //
579 if( K3b::DataItem* oldItem = dir->find( item->k3bName() ) ) {
580 //
581 // reuse an existing dir: move all child items into the old dir
582 //
583 if( oldItem->isDir() && item->isDir() ) {
584 QList<K3b::DataItem*> const& cl = dynamic_cast<K3b::DirItem*>( item )->children();
585 for( QList<K3b::DataItem*>::const_iterator it = cl.constBegin();
586 it != cl.constEnd(); ++it )
587 m_items.append( qMakePair( *it, dynamic_cast<K3b::DirItem*>( oldItem ) ) );
588
589 // FIXME: we need to remove the old dir item
590 }
591
592 //
593 // we cannot replace files in the old session with dirs and vice versa (I think)
594 // files are handled in K3b::FileItem constructor and dirs handled above
595 //
596 else if( oldItem->isFromOldSession() &&
597 item->isDir() != oldItem->isDir() ) {
598 QString newName;
599 if( getNewName( newName, dir, newName ) ) {
600 if( m_copyItems )
601 item = item->copy();
602 item->setK3bName( newName );
603 dir->addDataItem( item );
604 }
605 }
606
607 else if( m_bExistingItemsReplaceAll ) {
608 //
609 // if we replace an item from an old session K3b::DirItem::addDataItem takes care
610 // of replacing the item
611 //
612 if( !oldItem->isFromOldSession() )
613 delete oldItem;
614 if( m_copyItems )
615 item = item->copy();
616 dir->addDataItem( item );
617 }
618
619 else if( !m_bExistingItemsIgnoreAll ) {
620 switch( K3b::MultiChoiceDialog::choose( i18n("File already exists"),
621 i18n("<p>File <em>%1</em> already exists in "
622 "project folder <em>%2</em>.",
623 item->k3bName(),
624 '/' + dir->k3bPath() ),
625 QMessageBox::Warning,
626 this,
627 6,
628 KGuiItem( i18n("Replace"),
629 QString(),
630 i18n("Replace the existing file") ),
631 KGuiItem( i18n("Replace All"),
632 QString(),
633 i18n("Always replace existing files") ),
634 KGuiItem( i18n("Ignore"),
635 QString(),
636 i18n("Keep the existing file") ),
637 KGuiItem( i18n("Ignore All"),
638 QString(),
639 i18n("Always keep the existing file") ),
640 KGuiItem( i18n("Rename"),
641 QString(),
642 i18n("Rename the new file") ),
643 KStandardGuiItem::cancel() ) ) {
644 case 2: // replace all
645 m_bExistingItemsReplaceAll = true;
646 // fallthrough
647 case 1: // replace
648 //
649 // if we replace an item from an old session K3b::DirItem::addDataItem takes care
650 // of replacing the item
651 //
652 if( !oldItem->isFromOldSession() )
653 delete oldItem;
654 if( m_copyItems )
655 item = item->copy();
656 dir->addDataItem( item );
657 break;
658 case 4: // ignore all
659 m_bExistingItemsIgnoreAll = true;
660 // fallthrough
661 case 3: // ignore
662 // do nothing
663 break;
664 case 5: {// rename
665 QString newName;
666 if( getNewName( newName, dir, newName ) ) {
667 if( m_copyItems )
668 item = item->copy();
669 item->setK3bName( newName );
670 dir->addDataItem( item );
671 }
672 break;
673 }
674 case 6: // cancel
675 reject();
676 return;
677 }
678 }
679 }
680
681 //
682 // No old item with the same name
683 //
684 else {
685 if( m_copyItems )
686 item = item->copy();
687 dir->addDataItem( item );
688 }
689 }
690
691 if( m_items.isEmpty() ) {
692 m_dirSizeJob->cancel();
693 accept();
694 }
695 else {
696 updateProgress();
697 QMetaObject::invokeMethod( this, "slotCopyMoveItems", Qt::QueuedConnection );
698 }
699 }
700
701
reject()702 void K3b::DataUrlAddingDialog::reject()
703 {
704 m_bCanceled = true;
705 m_dirSizeJob->cancel();
706 QDialog::reject();
707 }
708
709
slotDirSizeDone(bool success)710 void K3b::DataUrlAddingDialog::slotDirSizeDone( bool success )
711 {
712 if( success ) {
713 m_totalFiles += m_dirSizeJob->totalFiles() + m_dirSizeJob->totalDirs();
714 if( m_dirSizeQueue.isEmpty() ) {
715 m_progressWidget->setValue( 100 );
716 updateProgress();
717 }
718 else {
719 m_dirSizeJob->setUrls( QList<QUrl>() << m_dirSizeQueue.back() );
720 m_dirSizeQueue.pop_back();
721 m_dirSizeJob->start();
722 }
723 }
724 }
725
726
updateProgress()727 void K3b::DataUrlAddingDialog::updateProgress()
728 {
729 if( m_totalFiles > 0 ) {
730 unsigned int p = 100*m_filesHandled/m_totalFiles;
731 if( p > m_lastProgress ) {
732 m_lastProgress = p;
733 m_progressWidget->setValue( p );
734 }
735 }
736 else {
737 // make sure the progress bar shows something
738 m_progressWidget->setValue( m_filesHandled );
739 }
740 }
741
742
getNewName(const QString & oldName,K3b::DirItem * dir,QString & newName)743 bool K3b::DataUrlAddingDialog::getNewName( const QString& oldName, K3b::DirItem* dir, QString& newName )
744 {
745 bool ok = true;
746 newName = oldName;
747 QValidator* validator = K3b::Validators::iso9660Validator( false, this );
748 int pos;
749 do {
750 newName = QInputDialog::getText( this,
751 i18n("Enter New Filename"),
752 i18n("A file with that name already exists. Please enter a new name:"),
753 QLineEdit::Normal,
754 newName, &ok );
755
756 } while( ok && validator->validate( newName, pos ) == QValidator::Acceptable && dir->find( newName ) );
757
758 delete validator;
759
760 return ok;
761 }
762
763
addHiddenFiles()764 bool K3b::DataUrlAddingDialog::addHiddenFiles()
765 {
766 if( m_iAddHiddenFiles == 0 ) {
767 // FIXME: the isVisible() stuff makes the static addUrls method not return (same below)
768 if( KMessageBox::questionYesNo( /*isVisible() ? */this/* : parentWidget()*/,
769 i18n("Do you also want to add hidden files?"),
770 i18n("Hidden Files"), KGuiItem(i18n("Add")), KGuiItem(i18n("Do Not Add")) ) == KMessageBox::Yes )
771 m_iAddHiddenFiles = 1;
772 else
773 m_iAddHiddenFiles = -1;
774 }
775
776 return ( m_iAddHiddenFiles == 1 );
777 }
778
779
addSystemFiles()780 bool K3b::DataUrlAddingDialog::addSystemFiles()
781 {
782 if( m_iAddSystemFiles == 0 ) {
783 if( KMessageBox::questionYesNo( /*isVisible() ? */this/* : parentWidget()*/,
784 i18n("Do you also want to add system files "
785 "(FIFOs, sockets, device files, and broken symlinks)?"),
786 i18n("System Files"), KGuiItem(i18n("Add")), KGuiItem(i18n("Do Not Add")) ) == KMessageBox::Yes )
787 m_iAddSystemFiles = 1;
788 else
789 m_iAddSystemFiles = -1;
790 }
791
792 return ( m_iAddSystemFiles == 1 );
793 }
794
795
resultMessage() const796 QString K3b::DataUrlAddingDialog::resultMessage() const
797 {
798 QString message;
799 if( !m_unreadableFiles.isEmpty() )
800 message += QString("<p><b>%1:</b><br>%2")
801 .arg( i18n("Insufficient permissions to read the following files") )
802 .arg( m_unreadableFiles.join( "<br>" ) );
803 if( !m_notFoundFiles.isEmpty() )
804 message += QString("<p><b>%1:</b><br>%2")
805 .arg( i18n("Unable to find the following files") )
806 .arg( m_notFoundFiles.join( "<br>" ) );
807 if( !m_nonLocalFiles.isEmpty() )
808 message += QString("<p><b>%1:</b><br>%2")
809 .arg( i18n("No non-local files supported") )
810 .arg( m_unreadableFiles.join( "<br>" ) );
811 if( !m_tooBigFiles.isEmpty() )
812 message += QString("<p><b>%1:</b><br>%2")
813 .arg( i18n("To burn files bigger than %1 please use %2",KIO::convertSize(0xFFFFFFFF),
814 QString("mkisofs >= 2.01.01a33 / genisoimage >= 1.1.4") ) )
815 .arg( m_tooBigFiles.join( "<br>" ) );
816 if( !m_mkisofsLimitationRenamedFiles.isEmpty() )
817 message += QString("<p><b>%1:</b><br>%2")
818 .arg( i18n("Some filenames had to be modified due to limitations in mkisofs") )
819 .arg( m_mkisofsLimitationRenamedFiles.join( "<br>" ) );
820 if( !m_invalidFilenameEncodingFiles.isEmpty() )
821 message += QString("<p><b>%1:</b><br>%2")
822 .arg( i18n("The following filenames have an invalid encoding. You may fix this "
823 "with the convmv tool") )
824 .arg( m_invalidFilenameEncodingFiles.join( "<br>" ) );
825
826 return message;
827 }
828
829
830