diff --git a/data/images/tag.png b/data/images/tag.png
new file mode 100644
index 000000000..0190a2de9
Binary files /dev/null and b/data/images/tag.png differ
diff --git a/data/images/tag.svg b/data/images/tag.svg
new file mode 100644
index 000000000..86e5e2ca4
--- /dev/null
+++ b/data/images/tag.svg
@@ -0,0 +1,597 @@
+
+
+
diff --git a/data/images/track-saved.png b/data/images/track-saved.png
new file mode 100644
index 000000000..9f3e2be32
Binary files /dev/null and b/data/images/track-saved.png differ
diff --git a/data/picard.qrc b/data/picard.qrc
index 69fb898bf..eee8672ac 100644
--- a/data/picard.qrc
+++ b/data/picard.qrc
@@ -27,5 +27,7 @@
images/match-90.png
images/match-100.png
images/magic-wand.png
+ images/track-saved.png
+ images/tag.png
\ No newline at end of file
diff --git a/picard/album.py b/picard/album.py
index d3c9a8a64..a29f8c178 100644
--- a/picard/album.py
+++ b/picard/album.py
@@ -121,10 +121,10 @@ class Album(DataObject):
def addLinkedFile(self, track, file):
index = self.tracks.index(track)
self.files.append(file)
- self.emit(QtCore.SIGNAL("trackUpdated"), track)
+ self.emit(QtCore.SIGNAL("track_updated"), track)
def removeLinkedFile(self, track, file):
- self.emit(QtCore.SIGNAL("trackUpdated"), track)
+ self.emit(QtCore.SIGNAL("track_updated"), track)
def getNumUnmatchedFiles(self):
return len(self.unmatched_files)
@@ -174,3 +174,7 @@ class Album(DataObject):
"""Return if this object can be removed."""
return True
+ def can_edit_tags(self):
+ """Return if this object supports tag editing."""
+ return False
+
diff --git a/picard/cluster.py b/picard/cluster.py
index b014e692a..427cbecca 100644
--- a/picard/cluster.py
+++ b/picard/cluster.py
@@ -58,3 +58,7 @@ class Cluster(QtCore.QObject):
"""Return if this object can be removed."""
return True
+ def can_edit_tags(self):
+ """Return if this object supports tag editing."""
+ return False
+
diff --git a/picard/file.py b/picard/file.py
index 89417d221..5706c086e 100644
--- a/picard/file.py
+++ b/picard/file.py
@@ -24,93 +24,77 @@ from PyQt4 import QtCore
from picard.metadata import Metadata
from picard.parsefilename import parseFileName
from picard.similarity import similarity
+from picard.util import LockableObject
-class File(QtCore.QObject):
+class File(LockableObject):
- _id_counter = 1
+ NEW = 0
+ CHANGED = 1
+ TO_BE_SAVED = 2
+ SAVED = 3
def __init__(self, filename):
- QtCore.QObject.__init__(self)
- self._id = File._id_counter
- File._id_counter += 1
- self.mutex = QtCore.QMutex(QtCore.QMutex.Recursive)
+ LockableObject.__init__(self)
+ self.id = self.new_id()
self.filename = filename
self.base_filename = os.path.basename(filename)
self.cluster = None
self.track = None
+ self.state = File.NEW
self.orig_metadata = Metadata()
self.metadata = Metadata()
def __str__(self):
return ('' % (self.id, self.base_filename)).encode("UTF-8")
- def lock(self):
- self.mutex.lock()
-
- def unlock(self):
- self.mutex.unlock()
+ __id_counter = 1
- def getId(self):
- return self._id
-
- id = property(getId)
+ @classmethod
+ def new_id(cls):
+ cls.__id_counter += 1
+ return cls.__id_counter
def save(self):
"""Save the file."""
- locker = QtCore.QMutexLocker(self.mutex)
- try:
- self._save()
- if self.config.setting["rename_files"]:
- format = self.config.setting["file_naming_format"]
- filename = self.tagger.evaluate_script(format, self.metadata)
- filename = os.path.basename(filename) + os.path.splitext(self.filename)[1]
- filename = os.path.join(os.path.dirname(self.filename), filename)
- os.rename(self.filename, filename)
- self.filename = filename
- except Exception, e:
- raise
- else:
- self.orig_metadata.copy(self.metadata)
- self.metadata.changed = False
-
- def _save(self):
- """Save metadata to the file."""
raise NotImplementedError
def remove_from_cluster(self):
- locker = QtCore.QMutexLocker(self.mutex)
if self.cluster is not None:
self.log.debug("%s being removed from %s", self, self.cluster)
self.cluster.remove_file(self)
self.cluster = None
def remove_from_track(self):
- locker = QtCore.QMutexLocker(self.mutex)
if self.track is not None:
self.log.debug("%s being removed from %s", self, self.track)
self.track.remove_file(self)
self.track = None
def move_to_cluster(self, cluster):
- locker = QtCore.QMutexLocker(self.mutex)
if cluster != self.cluster:
self.remove_from_cluster()
self.remove_from_track()
self.log.debug("%s being moved to %s", self, cluster)
+ self.state = self.CHANGED
self.cluster = cluster
self.cluster.add_file(self)
def move_to_track(self, track):
- locker = QtCore.QMutexLocker(self.mutex)
if track != self.track:
self.remove_from_cluster()
self.remove_from_track()
self.log.debug("%s being moved to %s", self, track)
+ self.state = self.CHANGED
+ if self.orig_metadata["musicbrainz_trackid"] and \
+ self.orig_metadata["musicbrainz_trackid"] == track.id:
+ self.state = self.SAVED
+ print self.metadata["musicbrainz_trackid"]
+ print track.id
+ print "state", self.state
self.track = track
self.track.add_file(self)
def get_similarity(self, metadata=None):
- locker = QtCore.QMutexLocker(self.mutex)
if not metadata:
metadata = self.metadata
return self.orig_metadata.compare(metadata)
@@ -123,3 +107,7 @@ class File(QtCore.QObject):
"""Return if this object can be removed."""
return True
+ def can_edit_tags(self):
+ """Return if this object supports tag editing."""
+ return True
+
diff --git a/picard/plugins/picardmutagen/mp3.py b/picard/plugins/picardmutagen/mp3.py
index 7f9f9c1e7..de22f8d04 100644
--- a/picard/plugins/picardmutagen/mp3.py
+++ b/picard/plugins/picardmutagen/mp3.py
@@ -39,7 +39,7 @@ class MutagenMP3File(File):
self.metadata.copy(self.orig_metadata)
- def _save(self):
+ def save(self):
"""Save ID3 tags to the file."""
tags = CompatID3(encode_filename(self.filename), translate=False)
@@ -52,9 +52,9 @@ class MutagenMP3File(File):
else:
v1 = 0
- if self.config.setting["id3v2_encoding"] == "utf-8":
+ if self.config.setting["id3v2_encoding"].lower() == "utf-8":
encoding = 3
- elif self.config.setting["id3v2_encoding"] == "utf-16":
+ elif self.config.setting["id3v2_encoding"].lower() == "utf-16":
encoding = 1
else:
encoding = 0
diff --git a/picard/resources.py b/picard/resources.py
index cf9706685..5ca4b18b9 100644
--- a/picard/resources.py
+++ b/picard/resources.py
@@ -1,6 +1,6 @@
# Resource object code
#
-# Created: ?t 14. IX 13:09:50 2006
+# Created: so 16. IX 15:26:50 2006
# by: The Resource Compiler for PyQt (Qt v4.1.3)
#
# WARNING! All changes made in this file will be lost!
@@ -1900,6 +1900,18 @@ qt_resource_data = "\
\x6a\xa1\x46\x96\xc1\x7c\xee\x56\x87\xab\x23\xd3\xcb\xdd\x81\xcf\
\x99\xc8\x57\x93\xb7\xb6\xbf\x05\x18\x00\xb3\x4c\x68\x8a\xf9\xd4\
\xd4\xe6\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
+\x00\x00\x00\x9c\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x10\x00\x00\x00\x10\x08\x03\x00\x00\x00\x28\x2d\x0f\x53\
+\x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xaf\xc8\x37\x05\x8a\xe9\
+\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\
+\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\
+\x79\x71\xc9\x65\x3c\x00\x00\x00\x06\x50\x4c\x54\x45\xff\xff\xff\
+\x00\x00\x00\x55\xc2\xd3\x7e\x00\x00\x00\x01\x74\x52\x4e\x53\x00\
+\x40\xe6\xd8\x66\x00\x00\x00\x0f\x49\x44\x41\x54\x78\xda\x62\x60\
+\x18\x05\xc8\x00\x20\xc0\x00\x01\x10\x00\x01\x3b\x42\x42\x4b\x00\
+\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
\x00\x00\x01\x08\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
@@ -2433,23 +2445,131 @@ qt_resource_data = "\
\xd1\xa4\xf0\xe7\x6f\x64\xe7\x61\x16\x79\xf3\xdf\xc4\x6f\x97\x71\
\x39\x10\x00\x75\x1a\x77\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\
\x60\x82\
-\x00\x00\x00\xe6\
+\x00\x00\x04\x2a\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x16\x00\x00\x00\x16\x08\x06\x00\x00\x00\xc4\xb4\x6c\x3b\
+\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
+\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\
+\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x2e\x6f\x72\
+\x67\x9b\xee\x3c\x1a\x00\x00\x03\xbc\x49\x44\x41\x54\x38\x8d\xb5\
+\xd5\x5f\x6c\x53\x55\x1c\x07\xf0\xef\xb9\xff\xda\x6e\xeb\x9f\xb9\
+\x75\x1d\x43\xb3\x08\x73\x41\xb2\xbf\xea\x36\x8d\xf2\x82\x89\xcc\
+\x08\x0f\x83\xae\x69\x70\xf2\xe0\x50\x94\x04\x12\x40\xcd\x66\x42\
+\x02\x6a\xdc\x83\x0f\x26\xc4\x07\xcd\x02\x1a\x30\xa6\xe9\x9a\x18\
+\x36\xf7\x00\x4a\x7c\x80\x28\x5d\x35\x5a\xc2\xd6\x06\xcd\x9a\xe9\
+\xd6\x6d\x4c\x60\x32\xb6\xde\xde\x9e\x73\x7f\x3e\xf4\x0e\x06\x91\
+\x39\x35\xfb\xe5\x9e\x7b\x4e\x72\xef\xfd\xfc\x6e\x7e\xe7\x9c\x7b\
+\x19\x11\x61\x35\x42\x5a\x15\x15\x80\xb2\x38\xa8\xab\xdb\x54\xcc\
+\x34\x63\x07\x40\x05\x24\x49\x73\x12\x89\xe1\x9f\x63\xb1\xa1\xff\
+\x0a\x33\x22\x42\x43\x73\x73\x23\x11\xfb\x1a\xc0\xb7\x60\x2c\x0d\
+\x90\x5b\x96\xa4\x76\x97\xb3\x68\xa1\xf2\x41\xdf\x34\x11\x18\x91\
+\x99\xca\x66\x8d\x03\xe1\x50\xe4\x97\x15\xc3\x75\x4d\x2d\xfd\x8c\
+\xe8\x72\xfc\x87\xa1\xb7\x01\x20\x10\xf4\x6f\x7f\xa0\xa4\xe4\xe4\
+\xe8\x58\xba\x70\xdb\xd6\x56\x6a\xac\xaf\x31\xc7\x27\x26\xc4\xe0\
+\xc0\x20\x17\x42\xf4\x72\xce\x0f\x87\x43\x91\xb9\x65\xe1\xba\x27\
+\x9a\xed\x00\xa6\x00\xac\x8f\xc7\xa2\xd7\x02\x41\xbf\x4d\x55\xd5\
+\xb1\xdd\xaf\x74\xfa\x86\x13\x57\x68\x64\x24\x89\xbd\xaf\xbf\xcc\
+\x55\x45\x23\x06\x86\x6f\xce\x9d\xcb\x5d\x38\x7f\xc1\xe0\x9c\xbf\
+\x01\xe0\xd3\x70\x28\xf2\xb7\xb3\x2f\x01\x78\x06\x40\x22\x1e\x8b\
+\x5e\x03\x00\x59\x96\xdf\xac\xae\xae\x76\x96\x95\x95\x61\x6c\x34\
+\xc5\x7e\x9f\x98\x40\x46\xcf\xd0\xcd\xb9\x59\x31\xb7\x70\x93\xb7\
+\xb6\x6e\x91\xbb\xbb\xbb\x0a\x1e\xdd\xb8\xe1\x43\x55\x55\x2e\x07\
+\x82\xfe\xa7\xee\x07\x4f\x03\x28\xb6\x4a\x50\xce\x18\xeb\xde\xba\
+\xed\x85\x82\x91\x44\x02\x43\x3f\xfe\x04\x15\x8c\xcd\x4c\x5f\x05\
+\x81\x60\x18\x59\x73\x72\x7a\xdc\x10\xc4\x8d\x3d\xaf\xee\xd1\xf6\
+\xed\xdb\xbf\xce\xeb\xf5\x9e\xdd\xd9\x11\xec\x0b\x04\xfd\x6b\xef\
+\x85\x13\x00\x72\xf5\x4d\x2d\xef\x00\x38\x58\x5b\x57\x2b\x7b\x3c\
+\x6e\xf8\xca\xbc\x50\x65\x42\x71\xb1\x93\x5c\x1e\x97\x49\xd6\x82\
+\x27\x02\xfd\x39\x37\xcb\xaf\x8c\x26\xe7\x5d\x9e\x22\xe3\xe8\x91\
+\xa3\xea\xae\x5d\x2f\x3d\xef\x70\x38\x92\xc1\x9d\x81\xc3\x81\xa0\
+\xdf\x76\x7b\xf2\xea\x9b\x5a\x7c\x00\x22\x9a\xaa\x6c\xac\x5a\xf7\
+\x90\xa7\xab\xeb\x2d\x49\xb3\x69\xc8\x66\x33\x42\x08\x21\x4c\x22\
+\xd3\x34\xf3\x43\x61\x0a\x61\x9a\xc2\x14\x22\xdf\x2b\xb2\x8a\x87\
+\x2b\xab\x0a\x9d\x45\x6e\xed\xf4\xe9\x2f\xf5\x33\x67\xce\xde\x32\
+\x4d\xb3\x56\x02\x80\x78\x2c\x3a\x1d\x8f\x45\x37\x19\x39\x5e\x9b\
+\xc9\xe8\x9f\xf5\xf5\x45\x16\x1c\x76\x07\x29\xb2\x4a\x60\x0c\x20\
+\x22\x22\x02\x81\xac\xe3\xce\xdb\xeb\x86\x2e\x86\x93\xf1\x1b\x99\
+\xcc\xbc\x51\x55\x55\xa5\x48\x92\x34\x1c\x0e\x45\xae\xdf\xb5\xf3\
+\xe2\xb1\x68\x5a\x53\x95\xd7\x52\xa3\xa9\xf4\x40\xff\x57\xba\x66\
+\x73\x30\x57\x91\x9b\x31\x49\xba\x83\x02\xf9\x93\x95\x0a\x44\xe4\
+\x74\xba\x54\x67\x91\xdb\x76\xfc\xf8\x09\x70\xce\xf7\x2e\xd6\xf8\
+\xae\x08\x87\x22\xb9\x5c\x2e\xf7\xf8\xc5\x8b\xdf\xf7\xbe\xff\xee\
+\x7b\x7a\x32\x99\x24\x5f\x69\xb9\xe2\x76\x16\xcb\x00\xb3\xf0\x3c\
+\x0b\x0b\xdf\xb0\xbe\xc6\x3d\x30\xd8\x9f\xcd\x66\xb3\x9f\x84\x43\
+\x91\x5f\x6f\xd7\xf8\x7e\x11\x08\xfa\x1f\xd1\x34\xed\xe3\xd2\xd2\
+\x92\xa6\x17\x3b\x3a\xec\x15\x15\x15\xe6\xd5\x3f\x26\xb3\x37\x66\
+\xaf\x1b\xa6\x55\x65\x6f\xa9\x4f\x2b\xf7\xae\x75\x1e\x3a\x74\x70\
+\x5e\x08\x51\xb9\xb8\x71\x96\x85\x97\x24\xd8\xa2\xaa\x6a\x6f\x43\
+\x63\x83\xa7\xdd\xdf\x6e\x53\x14\x59\x8c\x8d\xa7\x6e\xcd\x67\xe6\
+\xf9\x93\x8f\x3d\x5d\x72\xec\xa3\x63\xfa\xa5\xf8\xa5\x03\xe1\x50\
+\xe4\xc4\xe2\x33\x2b\x82\x2d\x5c\x91\x24\x69\xbf\x2c\xcb\x47\xda\
+\xda\xda\xa4\xcd\x9b\x9f\x55\x85\xe0\xe6\xd4\xd4\x14\x7a\x7a\x7a\
+\x46\x39\xe7\x35\x4b\x77\xe1\x8a\xe1\x25\x09\xbc\x8a\xa2\x7c\x50\
+\x58\x58\xd8\xde\xb9\xbb\x53\x3a\x75\xf2\x94\x98\x99\x99\x79\x2e\
+\x1c\x8a\x7c\xb7\xf4\xbe\x7f\x0d\x2f\x49\xd0\xa0\xa8\x4a\x2f\x08\
+\xbf\x7d\xf1\x79\x68\xc7\xbd\xd7\x97\x85\x19\x63\x0c\x80\xdd\x6a\
+\x36\x00\x1a\xf2\xdf\x70\x06\x80\x00\xf0\x35\x15\x6b\xe4\xc9\xf4\
+\xe4\x2c\x00\x1d\x80\xbe\xb8\xc6\xff\x09\x96\x2d\xd0\x0e\xc0\x01\
+\xa0\xc0\xea\x25\x00\x06\x80\x8c\xd5\x74\xab\x37\x56\x04\xff\x9f\
+\x58\xb5\x7f\xde\x5f\xf5\x72\xf5\x72\x11\x6f\xdf\x2b\x00\x00\x00\
+\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
+\x00\x00\x00\xf4\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
\x00\x00\x0d\x00\x00\x00\x0d\x08\x03\x00\x00\x00\x45\x35\x14\x4e\
\x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xaf\xc8\x37\x05\x8a\xe9\
\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\
\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\
-\x79\x71\xc9\x65\x3c\x00\x00\x00\x0c\x50\x4c\x54\x45\x19\x7b\x30\
-\x00\x5e\x20\x39\xb5\x4a\x74\x74\x74\xde\x81\xcf\x1f\x00\x00\x00\
-\x04\x74\x52\x4e\x53\xff\xff\xff\x00\x40\x2a\xa9\xf4\x00\x00\x00\
-\x50\x49\x44\x41\x54\x78\xda\x62\x60\x46\x06\x00\x01\xc4\x00\xc4\
-\x8c\x10\x00\x64\x01\x04\x10\x98\xc7\x00\x02\x20\x1e\x40\x00\x81\
-\x79\x4c\x20\x00\xe2\x01\x04\x10\x2a\x0f\x20\x80\x50\x79\x00\x01\
-\x84\xca\x03\x08\x20\x54\x1e\x40\x00\xa1\xf2\x00\x02\x08\x95\x07\
-\x10\x40\xa8\x3c\x80\x00\x42\xe5\x01\x04\x10\xaa\xcb\x00\x02\x0c\
-\x00\x88\x79\x01\x7c\x1c\x2f\xc7\x96\x00\x00\x00\x00\x49\x45\x4e\
-\x44\xae\x42\x60\x82\
+\x79\x71\xc9\x65\x3c\x00\x00\x00\x09\x50\x4c\x54\x45\x00\x58\x26\
+\x00\xa6\x51\xff\xff\xff\x25\xa5\x29\xf6\x00\x00\x00\x03\x74\x52\
+\x4e\x53\xff\xff\x00\xd7\xca\x0d\x41\x00\x00\x00\x62\x49\x44\x41\
+\x54\x78\xda\x62\x60\x42\x06\x00\x01\xc4\x80\x60\x31\x30\x30\x01\
+\x04\x10\x9c\xc7\xc0\xc8\xc8\xc8\x00\x10\x40\x0c\xc8\x1c\x26\x80\
+\x00\x62\x40\xe6\x30\x01\x04\x10\x03\x58\x2f\x94\xc3\x04\x10\x40\
+\x20\x01\x46\x18\x87\x09\x20\x80\x20\xe2\x50\x0e\x13\x40\x00\x31\
+\xc0\xf5\x80\x00\x40\x00\x81\x55\xc3\x38\x4c\x00\x01\xc4\x80\xe2\
+\x04\x80\x00\x62\x40\x71\x19\x40\x00\xa1\xf2\x00\x02\x0c\x00\x5d\
+\x64\x01\x0e\xd9\x8f\x83\x25\x00\x00\x00\x00\x49\x45\x4e\x44\xae\
+\x42\x60\x82\
+\x00\x00\x02\x32\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x10\x00\x00\x00\x10\x08\x03\x00\x00\x00\x28\x2d\x0f\x53\
+\x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xaf\xc8\x37\x05\x8a\xe9\
+\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\
+\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\
+\x79\x71\xc9\x65\x3c\x00\x00\x00\xcc\x50\x4c\x54\x45\xc6\xcc\x9b\
+\xea\xed\xcd\x9c\xde\x5c\x7b\xd3\x27\x75\xd1\x1c\xc3\xc8\x9a\xe1\
+\xe6\xb8\x78\xd2\x21\xd9\xe0\xa4\xda\xe0\xa5\xbd\xc0\xa4\xc9\xcb\
+\xbb\x80\xd4\x2f\xc7\xcb\xac\x85\xd6\x38\x76\xd2\x1d\x86\xd6\x38\
+\x8b\xd8\x42\xdc\xe2\xab\x93\xdb\x4e\xac\xe4\x75\x7a\xd3\x25\xe8\
+\xe8\xe5\xd9\xdb\xc8\xd0\xd7\x9e\xa2\xe0\x66\xf8\xf8\xf6\xbd\xc2\
+\x9a\x8a\xd8\x3f\xde\xe0\xd1\x9e\xdf\x60\xd4\xdb\xa0\x75\xd1\x1b\
+\x82\xd5\x32\x87\xd7\x3b\xa4\xe1\x6a\xe1\xe6\xb6\x74\xd1\x18\x8e\
+\xd9\x46\xdc\xe2\xa9\xa5\xe1\x6b\x7d\xd3\x2a\xc9\xcb\xb4\x94\xdb\
+\x4f\x91\xda\x4a\xc2\xc7\x9a\xd0\xd2\xc1\x7e\xd4\x2c\x84\xd5\x35\
+\xe5\xe9\xc0\xe0\xe5\xb4\x96\xdc\x52\x77\xd2\x1f\xdc\xde\xcd\x74\
+\xd1\x1a\xe3\xe8\xbc\xa8\xe2\x70\xb0\xe5\x7b\xce\xd2\xb5\x8c\xd8\
+\x43\x99\xdd\x57\x79\xd2\x23\xf8\xf9\xf6\xbe\xc2\x9b\xe4\xe8\xbe\
+\x47\x8a\x05\xbb\xc0\x97\xff\xff\xff\x49\x96\x4a\x76\x00\x00\x00\
+\x44\x74\x52\x4e\x53\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
+\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
+\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
+\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
+\xff\xff\xff\xff\xff\xff\xff\xff\x00\x71\xd2\x8f\x12\x00\x00\x00\
+\x9c\x49\x44\x41\x54\x78\xda\x5c\xcf\xd7\x0e\x83\x30\x0c\x05\x50\
+\xb3\xa1\x85\xee\xbd\xe8\xde\x7b\x6f\x62\xf3\xff\xff\xd4\x40\x8a\
+\x0a\xbd\x0f\xb1\x74\x24\x5f\x39\xe0\xff\x05\xc2\xb7\x4a\x51\xee\
+\x02\x88\x61\x18\xc6\x48\x80\xc7\xf0\x56\xba\x0e\x12\x30\xad\x77\
+\x64\xa4\x18\xc8\xcf\x43\x11\x9b\x31\x58\x2c\xc7\x79\xf4\x22\xe0\
+\xa5\x8f\x56\x3f\xf3\x5b\xe1\x90\xdb\xf6\xec\x44\x87\xbd\x99\x29\
+\x62\xa5\xd1\x9e\x13\xe9\xa8\x94\x5f\x06\x92\xce\x61\xbd\x9a\x5c\
+\x24\x0b\x8d\x63\x56\x45\xc9\x02\xf0\x69\xb8\xdf\x99\x1a\xaa\xdd\
+\xf3\x08\x4d\xed\xc4\xa1\xe6\x16\xc0\x11\xa7\x83\x53\xe1\xa5\x29\
+\xa2\x74\xd0\x1c\xcc\xf7\xf7\xb7\xb1\x7c\x04\x18\x00\x81\x3b\x35\
+\xe0\x3f\xf2\x0f\x99\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\
+\x82\
\x00\x00\x02\xd1\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
@@ -2553,6 +2673,10 @@ qt_resource_name = "\
\x0d\xba\x66\x47\
\x00\x50\
\x00\x69\x00\x63\x00\x61\x00\x72\x00\x64\x00\x33\x00\x32\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x09\
+\x07\xbc\x8f\xc7\
+\x00\x65\
+\x00\x6d\x00\x70\x00\x74\x00\x79\x00\x2e\x00\x70\x00\x6e\x00\x67\
\x00\x0c\
\x0e\xe2\x04\x27\
\x00\x6d\
@@ -2615,6 +2739,14 @@ qt_resource_name = "\
\x00\x54\
\x00\x6f\x00\x6f\x00\x6c\x00\x62\x00\x61\x00\x72\x00\x41\x00\x64\x00\x64\x00\x44\x00\x69\x00\x72\x00\x2e\x00\x70\x00\x6e\x00\x67\
\
+\x00\x07\
+\x0a\x7a\x57\xa7\
+\x00\x74\
+\x00\x61\x00\x67\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x0f\
+\x03\x8a\x2a\x47\
+\x00\x74\
+\x00\x72\x00\x61\x00\x63\x00\x6b\x00\x2d\x00\x73\x00\x61\x00\x76\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\
\x00\x0d\
\x03\x26\x60\x87\
\x00\x6d\
@@ -2627,33 +2759,36 @@ qt_resource_name = "\
qt_resource_struct = "\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
-\x00\x00\x00\x00\x00\x02\x00\x00\x00\x1b\x00\x00\x00\x02\
-\x00\x00\x02\x1c\x00\x00\x00\x00\x00\x01\x00\x00\x7e\xb2\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x1e\x00\x00\x00\x02\
+\x00\x00\x02\x34\x00\x00\x00\x00\x00\x01\x00\x00\x7f\x52\
\x00\x00\x00\x36\x00\x00\x00\x00\x00\x01\x00\x00\x04\x82\
\x00\x00\x00\xf2\x00\x00\x00\x00\x00\x01\x00\x00\x64\x1a\
-\x00\x00\x03\x4e\x00\x00\x00\x00\x00\x01\x00\x00\x93\xf5\
+\x00\x00\x03\x9e\x00\x00\x00\x00\x00\x01\x00\x00\x99\xbb\
+\x00\x00\x03\x7a\x00\x00\x00\x00\x00\x01\x00\x00\x98\xc3\
\x00\x00\x01\x1a\x00\x00\x00\x00\x00\x01\x00\x00\x69\x8f\
-\x00\x00\x01\xf2\x00\x00\x00\x00\x00\x01\x00\x00\x7a\x2f\
-\x00\x00\x02\x32\x00\x00\x00\x00\x00\x01\x00\x00\x81\x17\
+\x00\x00\x02\x0a\x00\x00\x00\x00\x00\x01\x00\x00\x7a\xcf\
+\x00\x00\x02\x4a\x00\x00\x00\x00\x00\x01\x00\x00\x81\xb7\
\x00\x00\x00\x12\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x00\xc6\x00\x00\x00\x00\x00\x01\x00\x00\x5b\x7c\
-\x00\x00\x02\x78\x00\x00\x00\x00\x00\x01\x00\x00\x88\x3a\
-\x00\x00\x01\xa0\x00\x00\x00\x00\x00\x01\x00\x00\x75\xad\
-\x00\x00\x03\x6e\x00\x00\x00\x00\x00\x01\x00\x00\x94\xdf\
-\x00\x00\x02\xa0\x00\x00\x00\x00\x00\x01\x00\x00\x89\x62\
+\x00\x00\x02\x90\x00\x00\x00\x00\x00\x01\x00\x00\x88\xda\
+\x00\x00\x01\xb8\x00\x00\x00\x00\x00\x01\x00\x00\x76\x4d\
+\x00\x00\x03\xbe\x00\x00\x00\x00\x00\x01\x00\x00\x9b\xf1\
+\x00\x00\x02\xb8\x00\x00\x00\x00\x00\x01\x00\x00\x8a\x02\
+\x00\x00\x01\x82\x00\x00\x00\x00\x00\x01\x00\x00\x74\xa1\
\x00\x00\x00\xd8\x00\x00\x00\x00\x00\x01\x00\x00\x5f\x23\
\x00\x00\x00\x9c\x00\x00\x00\x00\x00\x01\x00\x00\x0e\x87\
+\x00\x00\x03\x66\x00\x00\x00\x00\x00\x01\x00\x00\x94\x95\
\x00\x00\x00\x74\x00\x00\x00\x00\x00\x01\x00\x00\x0a\xb1\
-\x00\x00\x02\xca\x00\x00\x00\x00\x00\x01\x00\x00\x8a\xc1\
-\x00\x00\x03\x26\x00\x00\x00\x00\x00\x01\x00\x00\x90\xce\
+\x00\x00\x02\xe2\x00\x00\x00\x00\x00\x01\x00\x00\x8b\x61\
+\x00\x00\x03\x3e\x00\x00\x00\x00\x00\x01\x00\x00\x91\x6e\
\x00\x00\x00\x52\x00\x00\x00\x00\x00\x01\x00\x00\x07\x59\
-\x00\x00\x02\xde\x00\x00\x00\x00\x00\x01\x00\x00\x8c\xb7\
-\x00\x00\x01\xd4\x00\x00\x00\x00\x00\x01\x00\x00\x78\x46\
+\x00\x00\x02\xf6\x00\x00\x00\x00\x00\x01\x00\x00\x8d\x57\
+\x00\x00\x01\xec\x00\x00\x00\x00\x00\x01\x00\x00\x78\xe6\
\x00\x00\x01\x64\x00\x00\x00\x00\x00\x01\x00\x00\x6e\x6e\
-\x00\x00\x03\x08\x00\x00\x00\x00\x00\x01\x00\x00\x8f\xcd\
-\x00\x00\x02\x5a\x00\x00\x00\x00\x00\x01\x00\x00\x87\x36\
-\x00\x00\x01\xb6\x00\x00\x00\x00\x00\x01\x00\x00\x77\x54\
-\x00\x00\x01\x82\x00\x00\x00\x00\x00\x01\x00\x00\x74\xa1\
+\x00\x00\x03\x20\x00\x00\x00\x00\x00\x01\x00\x00\x90\x6d\
+\x00\x00\x02\x72\x00\x00\x00\x00\x00\x01\x00\x00\x87\xd6\
+\x00\x00\x01\xce\x00\x00\x00\x00\x00\x01\x00\x00\x77\xf4\
+\x00\x00\x01\x9a\x00\x00\x00\x00\x00\x01\x00\x00\x75\x41\
\x00\x00\x01\x46\x00\x00\x00\x00\x00\x01\x00\x00\x6d\x7c\
"
diff --git a/picard/tagger.py b/picard/tagger.py
index 2cf3c50cb..877ed22e6 100644
--- a/picard/tagger.py
+++ b/picard/tagger.py
@@ -74,7 +74,10 @@ class Tagger(QtGui.QApplication, ComponentManager, Component):
self.worker = WorkerThread()
self.connect(self.worker, QtCore.SIGNAL("add_files(const QStringList &)"), self.onAddFiles)
self.connect(self.worker, QtCore.SIGNAL("file_updated(int)"), QtCore.SIGNAL("file_updated(int)"))
-
+ self.connect(self.worker,
+ QtCore.SIGNAL("save_file_finished(PyObject*, bool)"),
+ self.save_file_finished)
+
self.browserIntegration = BrowserIntegration()
self.files = {}
@@ -208,8 +211,32 @@ class Tagger(QtGui.QApplication, ComponentManager, Component):
def save(self, objects):
"""Save the specified objects."""
for file in self.get_files_from_objects(objects):
- self.worker.save_file(file)
+ self.save_file(file)
+ def save_file(self, file):
+ """Save the file."""
+ file.lock_for_write()
+ try:
+ file.state = File.TO_BE_SAVED
+ finally:
+ file.unlock()
+ self.worker.save_file(file)
+
+ def save_file_finished(self, file, saved):
+ """Finalize file saving and notify views."""
+ if saved:
+ file.lock_for_write()
+ try:
+ file.orig_metadata.copy(file.metadata)
+ file.metadata.changed = False
+ file.state = File.SAVED
+ finally:
+ file.unlock()
+ if file.track:
+ self.emit(QtCore.SIGNAL("track_updated"), file.track)
+ else:
+ self.emit(QtCore.SIGNAL("file_updated"), file)
+
def remove(self, objects):
"""Remove the specified objects."""
files = []
@@ -232,7 +259,7 @@ class Tagger(QtGui.QApplication, ComponentManager, Component):
def load_album(self, album_id):
album = Album(unicode(album_id), "[loading album information]", None)
self.albums.append(album)
- self.connect(album, QtCore.SIGNAL("trackUpdated"), self, QtCore.SIGNAL("trackUpdated"))
+ self.connect(album, QtCore.SIGNAL("track_updated"), self, QtCore.SIGNAL("track_updated"))
self.emit(QtCore.SIGNAL("albumAdded"), album)
self.worker.load_album(album)
diff --git a/picard/track.py b/picard/track.py
index c328a89e4..d355c9930 100644
--- a/picard/track.py
+++ b/picard/track.py
@@ -73,3 +73,7 @@ class Track(DataObject):
return self.linked_file.can_remove()
return False
+ def can_edit_tags(self):
+ """Return if this object supports tag editing."""
+ return False
+
diff --git a/picard/ui/itemviews.py b/picard/ui/itemviews.py
index 786206df0..f0068710c 100644
--- a/picard/ui/itemviews.py
+++ b/picard/ui/itemviews.py
@@ -24,7 +24,6 @@ from picard.cluster import Cluster
from picard.file import File
from picard.track import Track
from picard.util import format_time, encode_filename, decode_filename
-from picard.ui.tageditor import TagEditor
from picard.config import TextOption
__all__ = ["FileTreeView", "AlbumTreeView"]
@@ -58,6 +57,18 @@ class BaseTreeView(QtGui.QTreeWidget):
self.setDropIndicatorShown(True)
self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
+ self.lookupAct = QtGui.QAction(QtGui.QIcon(":/images/search.png"), _("&Lookup"), self)
+
+ #self.analyze_action = QtGui.QAction(QtGui.QIcon(":/images/analyze.png"), _("&Analyze"), self)
+
+ self.contextMenu = QtGui.QMenu(self)
+ self.contextMenu.addAction(self.main_window.edit_tags_action)
+ self.contextMenu.addSeparator()
+ self.contextMenu.addAction(self.lookupAct)
+ self.contextMenu.addAction(self.main_window.analyze_action)
+ self.contextMenu.addAction(self.main_window.save_action)
+ self.contextMenu.addAction(self.main_window.remove_action)
+
self.objectToItem = {}
self.itemToObject = {}
@@ -124,13 +135,13 @@ class BaseTreeView(QtGui.QTreeWidget):
for item in items:
obj = self.getObjectFromItem(item)
if isinstance(obj, Album):
- album_ids.append(str(obj.getId()))
+ album_ids.append(str(obj.id))
elif isinstance(obj, Track):
print "track:", obj
if obj.is_linked():
file_ids.append(str(obj.linked_file.id))
elif isinstance(obj, File):
- file_ids.append(str(obj.getId()))
+ file_ids.append(str(obj.id))
mimeData = QtCore.QMimeData()
mimeData.setData("application/picard.album-list", "\n".join(album_ids))
mimeData.setData("application/picard.file-list", "\n".join(file_ids))
@@ -218,25 +229,7 @@ class FileTreeView(BaseTreeView):
def __init__(self, main_window, parent):
BaseTreeView.__init__(self, main_window, parent)
- # Create the context menu
-
- self.editTagsAct = QtGui.QAction(_("Edit &Tags..."), self)
- self.connect(self.editTagsAct, QtCore.SIGNAL("triggered()"), self.editTags)
-
- self.lookupAct = QtGui.QAction(QtGui.QIcon(":/images/search.png"), _("&Lookup"), self)
-
- self.analyzeAct = QtGui.QAction(QtGui.QIcon(":/images/analyze.png"), _("&Analyze"), self)
-
- self.contextMenu = QtGui.QMenu(self)
- self.contextMenu.addAction(self.editTagsAct)
- self.contextMenu.addSeparator()
- self.contextMenu.addAction(self.lookupAct)
- self.contextMenu.addAction(self.analyzeAct)
- self.contextMenu.addAction(self.main_window.save_action)
- self.contextMenu.addAction(self.main_window.remove_action)
-
# Prepare some common icons
-
self.dirIcon = QtGui.QIcon(":/images/dir.png")
self.fileIcon = QtGui.QIcon(":/images/file.png")
@@ -280,9 +273,9 @@ class FileTreeView(BaseTreeView):
#canAnalyze = True
canRemove = True
- self.editTagsAct.setEnabled(canEditTags)
- self.lookupAct.setEnabled(canLookup)
- self.analyzeAct.setEnabled(canAnalyze)
+ #self.editTagsAct.setEnabled(canEditTags)
+ #self.lookupAct.setEnabled(canLookup)
+ #self.analyze_action.setEnabled(canAnalyze)
#self.remove_action.setEnabled(canRemove)
self.contextMenu.popup(event.globalPos())
@@ -327,19 +320,10 @@ class FileTreeView(BaseTreeView):
self.unregisterObject(file)
self.updateCluster(cluster)
- def openTagEditor(self, obj):
- tagEditor = TagEditor(obj.metadata, self)
- tagEditor.exec_()
- self.emit(QtCore.SIGNAL("selectionChanged"), [obj])
-
- def editTags(self):
- objects = self.selected_objects()
- self.openTagEditor(objects[0])
-
def handleDoubleClick(self, index):
obj = self.itemToObject[self.itemFromIndex(index)]
- if isinstance(obj, File):
- self.openTagEditor(obj)
+ if obj.can_edit_tags():
+ self.main_window.edit_tags(obj)
class AlbumTreeView(BaseTreeView):
@@ -356,26 +340,35 @@ class AlbumTreeView(BaseTreeView):
QtGui.QIcon(":/images/match-90.png"),
QtGui.QIcon(":/images/match-100.png"),
]
+ self.icon_saved = QtGui.QIcon(":/images/track-saved.png")
self.connect(self.tagger, QtCore.SIGNAL("albumAdded"), self.addAlbum)
self.connect(self.tagger, QtCore.SIGNAL("albumRemoved"), self.remove_album)
- self.connect(self.tagger, QtCore.SIGNAL("trackUpdated"), self.updateTrack)
self.connect(self.tagger.worker, QtCore.SIGNAL("albumLoaded(QString)"),
self.updateAlbum)
+ self.connect(self.tagger, QtCore.SIGNAL("track_updated"),
+ self.update_track)
- def updateTrack(self, track):
+ def update_track(self, track):
# Update track background
item = self.getItemFromObject(track)
if track.is_linked():
- similarity = track.linked_file.get_similarity()
- item.setIcon(0, self.matchIcons[int(similarity * 5 + 0.5)])
+ file = track.linked_file
+ if file.state == File.SAVED:
+ similarity = 1.0
+ icon = self.icon_saved
+ else:
+ similarity = track.linked_file.get_similarity()
+ icon = self.matchIcons[int(similarity * 5 + 0.5)]
else:
similarity = 1
- item.setIcon(0, self.noteIcon)
+ icon = self.noteIcon
+
color = matchColor(similarity)
for i in range(3):
item.setBackgroundColor(i, color)
-
+ item.setIcon(0, icon)
+
# Update track name
albumItem = self.getItemFromObject(track.album)
albumItem.setText(0, track.album.getName())
diff --git a/picard/ui/mainwindow.py b/picard/ui/mainwindow.py
index 272f3f369..23d7ef574 100644
--- a/picard/ui/mainwindow.py
+++ b/picard/ui/mainwindow.py
@@ -31,6 +31,7 @@ from picard.ui.coverartbox import CoverArtBox
from picard.ui.metadatabox import MetadataBox
from picard.ui.itemviews import FileTreeView, AlbumTreeView
from picard.ui.options import OptionsDialog, OptionsDialogProvider
+from picard.ui.tageditor import TagEditor
class MainWindow(QtGui.QMainWindow):
@@ -92,7 +93,7 @@ class MainWindow(QtGui.QMainWindow):
self.connect(self.serverMetadataBox, QtCore.SIGNAL("file_updated(int)"), self, QtCore.SIGNAL("file_updated(int)"))
self.coverArtBox = CoverArtBox(self)
- if not self.showCoverArtAct.isChecked():
+ if not self.show_cover_art_action.isChecked():
self.coverArtBox.hide()
bottomLayout = QtGui.QHBoxLayout()
@@ -124,7 +125,7 @@ class MainWindow(QtGui.QMainWindow):
self.config.persist["window_position"] = self.pos()
self.config.persist["window_size"] = self.size()
self.config.persist["window_maximized"] = isMaximized
- self.config.persist["view_cover_art"] = self.showCoverArtAct.isChecked()
+ self.config.persist["view_cover_art"] = self.show_cover_art_action.isChecked()
self.fileTreeView.saveState()
self.albumTreeView.saveState()
@@ -139,40 +140,40 @@ class MainWindow(QtGui.QMainWindow):
self.statusBar().showMessage(_("Ready"))
def createActions(self):
- self.optionsAct = QtGui.QAction(QtGui.QIcon(":/images/ToolbarOptions.png"), "&Options...", self)
+ self.options_action = QtGui.QAction(QtGui.QIcon(":/images/ToolbarOptions.png"), "&Options...", self)
#self.openSettingsAct.setShortcut("Ctrl+O")
- self.connect(self.optionsAct, QtCore.SIGNAL("triggered()"), self.showOptions)
+ self.connect(self.options_action, QtCore.SIGNAL("triggered()"), self.showOptions)
- self.helpAct = QtGui.QAction(_("&Help..."), self)
+ self.help_action = QtGui.QAction(_("&Help..."), self)
# TR: Keyboard shortcut for "Help"
- self.helpAct.setShortcut(QtGui.QKeySequence(_("Ctrl+H")))
- #self.connect(self.helpAct, QtCore.SIGNAL("triggered()"), self.showHelp)
+ self.help_action.setShortcut(QtGui.QKeySequence(_("Ctrl+H")))
+ #self.connect(self.help_action, QtCore.SIGNAL("triggered()"), self.showHelp)
- self.aboutAct = QtGui.QAction(_("&About..."), self)
- #self.connect(self.aboutAct, QtCore.SIGNAL("triggered()"), self.showAbout)
+ self.about_action = QtGui.QAction(_("&About..."), self)
+ #self.connect(self.about_action, QtCore.SIGNAL("triggered()"), self.showAbout)
- self.add_filesAct = QtGui.QAction(QtGui.QIcon(":/images/ToolbarAddFiles.png"), _("&Add Files..."), self)
- self.add_filesAct.setStatusTip(_("Add files to the tagger"))
+ self.add_files_action = QtGui.QAction(QtGui.QIcon(":/images/ToolbarAddFiles.png"), _("&Add Files..."), self)
+ self.add_files_action.setStatusTip(_("Add files to the tagger"))
# TR: Keyboard shortcut for "Add Files..."
- self.add_filesAct.setShortcut(QtGui.QKeySequence(_("Ctrl+O")))
- self.connect(self.add_filesAct, QtCore.SIGNAL("triggered()"), self.add_files)
+ self.add_files_action.setShortcut(QtGui.QKeySequence(_("Ctrl+O")))
+ self.connect(self.add_files_action, QtCore.SIGNAL("triggered()"), self.add_files)
- self.addDirectoryAct = QtGui.QAction(QtGui.QIcon(":/images/ToolbarAddDir.png"), _("A&dd Directory..."), self)
- self.addDirectoryAct.setStatusTip(_("Add a directory to the tagger"))
+ self.add_directory_action = QtGui.QAction(QtGui.QIcon(":/images/ToolbarAddDir.png"), _("A&dd Directory..."), self)
+ self.add_directory_action.setStatusTip(_("Add a directory to the tagger"))
# TR: Keyboard shortcut for "Add Directory..."
- self.addDirectoryAct.setShortcut(QtGui.QKeySequence(_("Ctrl+D")))
- self.connect(self.addDirectoryAct, QtCore.SIGNAL("triggered()"), self.addDirectory)
+ self.add_directory_action.setShortcut(QtGui.QKeySequence(_("Ctrl+D")))
+ self.connect(self.add_directory_action, QtCore.SIGNAL("triggered()"), self.addDirectory)
self.save_action = QtGui.QAction(QtGui.QIcon(":/images/ToolbarSave.png"), _("&Save"), self)
- self.addDirectoryAct.setStatusTip(_("Save selected files"))
+ self.add_directory_action.setStatusTip(_("Save selected files"))
# TR: Keyboard shortcut for "Save"
self.save_action.setShortcut(QtGui.QKeySequence(_("Ctrl+S")))
self.save_action.setEnabled(False)
self.connect(self.save_action, QtCore.SIGNAL("triggered()"), self.save)
- self.submitAct = QtGui.QAction(QtGui.QIcon(":/images/ToolbarSubmit.png"), _("S&ubmit PUIDs to MusicBrainz"), self)
- self.submitAct.setEnabled(False)
- self.connect(self.submitAct, QtCore.SIGNAL("triggered()"), self.submit)
+ self.submit_action = QtGui.QAction(QtGui.QIcon(":/images/ToolbarSubmit.png"), _("S&ubmit PUIDs to MusicBrainz"), self)
+ self.submit_action.setEnabled(False)
+ self.connect(self.submit_action, QtCore.SIGNAL("triggered()"), self.submit)
self.exitAct = QtGui.QAction(_("E&xit"), self)
self.exitAct.setShortcut(QtGui.QKeySequence(_("Ctrl+Q")))
@@ -183,84 +184,86 @@ class MainWindow(QtGui.QMainWindow):
self.remove_action.setEnabled(False)
self.connect(self.remove_action, QtCore.SIGNAL("triggered()"), self.remove)
- self.showFileBrowserAct = QtGui.QAction(_("File &Browser"), self)
- self.showFileBrowserAct.setCheckable(True)
+ self.show_file_browser_action = QtGui.QAction(_("File &Browser"), self)
+ self.show_file_browser_action.setCheckable(True)
#if self.config.persi.value("persist/viewFileBrowser").toBool():
- # self.showFileBrowserAct.setChecked(True)
- # self.connect(self.showFileBrowserAct, QtCore.SIGNAL("triggered()"), self.showFileBrowser)
+ # self.show_file_browser_action.setChecked(True)
+ # self.connect(self.show_file_browser_action, QtCore.SIGNAL("triggered()"), self.showFileBrowser)
- self.showCoverArtAct = QtGui.QAction(_("&Cover Art"), self)
- self.showCoverArtAct.setCheckable(True)
+ self.show_cover_art_action = QtGui.QAction(_("&Cover Art"), self)
+ self.show_cover_art_action.setCheckable(True)
if self.config.persist["view_cover_art"]:
- self.showCoverArtAct.setChecked(True)
- self.connect(self.showCoverArtAct, QtCore.SIGNAL("triggered()"), self.showCoverArt)
+ self.show_cover_art_action.setChecked(True)
+ self.connect(self.show_cover_art_action, QtCore.SIGNAL("triggered()"), self.showCoverArt)
- self.searchAct = QtGui.QAction(QtGui.QIcon(":/images/search.png"), _("Search"), self)
- self.connect(self.searchAct, QtCore.SIGNAL("triggered()"), self.search)
+ self.search_action = QtGui.QAction(QtGui.QIcon(":/images/search.png"), _("Search"), self)
+ self.connect(self.search_action, QtCore.SIGNAL("triggered()"), self.search)
- self.listenAct = QtGui.QAction(QtGui.QIcon(":/images/ToolbarListen.png"), _("Listen"), self)
- self.listenAct.setEnabled(False)
- self.connect(self.listenAct, QtCore.SIGNAL("triggered()"), self.listen)
-
- self.cdLookupAct = QtGui.QAction(QtGui.QIcon(":/images/ToolbarLookup.png"), _("&Lookup CD"), self)
- self.cdLookupAct.setEnabled(False)
- self.cdLookupAct.setShortcut(QtGui.QKeySequence(_("Ctrl+L")))
+ self.cd_lookup_action = QtGui.QAction(QtGui.QIcon(":/images/ToolbarLookup.png"), _("&Lookup CD"), self)
+ self.cd_lookup_action.setEnabled(False)
+ self.cd_lookup_action.setShortcut(QtGui.QKeySequence(_("Ctrl+L")))
- self.analyzeAct = QtGui.QAction(QtGui.QIcon(":/images/analyze.png"), _("Anal&yze"), self)
- self.analyzeAct.setEnabled(False)
- self.analyzeAct.setShortcut(QtGui.QKeySequence(_("Ctrl+Y")))
+ self.analyze_action = QtGui.QAction(QtGui.QIcon(":/images/analyze.png"), _("Anal&yze"), self)
+ self.analyze_action.setEnabled(False)
+ self.analyze_action.setShortcut(QtGui.QKeySequence(_("Ctrl+Y")))
- self.clusterAct = QtGui.QAction(QtGui.QIcon(":/images/ToolbarCluster.png"), _("Cluster"), self)
- self.clusterAct.setEnabled(False)
- self.clusterAct.setShortcut(QtGui.QKeySequence(_("Ctrl+U")))
+ self.cluster_action = QtGui.QAction(QtGui.QIcon(":/images/ToolbarCluster.png"), _("Cluster"), self)
+ self.cluster_action.setEnabled(False)
+ self.cluster_action.setShortcut(QtGui.QKeySequence(_("Ctrl+U")))
- self.autoTagAct = QtGui.QAction(QtGui.QIcon(":/images/magic-wand.png"), _("Auto Tag"), self)
- self.autoTagAct.setShortcut(QtGui.QKeySequence(_("Ctrl+T")))
- self.connect(self.autoTagAct, QtCore.SIGNAL("triggered()"), self.autoTag)
+ self.auto_tag_action = QtGui.QAction(QtGui.QIcon(":/images/magic-wand.png"), _("Auto Tag"), self)
+ self.auto_tag_action.setShortcut(QtGui.QKeySequence(_("Ctrl+T")))
+ self.connect(self.auto_tag_action, QtCore.SIGNAL("triggered()"), self.autoTag)
+
+ self.edit_tags_action = QtGui.QAction(QtGui.QIcon(":/images/tag.png"),
+ _("Edit &Tags..."), self)
+ self.edit_tags_action.setEnabled(False)
+ self.connect(self.edit_tags_action, QtCore.SIGNAL("triggered()"),
+ self.edit_tags)
def createMenus(self):
self.fileMenu = self.menuBar().addMenu(_("&File"))
- self.fileMenu.addAction(self.add_filesAct)
- self.fileMenu.addAction(self.addDirectoryAct)
+ self.fileMenu.addAction(self.add_files_action)
+ self.fileMenu.addAction(self.add_directory_action)
self.fileMenu.addSeparator()
self.fileMenu.addAction(self.save_action)
- self.fileMenu.addAction(self.submitAct)
+ self.fileMenu.addAction(self.submit_action)
self.fileMenu.addSeparator()
self.fileMenu.addAction(self.exitAct)
self.editMenu = self.menuBar().addMenu(_("&Edit"))
self.editMenu.addSeparator()
- self.editMenu.addAction(self.optionsAct)
+ self.editMenu.addAction(self.options_action)
self.viewMenu = self.menuBar().addMenu(_("&View"))
- self.viewMenu.addAction(self.showFileBrowserAct)
- self.viewMenu.addAction(self.showCoverArtAct)
+ self.viewMenu.addAction(self.show_file_browser_action)
+ self.viewMenu.addAction(self.show_cover_art_action)
self.menuBar().addSeparator()
self.helpMenu = self.menuBar().addMenu(_("&Help"))
- self.helpMenu.addAction(self.helpAct)
- self.helpMenu.addAction(self.aboutAct)
+ self.helpMenu.addAction(self.help_action)
+ self.helpMenu.addAction(self.about_action)
def createToolBar(self):
self.mainToolBar = self.addToolBar(self.tr("File"))
self.mainToolBar.setObjectName("fileToolbar")
- self.mainToolBar.addAction(self.add_filesAct)
- self.mainToolBar.addAction(self.addDirectoryAct)
+ self.mainToolBar.addAction(self.add_files_action)
+ self.mainToolBar.addAction(self.add_directory_action)
self.mainToolBar.addSeparator()
self.mainToolBar.addAction(self.save_action)
- self.mainToolBar.addAction(self.submitAct)
- self.mainToolBar.addSeparator()
- self.mainToolBar.addAction(self.cdLookupAct)
- self.mainToolBar.addAction(self.analyzeAct)
- self.mainToolBar.addAction(self.clusterAct)
- self.mainToolBar.addAction(self.autoTagAct)
+ self.mainToolBar.addAction(self.submit_action)
self.mainToolBar.addSeparator()
+ self.mainToolBar.addAction(self.cd_lookup_action)
+ self.mainToolBar.addAction(self.analyze_action)
+ self.mainToolBar.addAction(self.cluster_action)
+ self.mainToolBar.addAction(self.auto_tag_action)
+ #self.mainToolBar.addSeparator()
+ self.mainToolBar.addAction(self.edit_tags_action)
self.mainToolBar.addAction(self.remove_action)
self.mainToolBar.addSeparator()
- self.mainToolBar.addAction(self.optionsAct)
- self.mainToolBar.addSeparator()
- self.mainToolBar.addAction(self.listenAct)
+ self.mainToolBar.addAction(self.options_action)
+ #self.mainToolBar.addSeparator()
self.searchToolBar = self.addToolBar(_("Search"))
self.searchToolBar.setObjectName("searchToolbar")
@@ -283,7 +286,7 @@ class MainWindow(QtGui.QMainWindow):
#hbox.addWidget(button, 0)
self.searchToolBar.addWidget(searchPanel)
- self.searchToolBar.addAction(self.searchAct)
+ self.searchToolBar.addAction(self.search_action)
def setStatusBarMessage(self, message):
"""Set the status bar message."""
@@ -335,12 +338,15 @@ class MainWindow(QtGui.QMainWindow):
"""Tell the tagger to remove the selected objects."""
self.tagger.remove(self.selected_objects)
- def listen(self):
- pass
-
def submit(self):
pass
-
+
+ def edit_tags(self, obj=None):
+ if not obj:
+ obj = self.selected_objects[0]
+ tagedit = TagEditor(obj.metadata, self)
+ tagedit.exec_()
+
def updateFileTreeSelection(self):
if not self.ignoreSelectionChange:
objs = self.fileTreeView.selected_objects()
@@ -362,15 +368,21 @@ class MainWindow(QtGui.QMainWindow):
def update_actions(self):
can_remove = False
can_save = False
+ can_edit_tags = False
for obj in self.selected_objects:
if obj.can_save():
can_save = True
if obj.can_remove():
can_remove = True
- if can_save and can_remove:
+ if obj.can_edit_tags():
+ can_edit_tags = True
+ if can_save and can_remove and can_edit_tags:
break
+ if len(self.selected_objects) != 1:
+ can_edit_tags = False
self.remove_action.setEnabled(can_remove)
self.save_action.setEnabled(can_save)
+ self.edit_tags_action.setEnabled(can_edit_tags)
def updateSelection(self, objects=None):
if objects is not None:
@@ -412,7 +424,7 @@ class MainWindow(QtGui.QMainWindow):
def showCoverArt(self):
"""Show/hide the cover art box."""
- if self.showCoverArtAct.isChecked():
+ if self.show_cover_art_action.isChecked():
self.coverArtBox.show()
else:
self.coverArtBox.hide()
@@ -421,3 +433,4 @@ class MainWindow(QtGui.QMainWindow):
files = [obj for obj in self.selected_objects if isinstance(obj, File)]
self.tagger.autoTag(files)
+
diff --git a/picard/ui/options/tags.py b/picard/ui/options/tags.py
index 6b8228217..05ad1720f 100644
--- a/picard/ui/options/tags.py
+++ b/picard/ui/options/tags.py
@@ -31,7 +31,7 @@ class TagsOptionsPage(Component):
BoolOption("setting", "clear_existing_tags", False),
BoolOption("setting", "write_id3v1", True),
BoolOption("setting", "write_id3v23", False),
- TextOption("setting", "id3v2_encoding", "UTF-8"),
+ TextOption("setting", "id3v2_encoding", "utf-8"),
]
def get_page_info(self):
diff --git a/picard/worker.py b/picard/worker.py
index 07fd9f7aa..9383f417c 100644
--- a/picard/worker.py
+++ b/picard/worker.py
@@ -102,8 +102,32 @@ class WorkerThread(QtCore.QThread):
def do_save_file(self, args):
file = args[1]
self.log.debug("Saving file %s", file)
+
+ file.lock_for_read()
+
self.emit(QtCore.SIGNAL("statusBarMessage(const QString &)"),
QtCore.QString(_(u"Saving file %s ...") % file.filename))
- file.save()
- self.emit(QtCore.SIGNAL("file_updated(int)"), file.id)
+
+ saved = True
+ try:
+ file.save()
+ except Exception, e:
+ self.log.error(e)
+ saved = False
+
+ if saved:
+ if self.config.setting["rename_files"]:
+ format = self.config.setting["file_naming_format"]
+ filename = self.tagger.evaluate_script(format, self.metadata)
+ filename = os.path.basename(filename) + \
+ os.path.splitext(self.filename)[1]
+ filename = os.path.join(os.path.dirname(file.filename),
+ filename)
+ os.rename(self.filename, filename)
+ self.filename = filename
+
+ file.unlock()
+
+ self.emit(QtCore.SIGNAL("save_file_finished(PyObject*, bool)"),
+ file, saved)