diff --git a/doc/Changelog.html b/doc/Changelog.html
index 99c10062..ef638e3e 100644
--- a/doc/Changelog.html
+++ b/doc/Changelog.html
@@ -20,6 +20,7 @@ a:visited { color: #0000EE; }
Added support for the QAAC Encoder, requires QuickTime v7.7.1 or newer (see FAQ doc for details)
Added Chinese and Taiwanese translations, thanks to 456Vv <123@456vv.com>
Added experimental support for dcaenc, created by Alexander E. Patrakov <patrakov@gmail.com>
+Added CSV export/import for Meta tags (see context-menu on the "Source Files" tab)
Updated Qt runtime libraries to v4.8.0 (2011-12-15), compiled with MSVC 10.0
Updated LAME encoder to v3.99.2 Final (2011-11-18), compiled with ICL 12.1.7 and MSVC 10.0 (details)
Updated MediaInfo to v0.7.52 (2011-12-19), compiled with ICL 12.1.6 and MSVC 10.0
diff --git a/etc/Translation/Blank.ts b/etc/Translation/Blank.ts
index 404b776e..85a824d3 100644
--- a/etc/Translation/Blank.ts
+++ b/etc/Translation/Blank.ts
@@ -677,15 +677,35 @@
FileListModel
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
LogViewDialog
@@ -750,7 +770,7 @@
-
+
@@ -1412,9 +1432,9 @@
-
-
-
+
+
+
@@ -1455,13 +1475,13 @@
-
+
-
+
@@ -1541,710 +1561,802 @@
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
-
+
-
+
-
-
+
+
-
+
-
-
-
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
-
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
-
-
+
+
+
-
+
-
+
-
+
-
-
-
+
+
+
-
+
-
+
-
+
-
-
+
+
-
-
+
+
-
-
-
+
+
+
-
+
-
+
-
+
-
+
-
-
-
+
+
+
-
+
-
+
-
+
-
-
-
+
+
+
-
+
-
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
-
-
-
+
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/etc/Translation/LameXP_DE.ts b/etc/Translation/LameXP_DE.ts
index 123a0fd5..cee679f0 100644
--- a/etc/Translation/LameXP_DE.ts
+++ b/etc/Translation/LameXP_DE.ts
@@ -536,6 +536,22 @@
Dateipfad
+
+
+ (Systemstandard)
+
+
+
+ ANSI Codepage für CSV Datei auswählen:
+
+
+
+ OK
+
+
+
+ Abbrechen
+
LogViewDialog
@@ -1734,6 +1750,70 @@
+
+
+ Metatags als CSV-Datei exportieren
+
+
+
+ Metatags aus CSV-Datei importieren
+
+
+
+ CSV-Datei speichern
+
+
+
+ CSV Datei
+
+
+
+ CSV Export
+
+
+
+ Keine Metatags gefunden, die exportiert werden können!
+
+
+
+ Die CSV-Datei konnte nicht zum Schreiben geöffnet werden!
+
+
+
+ Beim Schreiben der CSV-Datei ist ein Fehler aufgetreten!
+
+
+
+ Die CSV-Datei wurde erfolgreich erzeugt!
+
+
+
+ CSV-Datei öffnen
+
+
+
+ Die CSV-Datei konnte nicht zum Lesen geöffnet werden!
+
+
+
+ Beim Lesen der CSV-Datei ist ein Fehler aufgetreten!
+
+
+
+ Die CSV-Datei enthält keine unterstützten Datenfelder!
+
+
+
+ Die CSV-Datei ist unvollständig. Nicht alle Dateien wurden aktualisiert!
+
+
+
+ Die CSV-Datei wurde erfolgreich importiert!
+
+
+
+ CSV Import
+
MetaInfo
diff --git a/etc/Translation/LameXP_ES.ts b/etc/Translation/LameXP_ES.ts
index 0a324fcc..d3d1f32b 100644
--- a/etc/Translation/LameXP_ES.ts
+++ b/etc/Translation/LameXP_ES.ts
@@ -535,6 +535,22 @@
Ruta completa
+
+
+
+
+
+
+
+
+
+
+ Aceptar
+
+
+
+ Cancelar
+
LogViewDialog
@@ -1733,6 +1749,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
MetaInfo
diff --git a/etc/Translation/LameXP_FR.ts b/etc/Translation/LameXP_FR.ts
index b0392e5f..11992659 100644
--- a/etc/Translation/LameXP_FR.ts
+++ b/etc/Translation/LameXP_FR.ts
@@ -539,6 +539,22 @@
Chemin complet
+
+
+
+
+
+
+
+
+
+
+ OK
+
+
+
+ Annuler
+
LogViewDialog
@@ -1743,6 +1759,70 @@ Ouvrir le dossier récursivement...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
MetaInfo
diff --git a/etc/Translation/LameXP_IT.ts b/etc/Translation/LameXP_IT.ts
index aad2d054..e46e9e4e 100644
--- a/etc/Translation/LameXP_IT.ts
+++ b/etc/Translation/LameXP_IT.ts
@@ -536,6 +536,22 @@
Percorso
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Cancella
+
LogViewDialog
@@ -1734,6 +1750,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
MetaInfo
diff --git a/etc/Translation/LameXP_KR.ts b/etc/Translation/LameXP_KR.ts
index e850cd51..d7c08254 100644
--- a/etc/Translation/LameXP_KR.ts
+++ b/etc/Translation/LameXP_KR.ts
@@ -535,6 +535,22 @@
전체 경로
+
+
+
+
+
+
+
+
+
+
+ 확인
+
+
+
+ 취소
+
LogViewDialog
@@ -1733,6 +1749,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
MetaInfo
diff --git a/etc/Translation/LameXP_PL.ts b/etc/Translation/LameXP_PL.ts
index 50f33207..022b2a21 100644
--- a/etc/Translation/LameXP_PL.ts
+++ b/etc/Translation/LameXP_PL.ts
@@ -677,15 +677,35 @@
FileListModel
-
+
Tytuł
-
+
Pełna ścieżka
+
+
+
+ (Domyślne systemowe)
+
+
+
+
+
+
+
+
+
+ OK
+
+
+
+
+ Anuluj
+
LogViewDialog
@@ -750,7 +770,7 @@
-
+
Pokaż szczegóły
@@ -1412,9 +1432,9 @@
-
-
-
+
+
+
Sprawdź aktualizacje
@@ -1455,13 +1475,13 @@
-
+
Wyłącz przypominanie o aktualizacji
-
+
Wyłącz efekty dźwiękowe
@@ -1541,710 +1561,802 @@
Hibernuj zamiast zamykać
-
+
Dodawanie plików, prosze czekać...
-
-
+
+
Dostęp zablokowany
-
+
%1 plik(ów) zostało odrzuconych z powodu braku dostępu do pliku!
-
+
To przeważnie oznacza, że plik jest zablokowany przez inny proces.
-
+
Pliki CDDA
-
+
%1 plik(ów) zostało odrzuconych ponieważ to nie są właściwe pliki CDDA!
-
+
LameXP nie może teraz wyekstrachować ścieżki z płyty Audio-CD.
-
+
Zaleca się użycie w tym celu %1.
-
+
Cue Sheet
-
+
%1 plik(ów) zostało odrzuconych, ponieważ to nie są pliki obrazów Cue Sheet!
-
+
Prosze użyć przewodnika Cue Sheet w LameXP w celu importowania tych plików.
-
+
Pliki odrzucone
-
+
%1 plik(ów) zostało odrzuconych, ponieważ ich format nie został rozpoznany!
-
+
To przeważnie oznacza że plik jest uszkodzony lub format pliku nie jest wspierany.
-
+
Skanowanie folderu/ów, prosze czekać...
-
+
Tutaj możesz upuścić pliki dźwiękowe!
-
+
Otwórz plik w zewnętrznym programie
-
+
Otwórz lokalizację pliku
-
+
Otwórz wybrany folder
-
+
Zapamiętaj wybrany folder
-
+
Licencja odrzucona
-
+
Odrzuciłeś licencję. W takim razie program zostanie teraz zamknięty!
-
+
Naraska!
-
+
LameXP - Termin wygasł
-
+
Ta wersja demo (beta) LameXP wygasła %1.
-
+
LameXP jest darmowym oprogramowaniem i pełna wersja nie wygasa.
-
-
+
+
Wyjdź z programu
-
+
Najwyraźniej twoje oprogramowanie antywirusowe spowalnia uruchamianie się LameXP.
-
+
Prosze sprawdzić dokument %1 w celu dalszych szcegółów i rozwiązań!
-
+
Powolny start
-
-
-
-
-
-
+
+
+
+
+
+
Zamknij
-
-
+
+
Nie pokazuj ponownie
-
+
Ważna aktualizacja
-
+
Twoja wersja LameXP jest starsza niż rok! Czas na aktualizację!
-
-
-
+
+
+
Powiadomienie aktualizacji
-
+
Ostatnie sprawdzenie aktualizacji było ponad 14 dni temu. Sprawdzić teraz aktualizacje?
-
+
Nie sprawdzałeś jeszcze aktualizacji LameXP. Sprawdzić teraz aktualizacje?
-
+
Przełóż
-
+
LameXP wykrył że Twoja wersja kodera Nero AAC jest nieaktualna!
-
+
Wersją dostępną obecnie jest %1, Twoja wersja to %2.
-
+
n/d
-
+
Możesz pobrać najnowszą wersję kodera Nero AAC ze strony Nero:
-
+
Koder AAC jest nieaktualny
-
+
Nie można odnaleźć kodera Nero AAC. Kompresja formatu AAC zostanie wyłączona.
-
+
Prosze skopiować 'neroAacEnc.exe', 'neroAacDec.exe', oraz 'neroAacTag.exe' do folderu LameXP!
-
+
Folder aplikacji LameXP znajduje się tutaj:
-
+
Możesz pobrać koder Nero AAC za darmo z oficjalnej strony Nero:
-
+
Wsparcie dla AAC wyłączone
-
-
-
+
+
+
LameXP
-
+
Powinienieś dodać przynajmniej jeden plik do listy aby zacząć działać!
-
+
Nie znaleziono
-
+
Wybrany przez Ciebie folder tymczasowy TEMP już nie istnieje:
-
+
Przywróć domyślne
-
+
Anuluj
-
+
Ostrzeżenie o małej ilości miejsca na dysku
-
+
Jest mniej niż %1 GB dostępnego miejsca w systemowym folderze TEMP.
-
+
WERSJA DEMO
-
+
(Podpowiedź: Zignoruj nazwę pobranego pliku ZIP, w zamian sprawdż załączony w archiwum plik "changelog.txt"!)
-
+
Jest wysoce zalecane zwolnić miejsce na dysku zanim zaczniesz kompresję!
-
+
Twój folder TEMP znajduję się:
-
+
Przerwij proces kompresji
-
+
Wykonaj oczyszczanie dysku
-
-
+
+
Ignoruj
-
+
+
+
+
+
+
+
+
+
+
+
Pomijanie sprawdzania aktualizacji, prosze czekać...
-
+
Mało miejsca na dysku
-
+
Chcesz zacząć działać z małą ilością miejsca na dysku. Mogą wystąpić problemy!
-
+
Został wybrany koder, który nie jest wspierany!
-
+
Nie można zapisać do wybranej lokalizacji.
-
+
Prosze wybrać inną lokalizację!
-
+
Zaladuj plik językowy
-
+
Pliki językowe
-
+
Czy na pewno chcesz wyłączyć przypominanie o aktualizacjach?
-
-
-
-
-
-
-
+
+
+
+
+
+
+
Tak
-
-
-
-
-
-
-
+
+
+
+
+
+
+
Nie
-
+
Przypominanie o aktualizacjach wyłączone.
-
+
Prosze pamiętać o okresowym sprawdzaniu aktualizacji!
-
+
Przypominanie o aktualizacjach ponownie włączone.
-
+
Czy na pewno chcesz wyłączyć wszystkie dźwiękowe?
-
-
+
+
Efekty dźwiękowe
-
+
Wszystkie efekty dźwiękowe zostały wyłączone.
-
+
Efekty dźwiękowe zostały ponownie włączone.
-
-
-
+
+
+
Powiadomienia Nero AAC
-
+
Czy na pewno chcesz wyłączyć wszystkie powiadomienia Nero AAC?
-
+
Wszystkie powiadomienia kodera Nero AAC zostały wyłączone.
-
+
Powiadomienia Nero AAC zostały ponownie włączone.
-
-
-
+
+
+
Powiadomienia o powolnym starcie
-
+
Czy na pewno chcesz wyłączyć powiadomienia o powolnym starcie?
-
+
Powiadomienia o powolnym starcie zostały wyłączone.
-
+
Powiadomienia o powolnym starcie zostały ponownie włączone.
-
-
+
+
Otwórz plik Cue Sheet
-
-
+
+
Plik Cue Sheet
-
-
-
+
+
+
Aktualizacje Beta
-
+
Czy na pewno chcesz aby LameXP sprawdzał aktualizacje Beta?
-
+
Od teraz LameXP będzie sprawdzał aktualizacje Beta.
-
+
Sprawdź teraz
-
+
LameXP od teraz <i>nie będzie</i> sprawdzał aktualizacji Beta.
-
-
-
+
+
+
Hibernuj komputer
-
+
Czy na pewno chcesz aby komputer był hibernowany zamiast zamykany?
-
+
Od teraz LameXP będzie hibernował komputer zamiast zamykać.
-
+
LameXP od teraz <i>nie</i> będzie hibernował komputera tylko zamykał.
-
-
-
+
+
+
Integracja z systemem
-
+
Czy na pewno chcesz wyłączyć integrację LameXP z systemem?
-
+
Integracja LameXP z systemem zostałą wyłączona.
-
+
Integracja LameXP z systemem została ponownie włączona.
-
-
+
+
Dodaj plik(i)
-
-
+
+
Dodaj folder
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Nowy folder
-
+
Wprowadź nazwę nowego folderu:
-
+
Utworzenie folderu zakończone niepowodzeniem
-
+
Nowy folder nie mógł zostać stworzony:
-
+
Dysk tylko do odczytu lub brak praw dostępu!
-
-
-
-
+
+
+
+
Poziom jakości %1
-
-
-
+
+
+
Kompresja %1
-
-
-
+
+
+
Nieskompresowany
-
+
Najlepsza jakość (Bardzo wolno)
-
+
Wysoka jakość (Zalecane)
-
+
Średnia jakość (Domyślnie)
-
+
Niska jakość (Szybko)
-
+
Najniższa jakość (Bardzo szybko)
-
+
Nazwa pliku bez rozszerzenia
-
+
Numer ścieżki z zerem na początku
-
+
Nazwa ścieżki
-
+
Nazwa Artysty
-
+
Nazwa Albumu
-
+
Rok z (przynajmniej) czterema cyframi
-
+
Komentarz
-
+
Zabronione znaki w nazwach plików:
-
+
Zmień nazwy makr
-
+
%1 wątek/ki
-
+
Nie można zapisać do wybranej lokalizacji. Prosze wybierz inną lokalizację!
-
+
Już działa
-
+
LameXP już działa, przejdź do działającego programu!
diff --git a/etc/Translation/LameXP_RU.ts b/etc/Translation/LameXP_RU.ts
index 1a44da5a..e804a09b 100644
--- a/etc/Translation/LameXP_RU.ts
+++ b/etc/Translation/LameXP_RU.ts
@@ -538,6 +538,22 @@
Полный путь
+
+
+
+
+
+
+
+
+
+
+ ОК
+
+
+
+ Отмена
+
LogViewDialog
@@ -1739,6 +1755,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
MetaInfo
diff --git a/etc/Translation/LameXP_TW.ts b/etc/Translation/LameXP_TW.ts
index 8d491415..0784c51c 100644
--- a/etc/Translation/LameXP_TW.ts
+++ b/etc/Translation/LameXP_TW.ts
@@ -535,6 +535,22 @@
完整路徑
+
+
+ (系統默認)
+
+
+
+
+
+
+
+ 確定
+
+
+
+ 取消
+
LogViewDialog
@@ -1733,6 +1749,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
MetaInfo
diff --git a/etc/Translation/LameXP_UK.ts b/etc/Translation/LameXP_UK.ts
index 98ac9d99..abdd2ad1 100644
--- a/etc/Translation/LameXP_UK.ts
+++ b/etc/Translation/LameXP_UK.ts
@@ -536,6 +536,22 @@
Повний шлях
+
+
+ (Типово для ОС)
+
+
+
+
+
+
+
+ Ок
+
+
+
+ Відмінити
+
LogViewDialog
@@ -1734,6 +1750,70 @@
DCA Enc
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
MetaInfo
diff --git a/etc/Translation/LameXP_ZH.ts b/etc/Translation/LameXP_ZH.ts
index bb935486..d9f20530 100644
--- a/etc/Translation/LameXP_ZH.ts
+++ b/etc/Translation/LameXP_ZH.ts
@@ -535,6 +535,22 @@
完整路径
+
+
+ (系统默认)
+
+
+
+
+
+
+
+ 确定
+
+
+
+ 取消
+
LogViewDialog
@@ -1733,6 +1749,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
MetaInfo
diff --git a/res/Icons.qrc b/res/Icons.qrc
index 41db7cf5..1574edd9 100644
--- a/res/Icons.qrc
+++ b/res/Icons.qrc
@@ -59,6 +59,7 @@
icons/folder_image.png
icons/folder_explore.png
icons/folder_page.png
+ icons/folder_table.png
icons/font.png
icons/group.png
icons/house.png
diff --git a/res/localization/LameXP_DE.qm b/res/localization/LameXP_DE.qm
index e82064ed..812fb096 100644
Binary files a/res/localization/LameXP_DE.qm and b/res/localization/LameXP_DE.qm differ
diff --git a/res/localization/LameXP_ES.qm b/res/localization/LameXP_ES.qm
index a49b90cd..1bce12ea 100644
Binary files a/res/localization/LameXP_ES.qm and b/res/localization/LameXP_ES.qm differ
diff --git a/res/localization/LameXP_FR.qm b/res/localization/LameXP_FR.qm
index 7ad42a05..3ceff8c9 100644
Binary files a/res/localization/LameXP_FR.qm and b/res/localization/LameXP_FR.qm differ
diff --git a/res/localization/LameXP_IT.qm b/res/localization/LameXP_IT.qm
index a8491a0a..b9023133 100644
Binary files a/res/localization/LameXP_IT.qm and b/res/localization/LameXP_IT.qm differ
diff --git a/res/localization/LameXP_KR.qm b/res/localization/LameXP_KR.qm
index e98c8642..c696e742 100644
Binary files a/res/localization/LameXP_KR.qm and b/res/localization/LameXP_KR.qm differ
diff --git a/res/localization/LameXP_PL.qm b/res/localization/LameXP_PL.qm
index 9042d0e9..c681e96a 100644
Binary files a/res/localization/LameXP_PL.qm and b/res/localization/LameXP_PL.qm differ
diff --git a/res/localization/LameXP_RU.qm b/res/localization/LameXP_RU.qm
index eb546a72..d774dc53 100644
Binary files a/res/localization/LameXP_RU.qm and b/res/localization/LameXP_RU.qm differ
diff --git a/res/localization/LameXP_TW.qm b/res/localization/LameXP_TW.qm
index 2a00b429..f24e6c7c 100644
Binary files a/res/localization/LameXP_TW.qm and b/res/localization/LameXP_TW.qm differ
diff --git a/res/localization/LameXP_UK.qm b/res/localization/LameXP_UK.qm
index aacac040..69fe2e85 100644
Binary files a/res/localization/LameXP_UK.qm and b/res/localization/LameXP_UK.qm differ
diff --git a/res/localization/LameXP_ZH.qm b/res/localization/LameXP_ZH.qm
index af6bdb6b..ec2a44bb 100644
Binary files a/res/localization/LameXP_ZH.qm and b/res/localization/LameXP_ZH.qm differ
diff --git a/src/Config.h b/src/Config.h
index 2cdd1172..22ffc381 100644
--- a/src/Config.h
+++ b/src/Config.h
@@ -30,7 +30,7 @@
#define VER_LAMEXP_MINOR_LO 4
#define VER_LAMEXP_TYPE Alpha
#define VER_LAMEXP_PATCH 13
-#define VER_LAMEXP_BUILD 867
+#define VER_LAMEXP_BUILD 870
///////////////////////////////////////////////////////////////////////////////
// Tool versions (minimum expected versions!)
diff --git a/src/Dialog_MainWindow.cpp b/src/Dialog_MainWindow.cpp
index de65afd3..9a042bf3 100644
--- a/src/Dialog_MainWindow.cpp
+++ b/src/Dialog_MainWindow.cpp
@@ -127,6 +127,7 @@ MainWindow::MainWindow(FileListModel *fileListModel, AudioFileModel *metaInfo, S
m_findFileContextAction = m_sourceFilesContextMenu->addAction(QIcon(":/icons/folder_go.png"), "N/A");
m_sourceFilesContextMenu->addSeparator();
m_exportCsvContextAction = m_sourceFilesContextMenu->addAction(QIcon(":/icons/table_save.png"), "N/A");
+ m_importCsvContextAction = m_sourceFilesContextMenu->addAction(QIcon(":/icons/folder_table.png"), "N/A");
SET_FONT_BOLD(m_showDetailsContextAction, true);
connect(buttonAddFiles, SIGNAL(clicked()), this, SLOT(addFilesButtonClicked()));
connect(buttonRemoveFile, SIGNAL(clicked()), this, SLOT(removeFileButtonClicked()));
@@ -144,6 +145,8 @@ MainWindow::MainWindow(FileListModel *fileListModel, AudioFileModel *metaInfo, S
connect(m_previewContextAction, SIGNAL(triggered(bool)), this, SLOT(previewContextActionTriggered()));
connect(m_findFileContextAction, SIGNAL(triggered(bool)), this, SLOT(findFileContextActionTriggered()));
connect(m_exportCsvContextAction, SIGNAL(triggered(bool)), this, SLOT(exportCsvContextActionTriggered()));
+ connect(m_importCsvContextAction, SIGNAL(triggered(bool)), this, SLOT(importCsvContextActionTriggered()));
+
//Setup "Output" tab
m_fileSystemModel = new QFileSystemModelEx();
@@ -707,6 +710,7 @@ void MainWindow::changeEvent(QEvent *e)
m_showFolderContextAction->setText(tr("Browse Selected Folder"));
m_addFavoriteFolderAction->setText(tr("Bookmark Current Output Folder"));
m_exportCsvContextAction->setText(tr("Export Meta Tags to CSV File"));
+ m_importCsvContextAction->setText(tr("Import Meta Tags from CSV File"));
//Force GUI update
m_metaInfoModel->clearData();
@@ -2131,6 +2135,9 @@ void MainWindow::handleDelayedFiles(void)
addFiles(selectedFiles);
}
+/*
+ * Export Meta tags to CSV file
+ */
void MainWindow::exportCsvContextActionTriggered(void)
{
TEMP_HIDE_DROPBOX
@@ -2159,18 +2166,76 @@ void MainWindow::exportCsvContextActionTriggered(void)
m_settings->mostRecentInputPath(QFileInfo(selectedCsvFile).canonicalPath());
switch(m_fileListModel->exportToCsv(selectedCsvFile))
{
- case 1:
- QMessageBox::critical(this, tr("CSV Export"), NOBR(tr("There are no meta tags that could be exported!")));
+ case FileListModel::CsvError_NoTags:
+ QMessageBox::critical(this, tr("CSV Export"), NOBR(tr("Sorry, there are no meta tags that can be exported!")));
break;
- case 2:
+ case FileListModel::CsvError_FileOpen:
QMessageBox::critical(this, tr("CSV Export"), NOBR(tr("Sorry, failed to open CSV file for writing!")));
break;
- case 3:
+ case FileListModel::CsvError_FileWrite:
QMessageBox::critical(this, tr("CSV Export"), NOBR(tr("Sorry, failed to write to the CSV file!")));
break;
- default:
+ case FileListModel::CsvError_OK:
QMessageBox::information(this, tr("CSV Export"), NOBR(tr("The CSV files was created successfully!")));
break;
+ default:
+ qWarning("exportToCsv: Unknown return code!");
+ }
+ }
+ )
+}
+
+
+/*
+ * Import Meta tags from CSV file
+ */
+void MainWindow::importCsvContextActionTriggered(void)
+{
+ TEMP_HIDE_DROPBOX
+ (
+ QString selectedCsvFile;
+
+ if(USE_NATIVE_FILE_DIALOG)
+ {
+ selectedCsvFile = QFileDialog::getOpenFileName(this, tr("Open CSV file"), m_settings->mostRecentInputPath(), QString("%1 (*.csv)").arg(tr("CSV File")));
+ }
+ else
+ {
+ QFileDialog dialog(this, tr("Open CSV file"));
+ dialog.setFileMode(QFileDialog::ExistingFile);
+ dialog.setNameFilter(QString("%1 (*.csv)").arg(tr("CSV File")));
+ dialog.setDirectory(m_settings->mostRecentInputPath());
+ if(dialog.exec())
+ {
+ selectedCsvFile = dialog.selectedFiles().first();
+ }
+ }
+
+ if(!selectedCsvFile.isEmpty())
+ {
+ m_settings->mostRecentInputPath(QFileInfo(selectedCsvFile).canonicalPath());
+ switch(m_fileListModel->importFromCsv(this, selectedCsvFile))
+ {
+ case FileListModel::CsvError_FileOpen:
+ QMessageBox::critical(this, tr("CSV Import"), NOBR(tr("Sorry, failed to open CSV file for reading!")));
+ break;
+ case FileListModel::CsvError_FileRead:
+ QMessageBox::critical(this, tr("CSV Import"), NOBR(tr("Sorry, failed to read from the CSV file!")));
+ break;
+ case FileListModel::CsvError_NoTags:
+ QMessageBox::critical(this, tr("CSV Import"), NOBR(tr("Sorry, the CSV file does not contain any known fields!")));
+ break;
+ case FileListModel::CsvError_Incomplete:
+ QMessageBox::warning(this, tr("CSV Import"), NOBR(tr("CSV file is incomplete. Not all files were updated!")));
+ break;
+ case FileListModel::CsvError_OK:
+ QMessageBox::information(this, tr("CSV Import"), NOBR(tr("The CSV files was imported successfully!")));
+ break;
+ case FileListModel::CsvError_Aborted:
+ /* User aborted, ignore! */
+ break;
+ default:
+ qWarning("exportToCsv: Unknown return code!");
}
}
)
diff --git a/src/Dialog_MainWindow.h b/src/Dialog_MainWindow.h
index 08b67228..a983c9f4 100644
--- a/src/Dialog_MainWindow.h
+++ b/src/Dialog_MainWindow.h
@@ -90,6 +90,7 @@ private slots:
void handleDelayedFiles(void);
void hibernateComputerActionTriggered(bool checked);
void importCueSheetActionTriggered(bool checked);
+ void importCsvContextActionTriggered(void);
void initOutputFolderModel(void);
void languageActionActivated(QAction *action);
void languageFromFileActionActivated(bool checked);
@@ -185,6 +186,7 @@ private:
QAction *m_showFolderContextAction;
QAction *m_addFavoriteFolderAction;
QAction *m_exportCsvContextAction;
+ QAction *m_importCsvContextAction;
QActionGroup *m_languageActionGroup;
QActionGroup *m_styleActionGroup;
QActionGroup *m_tabActionGroup;
diff --git a/src/Model_FileList.cpp b/src/Model_FileList.cpp
index 7ca633ff..f96790e8 100644
--- a/src/Model_FileList.cpp
+++ b/src/Model_FileList.cpp
@@ -21,9 +21,17 @@
#include "Model_FileList.h"
+#include "Global.h"
+
#include
#include
#include
+#include
+#include
+#include
+
+#define EXPAND(STR) QString(STR).leftJustified(96, ' ')
+#define CHECK_HDR(STR,NAM) (!(STR).compare((NAM), Qt::CaseInsensitive))
////////////////////////////////////////////////////////////
// Constructor & Destructor
@@ -245,12 +253,16 @@ int FileListModel::exportToCsv(const QString &outFile)
if(!(haveTitle || haveArtist || haveAlbum || haveGenre || haveYear || haveComment))
{
- return 1;
+ return CsvError_NoTags;
}
QFile file(outFile);
- if(file.open(QIODevice::WriteOnly))
+ if(!file.open(QIODevice::WriteOnly))
+ {
+ return CsvError_FileOpen;
+ }
+ else
{
QStringList line;
@@ -265,13 +277,9 @@ int FileListModel::exportToCsv(const QString &outFile)
if(file.write(line.join(";").append("\r\n").toUtf8().prepend("\xef\xbb\xbf")) < 1)
{
file.close();
- return 3;
+ return CsvError_FileWrite;
}
}
- else
- {
- return 2;
- }
for(int i = 0; i < nFiles; i++)
{
@@ -288,10 +296,186 @@ int FileListModel::exportToCsv(const QString &outFile)
if(file.write(line.replaceInStrings(";", ",").join(";").append("\r\n").toUtf8()) < 1)
{
file.close();
- return 3;
+ return CsvError_FileWrite;
}
}
file.close();
- return 0;
+ return CsvError_OK;
+}
+
+int FileListModel::importFromCsv(QWidget *parent, const QString &inFile)
+{
+ QFile file(inFile);
+ if(!file.open(QIODevice::ReadOnly))
+ {
+ return CsvError_FileOpen;
+ }
+
+ QTextCodec *codec = NULL;
+ QByteArray bomCheck = file.peek(16);
+
+ if((!bomCheck.isEmpty()) && bomCheck.startsWith("\xef\xbb\xbf"))
+ {
+ codec = QTextCodec::codecForName("UTF-8");
+ }
+ else if((!bomCheck.isEmpty()) && bomCheck.startsWith("\xff\xfe"))
+ {
+ codec = QTextCodec::codecForName("UTF-16LE");
+ }
+ else if((!bomCheck.isEmpty()) && bomCheck.startsWith("\xfe\xff"))
+ {
+ codec = QTextCodec::codecForName("UTF-16BE");
+ }
+ else
+ {
+ const QString systemDefault = tr("(System Default)");
+
+ QStringList codecList;
+ codecList.append(systemDefault);
+ codecList.append(lamexp_available_codepages());
+
+ QInputDialog *input = new QInputDialog(parent);
+ input->setLabelText(EXPAND(tr("Select ANSI Codepage for CSV file:")));
+ input->setOkButtonText(tr("OK"));
+ input->setCancelButtonText(tr("Cancel"));
+ input->setTextEchoMode(QLineEdit::Normal);
+ input->setComboBoxItems(codecList);
+
+ if(input->exec() < 1)
+ {
+ LAMEXP_DELETE(input);
+ return CsvError_Aborted;
+ }
+
+ if(input->textValue().compare(systemDefault, Qt::CaseInsensitive))
+ {
+ qDebug("User-selected codec is: %s", input->textValue().toLatin1().constData());
+ codec = QTextCodec::codecForName(input->textValue().toLatin1().constData());
+ }
+ else
+ {
+ qDebug("Going to use the system's default codec!");
+ codec = QTextCodec::codecForName("System");
+ }
+
+ LAMEXP_DELETE(input);
+ }
+
+ bomCheck.clear();
+
+ //----------------------//
+
+ QTextStream stream(&file);
+ stream.setAutoDetectUnicode(false);
+ stream.setCodec(codec);
+
+ QStringList header = stream.readLine().simplified().split(";", QString::KeepEmptyParts);
+
+ const int nCols = header.count();
+ const int nFiles = m_fileList.count();
+
+ if(nCols < 1)
+ {
+ return CsvError_FileRead;
+ }
+
+ for(int i = 0; i < nCols; i++)
+ {
+ header[i] = header[i].trimmed();
+ }
+
+ bool *ignore = new bool[nCols];
+ memset(ignore, 0, sizeof(bool) * nCols);
+
+ //----------------------//
+
+ for(int i = 0; i < nFiles; i++)
+ {
+ if(stream.atEnd())
+ {
+ LAMEXP_DELETE_ARRAY(ignore);
+ return CsvError_Incomplete;
+ }
+
+ QStringList line = stream.readLine().split(";", QString::KeepEmptyParts);
+
+ if(line.count() < header.count())
+ {
+ qWarning("Skipping an incomplete line in CSV file!");
+ continue;
+ }
+
+ for(int j = 0; j < nCols; j++)
+ {
+ if(ignore[j])
+ {
+ continue;
+ }
+ else if(CHECK_HDR(header.at(j), "POSITION"))
+ {
+ bool ok = false;
+ unsigned int temp = line.at(j).trimmed().toUInt(&ok);
+ if(ok) m_fileList[i].setFilePosition(temp);
+ }
+ else if(CHECK_HDR(header.at(j), "TITLE"))
+ {
+ QString temp = line.at(j).trimmed();
+ if(!temp.isEmpty()) m_fileList[i].setFileName(temp);
+ }
+ else if(CHECK_HDR(header.at(j), "ARTIST"))
+ {
+ QString temp = line.at(j).trimmed();
+ if(!temp.isEmpty()) m_fileList[i].setFileArtist(temp);
+ }
+ else if(CHECK_HDR(header.at(j), "ALBUM"))
+ {
+ QString temp = line.at(j).trimmed();
+ if(!temp.isEmpty()) m_fileList[i].setFileAlbum(temp);
+ }
+ else if(CHECK_HDR(header.at(j), "GENRE"))
+ {
+ QString temp = line.at(j).trimmed();
+ if(!temp.isEmpty()) m_fileList[i].setFileGenre(temp);
+ }
+ else if(CHECK_HDR(header.at(j), "YEAR"))
+ {
+ bool ok = false;
+ unsigned int temp = line.at(j).trimmed().toUInt(&ok);
+ if(ok) m_fileList[i].setFileYear(temp);
+ }
+ else if(CHECK_HDR(header.at(j), "COMMENT"))
+ {
+ QString temp = line.at(j).trimmed();
+ if(!temp.isEmpty()) m_fileList[i].setFileComment(temp);
+ }
+ else
+ {
+ qWarning("Unkonw field '%s' will be ignored!", header.at(j).toUtf8().constData());
+ ignore[j] = true;
+ }
+ }
+
+ bool noFieldsLeft = true;
+
+ for(int j = 0; j < nCols; j++)
+ {
+ if(!ignore[j])
+ {
+ noFieldsLeft = false;
+ break;
+ }
+ }
+
+ if(noFieldsLeft)
+ {
+ LAMEXP_DELETE_ARRAY(ignore);
+ return CsvError_NoTags;
+ }
+ }
+
+ //----------------------//
+
+ LAMEXP_DELETE_ARRAY(ignore);
+ return CsvError_OK;
}
diff --git a/src/Model_FileList.h b/src/Model_FileList.h
index d7ce173e..3df1890b 100644
--- a/src/Model_FileList.h
+++ b/src/Model_FileList.h
@@ -50,6 +50,20 @@ public:
//CSV export/import
int exportToCsv(const QString &outFile);
+ int importFromCsv(QWidget *parent, const QString &inFile);
+
+ //Public types
+ enum
+ {
+ CsvError_OK = 0,
+ CsvError_NoTags = 1,
+ CsvError_FileOpen = 2,
+ CsvError_FileRead = 3,
+ CsvError_FileWrite = 4,
+ CsvError_Incomplete = 5,
+ CsvError_Aborted = 6
+ }
+ CsvError;
public slots:
void addFile(const QString &filePath);