6
6
* \author Urs Fleisch
9
* Copyright (C) 2003-2007 Urs Fleisch
11
* This file is part of Kid3.
13
* Kid3 is free software; you can redistribute it and/or modify
14
* it under the terms of the GNU General Public License as published by
15
* the Free Software Foundation; either version 2 of the License, or
16
* (at your option) any later version.
18
* Kid3 is distributed in the hope that it will be useful,
19
* but WITHOUT ANY WARRANTY; without even the implied warranty of
20
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
* GNU General Public License for more details.
23
* You should have received a copy of the GNU General Public License
24
* along with this program. If not, see <http://www.gnu.org/licenses/>.
10
27
#include "config.h"
68
* @param parent parent widget
53
FileList::FileList(QWidget* parent, const char* name, Qt::WFlags f) :
54
Q3ListView(parent, name, f), m_currentItemInDir(0), m_process(0)
70
FileList::FileList(QWidget* parent) :
71
#if QT_VERSION >= 0x040000
72
QTreeWidget(parent), m_iterator(0),
76
m_currentItemInDir(0), m_process(0)
78
#if QT_VERSION >= 0x040000
79
setSelectionMode(ExtendedSelection);
80
setSortingEnabled(false);
82
setContextMenuPolicy(Qt::CustomContextMenu);
83
connect(this, SIGNAL(customContextMenuRequested(const QPoint&)),
84
this, SLOT(customContextMenu(const QPoint&)));
85
connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)),
86
this, SLOT(expandItem(QTreeWidgetItem*)));
87
connect(this, SIGNAL(itemCollapsed(QTreeWidgetItem*)),
88
this, SLOT(collapseItem(QTreeWidgetItem*)));
89
connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)),
90
this, SLOT(expandOrCollapseEmptyItem(QTreeWidgetItem*)));
56
92
setSelectionMode(Extended);
60
#if QT_VERSION >= 0x040000
61
connect(this, SIGNAL(contextMenuRequested(Q3ListViewItem*, const QPoint&, int)),
62
this, SLOT(contextMenu(Q3ListViewItem*, const QPoint&)));
64
95
connect(this, SIGNAL(contextMenuRequested(QListViewItem*, const QPoint&, int)),
65
96
this, SLOT(contextMenu(QListViewItem*, const QPoint&)));
81
116
QSize FileList::sizeHint() const
83
return QSize(fontMetrics().maxWidth() * 25, Q3ListView::sizeHint().height());
118
return QSize(fontMetrics().maxWidth() * 25,
119
#if QT_VERSION >= 0x040000
120
QTreeWidget::sizeHint().height()
122
QListView::sizeHint().height()
91
132
* @return next item with file.
93
static FileListItem* getNextItemWithFile(Q3ListViewItemIterator& it)
134
static FileListItem* getNextItemWithFile(
135
#if QT_VERSION >= 0x040000
136
QTreeWidgetItemIterator& it
138
QListViewItemIterator& it
95
Q3ListViewItem* lvItem;
96
while ((lvItem = it.current()) != 0) {
142
#if QT_VERSION >= 0x040000
143
QTreeWidgetItem* lvItem;
145
QListViewItem* lvItem;
147
while ((lvItem = *it) != 0) {
97
148
FileListItem* flItem = dynamic_cast<FileListItem*>(lvItem);
98
149
if (flItem && flItem->getFile()) {
111
162
FileListItem* FileList::first()
113
m_iterator = Q3ListViewItemIterator(this);
164
#if QT_VERSION >= 0x040000
166
if (topLevelItemCount() > 0) {
167
m_iterator = new QTreeWidgetItemIterator(this);
168
return getNextItemWithFile(*m_iterator);
174
m_iterator = QListViewItemIterator(this);
114
175
return getNextItemWithFile(m_iterator);
122
184
FileListItem* FileList::next()
124
if (m_iterator.current()) {
186
#if QT_VERSION >= 0x040000
187
if (m_iterator && **m_iterator) {
189
return getNextItemWithFile(*m_iterator);
126
194
return getNextItemWithFile(m_iterator);
136
205
* @return next item with file.
138
static FileListItem* getNextItemWithFileInDir(Q3ListViewItem* lvItem)
207
static FileListItem* getNextItemWithFileInDir(
208
#if QT_VERSION >= 0x040000
209
QTreeWidgetItem* lvItem
211
QListViewItem* lvItem
141
216
FileListItem* flItem = dynamic_cast<FileListItem*>(lvItem);
142
217
if (flItem && flItem->getFile()) {
220
#if QT_VERSION >= 0x040000
221
QTreeWidgetItem* parent = lvItem->parent();
222
QTreeWidgetItemIterator it(lvItem);
224
if (lvItem && lvItem->parent() != parent) {
145
228
lvItem = lvItem->nextSibling();
155
239
FileListItem* FileList::firstInDir()
157
// try to get the currently selected directory
158
Q3ListViewItemIterator it(this, Q3ListViewItemIterator::Selected);
159
FileListItem* item = dynamic_cast<FileListItem*>(it.current());
241
#if QT_VERSION >= 0x040000
242
// try to get the currently selected directory
243
if (topLevelItemCount() <= 0) {
244
m_currentItemInDir = 0;
247
QTreeWidgetItemIterator it(this, QTreeWidgetItemIterator::Selected);
248
FileListItem* item = dynamic_cast<FileListItem*>(*it);
250
(item->getDirInfo() ||
252
(item = dynamic_cast<FileListItem*>(item->parent())) != 0))) {
254
getNextItemWithFileInDir(item->child(0));
256
// if not found, get the first child of the list view
258
getNextItemWithFileInDir(topLevelItem(0));
261
// if still not found, get the first file in the list view
262
if (!m_currentItemInDir) {
263
it = QTreeWidgetItemIterator(this);
264
m_currentItemInDir = getNextItemWithFile(it);
267
return m_currentItemInDir;
269
// try to get the currently selected directory
270
QListViewItemIterator it(this, QListViewItemIterator::Selected);
271
FileListItem* item = dynamic_cast<FileListItem*>(*it);
161
273
(item->getDirInfo() ||
162
274
(item->getFile() &&
172
284
// if still not found, get the first file in the list view
173
285
if (!m_currentItemInDir) {
174
it = Q3ListViewItemIterator(this);
286
it = QListViewItemIterator(this);
175
287
m_currentItemInDir = getNextItemWithFile(it);
178
290
return m_currentItemInDir;
186
299
FileListItem* FileList::nextInDir()
188
301
if (m_currentItemInDir) {
302
#if QT_VERSION >= 0x040000
303
QTreeWidgetItem* parent = m_currentItemInDir->parent();
304
QTreeWidgetItemIterator it(m_currentItemInDir);
305
QTreeWidgetItem* nextSibling = *(++it);
306
if (nextSibling && nextSibling->parent() != parent) {
309
m_currentItemInDir = getNextItemWithFileInDir(nextSibling);
189
311
m_currentItemInDir =
190
312
getNextItemWithFileInDir(m_currentItemInDir->nextSibling());
192
315
return m_currentItemInDir;
200
323
int FileList::numFilesSelected()
202
325
int numSelected = 0;
203
Q3ListViewItemIterator it(this, Q3ListViewItemIterator::Selected);
326
#if QT_VERSION >= 0x040000
327
if (topLevelItemCount() <= 0) {
330
QTreeWidgetItemIterator it(this, QTreeWidgetItemIterator::Selected);
332
QListViewItemIterator it(this, QListViewItemIterator::Selected);
204
334
FileListItem* item;
205
335
while ((item = getNextItemWithFile(it)) != 0) {
215
345
void FileList::selectNextFile()
217
Q3ListViewItem* item = currentItem();
347
#if QT_VERSION >= 0x040000
348
QTreeWidgetItem* item = currentItem();
349
if (item && (item = *(++QTreeWidgetItemIterator(item))) != 0) {
351
setCurrentItem(item);
352
setItemSelected(item, true);
355
QListViewItem* item = currentItem();
218
356
if (item && (item = item->itemBelow()) != 0) {
219
357
clearSelection();
220
358
setCurrentItem(item);
221
setSelected(item, true);
359
setSelected(item, true);
228
367
void FileList::selectPreviousFile()
230
Q3ListViewItem* item = currentItem();
369
#if QT_VERSION >= 0x040000
370
QTreeWidgetItem* item = currentItem();
371
if (item && (item = *(--QTreeWidgetItemIterator(item))) != 0) {
373
setCurrentItem(item);
374
setItemSelected(item, true);
377
QListViewItem* item = currentItem();
231
378
if (item && (item = item->itemAbove()) != 0) {
232
379
clearSelection();
233
380
setCurrentItem(item);
234
setSelected(item, true);
381
setSelected(item, true);
239
* Create a TaggedFile subclass depending on the file extension.
241
* @param di directory information
244
* @return tagged file, 0 if no type found.
246
TaggedFile* FileList::createTaggedFile(const DirInfo* di, const QString& fn)
248
TaggedFile* taggedFile = 0;
250
if (fn.right(4).lower() == ".ogg")
251
taggedFile = new OggFile(di, fn);
255
if (fn.right(5).lower() == ".flac")
256
taggedFile = new FlacFile(di, fn);
260
if (fn.right(4).lower() == ".mp3"
262
&& Kid3App::s_miscCfg.m_id3v2Version != MiscConfig::ID3v2_4_0
265
taggedFile = new Mp3File(di, fn);
269
taggedFile = new TagLibFile(di, fn);
279
389
* @param dirInfo information about directory
280
390
* @param item parent directory item or 0 if top-level
281
391
* @param listView parent list view if top-level, else 0
392
* @param fileName name of file to select (optional, else empty)
283
394
void FileList::readSubDirectory(DirInfo* dirInfo, FileListItem* item,
395
FileList* listView, const QString& fileName)
286
397
if (!dirInfo) return;
287
398
QString dirname = dirInfo->getDirname();
289
400
FileListItem* last = 0;
290
401
QDir dir(dirname);
291
402
QStringList dirContents = dir.entryList(QDir::Dirs) +
403
#if QT_VERSION >= 0x040000
404
dir.entryList(Kid3App::s_miscCfg.m_nameFilter.split(' '), QDir::Files);
292
406
dir.entryList(Kid3App::s_miscCfg.m_nameFilter, QDir::Files);
293
408
for (QStringList::Iterator it = dirContents.begin();
294
409
it != dirContents.end(); ++it) {
295
410
QString filename = dirname + QDir::separator() + *it;
296
411
if (!QFileInfo(filename).isDir()) {
297
TaggedFile* taggedFile = createTaggedFile(dirInfo, *it);
412
TaggedFile* taggedFile = TaggedFile::createFile(dirInfo, *it);
298
413
if (taggedFile) {
300
415
last = new FileListItem(item, last, taggedFile);
301
416
} else if (listView) {
302
417
last = new FileListItem(listView, last, taggedFile);
419
if (!fileName.isEmpty() && fileName == *it && last && listView) {
420
listView->clearSelection();
421
listView->setCurrentItem(last);
422
#if QT_VERSION >= 0x040000
423
listView->setItemSelected(last, true);
425
listView->setSelected(last, true);
426
listView->ensureItemVisible(last);
323
448
* Fill the filelist with the files found in a directory.
325
* @param name path of directory
450
* @param name path of directory
451
* @param fileName name of file to select (optional, else empty)
326
453
* @return false if name is not directory path, else true.
328
bool FileList::readDir(const QString& name)
455
bool FileList::readDir(const QString& name, const QString& fileName)
330
457
QFileInfo file(name);
331
458
if(file.isDir()) {
333
m_dirInfo.setDirname(file.absFilePath());
334
readSubDirectory(&m_dirInfo, 0, this);
460
m_dirInfo.setDirname(file.QCM_absoluteFilePath());
461
readSubDirectory(&m_dirInfo, 0, this, fileName);
363
494
* @param item list box item
364
495
* @param pos position where context menu is drawn on screen
366
void FileList::contextMenu(Q3ListViewItem* item, const QPoint& pos)
497
void FileList::contextMenu(
498
#if QT_VERSION >= 0x040000
499
QTreeWidgetItem* item,
368
505
if (item && !Kid3App::s_miscCfg.m_contextMenuCommands.empty()) {
369
Q3PopupMenu menu(this);
506
#if QT_VERSION >= 0x040000
508
menu.addAction(i18n("&Rename"), this, SLOT(renameFile()));
509
menu.addAction(i18n("&Delete"), this, SLOT(deleteFile()));
511
for (MiscConfig::MenuCommandList::const_iterator
512
it = Kid3App::s_miscCfg.m_contextMenuCommands.begin();
513
it != Kid3App::s_miscCfg.m_contextMenuCommands.end();
515
menu.addAction((*it).getName());
518
connect(&menu, SIGNAL(triggered(QAction*)), this, SLOT(executeAction(QAction*)));
520
QPopupMenu menu(this);
370
521
menu.insertItem(i18n("&Rename"), this, SLOT(renameFile()));
371
522
menu.insertItem(i18n("&Delete"), this, SLOT(deleteFile()));
373
for (Q3ValueList<MiscConfig::MenuCommand>::const_iterator
524
for (MiscConfig::MenuCommandList::const_iterator
374
525
it = Kid3App::s_miscCfg.m_contextMenuCommands.begin();
375
526
it != Kid3App::s_miscCfg.m_contextMenuCommands.end();
377
528
menu.insertItem((*it).getName(), this, SLOT(executeContextCommand(int)), 0, id);
380
532
menu.setMouseTracking(true);
439
591
} else if (*it == "%uF") {
440
592
// list of URLs or URL
442
url.setProtocol("file");
594
url.QCM_setScheme("file");
443
595
for (QStringList::const_iterator fit = files.begin();
444
596
fit != files.end();
446
url.setFileName(*fit);
447
fmt.push_back(url.toString(
448
#if QT_VERSION < 0x040000
598
url.QCM_setPath(*fit);
599
fmt.push_back(url.toString());
454
602
const int numTagCodes = 3;
459
607
tagStr[1] = tagStr[0];
460
608
tagStr[2] = Kid3App::s_miscCfg.m_browser;
462
int sepPos = tagStr[1].findRev('/');
610
int sepPos = tagStr[1].QCM_lastIndexOf('/');
463
611
if (sepPos < 0) {
464
sepPos = tagStr[1].findRev(QDir::separator());
612
sepPos = tagStr[1].QCM_lastIndexOf(QDir::separator());
466
614
if (sepPos >= 0) {
467
615
tagStr[1].truncate(sepPos);
471
619
QString str = StandardTags::replacePercentCodes(*it, tagCode, tagStr, numTagCodes);
474
if ((ufPos = str.find("%uf")) != -1 && !files.empty()) {
622
if ((ufPos = str.QCM_indexOf("%uf")) != -1 && !files.empty()) {
476
url.setProtocol("file");
477
url.setFileName(files.front());
478
str.replace(ufPos, 3, url.toString(
479
#if QT_VERSION < 0x040000
624
url.QCM_setScheme("file");
625
url.QCM_setPath(files.front());
626
str.replace(ufPos, 3, url.toString());
485
629
if (firstSelectedItem) {
533
677
args.push_back(str);
536
end = cmd.find(' ', begin + 1);
680
end = cmd.QCM_indexOf(' ', begin + 1);
537
681
if (end == -1) end = len;
538
682
args.push_back(cmd.mid(begin, end - begin));
699
* Execute a context menu action.
701
* @param action action of selected menu
703
#if QT_VERSION >= 0x040000
704
void FileList::executeAction(QAction* action)
707
QString name = action->text();
709
for (MiscConfig::MenuCommandList::const_iterator
710
it = Kid3App::s_miscCfg.m_contextMenuCommands.begin();
711
it != Kid3App::s_miscCfg.m_contextMenuCommands.end();
713
if (name == (*it).getName()) {
714
executeContextCommand(id);
722
void FileList::executeAction(QAction*) {}
555
726
* Rename the selected file(s).
557
728
void FileList::renameFile()
564
735
if (item->isInSelection() &&
565
736
(taggedFile = item->getFile()) != 0) {
567
QString newFileName = QInputDialog::getText(
738
QString newFileName = QInputDialog::QCM_getText(
568
740
i18n("Rename File"),
569
741
i18n("Enter new file name:"),
570
QLineEdit::Normal, taggedFile->getFilename(), &ok, this);
742
QLineEdit::Normal, taggedFile->getFilename(), &ok);
571
743
if (ok && !newFileName.isEmpty()) {
572
744
if (taggedFile->isChanged()) {
573
745
taggedFile->setFilename(newFileName);
582
754
item->setFile(0);
583
755
if (QDir().rename(absFilename, newPath)) {
584
756
TaggedFile* newTaggedFile =
585
createTaggedFile(dirInfo, newFileName);
757
TaggedFile::createFile(dirInfo, newFileName);
586
758
if (newTaggedFile) {
587
759
item->setFile(newTaggedFile);
588
760
fileRenamed = true;
591
item->setFile(createTaggedFile(dirInfo, filename));
763
item->setFile(TaggedFile::createFile(dirInfo, filename));
592
764
QMessageBox::warning(
593
765
0, i18n("File Error"),
594
766
i18n("Error while renaming:\n") +
612
784
(dirInfo = item->getDirInfo()) != 0) {
613
785
QFileInfo fi(dirInfo->getDirname());
615
QString newDirName = QInputDialog::getText(
787
QString newDirName = QInputDialog::QCM_getText(
616
789
i18n("Rename Directory"),
617
790
i18n("Enter new directory name:"),
618
QLineEdit::Normal, fi.fileName(), &ok, this);
791
QLineEdit::Normal, fi.fileName(), &ok);
619
792
if (ok && !newDirName.isEmpty()) {
793
#if QT_VERSION >= 0x040000
794
QString newPath = fi.dir().path() + '/' + newDirName;
620
796
QString newPath = fi.dirPath() + '/' + newDirName;
621
798
if (QDir().rename(dirInfo->getDirname(), newPath)) {
622
799
item->setDirName(newPath);
650
827
#ifdef CONFIG_USE_KDE
651
828
if (KMessageBox::warningContinueCancelList(
830
#if KDE_VERSION >= 0x035c00
831
i18np("Do you really want to delete this item?",
832
"Do you really want to delete these %1 items?", numFiles),
834
i18n("Delete Files"),
835
KStandardGuiItem::del(), KStandardGuiItem::cancel(), QString(),
653
837
i18n("Do you really want to delete this item?",
654
838
"Do you really want to delete these %n items?", numFiles),
656
i18n("Delete Files"), KStdGuiItem::del(), QString::null,
840
i18n("Delete Files"),
841
KStdGuiItem::del(), QString::null,
657
843
KMessageBox::Dangerous) == KMessageBox::Continue)
659
845
QString txt = numFiles > 1 ?
686
875
// The file must be closed before deleting on Windows.
687
876
item->setFile(0);
688
877
if (QDir().remove(absFilename)) {
878
#if QT_VERSION >= 0x040000
879
itemsToDelete.append(item);
691
item->setFile(createTaggedFile(dirInfo, filename));
884
item->setFile(TaggedFile::createFile(dirInfo, filename));
692
885
files.push_back(absFilename);
891
#if QT_VERSION >= 0x040000
892
qDeleteAll(itemsToDelete);
893
itemsToDelete.clear();
698
895
if (!files.empty()) {
699
896
#ifdef CONFIG_USE_KDE
700
897
KMessageBox::errorList(
701
0, i18n("Error while deleting this item:",
702
"Error while deleting these %n items:", files.size()),
899
#if KDE_VERSION >= 0x035c00
900
i18np("Error while deleting this item:",
901
"Error while deleting these %1 items:", files.size()),
903
i18n("Error while deleting this item:",
904
"Error while deleting these %n items:", files.size()),
704
907
i18n("File Error"));
727
930
i18n("Do you really want to delete this item?"),
728
931
QStringList(dirInfo->getDirname()),
729
i18n("Delete Files"), KStdGuiItem::del(), QString::null,
932
i18n("Delete Files"),
933
#if KDE_VERSION >= 0x035c00
934
KStandardGuiItem::del(), KStandardGuiItem::cancel(), QString(),
936
KStdGuiItem::del(), QString::null,
730
938
KMessageBox::Dangerous) == KMessageBox::Continue
732
940
#if QT_VERSION >= 0x030100
972
#if QT_VERSION >= 0x040000
978
void FileList::expandItem(QTreeWidgetItem* item)
980
FileListItem* fli = dynamic_cast<FileListItem*>(item);
991
void FileList::collapseItem(QTreeWidgetItem* item)
993
FileListItem* fli = dynamic_cast<FileListItem*>(item);
1000
* Display a custom context menu with operations for selected files.
1002
* @param pos position where context menu is drawn on screen
1004
void FileList::customContextMenu(const QPoint& pos)
1006
contextMenu(currentItem(), mapToGlobal(pos));
1010
* Expand or collapse an item which has no children.
1014
void FileList::expandOrCollapseEmptyItem(QTreeWidgetItem* item)
1016
FileListItem* fli = dynamic_cast<FileListItem*>(item);
1017
if (fli && fli->getDirInfo() && !item->childCount()) {
1018
fli->setOpen(!fli->isOpen());
1022
void FileList::expandItem(QTreeWidgetItem*) {}
1023
void FileList::collapseItem(QTreeWidgetItem*) {}
1024
void FileList::customContextMenu(const QPoint&) {}
1025
void FileList::expandOrCollapseEmptyItem(QTreeWidgetItem*) {}