[jbrout/el6] Clean up and some fixes.

Matej Cepl mcepl at fedoraproject.org
Sun Jan 15 02:11:13 UTC 2012


commit 9b4ca1a3f6941dd33e3a6152cbfbdf86d4a21efa
Author: Matěj Cepl <mcepl at redhat.com>
Date:   Sun Jan 15 03:13:13 2012 +0100

    Clean up and some fixes.

 .gitignore                                         |    2 +-
 ...Reformatting-to-make-code-PEP8-compatible.patch | 2384 ++++++++++++++++++++
 ...rkill-solution-for-illegal-XML-characters.patch |  139 ++
 0003-IPTC-charset-backtrace.patch                  |   35 +
 0004-Gtk-Tooltips-depreceated.patch                |   39 +
 jbrout-no-pyexiv2-warning.patch                    |   34 +-
 jbrout-purge-no-attribute-attrib.patch             |    7 +-
 jbrout.spec                                        |   19 +-
 8 files changed, 2635 insertions(+), 24 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index e54821b..d8edeeb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
 jbrout-0.3.282.tar.xz
 jbrout_0.3.284.tar.gz
 jbrout.tar.bz2
-/jbrout.tar.bz2
+/jbrout
diff --git a/0001-Reformatting-to-make-code-PEP8-compatible.patch b/0001-Reformatting-to-make-code-PEP8-compatible.patch
new file mode 100644
index 0000000..6eeda4e
--- /dev/null
+++ b/0001-Reformatting-to-make-code-PEP8-compatible.patch
@@ -0,0 +1,2384 @@
+--- a/jbrout/jbrout/db.py
++++ b/jbrout/jbrout/db.py
+@@ -13,34 +13,33 @@
+ ## GNU General Public License for more details.
+ ##
+ 
+-from lxml.etree import Element,ElementTree
+-from xml.etree import ElementTree as et
+-import lxml
+-import traceback
+-from datetime import datetime
+-#~ import cElementTree as ElementTree
++from common import cd2d, cd2d
++from commongtk import Buffer, rgb, Img, Buffer, rgb, Img
++from libs.dict4ini import DictIni
++from lxml.etree import Element, ElementTree
++import lxml.etree as et
++from subprocess import Popen, PIPE, Popen, PIPE
++from tools import PhotoCmd, supportedFormats, PhotoCmd, supportedFormats
++import char_utils
+ import gc
+-
++import gobject
++import gtk
++import os
++import re
++import sys
++import thread
++import shutil
++import stat
++import string
+ import pygtk
+ pygtk.require('2.0')
+-import gtk
+-import gobject
+ 
+ 
+-from common import cd2d
+-from tools import PhotoCmd,supportedFormats
+-import os,re,sys,thread,shutil,stat,string
+-
+-from libs.dict4ini import DictIni
+-from commongtk import Buffer,rgb,Img
+-
+-from subprocess import Popen,PIPE
+-
+-def walktree (top = ".", depthfirst = True):
++def walktree (top=".", depthfirst=True):
+     try:
+         names = os.listdir(top)
+     except WindowsError: #protected dirs in win
+-        names=[]
++        names = []
+ 
+     if not depthfirst:
+         yield top, names
+@@ -50,13 +49,15 @@ def walktree (top = ".", depthfirst = Tr
+         except os.error:
+             continue
+         if stat.S_ISDIR(st.st_mode) and not name.startswith("."):
+-            for (newtop, children) in walktree (os.path.join(top, name), depthfirst):
++            for (newtop, children) in walktree(os.path.join(top, name),
++                    depthfirst):
+                 yield newtop, children
+     if depthfirst:
+         yield top, names
+ 
+-def dec(s): # ensure that a return from etree is in utf-8
+-    if s!=None:
++
++def dec(s):  # ensure that a return from etree is in utf-8
++    if s != None:
+         return s.decode("utf_8")
+ 
+ 
+@@ -66,7 +67,7 @@ class DBPhotos:
+     normalizeName = False
+     autorotAtImport = False
+ 
+-    def __init__(self,file):
++    def __init__(self, file):
+         if os.path.isfile(file):
+             self.root = ElementTree(file=file).getroot()
+         else:
+@@ -81,52 +82,51 @@ class DBPhotos:
+             nodeB = None
+ 
+         if nodeB:
+-            ln=nodeB.xpath("""/db/basket/p""")
+-            nodeB.getparent().remove(nodeB) # adios old basket !
++            ln = nodeB.xpath("""/db/basket/p""")
++            nodeB.getparent().remove(nodeB)  # adios old basket !
+         #==== simple basket convertion (without verification)
+ 
+-
+-    def setNormalizeName(self,v):
+-        assert v in (True,False)
++    def setNormalizeName(self, v):
++        assert v in (True, False)
+         DBPhotos.normalizeName = v
+ 
+-    def setNormalizeNameFormat(self,v):
+-        assert isinstance(v,basestring)
++    def setNormalizeNameFormat(self, v):
++        assert isinstance(v, basestring)
+         PhotoCmd.setNormalizeNameFormat(v)
+ 
+-    def setAutorotAtImport(self,v):
+-        assert v in (True,False)
++    def setAutorotAtImport(self, v):
++        assert v in (True, False)
+         DBPhotos.autorotAtImport = v
+ 
+-
+-    def add(self,path,tags={}):
+-        assert type(path)==unicode
++    def add(self, path, tags={}):
++        assert type(path) == unicode
+         assert os.path.isdir(path)
+         path = os.path.normpath(path)
+ 
+         ln = self.root.xpath(u"""//folder[@name="%s"]""" % path)
+-        assert len(ln)<=1
++        assert len(ln) <= 1
+         if ln:
+             nodeFolder = ln[0]
+             filesInBasket = [i.file for i in self.getBasket(nodeFolder)]
+             nodeFolder.getparent().remove(nodeFolder)
+         else:
+-            filesInBasket=[]
++            filesInBasket = []
+ 
+         files = []
+-        for (basepath, children) in walktree(path,False):
+-            dir=basepath
++        for (basepath, children) in walktree(path, False):
++            dir = basepath
+             try:
+                 nodeDir = self.root.xpath(u"""//folder[@name="%s"]""" % dir)[0]
+             except:
+-                nodeDir=None
++                nodeDir = None
+ 
+             if nodeDir is None:
+-                rep=[]
++                rep = []
+                 while True:
+                     rep.append(dir)
+-                    dir,n = os.path.split(dir)
+-                    if not n: break
++                    dir, n = os.path.split(dir)
++                    if not n:
++                        break
+                 rep.reverse()
+ 
+                 node = self.root
+@@ -135,44 +135,44 @@ class DBPhotos:
+                         nodeDir = node.xpath(u"""folder[@name="%s"]""" % r)[0]
+                     except:
+                         nodeDir = Element("folder", name=r)
+-                        node.append( nodeDir )
++                        node.append(nodeDir)
+ 
+-                        FolderNode(nodeDir)._updateInfo() # read comments
++                        FolderNode(nodeDir)._updateInfo()  # read comments
+ 
+                     node = nodeDir
+-                nodeDir=node
++                nodeDir = node
+ 
+             for child in children:
+-                if child.split('.')[-1].lower() in supportedFormats :
++                if child.split('.')[-1].lower() in supportedFormats:
+                     file = os.path.join(basepath, child)
+                     if os.path.isfile(file):
+-                        files.append((file,nodeDir))
++                        files.append((file, nodeDir))
+ 
+         yield len(files)   # first yield is the total number of files
+ 
+-        i=0
+-        for (file,nodeDir) in files:
++        i = 0
++        for (file, nodeDir) in files:
+             yield i
+-            i+=1
++            i += 1
+             #OLD TOOLS:
+             #file = PhotoCmd.prepareFile(file,
+             #                needRename=DBPhotos.normalizeName,
+             #                needAutoRot=DBPhotos.autorotAtImport,
+             #                )
+-            self.__addPhoto( nodeDir,file ,tags,filesInBasket)
++            self.__addPhoto(nodeDir, file, tags, filesInBasket)
+ 
+         ln = self.root.xpath(u"""//folder[@name="%s"]""" % path)
+         if ln:
+-            yield FolderNode( ln[0] )
++            yield FolderNode(ln[0])
+         else:
+             yield None
+ 
+-    def __addPhoto(self,nodeDir,file,tags,filesInBasket):
+-        assert type(file)==unicode
+-        dir,name= os.path.split(file)
++    def __addPhoto(self, nodeDir, file, tags, filesInBasket):
++        assert type(file) == unicode
++        dir, name = os.path.split(file)
+ 
+         newNode = Element("photo")
+-        nodeDir.append( newNode )
++        nodeDir.append(newNode)
+ 
+         node = PhotoNode(newNode)
+         if file in filesInBasket:
+@@ -183,42 +183,45 @@ class DBPhotos:
+                             needAutoRename=DBPhotos.normalizeName,
+                             needAutoRotation=DBPhotos.autorotAtImport,
+                            )
+-            if iii.exifdate=="":
++            if iii.exifdate == "":
+                 # exif is not present, and photocmd can't reach
+                 # to recreate minimal exif tags (because it's readonly ?)
+                 # we can't continue to import this photo
+-                raise Exception("Exif couldn't be set in this picture (readonly?)")
++                raise Exception("Exif couldn't be set " +
++                    "in this picture (readonly?)")
+         except:
+             # getback the stack trace exception
+             import traceback
+-            err=traceback.format_exc()
++            err = traceback.format_exc()
+ 
+             # remove the bad node
+             nodeDir.remove(newNode)
+ 
+             # and raise exception
+-            raise Exception(err+"\nPhoto has incorrect exif/iptc tags, can't be imported :\n"+str([file,]))
++            raise Exception(err + """
++    Photo has incorrect exif/iptc tags, can't be imported :
++    """ + str([file, ]))
+             return None
+         else:
+-            importedTags=node.updateInfo( iii )
+-            for i in importedTags:  tags[i]=i # feed the dict of tags
++            importedTags = node.updateInfo(iii)
++            for i in importedTags:
++                tags[i] = i  # feed the dict of tags
+ 
+             return node
+ 
+     def getRootFolder(self):
+-        if len(self.root)>0:
++        if len(self.root) > 0:
+             return FolderNode(self.root[0])
+ 
+     def redoIPTC(self):
+         """ refresh IPTC in file and db """
+         ln = self.root.xpath(u"""//photo[t]""")
+         for i in ln:
+-            p=PhotoNode(i)
++            p = PhotoNode(i)
+             print p.name
+             pc = PhotoCmd(p.file)
+             pc._write()             # rewrite iptc in file
+-            p.updateInfo( pc )      # rewrite iptc in db.xml
+-
++            p.updateInfo(pc)      # rewrite iptc in db.xml
+ 
+     def getMinMaxDates(self):
+         """ return a tuple of the (min,max) of photo dates
+@@ -229,42 +232,40 @@ class DBPhotos:
+             ma = 11111111111111
+             mi = 99999999999999
+             for i in ln:
+-                a=int( i.attrib["date"] )
+-                ma = max(a,ma)
+-                mi = min(a,mi)
+-            return cd2d(str(mi)),cd2d(str(ma))
++                a = int(i.attrib["date"])
++                ma = max(a, ma)
++                mi = min(a, mi)
++            return cd2d(str(mi)), cd2d(str(ma))
+ 
+-
+-    def select(self,xpath,fromNode=None):
+-        ln=self.root.xpath(xpath)
++    def select(self, xpath, fromNode=None):
++        ln = self.root.xpath(xpath)
+         if ln:
+             return [PhotoNode(i) for i in ln]
+         else:
+             return []
+ 
+-
+     def toXml(self):
+         """ for tests only """
+         from StringIO import StringIO
+-        fid=StringIO()
++        fid = StringIO()
+         fid.write("""<?xml version="1.0" encoding="UTF-8"?>""")
+-        ElementTree(self.root).write(fid,encoding="utf-8")
++        ElementTree(self.root).write(fid, encoding="utf-8")
+         return fid.getvalue()
+ 
+     def save(self):
+         """ save the db, and a basket.txt file """
+-        fid = open(self.file,"w")
++        fid = open(self.file, "w")
+         fid.write("""<?xml version="1.0" encoding="UTF-8"?>""")
+-        ElementTree(self.root).write(fid,encoding="utf-8")
++        ElementTree(self.root).write(fid, encoding="utf-8")
+         fid.close()
+ 
+         # save a "simple txt file" of basket'files near db.xml
+         # (could be used in another prog ?)
+-        file = os.path.join( os.path.dirname(self.file),"basket.txt")
++        file = os.path.join(os.path.dirname(self.file), "basket.txt")
+         if self.isBasket():
+-            list =[i.file for i in self.getBasket()]
++            list = [i.file for i in self.getBasket()]
+ 
+-            fid = open(file,"w")
++            fid = open(file, "w")
+             if fid:
+                 fid.write((u"\n".join(list)).encode("utf_8"))
+                 fid.close()
+@@ -273,40 +274,44 @@ class DBPhotos:
+                 os.unlink(file)
+             except:
+                 pass
+-    #--------------------------------------------------------------------------------
++
++    #-------------------------------------------------------------------------
+     # basket methods
+-    #--------------------------------------------------------------------------------
++    #-------------------------------------------------------------------------
+     def isBasket(self):
+-        return len(self.root.xpath("//photo[@basket='1']"))>0
++        return len(self.root.xpath("//photo[@basket='1']")) > 0
+ 
+     def clearBasket(self):
+-        ln=self.getBasket()
++        ln = self.getBasket()
+         for i in ln:
+             i.removeFromBasket()
+ 
+-    def getBasket(self,nodeFrom=None):
++    def getBasket(self, nodeFrom=None):
+         if nodeFrom is not None:
+-            return [PhotoNode(i) for i in nodeFrom.xpath("//photo[@basket='1']")]
++            return [PhotoNode(i) for i in
++                nodeFrom.xpath("//photo[@basket='1']")]
+         else:
+-            return [PhotoNode(i) for i in self.root.xpath("//photo[@basket='1']")]
+-
++            return [PhotoNode(i) for i in
++                self.root.xpath("//photo[@basket='1']")]
+ 
+ 
+ class FolderNode(object):
+     """ A folder node containing photo nodes"""
+-    commentFile="album.txt"
++    commentFile = "album.txt"
+ 
+-    def __init__(self,n):
+-        assert n.tag in ["folder","db"]
++    def __init__(self, n):
++        assert n.tag in ["folder", "db"]
+         self.__node = n
+ 
+-    def __getName(self):    return os.path.basename(self.__node.attrib["name"])
++    def __getName(self):
++        return os.path.basename(self.__node.attrib["name"])
+     name = property(__getName)
+ 
+     #~ def __getIsReadOnly(self):   return not os.access( self.file, os.W_OK)
+     #~ isReadOnly = property(__getIsReadOnly)
+ 
+-    def __getFile(self):  return self.__node.attrib["name"]
++    def __getFile(self):
++        return self.__node.attrib["name"]
+     file = property(__getFile)
+ 
+     def __getComment(self):
+@@ -319,20 +324,20 @@ class FolderNode(object):
+ 
+     def __getExpand(self):
+         if "expand" in self.__node.attrib:
+-            return (self.__node.attrib["expand"]!="0")
++            return (self.__node.attrib["expand"] != "0")
+         else:
+             return True
+     expand = property(__getExpand)
+ 
+-    def _getNode(self): # special
++    def _getNode(self):  # special
+         return self.__node
+ 
+     def getParent(self):
+-        return FolderNode( self.__node.getparent() )
++        return FolderNode(self.__node.getparent())
+ 
+     def getFolders(self):
+-        ln=[FolderNode(i) for i in self.__node.xpath("folder")]
+-        ln.sort(cmp=lambda x,y: cmp(x.name.lower(),y.name.lower()))
++        ln = [FolderNode(i) for i in self.__node.xpath("folder")]
++        ln.sort(cmp=lambda x, y: cmp(x.name.lower(), y.name.lower()))
+         return ln
+ 
+     def getPhotos(self):
+@@ -341,22 +346,22 @@ class FolderNode(object):
+     def getAllPhotos(self):
+         return self._select("descendant::photo")
+ 
+-    def _select(self,xpath):
++    def _select(self, xpath):
+         """ 'xpath' should only target photo node """
+         class PhotoNodes(list):
+-            def __init__(self,l,xpath):
+-                list.__init__(self,l)
+-                self.xpath=xpath
+-
+-        ln=self.__node.xpath(xpath)
+-        ll= [PhotoNode(i) for i in ln]
+-        return PhotoNodes(ll,"//folder[@name='%s']/%s"%(self.file,xpath))
+-
+-    def setComment(self,t):
+-        assert type(t)==unicode
+-        file = os.path.join(self.file,FolderNode.commentFile)
+-        if t=="": # if is the "kill comment"
+-            if os.path.isfile(file): # if files exists, kill it
++            def __init__(self, l, xpath):
++                list.__init__(self, l)
++                self.xpath = xpath
++
++        ln = self.__node.xpath(xpath)
++        ll = [PhotoNode(i) for i in ln]
++        return PhotoNodes(ll, "//folder[@name='%s']/%s" % (self.file, xpath))
++
++    def setComment(self, t):
++        assert type(t) == unicode
++        file = os.path.join(self.file, FolderNode.commentFile)
++        if t == "":  # if is the "kill comment"
++            if os.path.isfile(file):  # if files exists, kill it
+                 try:
+                     os.unlink(file)
+                     return True
+@@ -365,9 +370,9 @@ class FolderNode(object):
+ 
+             return True
+         else:
+-            fid=open( file ,"w" )
++            fid = open(file, "w")
+             if fid:
+-                fid.write( t.encode("utf_8") )
++                fid.write(t.encode("utf_8"))
+                 fid.close()
+                 self._updateInfo()
+                 return True
+@@ -376,45 +381,44 @@ class FolderNode(object):
+ 
+     def _updateInfo(self):
+         ln = self.__node.xpath("c")
+-        assert len(ln) in [0,1]
++        assert len(ln) in [0, 1]
+         if ln:
+-            nodeComment =ln[0]
++            nodeComment = ln[0]
+         else:
+-            nodeComment =None
++            nodeComment = None
+ 
+         comment = None
+-        file = os.path.join(self.file,FolderNode.commentFile)
++        file = os.path.join(self.file, FolderNode.commentFile)
+         if os.path.isfile(file):
+-            fid=open( file ,"r" )
++            fid = open(file, "r")
+             if fid:
+                 comment = fid.read().decode("utf_8")
+                 fid.close()
+ 
+         if comment:
+-            if nodeComment ==  None:
++            if nodeComment == None:
+                 nodeComment = Element("c")
+                 nodeComment.text = comment
+                 self.__node.append(nodeComment)
+             else:
+                 nodeComment.text = comment
+         else:
+-            if nodeComment !=  None:
++            if nodeComment != None:
+                 self.__node.remove(nodeComment)
+ 
+-
+-    def setExpand(self,bool):
++    def setExpand(self, bool):
+         if bool:
+             self.__node.attrib["expand"] = "1"
+         else:
+             self.__node.attrib["expand"] = "0"
+ 
+-    def rename(self,newname):
+-        assert type(newname)==unicode
++    def rename(self, newname):
++        assert type(newname) == unicode
+         oldname = self.file
+-        newname = os.path.join( os.path.dirname(oldname), newname )
++        newname = os.path.join(os.path.dirname(oldname), newname)
+         if not (os.path.isdir(newname) or os.path.isfile(newname)):
+             try:
+-                shutil.move( oldname, newname )
++                shutil.move(oldname, newname)
+                 moved = True
+             except os.error, detail:
+                 raise Exception(detail)
+@@ -425,16 +429,17 @@ class FolderNode(object):
+ 
+                 ln = self.__node.xpath("descendant::folder")
+                 for i in ln:
+-                    i.attrib["name"] = newname + i.attrib["name"][len(oldname):]
++                    i.attrib["name"] = newname + \
++                        i.attrib["name"][len(oldname):]
+                 return True
+ 
+         return False
+ 
+-    def createNewFolder(self,newname):
+-        assert type(newname)==unicode
+-        newname = os.path.join( self.file, newname )
+-        ll=[i for i in self.getFolders() if i.file==newname]
+-        if len(ll)==1:
++    def createNewFolder(self, newname):
++        assert type(newname) == unicode
++        newname = os.path.join(self.file, newname)
++        ll = [i for i in self.getFolders() if i.file == newname]
++        if len(ll) == 1:
+             # folder is already mapped in the db/xml
+             # so no creation
+             return False
+@@ -449,16 +454,16 @@ class FolderNode(object):
+                     # this is a real new folder
+                     # let's create it in the FS
+                     try:
+-                       os.mkdir( newname )
+-                       created = True
++                        os.mkdir(newname)
++                        created = True
+                     except os.error, detail:
+-                       raise Exception(detail)
+-                       created = False
++                        raise Exception(detail)
++                        created = False
+ 
+                 if created:
+                     #so map the folder to a db node
+                     nodeDir = Element("folder", name=newname)
+-                    self.__node.append( nodeDir )
++                    self.__node.append(nodeDir)
+                     return FolderNode(nodeDir)
+         return False
+ 
+@@ -470,13 +475,13 @@ class FolderNode(object):
+         """ delete real folder and node """
+         if os.path.isdir(self.file):
+             try:
+-               shutil.rmtree( self.file )
+-               deleted = True
++                shutil.rmtree(self.file)
++                deleted = True
+             except os.error, detail:
+-               raise Exception(detail)
+-               deleted = False
++                raise Exception(detail)
++                deleted = False
+         else:
+-            deleted=True
++            deleted = True
+ 
+         if deleted:
+             self.remove()
+@@ -484,13 +489,13 @@ class FolderNode(object):
+ 
+         return False
+ 
+-    def moveToFolder(self,nodeFolder):
++    def moveToFolder(self, nodeFolder):
+         assert nodeFolder.__class__ == FolderNode
+         oldname = self.file
+-        newname = os.path.join(nodeFolder.file,self.name)
++        newname = os.path.join(nodeFolder.file, self.name)
+         if not (os.path.isdir(newname) or os.path.isfile(newname)):
+             try:
+-                shutil.move( oldname, newname )
++                shutil.move(oldname, newname)
+                 moved = True
+             except os.error, detail:
+                 raise Exception(detail)
+@@ -503,11 +508,11 @@ class FolderNode(object):
+ 
+                 ln = self.__node.xpath("descendant::folder")
+                 for i in ln:
+-                    i.attrib["name"] = newname + i.attrib["name"][len(oldname):]
++                    i.attrib["name"] = newname + \
++                        i.attrib["name"][len(oldname):]
+                 return self
+         return False
+ 
+-
+ #~ def pixbuf_from_data(data):
+     #~ loader = gtk.gdk.PixbufLoader ('jpeg')
+     #~ loader.write (data, len (data))
+@@ -524,9 +529,7 @@ class FolderNode(object):
+         #~ finally:
+             #~ gtk.threads_leave()
+     #~ gobject.idle_add(idle_func)
+-
+-
+-
++#
+ #~ class PhotoFile:
+     #~ @staticmethod
+     #~ def generate(path):
+@@ -555,22 +558,24 @@ class PhotoNode(object):
+       Class PhotoNode
+       to manipulate a node photo in the dom of album.xml.
+     """
+-    def __init__(self,node):
++    def __init__(self, node):
+         assert node.tag == "photo"
+         self.__node = node
+ 
+-    def __getName(self):    return self.__node.attrib["name"]
++    def __getName(self):
++        return self.__node.attrib["name"]
+     name = property(__getName)
+ 
+-    def __getfolderName(self):    return os.path.basename(self.folder)
++    def __getfolderName(self):
++        return os.path.basename(self.folder)
+     folderName = property(__getfolderName)
+ 
+-    def __getIsReadOnly(self):    return not os.access( self.file, os.W_OK)
++    def __getIsReadOnly(self):
++        return not os.access(self.file, os.W_OK)
+     isReadOnly = property(__getIsReadOnly)
+ 
+-
+     def __getTags(self):
+-        l=[dec(i.text) for i in self.__node.xpath("t")]
++        l = [dec(i.text) for i in self.__node.xpath("t")]
+         l.sort()
+         return l
+     tags = property(__getTags)
+@@ -584,47 +589,54 @@ class PhotoNode(object):
+     comment = property(__getComment)
+ 
+     def __getRating(self): # 0x4746 18246 IFD0 Exif.Image.Rating Short
+-            ln = self.__node.xpath("r") # saved decimal like <r>5</r>
+-            if ln:
+-                return int(ln[0].text)
+-            else:
+-                return 0
++       ln = self.__node.xpath("r") # saved decimal like <r>5</r>
++       if ln:
++           return int(ln[0].text)
++       else:
++           return 0
+     rating = property(__getRating)
+ 
+-    def __getDate(self): return self.__node.attrib["date"]  # if exif -> exifdate else filedate
++    def __getDate(self):
++        # if exif -> exifdate else filedate
++        return self.__node.attrib["date"]
+     date = property(__getDate)
+ 
+-    def __getResolution(self): return self.__node.attrib["resolution"]
++    def __getResolution(self):
++        return self.__node.attrib["resolution"]
+     resolution = property(__getResolution)
+ 
+-    def __getReal(self): return self.__node.attrib["real"]  # if exifdate -> true else false
++    def __getReal(self):
++        # if exifdate -> true else false
++        return self.__node.attrib["real"]
+     real = property(__getReal)
+ 
+     def __getFolder(self):
+         try:
+-            na=dec(self.__node.getparent().attrib["name"])
++            na = dec(self.__node.getparent().attrib["name"])
+         except Exception as exc:
+-            print >>sys.stderr, "Catching exception e:\n%s" % exc
+-            print >>sys.stderr, "self.__node = %s" % \
++            print >> sys.stderr, "Catching exception e:\n%s" % exc
++            print >> sys.stderr, "self.__node = %s" % \
+                 et.tostring(self.__node, encoding="utf-8")
+-            print >>sys.stderr, "self.__node.getparent() = %s" % \
++            print >> sys.stderr, "self.__node.getparent() = %s" % \
+                 et.tostring(self.__node.getparent(), encoding="utf-8")
+-        assert type(na)==unicode
++        assert type(na) == unicode
+         return na
+     folder = property(__getFolder)
+ 
+-    def __getFile(self):  return dec(os.path.join(self.__getFolder(),self.__getName()))
++    def __getFile(self):
++        return dec(os.path.join(self.__getFolder(), self.__getName()))
+     file = property(__getFile)
+ 
+     def getParent(self):
+-        return FolderNode( self.__node.getparent() )
++        return FolderNode(self.__node.getparent())
+ 
+-    def __getIsInBasket(self):  return (self.__node.get("basket")=="1")
++    def __getIsInBasket(self):
++        return (self.__node.get("basket") == "1")
+     isInBasket = property(__getIsInBasket)
+ 
+ 
+     def addToBasket(self):
+-        self.__node.set("basket","1")
++        self.__node.set("basket", "1")
+ 
+     def removeFromBasket(self):
+         if self.isInBasket:
+@@ -638,58 +650,58 @@ class PhotoNode(object):
+     def getThumb(self):
+         """ Get thumb from exif data"""
+         if self.real == "yes":  # real photo (exifdate !)
+-            backGroundColor=None
++            backGroundColor = None
+             pb_nothumb = Buffer.pixbufNT
+-            pb_notfound =Buffer.pixbufNF
+-            pb_error   =Buffer.pixbufERR
+-        else:                   # photo with hadn't got exif before (exif setted by jbrout)
+-            backGroundColor=rgb(255,0,0)
++            pb_notfound = Buffer.pixbufNF
++            pb_error = Buffer.pixbufERR
++        else:
++            # photo with hadn't got exif before (exif setted by jbrout)
++            backGroundColor = rgb(255, 0, 0)
+             pb_nothumb = Buffer.pixbufNTNE
+-            pb_notfound =Buffer.pixbufNFNE
+-            pb_error   =Buffer.pixbufERRNE
++            pb_notfound = Buffer.pixbufNFNE
++            pb_error = Buffer.pixbufERRNE
+ 
+         try:
+-            i=Img(thumb=self.file)
+-            pb= i.resizeC(160,backGroundColor).pixbuf
+-        except IOError: # 404
+-            pb= pb_notfound
+-        except KeyError: # no exif
+-            pb= pb_nothumb
++            i = Img(thumb=self.file)
++            pb = i.resizeC(160, backGroundColor).pixbuf
++        except IOError:  # 404
++            pb = pb_notfound
++        except KeyError:  # no exif
++            pb = pb_nothumb
+         except:
+-            pb=pb_error
++            pb = pb_error
+             raise
+ 
+-
+         return pb
+ 
+     def getImage(self):
+         file = self.file
+         # XXX external call while pyexiv2 can't handle it
+-        extension=file.split('.')[-1].lower()
++        extension = file.split('.')[-1].lower()
+         if extension == 'nef':
+-            data=Popen(["exiftool","-b","-JpgFromRaw","%s"%file],stdout=PIPE).communicate()[0]
+-            loader = gtk.gdk.PixbufLoader ('jpeg')
+-            loader.write (data, len (data))
+-            im = loader.get_pixbuf ()
+-            loader.close ()
++            data = Popen(["exiftool", "-b", "-JpgFromRaw", "%s" % file],
++                stdout=PIPE).communicate()[0]
++            loader = gtk.gdk.PixbufLoader('jpeg')
++            loader.write(data, len(data))
++            im = loader.get_pixbuf()
++            loader.close()
+             return im
+         else:
+             return gtk.gdk.pixbuf_new_from_file(file)
+ 
+-
+-    def moveToFolder(self,nodeFolder):
++    def moveToFolder(self, nodeFolder):
+         assert nodeFolder.__class__ == FolderNode
+ 
+         name = self.name
+-        while os.path.isfile(os.path.join(nodeFolder.file,name) ):
+-            name=PhotoCmd.giveMeANewName(name)
++        while os.path.isfile(os.path.join(nodeFolder.file, name)):
++            name = PhotoCmd.giveMeANewName(name)
+ 
+         try:
+-            shutil.move( self.file, os.path.join(nodeFolder.file,name) )
+-            moved=True
++            shutil.move(self.file, os.path.join(nodeFolder.file, name))
++            moved = True
+         except os.error, detail:
+             raise Exception(detail)
+-            moved=False
++            moved = False
+ 
+         if moved:
+             self.__node.attrib["name"] = name
+@@ -698,50 +710,52 @@ class PhotoNode(object):
+             nf.append(self.__node)
+             return True
+ 
+-    def rotate(self,sens):
+-        assert sens in ["R","L"]
++    def rotate(self, sens):
++        assert sens in ["R", "L"]
+ 
+         pc = PhotoCmd(self.file)
+         pc.rotate(sens)
+         self.updateInfo(pc)
+ 
+-    def transform(self,sens):
+-        assert sens in ["auto","rotate90","rotate180","rotate270","flipHorizontal","flipVertical","transpose","transverse"]
++    def transform(self, sens):
++        assert sens in ["auto", "rotate90", "rotate180",
++            "rotate270", "flipHorizontal", "flipVertical",
++            "transpose", "transverse"]
+ 
+         pc = PhotoCmd(self.file)
+         pc.transform(sens)
+         self.updateInfo(pc)
+ 
+-    def setComment(self,txt):
+-        assert type(txt)==unicode
++    def setComment(self, txt):
++        assert type(txt) == unicode
+ 
+         pc = PhotoCmd(self.file)
+         if pc.addComment(txt):
+             self.updateInfo(pc)
+ 
+-    def setRating(self,val):
+-        assert type(val)==int
++    def setRating(self, val):
++        assert type(val) == int
+ 
+         pc = PhotoCmd(self.file)
+-        if pc.addRating(val): # always true
++        if pc.addRating(val):  # always true
+             self.updateInfo(pc)
+ 
+-    def addTag(self,tag):
+-        assert type(tag)==unicode
++    def addTag(self, tag):
++        assert type(tag) == unicode
+ 
+         pc = PhotoCmd(self.file)
+         if pc.add(tag):
+             self.updateInfo(pc)
+ 
+-    def addTags(self,tags):
+-        assert type(tags)==list
++    def addTags(self, tags):
++        assert type(tags) == list
+ 
+         pc = PhotoCmd(self.file)
+         if pc.addTags(tags):
+             self.updateInfo(pc)
+ 
+-    def delTag(self,tag):
+-        assert type(tag)==unicode
++    def delTag(self, tag):
++        assert type(tag) == unicode
+ 
+         pc = PhotoCmd(self.file)
+         if pc.sub(tag):
+@@ -757,10 +771,12 @@ class PhotoNode(object):
+         pc.rebuildExifTB()
+         self.updateInfo(pc)
+ 
+-    def copyTo(self,path,resize=None, keepInfo=True, delTags=False, delCom=False):
++    def copyTo(self, path, resize=None, keepInfo=True,
++            delTags=False, delCom=False):
+         """ copy self to the path "path", and return its newfilename or none
+-            by default, it keeps IPTC/THUMB/EXIF, but it can be removed by setting
+-            keepInfo at False. In all case, new file keep its filedate system
++            by default, it keeps IPTC/THUMB/EXIF, but it can be removed by
++            setting keepInfo at False. In all case, new file keep its filedate
++            system
+ 
+             image can be resized/recompressed (preserving ratio) if resize
+             (which is a tuple=(size,qual)) is provided:
+@@ -768,48 +784,48 @@ class PhotoNode(object):
+                 if size is a int : it's the desired largest side
+                 qual : is the percent for the quality
+         """
+-        assert type(path)==unicode, "photonod.copyTo() : path is not unicode"
+-        dest = os.path.join( path, self.name)
++        assert type(path) == unicode, "photonod.copyTo() : path is not unicode"
++        dest = os.path.join(path, self.name)
+ 
+         while os.path.isfile(dest):
+-            dest = os.path.join( path, PhotoCmd.giveMeANewName(os.path.basename(dest)) )
++            dest = os.path.join(path,
++                PhotoCmd.giveMeANewName(os.path.basename(dest)))
+ 
+         if resize:
+-            assert len(resize)==2
+-            size,qual = resize
+-            assert type(size) in [int,float]
+-
+-            pb = self.getImage() # a gtk.PixBuf
+-            (wx,wy) = pb.get_width(),pb.get_height()
++            assert len(resize) == 2
++            size, qual = resize
++            assert type(size) in [int, float]
+ 
++            pb = self.getImage()  # a gtk.PixBuf
++            (wx, wy) = pb.get_width(), pb.get_height()
+ 
+             # compute the new size -> wx/wy
+-            if type(size)==float:
++            if type(size) == float:
+                 # size is a percent
+-                size = int(size*100)
+-                wx = int(wx*size / 100)
+-                wy = int(wy*size / 100)
++                size = int(size * 100)
++                wx = int(wx * size / 100)
++                wy = int(wy * size / 100)
+ 
+             else:
+                 # size is the largest side in pixels
+-                if wx>wy:
++                if wx > wy:
+                     # format landscape
+-                    wx, wy = size, (size * wy)/wx
++                    wx, wy = size, (size * wy) / wx
+                 else:
+                     # format portrait
+-                    wx, wy = (size * wx)/wy, size
+-
++                    wx, wy = (size * wx) / wy, size
+ 
+-            pb = pb.scale_simple(wx,wy,3)   # 3= best quality (gtk.gdk.INTERP_HYPER)
+-            pb.save(dest, "jpeg", {"quality":str(int(qual))})
++            # 3= best quality (gtk.gdk.INTERP_HYPER)
++            pb = pb.scale_simple(wx, wy, 3)
++            pb.save(dest, "jpeg", {"quality": str(int(qual))})
+ 
+             if keepInfo:
+                 pc = PhotoCmd(self.file)
+                 pc.copyInfoTo(dest)
+             del(pb)
+-            gc.collect() # so it cleans pixbufs
++            gc.collect()  # so it cleans pixbufs
+         else:
+-            shutil.copy2(self.file,dest)
++            shutil.copy2(self.file, dest)
+             if not keepInfo:
+                 # we must destroy info
+                 PhotoCmd(dest).destroyInfo()
+@@ -821,13 +837,13 @@ class PhotoNode(object):
+ 
+         return dest
+ 
+-    def getInfoFrom(self,copy):
++    def getInfoFrom(self, copy):
+         """ rewrite info from a 'copy' to the file (exif, iptc, ...)
+             and rebuild thumb
+             (used to ensure everything is back after a run in another program
+              see plugin 'touch')
+         """
+-        pc=PhotoCmd(copy)
++        pc = PhotoCmd(copy)
+         pc.copyInfoTo(self.file)
+ 
+         #and update infos
+@@ -839,12 +855,13 @@ class PhotoNode(object):
+     #~ def repair(self):
+         #~ pc = PhotoCmd(self.file)
+         #~ pc.repair()                 # kill exif tags ;-(
+-        #~ pc.rebuildExifTB()          # recreate "fake exif tags" with exifutils and thumbnails
++        #~ pc.rebuildExifTB() - recreate "fake exif tags" with exifutils &
++        #    thumbnails
+         #~ self.updateInfo(pc)
+ 
+-    def redate(self,w,d,h,m,s ):
++    def redate(self, w, d, h, m, s):
+         pc = PhotoCmd(self.file)
+-        pc.redate(w,d,h,m,s)
++        pc.redate(w, d, h, m, s)
+         self.updateInfo(pc)
+         self.updateName()
+ 
+@@ -858,22 +875,22 @@ class PhotoNode(object):
+         #photo has been redated
+         #it should be renamed if in config ...
+         if DBPhotos.normalizeName:
+-            pc = PhotoCmd(self.file,needAutoRename=True)
++            pc = PhotoCmd(self.file, needAutoRename=True)
+             self.updateInfo(pc)
+ 
+         return True
+ 
+-    def updateInfo(self,pc):
++    def updateInfo(self, pc):
+         """ feel the node with REALS INFOS from "pc"(PhotoCmd)
+             return the tags
+         """
+-        assert pc.__class__==PhotoCmd
++        assert pc.__class__ == PhotoCmd
+ 
+         wasInBasket = self.isInBasket
+ 
+         self.__node.clear()
+-        self.__node.attrib["name"]=os.path.basename(pc.file)
+-        self.__node.attrib["resolution"]=pc.resolution
++        self.__node.attrib["name"] = os.path.basename(pc.file)
++        self.__node.attrib["resolution"] = pc.resolution
+ 
+         # OLD PhotoCmd
+         #~ if pc.exifdate:
+@@ -884,11 +901,11 @@ class PhotoNode(object):
+             #~ self.__node.attrib["real"]="no"
+ 
+         # NEW PhotoCmd (always a exifdate)
+-        self.__node.attrib["date"]=pc.exifdate
++        self.__node.attrib["date"] = pc.exifdate
+         if pc.isreal:
+-            self.__node.attrib["real"]="yes"
++            self.__node.attrib["real"] = "yes"
+         else:
+-            self.__node.attrib["real"]="no"
++            self.__node.attrib["real"] = "no"
+ 
+         if pc.tags:
+             for tag in pc.tags:
+@@ -897,7 +914,7 @@ class PhotoNode(object):
+                 self.__node.append(nodeTag)
+         if pc.comment:
+             nodeComment = Element("c")
+-            nodeComment.text = pc.comment.replace(u'\x00',u' ')
++            nodeComment.text = pc.comment.replace(u'\x00', u' ')
+             self.__node.append(nodeComment)
+         if pc.rating:
+             nodeRating = Element("r")
+@@ -914,11 +931,11 @@ class PhotoNode(object):
+         get real infos from photocmd
+         """
+         pc = PhotoCmd(self.file)
+-        info={}
++        info = {}
+         info["tags"] = pc.tags
+         info["comment"] = pc.comment
+         info["exifdate"] = pc.exifdate
+-        info["rating"] = pc.rating # huh, did i use that?
++        info["rating"] = pc.rating  # huh, did i use that?
+         info["filedate"] = pc.filedate
+         info["resolution"] = pc.resolution
+         info["readonly"] = pc.readonly
+@@ -929,19 +946,18 @@ class PhotoNode(object):
+     def getThumbSize(self):
+         """Get the size (width,height) of the thumbnail"""
+         try:
+-            thumbnail=Img(thumb=self.file)
+-            return (thumbnail.width,thumbnail.height)
+-        except IOError: # 404
+-            return (-1,-1)
+-
++            thumbnail = Img(thumb=self.file)
++            return (thumbnail.width, thumbnail.height)
++        except IOError:  # 404
++            return (-1, -1)
+ 
+     def delete(self):
+         try:
+-           os.unlink( self.file )
+-           deleted = True
++            os.unlink(self.file)
++            deleted = True
+         except os.error, detail:
+-           raise Exception(detail)
+-           deleted = False
++            raise Exception(detail)
++            deleted = False
+ 
+         if deleted:
+             self.__node.getparent().remove(self.__node)
+@@ -950,10 +966,10 @@ class PhotoNode(object):
+         return False
+ 
+ 
+-# ============================================================================================
++# ============================================================================
+ class DBTags:
+     """ Class to manage tags tree """
+-    def __init__(self,file):
++    def __init__(self, file):
+         if os.path.isfile(file):
+             self.root = ElementTree(file=file).getroot()
+         else:
+@@ -962,14 +978,15 @@ class DBTags:
+ 
+     def getAllTags(self):
+         """ return list of tuples (tag, parent catg)"""
+-        l=[(n.text,n.getparent().get("name")) for n in self.root.xpath("//tag")]
+-        l.sort(cmp= lambda x,y: cmp(x[0].lower(),y[0].lower()))
++        l = [(n.text, n.getparent().get("name"))
++            for n in self.root.xpath("//tag")]
++        l.sort(cmp=lambda x, y: cmp(x[0].lower(), y[0].lower()))
+         return l
+ 
+     def save(self):
+-        fid = open(self.file,"w")
++        fid = open(self.file, "w")
+         fid.write("""<?xml version="1.0" encoding="UTF-8"?>""")
+-        ElementTree(self.root).write(fid,encoding="utf-8")
++        ElementTree(self.root).write(fid, encoding="utf-8")
+         fid.close()
+ 
+     #def getTagForKey(self,key):
+@@ -1003,14 +1020,14 @@ class DBTags:
+     def getRootTag(self):
+         return CatgNode(self.root)
+ 
+-    def updateImportedTags( self, importedTags ):
+-        assert type(importedTags)==list
++    def updateImportedTags(self, importedTags):
++        assert type(importedTags) == list
+ 
+         r = self.getRootTag()
+         existingTags = [i.name for i in r.getAllTags()]
+ 
+         # compare existing and imported tags -> newTags
+-        newTags=[]
++        newTags = []
+         for tag in importedTags:
+             if tag not in existingTags:
+                 newTags.append(tag)
+@@ -1019,43 +1036,48 @@ class DBTags:
+             # create a category imported
+             nom = u"Imported Tags"
+             while 1:
+-                nc=r.addCatg(nom)
+-                if nc!=None:
++                nc = r.addCatg(nom)
++                if nc != None:
+                     break
+                 else:
+-                    nom+=u"!"
++                    nom += u"!"
+ 
+             for tag in newTags:
+-                ret=nc.addTag(tag)
+-                assert ret!=None,"tag '%s' couldn't be added"%tag
++                ret = nc.addTag(tag)
++                assert ret != None, "tag '%s' couldn't be added" % tag
+ 
+         return len(newTags)
+ 
++
+ class TagNode(object):
+     """ """
+-    def __init__(self,n):
++    def __init__(self, n):
+         assert n.tag == "tag"
+         self.__node = n
+ 
+-    def __getName(self): return dec(self.__node.text)
++    def __getName(self):
++        return dec(self.__node.text)
+     name = property(__getName)
+ 
+-    def __getKey(self): return dec(self.__node.get("key"))
+-    def __setKey(self,v): self.__node.set("key",v)
+-    key = property(__getKey,__setKey)
++    def __getKey(self):
++        return dec(self.__node.get("key"))
++
++    def __setKey(self, v):
++        self.__node.set("key", v)
++    key = property(__getKey, __setKey)
+ 
+     def remove(self):
+         self.__node.getparent().remove(self.__node)
+ 
+-    def moveToCatg(self,c):
+-        assert type(c)==CatgNode
++    def moveToCatg(self, c):
++        assert type(c) == CatgNode
+         self.remove()
+         c._appendToCatg(self.__node)
+ 
+ 
+ class CatgNode(object):
+     """ """
+-    def __init__(self,n):
++    def __init__(self, n):
+         assert n.tag == "tags"
+         self.__node = n
+ 
+@@ -1068,65 +1090,65 @@ class CatgNode(object):
+ 
+     def __getExpand(self):
+         if "expand" in self.__node.attrib:
+-            return (self.__node.attrib["expand"]!="0")
++            return (self.__node.attrib["expand"] != "0")
+         else:
+             return True
+     expand = property(__getExpand)
+ 
+     def getTags(self):
+-        l=[TagNode(i) for i in self.__node.xpath("tag")]
+-        l.sort( cmp=lambda x,y: cmp(x.name,y.name) )
++        l = [TagNode(i) for i in self.__node.xpath("tag")]
++        l.sort(cmp=lambda x, y: cmp(x.name, y.name))
+         return l
++
+     def getCatgs(self):
+         return [CatgNode(i) for i in self.__node.xpath("tags")]
+ 
+     def getAllTags(self):
+-        l= self.getTags()
++        l = self.getTags()
+         for i in self.getCatgs():
+-            l.extend( i.getAllTags() )
+-        l.sort(cmp=lambda x,y: cmp(x.name,y.name))
++            l.extend(i.getAllTags())
++        l.sort(cmp=lambda x, y: cmp(x.name, y.name))
+         return l
+ 
+-    def addTag(self,t):
+-        assert type(t)==unicode
+-        if self.isUnique("tag",t):
++    def addTag(self, t):
++        assert type(t) == unicode
++        if self.isUnique("tag", t):
+             n = Element("tag")
+             n.text = t
+             self.__node.append(n)
+             return TagNode(n)
+ 
+-    def rename(self,newName):
++    def rename(self, newName):
+         self.__node.attrib["name"] = newName
+ 
+-
+     def remove(self):
+         self.__node.getparent().remove(self.__node)
+ 
+-    def moveToCatg(self,c):
++    def moveToCatg(self, c):
+         self.remove()
+         c._appendToCatg(self.__node)
+ 
+-    def _appendToCatg(self,element):
++    def _appendToCatg(self, element):
+         self.__node.append(element)
+ 
+-    def addCatg(self,t):
+-        assert type(t)==unicode
+-        if self.isUnique("tags",t):
+-            n = Element("tags",name=t)
++    def addCatg(self, t):
++        assert type(t) == unicode
++        if self.isUnique("tags", t):
++            n = Element("tags", name=t)
+             self.__node.append(n)
+             return CatgNode(n)
+ 
+-    def setExpand(self,bool):
++    def setExpand(self, bool):
+         if bool:
+             self.__node.attrib["expand"] = "1"
+         else:
+             self.__node.attrib["expand"] = "0"
+ 
+-    def isUnique(self,type,name):
+-        if type=="tag":
+-            ln=[dec(i.text) for i in self.__node.xpath("//tag")]
++    def isUnique(self, type, name):
++        if type == "tag":
++            ln = [dec(i.text) for i in self.__node.xpath("//tag")]
+         else:
+-            ln=[CatgNode(i).name for i in self.__node.xpath("//tags")]
++            ln = [CatgNode(i).name for i in self.__node.xpath("//tags")]
+         return name not in ln
+ 
+ 
+@@ -1158,4 +1180,3 @@ if __name__ == "__main__":
+     #~ for i in ln:
+         #~ print i.name, i.file
+     #~ print ln[0].getParent()
+-
+--- a/jbrout/jbrout/pyexiv.py
++++ b/jbrout/jbrout/pyexiv.py
+@@ -19,7 +19,7 @@
+ pyexiv2 wrapper
+ ===============
+ 
+-map old methods/objects from pyexiv2(<2), to be able to work with versions 1 & 2
++map old methods/objects from pyexiv2(<2), to work with versions 1 & 2
+ 
+ """
+ import re
+@@ -32,72 +32,82 @@ except:
+     sys.exit(-1)
+ 
+ 
+-
+-
+ ###############################################################################
+ class Exiv2Metadata(object):
+ ###############################################################################
+     """ pyexiv2 > 0.2 """
+-    def __init__(self,md):
+-        self._md=md
++    def __init__(self, md):
++        self._md = md
+ 
+     #============================================== V 0.1 api
+     def readMetadata(self):
+         return self._md.read()
++
+     def writeMetadata(self):
+-        self._md["Iptc.Envelope.CharacterSet"] = ['\x1b%G',] # set Charset as UTF8
++        # set CharacterSet as UTF8
++        self._md["Iptc.Envelope.CharacterSet"] = ['\x1b%G', ]
+         return self._md.write()
+-    def __getitem__(self,k):
+-        v=self._md[k]
+-        if hasattr(v,"value"):
++
++    def __getitem__(self, k):
++        v = self._md[k]
++        if hasattr(v, "value"):
+             return v.value
+-        elif hasattr(v,"values"):
++        elif hasattr(v, "values"):
+             return tuple(v.values)
+         else:
+             raise
+-    def __setitem__(self,k,v):
+-        self._md[k]=v
+-    def __delitem__(self,k):
++
++    def __setitem__(self, k, v):
++        self._md[k] = v
++
++    def __delitem__(self, k):
+         del self._md[k]
++
+     def getComment(self):
+         return self._md.comment
+-    def setComment(self,v):
+-        self._md.comment=v
++
++    def setComment(self, v):
++        self._md.comment = v
++
+     def clearComment(self):
+-        self._md.comment=None
++        self._md.comment = None
+ 
+     def getThumbnailData(self):
+-        l=[i.data for i in self._md.previews]
++        l = [i.data for i in self._md.previews]
+         if l:
+-            return [None,l[0]]
++            return [None, l[0]]
+         else:
+             return []
+ 
+-    def setThumbnailData(self,o):
+-        if pyexiv2.version_info > (0,2,2):
++    def setThumbnailData(self, o):
++        if pyexiv2.version_info > (0, 2, 2):
+             self._md.exif_thumbnail.data = o
+         else:
+-            print "***WARNING*** : not implemented : setThumbnailData (you need pyexiv2>0.2.2)"
++            print "***WARNING*** : not implemented : " + \
++            "setThumbnailData (you need pyexiv2>0.2.2)"
++
+     def deleteThumbnail(self):
+-        if pyexiv2.version_info > (0,2,2):
++        if pyexiv2.version_info > (0, 2, 2):
+             self._md.exif_thumbnail.erase()
+         else:
+-            print "***WARNING*** : not implemented : deleteThumbnail (you need pyexiv2>0.2.2)"
++            print "***WARNING*** : not implemented : " + \
++            "deleteThumbnail (you need pyexiv2>0.2.2)"
+ 
+     def exifKeys(self):
+         return self._md.exif_keys
++
+     def iptcKeys(self):
+         return self._md.iptc_keys
+ 
+-    def tagDetails(self,k):               # see viewexif plugin
+-        md=self._md[k]
+-        if hasattr(md,"label"):
+-            lbl=getattr(md,"label")
+-        elif hasattr(md,"title"):
+-            lbl=getattr(md,"title")
+-        return [lbl,md.description,]
++    def tagDetails(self, k):               # see viewexif plugin
++        md = self._md[k]
++        if hasattr(md, "label"):
++            lbl = getattr(md, "label")
++        elif hasattr(md, "title"):
++            lbl = getattr(md, "title")
++        return [lbl, md.description, ]
+ 
+-    def interpretedExifValue(self,k):   # see viewexif plugin
++    def interpretedExifValue(self, k):   # see viewexif plugin
+         return self._md[k].human_value
+     #==============================================
+ 
+@@ -105,20 +115,22 @@ class Exiv2Metadata(object):
+     def xmpKeys(self):
+         return self._md.xmp_keys
+ 
+-
+     def getTags(self):
+         """ return a list of merged tags (xmp+iptc) (list of str)"""
+         # Authoritative reference
+-        # http://www.iptc.org/std/Iptc4xmpCore/1.0/documentation/Iptc4xmpCore_1.0-doc-CpanelsUserGuide_13.pdf
++        # http://www.iptc.org/std/Iptc4xmpCore/1.0/documentation\
++        # /Iptc4xmpCore_1.0-doc-CpanelsUserGuide_13.pdf
+         # however
+-        # http://metadataworkinggroup.com/pdf/mwg_guidance.pdf says on page 35 that
++        # http://metadataworkinggroup.com/pdf/mwg_guidance.pdf page 35:
+         ## IPTC Keywords is mapped to XMP (dc:subject)
+         # It seems that the latter is true ... at least according to
+         # http://trac.yorba.org/wiki/PhotoTags
+ 
+-        li=[]
++        li = []
+         if "Iptc.Application2.Keywords" in self._md.iptc_keys:
+-            li=[str(i.strip("\x00")) for i in self._md["Iptc.Application2.Keywords"].value]    #digikam patch
++            # digikam patch
++            li = [str(i.strip("\x00"))
++                  for i in self._md["Iptc.Application2.Keywords"].value]
+             # assume UTF8
+         lk = []
+         if "Xmp.iptc.Keywords" in self._md.xmp_keys:
+@@ -130,17 +142,17 @@ class Exiv2Metadata(object):
+             for xel in self._md["Xmp.dc.subject"].value:
+                 lx.extend([x.strip() for x in xel.encode("utf-8").split(",")])
+ 
+-        ll=list(set(li+lx+lk))
++        ll = list(set(li + lx + lk))
+         ll.sort()
+         return ll
+ 
+-
+-    def setTags(self,l):
++    def setTags(self, l):
+         for i in l:
+-            assert type(i)==unicode
++            assert type(i) == unicode
+ 
+         if l:
+-            self._md["Iptc.Application2.Keywords"] = [i.encode("utf_8") for i in l]
++            self._md["Iptc.Application2.Keywords"] = \
++                [i.encode("utf_8") for i in l]
+             self._md["Xmp.dc.subject"] = [",".join(l)]
+         else:
+             del self._md["Iptc.Application2.Keywords"]
+@@ -148,7 +160,6 @@ class Exiv2Metadata(object):
+         if 'Xmp.iptc.Keywords' in self._md.xmp_keys:
+             del self._md['Xmp.iptc.Keywords']
+ 
+-
+     def clearTags(self):
+         if "Iptc.Application2.Keywords" in self._md.iptc_keys:
+             del self._md["Iptc.Application2.Keywords"]
+@@ -157,8 +168,8 @@ class Exiv2Metadata(object):
+         if 'Xmp.iptc.Keywords' in self._md.xmp_keys:
+             del self._md['Xmp.iptc.Keywords']
+ 
+-
+-    def copyToFile(self, destFilename, exif=True, iptc=True, xmp=True, comment=True):
++    def copyToFile(self, destFilename, exif=True, iptc=True, xmp=True,
++                   comment=True):
+         dest = pyexiv2.ImageMetadata(destFilename)
+         dest.read()
+         self._md.copy(dest, exif, iptc, xmp, comment)
+@@ -166,65 +177,65 @@ class Exiv2Metadata(object):
+ 
+     #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ 
+-
+-
+-if not hasattr(pyexiv2,"Image"):    # Only here to make the following code
++if not hasattr(pyexiv2, "Image"):    # Only here to make the following code
+     class Fake(object):             # compliant with old objects from 0.1
+-        def __init__(self,f):       # when using 0.2 version
++        def __init__(self, f):       # when using 0.2 version
+             pass                    # else it can't compile ;-)
+-    pyexiv2.Image=Fake
++    pyexiv2.Image = Fake
++
+ 
+ ###############################################################################
+ class Exiv1Metadata(pyexiv2.Image):
+ ###############################################################################
+     """ pyexiv2 < 0.2 """
+-    def __init__(self,f):
+-        pyexiv2.Image.__init__(self,f)
+-
++    def __init__(self, f):
++        pyexiv2.Image.__init__(self, f)
+ 
+     def writeMetadata(self):
+-        self["Iptc.Envelope.CharacterSet"] = ['\x1b%G',]   # set Charset as UTF8
++        # set CharacterSet as UTF8
++        self["Iptc.Envelope.CharacterSet"] = ['\x1b%G', ]
+         return pyexiv2.Image.writeMetadata(self)
+ 
+-
+     #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- new apis
+     def xmpKeys(self):
+         return []
+ 
+     def getTags(self):
+         try:
+-            l=self["Iptc.Application2.Keywords"]
++            l = self["Iptc.Application2.Keywords"]
+             if type(l) == tuple:
+-                ll = [i.strip("\x00") for i in l] # strip("\x00") = digikam patch
++                # strip("\x00") = digikam patch
++                ll = [i.strip("\x00") for i in l]
+                 ll.sort()
+             else:
+-                ll = [l.strip("\x00"),]
++                ll = [l.strip("\x00"), ]
+         except KeyError:
+             ll = []
+         return ll   # many case = list of utf8 strings
+ 
+-    def setTags(self,l):
++    def setTags(self, l):
+         for i in l:
+-            assert type(i)==unicode
++            assert type(i) == unicode
+ 
+         self["Iptc.Application2.Keywords"] = [i.encode("utf_8") for i in l]
+ 
+     def clearTags(self):
+         try:
+-            prec = self["Iptc.Application2.Keywords"] #TODO: to bypass a bug in pyexiv2
++            # TODO: to bypass a bug in pyexiv2
++            prec = self["Iptc.Application2.Keywords"]
+             self["Iptc.Application2.Keywords"] = []
+         except:
+             pass
+ 
+-
+-    def copyToFile(self, destFilename, exif=True, iptc=True, xmp=True, comment=True):
++    def copyToFile(self, destFilename, exif=True, iptc=True, xmp=True,
++                   comment=True):
+         dest = pyexiv2.Image(destFilename)
+         dest.readMetadata()
+         # delete all tags :
+         for i in (dest.exifKeys() + dest.iptcKeys()):
+             try:
+                 del dest[i]
+-            except KeyError: # 'tag not set'
++            except KeyError:  # 'tag not set'
+                 # the tag seems not to be here, so
+                 # we don't need to clear it, no ?
+                 pass
+@@ -234,24 +245,31 @@ class Exiv1Metadata(pyexiv2.Image):
+ 
+         # copy all exif/iptc/xmp/comment info as directed
+         l = []
+-        if exif: l= l+self.exifKeys()
+-        if exif: l= l+self.iptcKeys()
++        if exif:
++            l = l + self.exifKeys()
++        if exif:
++            l = l + self.iptcKeys()
+         for i in l:
+-            if i not in ["Exif.Photo.UserComment",]: # key "Exif.Photo.UserComment" bugs always ?!
+-                if not i.startswith("Exif.Thumbnail"):  # don't need exif.thumb things because it's copied after
+-                    if len(re.findall('0x0',i))==0: # Work around to fix error in pyev2 with most unknown makernoite tags
+-                        # TODO: fix nasty bodge to get around pyexiv2 issues with multi part exif fields
++            # key "Exif.Photo.UserComment" bugs always ?!
++            if i not in ["Exif.Photo.UserComment", ]:
++                # don't need exif.thumb things because it's copied after
++                if not i.startswith("Exif.Thumbnail"):
++                    # Work around to fix error in pyev2 with most unknown
++                    # makernoite tags
++                    if len(re.findall('0x0', i)) == 0:
++                        # TODO: fix nasty bodge to get around pyexiv2 issues
++                        #  with multi part exif fields
+                         # known not to copy the following:
+                         #   - unknown maker not fields
+                         #   - lens data for canon
+                         try:
+-                            dest[i] =self[i]
++                            dest[i] = self[i]
+                         except:
+-                            print "Problems copying %s keyword" %i
++                            print "Problems copying %s keyword" % i
+ 
+         # copy comment
+         if comment:
+-            dest.setComment( self.getComment() )
++            dest.setComment(self.getComment())
+ 
+         # copy exif thumbnail
+         if exif:
+@@ -260,9 +278,8 @@ class Exiv1Metadata(pyexiv2.Image):
+     #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ 
+ 
+-
+ def Image(f):
+-    if hasattr(pyexiv2,"ImageMetadata"):
++    if hasattr(pyexiv2, "ImageMetadata"):
+         # pyexiv2 >= 0.2
+         return Exiv2Metadata(pyexiv2.ImageMetadata(f))
+     else:
+@@ -270,21 +287,21 @@ def Image(f):
+         return Exiv1Metadata(f)
+ 
+ if __name__ == "__main__":
+-    t=Image("/home/manatlan/Documents/python/tests_libs_python/TestJPG/p20030830_130202 (copie).jpg")
+-    #~ t=Image("/home/manatlan/Documents/python/tests_libs_python/TestJPG/p20030830_130202.jpg")
++    t = Image("/home/manatlan/Documents/python/tests_libs_python" + \
++              "/TestJPG/p20030830_130202 (copie).jpg")
+     #~ t=Image("/home/manatlan/Desktop/fotaux/autorot/p20020115_173654(1).jpg")
+     t.readMetadata()
+ 
+     ##----
+     #aa=t._image["Xmp.dc.subject"].raw_value[0]
+-    #import chardet; print chardet.detect(aa) # in fact, it's latin1 encoded as utf8
++    #import chardet; print chardet.detect(aa) # it's latin1 encoded as utf8
+     #print aa.decode("utf_8").encode("latin1")
+     ##----
+ 
+     #t.setThumbnailData("")
+ 
+-    L=t.getTags()
+-    print "===>",L
++    L = t.getTags()
++    print "===>", L
+ 
+     #t=Image("/home/manatlan/Desktop/fotaux/autorot/jpg/p20090319_061423.jpg")
+     #t.readMetadata()
+--- a/jbrout/jbrout/winshow.py
++++ b/jbrout/jbrout/winshow.py
+@@ -11,82 +11,90 @@
+ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ ## GNU General Public License for more details.
+ ##
+-import gtk,gc,pango,gobject,os
+-from __main__ import Buffer,GladeApp,JBrout
++from __main__ import Buffer, GladeApp, JBrout
++from common import cd2rd, format_file_size_for_display
+ from commongtk import WinKeyTag
+-from common import cd2rd,format_file_size_for_display
+ from jbrout.externaltools import ExternalTools
++import gtk
++import gc
++import pango
++import gobject
++import os
+ #TODO: add ops : add/del from basket
+ #TODO: add ops : external tools
+ 
++
+ class TagList(gtk.VBox):
+-    #TODO: this object need to display its parent Hscrollbar when needed (does'nt work ?!)
+-    def __init__(self,callbackRemove):
++    # TODO: this object need to display its parent Hscrollbar
++    # when needed (does'nt work ?!)
++    def __init__(self, callbackRemove):
+         #self.b = gtk.Button()
+         #self.b.set_label("Test bouton")
+         gtk.VBox.__init__(self)
+         self.__callbackRemove = callbackRemove
+-        self.__tags= dict(JBrout.tags.getAllTags())
++        self.__tags = dict(JBrout.tags.getAllTags())
+ 
+-    def fill(self,ll):
+-        ll.sort(lambda a,b: cmp(a.lower(),b.lower()))
++    def fill(self, ll):
++        ll.sort(lambda a, b: cmp(a.lower(), b.lower()))
+         self.__ll = ll
+         self.__refresh()
+ 
+     def __refresh(self):
+-        l=self.get_children()
++        l = self.get_children()
+         for a in l:
+             a.destroy()
+             del a
+ 
+         for i in self.__ll:
+ 
+-            hb=gtk.HBox()
+-            lbl=gtk.Label()
+-            lbl.set_label("%s (%s)" %(i,self.__tags[i]))
+-            hb.pack_start(lbl,False,False)
+-            btn=gtk.Button()
++            hb = gtk.HBox()
++            lbl = gtk.Label()
++            lbl.set_label("%s (%s)" % (i, self.__tags[i]))
++            hb.pack_start(lbl, False, False)
++            btn = gtk.Button()
+             btn.set_label("X")
+-            btn.connect('button-press-event', self.__callbackRemove,i)
+-            hb.pack_end(btn,False)
++            btn.connect('button-press-event', self.__callbackRemove, i)
++            hb.pack_end(btn, False)
+ 
+-            self.pack_start(hb,False)
++            self.pack_start(hb, False)
+ 
+         self.resize_children()
+         self.show_all()
+ 
+ 
+-
+ class WinShow(GladeApp):
+     """ a window that displays an image in full screen
+         it may switch to next/previous image
+         WinShow receives a set of jbrout.db.PhotoNodes
+     """
+-    glade=os.path.join(os.path.dirname(os.path.dirname(__file__)),'data','jbrout.glade')
+-    window="WinShow"
+-
+-    def init(self, ln,idx,showInfo=True,isModify=False,selected=[]):
+-        self.ln=[]+ln
+-        self.idx=idx
+-        if len(selected)>1:
+-            self.selected=selected # to be able to handle a new selection (reselect with space)
++    glade = os.path.join(os.path.dirname(os.path.dirname(__file__)),
++                         'data', 'jbrout.glade')
++    window = "WinShow"
++
++    def init(self, ln, idx, showInfo=True, isModify=False, selected=[]):
++        self.ln = [] + ln
++        self.idx = idx
++        if len(selected) > 1:
++            # to be able to handle a new selection (reselect with space)
++            self.selected = selected
+         else:
+-            self.selected=[]
+-        self.removed=[]  # deleted items
+-        self.invalidThumbs=[]
+-
+-        self.zoom=False
+-
+-        self.win_width=0
+-        self.win_height=0
+-        self.pointer_position = (0,0,0,0) #x, y, screen_width, screen_height
+-
+-        self.isBasketUpdate=False
+-        self.needInfo=showInfo
+-        self.isModify=isModify
++            self.selected = []
++        self.removed = []  # deleted items
++        self.invalidThumbs = []
++
++        self.zoom = False
++
++        self.win_width = 0
++        self.win_height = 0
++        # x, y, screen_width, screen_height
++        self.pointer_position = (0, 0, 0, 0)
++
++        self.isBasketUpdate = False
++        self.needInfo = showInfo
++        self.isModify = isModify
+ 
+-        PixbufCache._file=None
+-        PixbufCache._cache=None
++        PixbufCache._file = None
++        PixbufCache._cache = None
+ 
+         self.taglist = TagList(self.on_remove_tag)
+         self.sc_tags.add_with_viewport(self.taglist)
+@@ -95,36 +103,38 @@ class WinShow(GladeApp):
+ 
+         self.hpShow.add2(self.viewer)
+ 
+-        image=gtk.Image()
++        image = gtk.Image()
+         image.set_from_pixbuf(Buffer.pbBasket)
+         image.show()
+ 
+         self.basket.set_icon_widget(image)
+         self.toolbar1.set_style(gtk.TOOLBAR_ICONS)
+ 
+-        #=======================================================================
++        #======================================================================
+         # put real plugins
+-        #=======================================================================
++        #======================================================================
+         self.tooltips = gtk.Tooltips()
+         if isModify:
+-            l=JBrout.plugins.request("PhotosProcess",isIcon=True)
++            l = JBrout.plugins.request("PhotosProcess", isIcon=True)
+         else:
+-            l=JBrout.plugins.request("PhotosProcess",isIcon=True,isAlter=False)
++            l = JBrout.plugins.request("PhotosProcess", isIcon=True,
++                                       isAlter=False)
+ 
+-        for instance,callback,props in l:
+-            image=gtk.Image()
++        for instance, callback, props in l:
++            image = gtk.Image()
+             image.set_from_file(props["icon"])
+             image.show()
+ 
+             bb = gtk.ToolButton(image)
+             txt = props["label"]
+-            if props["key"]: txt+=" (ctrl + %s)"%props["key"]
++            if props["key"]:
++                txt += " (ctrl + %s)" % props["key"]
+             bb.set_tooltip(self.tooltips, txt)
+-            bb.connect("clicked", self.on_selecteur_menu_select_plugin,callback)
++            bb.connect("clicked", self.on_selecteur_menu_select_plugin,
++                       callback)
+             self.toolbar1.insert(bb, 3)
+             bb.show()
+-        #=======================================================================
+-
++        #====================================================================
+ 
+         self.main_widget.show_all()
+         self.main_widget.fullscreen()
+@@ -132,61 +142,65 @@ class WinShow(GladeApp):
+ 
+         self.draw()
+ 
+-    def on_selecteur_menu_select_plugin(self,ib,callback):
++    def on_selecteur_menu_select_plugin(self, ib, callback):
+         currentNode = self.ln[self.idx]
+         if self.isModify and not currentNode.isReadOnly:
+ 
+-            ret=callback([currentNode,])   #TODO: try/cath here
++            ret = callback([currentNode, ])   # TODO: try/cath here
+ 
+-            if ret: # if change have be done ...
++            if ret:  # if change have be done ...
+                 if currentNode not in self.invalidThumbs:
+                     self.invalidThumbs.append(currentNode)
+ 
+                 self.draw(forceReload=True)
+ 
+-            #TODO: if plugin "redate" is called, we'll need to redraw "time tab"
++            # TODO: if plugin "redate" is called, we'll need to
++            # redraw "time tab"
+ 
+-    def on_eb_scroll_event(self,widget,b):
++    def on_eb_scroll_event(self, widget, b):
+         #print "eb scroll event !"
+-        if int(b.direction)==1:
+-            self.idx+=1
++        if int(b.direction) == 1:
++            self.idx += 1
+         else:
+-            self.idx-=1
++            self.idx -= 1
+         self.draw()
+ 
+     def on_WinShow_key_press_event(self, widget, b):
+-        key= gtk.gdk.keyval_name(b.keyval).lower()
++        key = gtk.gdk.keyval_name(b.keyval).lower()
+         isCtrl = b.state & gtk.gdk.CONTROL_MASK
+         if isCtrl:
+             currentNode = self.ln[self.idx]
+             if self.isModify and not currentNode.isReadOnly:
+-                pluginsWithKey = JBrout.plugins.request("PhotosProcess",isKey=True)
++                pluginsWithKey = JBrout.plugins.request("PhotosProcess",
++                                            isKey=True)
+             else:
+-                pluginsWithKey = JBrout.plugins.request("PhotosProcess",isKey=True,isAlter=False)
++                pluginsWithKey = JBrout.plugins.request("PhotosProcess",
++                                            isKey=True, isAlter=False)
+ 
+-            for instance,callback,props in pluginsWithKey:
+-                if props["key"]==key:
+-                    self.on_selecteur_menu_select_plugin("?!?",callback)   #TODO: what's ib ? see "?!?"
++            for instance, callback, props in pluginsWithKey:
++                if props["key"] == key:
++                    # TODO: what's ib ? see "?!?"
++                    self.on_selecteur_menu_select_plugin("?!?", callback)
+                     return 1
+         else:
+             if (key == "page_up") or (key == "up") or (key == "left"):
+-                self.idx-=1
++                self.idx -= 1
+                 self.draw()
+             elif (key == "page_down") or (key == "down") or (key == "right"):
+-                self.idx+=1
++                self.idx += 1
+                 self.draw()
+-            elif key=="home":
+-                self.idx=0
++            elif key == "home":
++                self.idx = 0
+                 self.draw()
+-            elif key=="end":
+-                self.idx=len(self.ln) -1
++            elif key == "end":
++                self.idx = len(self.ln) - 1
+                 self.draw()
+-            elif key=="escape":
+-                self.quit();
++            elif key == "escape":
++                self.quit()
+ 
+-            elif key=="space":
++            elif key == "space":
+                 # add/remove this photo to selection
+-                node=self.ln[self.idx]
++                node = self.ln[self.idx]
+                 if node in self.selected:
+                     self.selected.remove(node)
+                 else:
+@@ -194,36 +208,37 @@ class WinShow(GladeApp):
+                 self.draw()
+             elif key == "f11":
+                 self.on_zoom_toggled()
+-            elif key=="backspace":
++            elif key == "backspace":
+                 # clear selection
+-                self.selected=[]
++                self.selected = []
+                 self.draw()
+-            elif key=="delete":
++            elif key == "delete":
+                 # delete
+                 self.on_delete_clicked(None)    # and call draw
+-            elif key=="insert" or key=="f9":
++            elif key == "insert" or key == "f9":
+                 self.needInfo = not self.needInfo
+                 self.draw()
+             else:
+                 # print key
+                 currentNode = self.ln[self.idx]
+                 if self.isModify and not currentNode.isReadOnly:
+-                    if b.keyval<255 and b.string.strip()!="":
+-                        wk=WinKeyTag(_("Apply to this photo"),b.string,JBrout.tags.getAllTags())
+-                        ret=wk.loop()
++                    if b.keyval < 255 and b.string.strip() != "":
++                        wk = WinKeyTag(_("Apply to this photo"), b.string,
++                                       JBrout.tags.getAllTags())
++                        ret = wk.loop()
+                         self.main_widget.fullscreen()
+                         if ret:
+                             tag = ret[0]
+                             currentNode.addTag(tag)
+                             self.draw()
+-                    elif b.string>='0' and b.string<='5':
++                    elif b.string >= '0' and b.string <= '5':
+                         # capture keypad 0-5 for rating
+                         currentNode.setRating(int(b.string))
+                         self.draw()
+ 
+                 return 0
+ 
+-    def draw(self,forceReload=False):
++    def draw(self, forceReload=False):
+         """
+         Draws the currently selected photo in the full screen view
+ 
+@@ -231,11 +246,11 @@ class WinShow(GladeApp):
+         cursor = gtk.gdk.Cursor(gtk.gdk.WATCH)
+         self.main_widget.window.set_cursor(cursor)
+ 
+-        gobject.idle_add( self._draw, forceReload )
++        gobject.idle_add(self._draw, forceReload)
+ 
+-    def _draw(self,forceReload):
++    def _draw(self, forceReload):
+         if self.idx >= len(self.ln):
+-            self.idx = len(self.ln)-1
++            self.idx = len(self.ln) - 1
+         if self.idx < 0:
+             self.idx = 0
+ 
+@@ -246,17 +261,17 @@ class WinShow(GladeApp):
+             return
+         try:
+             info = node.getInfo()
+-            ltags=info["tags"]
+-            folder=node.folderName
+-            filename=os.path.basename(node.file)
+-            resolution=info["resolution"]
++            ltags = info["tags"]
++            folder = node.folderName
++            filename = os.path.basename(node.file)
++            resolution = info["resolution"]
+ 
+-            comment=info["comment"]
+-            exifdate=cd2rd(info["exifdate"])
++            comment = info["comment"]
++            exifdate = cd2rd(info["exifdate"])
+     #        filedate=cd2rd(info["filedate"])
+-            filesize=format_file_size_for_display(info["filesize"])
++            filesize = format_file_size_for_display(info["filesize"])
+ 
+-            rating=info["rating"]
++            rating = info["rating"]
+ 
+             msg = _("""
+ %(exifdate)s
+@@ -276,9 +291,9 @@ COMMENT :
+ 
+ TAGS :
+ """) % locals()
+-        except Exception,m:
++        except Exception, m:
+             msg = ""
+-            ltags=[]
++            ltags = []
+             print m
+ 
+         if self.isModify and not node.isReadOnly:
+@@ -288,9 +303,9 @@ TAGS :
+ 
+         self.taglist.fill(ltags)
+ 
+-        d=Display()
++        d = Display()
+         d.node = None
+-        self.viewer.display=d   # prevent toggle event
++        self.viewer.display = d   # prevent toggle event
+         self.basket.set_active(node.isInBasket)
+ 
+         if self.needInfo:
+@@ -298,48 +313,51 @@ TAGS :
+         else:
+             self.vb_info.hide()
+ 
+-        d=Display()
++        d = Display()
+         d.node = node
+         if forceReload:
+-            d.image = PixbufCache().get(node,forceReload)
++            d.image = PixbufCache().get(node, forceReload)
+         else:
+             d.image = PixbufCache().get(node)
+-        d.title = "%d/%d"%(self.idx+1,len(self.ln))
++        d.title = "%d/%d" % (self.idx + 1, len(self.ln))
+         try:
+             self.lbl_info.set_text(msg)
+-        except Exception,m:
++        except Exception, m:
+             self.lbl_info.set_text("")
+-            print "*ERROR* bad characters in jpeg info : ",m
++            print "*ERROR* bad characters in jpeg info : ", m
+         d.isSelected = (node in self.selected)
+         d.nbSelected = len(self.selected)
+         d.rating = rating
+-        self.viewer.show( d,self.zoom,self.pointer_position )
++        self.viewer.show(d, self.zoom, self.pointer_position)
+         gc.collect()
+ 
+         self.main_widget.window.set_cursor(None)
+ 
+-    def on_WinShow_delete_event(self,*args):
++    def on_WinShow_delete_event(self, *args):
+         self.quit()
+ 
+     def on_WinShow_button_press_event(self, widget, data):
+         #print "winshow button press"
+-        screen_width, screen_height=data.window.get_size()
++        screen_width, screen_height = data.window.get_size()
+         pointer_x, pointer_y = data.get_coords()
+-        self.pointer_position=(pointer_x, pointer_y, screen_width,screen_height)
+-        #print "Type %s, button %s, x = %s,y = %s" % (data.type, data.button, self.pointer_position[0], self.pointer_position[1])
+-        if data.button == 1: #left click does zoom
++        self.pointer_position = (pointer_x, pointer_y, screen_width,
++                                 screen_height)
++        # print "Type %s, button %s, x = %s,y = %s" % \
++        # (data.type, data.button, self.pointer_position[0],
++        #  self.pointer_position[1])
++        if data.button == 1:  # left click does zoom
+             self.on_zoom_toggled()
+-        elif data.button == 2 : #center click
++        elif data.button == 2:  # center click
+             self.quit()
+-        else: #button 3 closes
++        else:  # button 3 closes
+             self.quit()
+ 
+-    def on_remove_tag(self,widget,event,tag):
++    def on_remove_tag(self, widget, event, tag):
+         currentNode = self.viewer.display.node
+         currentNode.delTag(tag)
+         self.draw()
+ 
+-    def on_delete_clicked(self,*args):
++    def on_delete_clicked(self, *args):
+         if self.isModify:
+             node = self.ln[self.idx]
+             #currentNode = self.viewer.display.node
+@@ -347,23 +365,24 @@ TAGS :
+             self.removed.append(node)
+             self.draw()
+ 
+-    def on_basket_toggled(self,widget):
++    def on_basket_toggled(self, widget):
+         currentNode = self.viewer.display.node
+         if currentNode:
+             if widget.get_active():
+                 currentNode.addToBasket()
+             else:
+                 currentNode.removeFromBasket()
+-            self.isBasketUpdate=True
++            self.isBasketUpdate = True
+ 
+     def on_zoom_toggled(self):
+-        self.zoom= not self.zoom
++        self.zoom = not self.zoom
+         self.draw()
+ 
++
+ class ImageShow(gtk.DrawingArea):
+     def __init__(self):
+-        self.zoom=False
+-        self.pointer_position=(0,0,0,0) #x,y,window_width,window_height
++        self.zoom = False
++        self.pointer_position = (0, 0, 0, 0)  # x,y,window_width,window_height
+         super(gtk.DrawingArea, self).__init__()
+         self.connect("expose_event", self.expose)
+ 
+@@ -375,7 +394,8 @@ class ImageShow(gtk.DrawingArea):
+         # set a clip region for the expose event
+         context.rectangle(event.area.x, event.area.y,
+                                event.area.width, event.area.height)
+-        #print "expose : x:%s y:%s witdth:%s height:%s" %(event.area.x, event.area.y,event.area.width,event.area.height)
++        # print "expose : x:%s y:%s witdth:%s height:%s" \
++        # %(event.area.x, event.area.y,event.area.width,event.area.height)
+         context.clip()
+ 
+         self.draw(context)
+@@ -383,49 +403,51 @@ class ImageShow(gtk.DrawingArea):
+         return False
+ 
+     def draw(self, context):
+-        fond=(0,0,0,0.4)
++        fond = (0, 0, 0, 0.4)
+         rect = self.get_allocation()
+ 
+-        context.set_source_rgb(0,0,0)
++        context.set_source_rgb(0, 0, 0)
+         context.paint()
+ 
+         if self.display:
+             if self.display.image:
+                 context.save()
+                 #print self.zoom
+-                pb,x,y=render(self.display.image,rect.width,rect.height,self.zoom,self.pointer_position)
+-                context.set_source_pixbuf(pb,x,y)
++                pb, x, y = render(self.display.image, rect.width, rect.height,
++                                  self.zoom, self.pointer_position)
++                context.set_source_pixbuf(pb, x, y)
+                 context.paint()
+                 context.restore()
+ 
+-
+             if self.display.title:
+                 context.set_source_rgba(*fond)
+-                context.rectangle(0,0,200,30)
++                context.rectangle(0, 0, 200, 30)
+                 context.fill()
+ 
+                 title = self.display.title
+                 if self.display.isSelected:
+-                    context.set_source_rgb(1,1,0)
+-                    title+="*"
++                    context.set_source_rgb(1, 1, 0)
++                    title += "*"
+                 else:
+-                    context.set_source_rgb(1,1,1)
++                    context.set_source_rgb(1, 1, 1)
+ 
+-                context.move_to(20,20)
++                context.move_to(20, 20)
+                 context.set_font_size(20)
+                 context.show_text(title)
+ 
+-                if self.display.nbSelected>0:
+-                    context.set_source_rgb(1,1,0)
+-                    context.rel_move_to(5,0)
++                if self.display.nbSelected > 0:
++                    context.set_source_rgb(1, 1, 0)
++                    context.rel_move_to(5, 0)
+                     context.set_font_size(12)
+-                    context.show_text(_("(%d selected)") % self.display.nbSelected)
++                    context.show_text(_("(%d selected)") %
++                                      self.display.nbSelected)
+ 
+                 if self.display.rating:
+-                    context.move_to(rect.width-35, 20)
+-                    context.set_source_rgb(1,1,1)
++                    context.move_to(rect.width - 35, 20)
++                    context.set_source_rgb(1, 1, 1)
+                     context.set_font_size(12)
+-                    context.show_text((self.display.rating*"*")+((5-self.display.rating)*"-"))
++                    context.show_text((self.display.rating * "*")
++                                      + ((5 - self.display.rating) * "-"))
+ 
+             #if self.display.info:
+             #    wx=200
+@@ -439,96 +461,103 @@ class ImageShow(gtk.DrawingArea):
+             #
+             #    layout=context.create_layout ()
+             #    layout.set_text(self.display.info)
+-            #    layout.set_font_description(pango.FontDescription ("courier 8"))
++            #    layout.set_font_description(pango.FontDescription("courie 8"))
+             #    layout.set_width((wx-5)*1000)
+             #    layout.set_wrap(1)
+             #    context.show_layout(layout)
+ 
+-
+-    def show(self,d,zoom=False,pointer_position=()):
++    def show(self, d, zoom=False, pointer_position=()):
+         # store instance display
+         self.display = d
+-        self.zoom=zoom
+-        self.pointer_position=pointer_position
++        self.zoom = zoom
++        self.pointer_position = pointer_position
+ 
+         # and trig expose event to redraw all
+         rect = self.get_allocation()
+-        self.queue_draw_area(0,0,rect.width,rect.height)
++        self.queue_draw_area(0, 0, rect.width, rect.height)
+ 
+-def fit(orig_width, orig_height, dest_width, dest_height,zoom=False,pointer_position=(0,0,0,0)):
++
++def fit(orig_width, orig_height, dest_width, dest_height, zoom=False,
++        pointer_position=(0, 0, 0, 0)):
+     if orig_width == 0 or orig_height == 0:
+         return 0, 0
+-    scale = min(dest_width/orig_width, dest_height/orig_height)
++    scale = min(dest_width / orig_width, dest_height / orig_height)
+     if scale > 1:
+         scale = 1
+-    if zoom==True:
+-        scale=1
++    if zoom == True:
++        scale = 1
+     #print "fit: zoom %s scale %s" % (zoom, scale)
+     fit_width = scale * orig_width
+     fit_height = scale * orig_height
+     return int(fit_width), int(fit_height)
+ 
+-def render(pb,maxw,maxh,zoom=1,pointer_position=(0,0,0,0)):
++
++def render(pb, maxw, maxh, zoom=1, pointer_position=(0, 0, 0, 0)):
+     """ resize pixbuf 'pb' to fit in box maxw*maxh
+         return the new pixbuf and x,y to center it
+     """
+-    (wx,wy) = pb.get_width(),pb.get_height()
+-    dwx,dwy = fit(wx,wy,float(maxw),float(maxh),zoom)
+-    image_x, image_y = fit(wx,wy,float(maxw),float(maxh), False)
+-    pb = pb.scale_simple(dwx,dwy,gtk.gdk.INTERP_NEAREST)
+-    if pointer_position==(0,0,0,0) or zoom==False:
+-        ratiox=ratioy=1.0/2
++    (wx, wy) = pb.get_width(), pb.get_height()
++    dwx, dwy = fit(wx, wy, float(maxw), float(maxh), zoom)
++    image_x, image_y = fit(wx, wy, float(maxw), float(maxh), False)
++    pb = pb.scale_simple(dwx, dwy, gtk.gdk.INTERP_NEAREST)
++    if pointer_position == (0, 0, 0, 0) or zoom == False:
++        ratiox = ratioy = 1.0 / 2
+     else:
+-        (mouse_x,mouse_y,screen_width,screen_height)=pointer_position
++        (mouse_x, mouse_y, screen_width, screen_height) = pointer_position
+ 
+-        mouse_x=max(mouse_x -(screen_width-maxw),0) #remove pane space
+-        mouse_x=max(mouse_x-(maxw-image_x)/2.0, 0)
+-        if mouse_x>image_x:
+-            mouse_x=image_x
+-        mouse_y=max(mouse_y -(screen_height-maxh),0) #remove eventual space
+-        mouse_y=max(mouse_y-(maxh-image_y)/2.0, 0)
+-        if mouse_y>dwy:
+-            mouse_y=dwy
++        mouse_x = max(mouse_x - (screen_width - maxw), 0)  # remove pane space
++        mouse_x = max(mouse_x - (maxw - image_x) / 2.0, 0)
++        if mouse_x > image_x:
++            mouse_x = image_x
++        mouse_y = max(mouse_y - (screen_height - maxh), 0)  # remove all space
++        mouse_y = max(mouse_y - (maxh - image_y) / 2.0, 0)
++        if mouse_y > dwy:
++            mouse_y = dwy
+ 
+-        ratiox=1.0*(mouse_x/image_x)
+-        ratioy=1.0*(mouse_y/image_y)
++        ratiox = 1.0 * (mouse_x / image_x)
++        ratioy = 1.0 * (mouse_y / image_y)
+     if zoom:
+-        x,y=maxw/2 - min(wx-maxw/2, max(maxw/2,dwx*ratiox)), maxh/2 - min(wy-maxh/2, max(maxh/2, dwy*ratioy))
++        x, y = maxw / 2 - min(wx - maxw / 2, max(maxw / 2, dwx * ratiox)), \
++            maxh / 2 - min(wy - maxh / 2, max(maxh / 2, dwy * ratioy))
+     else:
+-        x,y=(maxw - dwx)*ratiox,(maxh - dwy)*ratioy
++        x, y = (maxw - dwx) * ratiox, (maxh - dwy) * ratioy
++
++    return pb, x, y
+ 
+-    return pb,x,y
+ 
+ class Display(object):
+     """ container class to pass params """
+     pass
+-    node=None
++    node = None
++
+ 
+ class PixbufCache(object):
+     """ class to cache pixbuf by filename"""
+-    _cache=None
+-    _file=None
+-    def get(self,node,forceReload=False):
++    _cache = None
++    _file = None
++
++    def get(self, node, forceReload=False):
+         file = node.file
+-        if file == PixbufCache._file :
++        if file == PixbufCache._file:
+             if forceReload:
+-                PixbufCache._cache=node.getImage()
++                PixbufCache._cache = node.getImage()
+         else:
+             PixbufCache._file = file
+             if os.path.isfile(file):
+                 try:
+-                    PixbufCache._cache=node.getImage()
+-                except Exception,m:
+-                    print "*WARNING* can't load this file : ",(file,),m
+-                    PixbufCache._cache=None
++                    PixbufCache._cache = node.getImage()
++                except Exception, m:
++                    print "*WARNING* can't load this file : ", (file,), m
++                    PixbufCache._cache = None
+             else:
+-                PixbufCache._cache=None
++                PixbufCache._cache = None
+         return PixbufCache._cache
+ 
+ 
+ #class WinComment(GladeApp):
+ #    """ Creates and handles the dialog for Editing photo comments """
+-#    glade=os.path.join(os.path.dirname(os.path.dirname(__file__)),'data','jbrout.glade')
++#    glade=os.path.join(os.path.dirname(os.path.dirname(__file__)),'data',
++#        'jbrout.glade')
+ #    window="WinComment"
+ #
+ #    def init(self,comment):
+@@ -544,7 +573,8 @@ class PixbufCache(object):
+ #        """ Handles the rotate right button """
+ #        start=self.tbufComment.get_start_iter()
+ #        end =self.tbufComment.get_end_iter()
+-#        self.quit(True,self.tbufComment.gset_mnemonic_modifieret_text(start,end,False))
++#        self.quit(True,self.tbufComment.\
++#           gset_mnemonic_modifieret_text(start,end,False))
+ #
+ #    def on_WinGetComment_delete_event(self,*args):
+ #        """ Handles window delete (close) events """
+@@ -554,4 +584,3 @@ class PixbufCache(object):
+ if __name__ == "__main__":
+     # self test
+     pass
+-
diff --git a/0002-Overkill-solution-for-illegal-XML-characters.patch b/0002-Overkill-solution-for-illegal-XML-characters.patch
new file mode 100644
index 0000000..ecf87f3
--- /dev/null
+++ b/0002-Overkill-solution-for-illegal-XML-characters.patch
@@ -0,0 +1,139 @@
+--- a/jbrout/jbrout/db.py
++++ b/jbrout/jbrout/db.py
+@@ -914,7 +914,13 @@ class PhotoNode(object):
+                 self.__node.append(nodeTag)
+         if pc.comment:
+             nodeComment = Element("c")
+-            nodeComment.text = pc.comment.replace(u'\x00', u' ')
++            try:
++                nodeComment.text = char_utils.make_xml_string_legal(pc.comment)
++            except ValueError, f:
++                print "exception = %s" % f
++                print "nodeComment = %s" % nodeComment
++                print "pc.comment = %s" % pc.comment
++                raise
+             self.__node.append(nodeComment)
+         if pc.rating:
+             nodeRating = Element("r")
+--- /dev/null
++++ b/jbrout/jbrout/char_utils.py
+@@ -0,0 +1,119 @@
++import re
++# Originally from http://boodebr.org/main/python/all-about-python-and-unicode#UNI_XML
++# http://coreapython.hosting.paran.com/boodebr\
++#    /All%20About%20Python%20and%20Unicode%20%20boodebr_org.htm#UNI_XML
++
++def raw_illegal_xml_regex():
++    """    
++    I want to define a regexp to match *illegal* characters.
++    That way, I can do "re.search()" to find a single character,
++    instead of "re.match()" to match the entire string. [Based on
++    my assumption that .search() would be faster in this case.]
++
++    Here is a verbose map of the XML character space (as defined
++    in section 2.2 of the XML specification):
++    
++         u0000 - u0008           = Illegal
++         u0009 - u000A           = Legal
++         u000B - u000C           = Illegal
++         u000D                   = Legal
++         u000E - u001F           = Illegal
++         u0020 - uD7FF           = Legal
++         uD800 - uDFFF           = Illegal (See note!)
++         uE000 - uFFFD           = Legal
++         uFFFE - uFFFF           = Illegal
++         U00010000 - U0010FFFF = Legal (See note!)
++    
++    Note:
++    
++       The range U00010000 - U0010FFFF is coded as 2-character sequences
++       using the codes (D800-DBFF),(DC00-DFFF), which are both illegal
++       when used as single chars, from above.
++    
++       Python won't let you define \U character ranges, so you can't
++       just say '\U00010000-\U0010FFFF'. However, you can take advantage
++       of the fact that (D800-DBFF) and (DC00-DFFF) are illegal, unless
++       part of a 2-character sequence, to match for the \U characters.
++    """
++
++    # First, add a group for all the basic illegal areas above
++    re_xml_illegal = u'([\u0000-\u0008\u000b-\u000c\u000e-\u001f\ufffe-\uffff])'
++
++    re_xml_illegal += u"|"
++
++    # Next, we know that (uD800-uDBFF) must ALWAYS be followed by (uDC00-uDFFF),
++    # and (uDC00-uDFFF) must ALWAYS be preceded by (uD800-uDBFF), so this
++    # is how we check for the U00010000-U0010FFFF range. There are also special
++    # case checks for start & end of string cases.
++
++    # I've defined this oddly due to the bug mentioned at the top of this file
++    re_xml_illegal += u'([%s-%s][^%s-%s])|([^%s-%s][%s-%s])|([%s-%s]$)|(^[%s-%s])' % \
++                      (unichr(0xd800), unichr(0xdbff), unichr(0xdc00), unichr(0xdfff),
++                       unichr(0xd800), unichr(0xdbff), unichr(0xdc00), unichr(0xdfff),
++                       unichr(0xd800), unichr(0xdbff), unichr(0xdc00), unichr(0xdfff))
++
++    return re_xml_illegal
++
++def make_illegal_xml_regex():
++    return re.compile(raw_illegal_xml_regex())
++
++c_re_xml_illegal = make_illegal_xml_regex()
++
++def is_legal_xml(uval):
++    """
++    Given a Unicode object, figure out if it is legal
++    to place it in an XML file.
++    """
++    return (c_re_xml_illegal.search(uval) == None)
++
++def make_xml_string_legal(instr):
++    return c_re_xml_illegal.sub('', instr)
++
++def is_legal_xml_char(uchar):
++    """
++    Check if a single unicode char is XML-legal.
++    (This is faster that running the full 'is_legal_xml()' regexp
++    when you need to go character-at-a-time. For string-at-a-time
++    of course you want to use is_legal_xml().)
++
++    USAGE NOTE:
++       If you want to use this in a 'for' loop,
++       make sure use usplit(), e.g.:
++          
++       for c in usplit( uval ):
++          if is_legal_xml_char(c):
++                 ... 
++
++       Otherwise, the first char of a legal 2-character
++       sequence will be incorrectly tagged as illegal, on
++       Pythons where \U is stored as 2-chars.
++    """
++
++    # due to inconsistencies in how \U is handled (based on
++    # how Python was compiled) it is shorter to test for
++    # illegal chars than legal ones, and invert the result.
++    #
++    # (as one example: (u'\ud900' > u'\U00100000') can be True,
++    # depending on how Python was compiled. Testing for illegal chars
++    # lets us stick with the single char sequences (all 2-char
++    # sequences are legal for XML).
++
++    if len(uchar) == 1:
++        return not \
++               (
++               (uchar >= u'\u0000' and uchar <= u'\u0008') or \
++               (uchar >= u'\u000b' and uchar <= u'\u000c') or \
++               (uchar >= u'\u000e' and uchar <= u'\u001f') or \
++               # always illegal as single chars
++               (uchar >= unichr(0xd800) and uchar <= unichr(0xdfff)) or \
++               (uchar >= u'\ufffe' and uchar <= u'\uffff')
++               )
++    elif len(uchar) == 2:
++        # all 2-char codings are legal in XML
++        # (this looks weird, but remember that even after calling
++        # usplit(), \U00010000 is STILL len() of 2, usplit() just
++        # made it a single listitem
++        return True
++
++    else:
++        raise Exception("Must pass a single character to is_legal_xml_char")
diff --git a/0003-IPTC-charset-backtrace.patch b/0003-IPTC-charset-backtrace.patch
new file mode 100644
index 0000000..7bae7d2
--- /dev/null
+++ b/0003-IPTC-charset-backtrace.patch
@@ -0,0 +1,35 @@
+--- a/jbrout/jbrout/pyexiv.py
++++ b/jbrout/jbrout/pyexiv.py
+@@ -31,6 +31,9 @@ except:
+     print "You should install pyexiv2 (>=0.1.2)"
+     sys.exit(-1)
+ 
++import logging
++logging.basicConfig(format='%(levelname)s:%(funcName)s:%(message)s',
++    level=logging.DEBUG)
+ 
+ ###############################################################################
+ class Exiv2Metadata(object):
+@@ -45,7 +48,21 @@ class Exiv2Metadata(object):
+ 
+     def writeMetadata(self):
+         # set CharacterSet as UTF8
+-        self._md["Iptc.Envelope.CharacterSet"] = ['\x1b%G', ]
++        logging.debug("self._md.iptc_keys = %s" % self._md.iptc_keys)
++        logging.debug("self._md.iptc_charset = %s" % hasattr(self._md, "iptc_charset"))
++        # For compatibility with pyexiv2 >= 0.3.2
++        if hasattr(self._md, "iptc_charset"):
++            # remove Iptc.Envelope.CharacterSet tag
++            # it doesn't work with pyexiv2 O.3.2 which wants to do everything
++            # on its own.
++            if "Iptc.Envelope.CharacterSet" in self._md.iptc_keys:
++                del self._md["Iptc.Envelope.CharacterSet"]
++
++            # Currently pyexiv2.IptcTag("Iptc.Envelope.CharacterSet", ['\x1b%G',])
++            # fails with Invalidvalue error. FIXME
++            #self._md.iptc_charset = "utf-8"
++        else:
++            self._md["Iptc.Envelope.CharacterSet"] = ['\x1b%G', ] # set Charset as UTF8
+         return self._md.write()
+ 
+     def __getitem__(self, k):
diff --git a/0004-Gtk-Tooltips-depreceated.patch b/0004-Gtk-Tooltips-depreceated.patch
new file mode 100644
index 0000000..30d08ac
--- /dev/null
+++ b/0004-Gtk-Tooltips-depreceated.patch
@@ -0,0 +1,39 @@
+--- a/jbrout/jbrout.py
++++ b/jbrout/jbrout.py
+@@ -947,8 +947,6 @@ class Window(GladeApp):
+ 
+ 
+         # build the "plugins buttons"
+-        self.tooltips = gtk.Tooltips()
+-
+         if JBrout.modify:
+             l=JBrout.plugins.request("PhotosProcess",isIcon=True)
+         else:
+@@ -962,7 +960,7 @@ class Window(GladeApp):
+             bb = gtk.ToolButton(image)
+             txt = props["label"]
+             if props["key"]: txt+=" (ctrl + %s)"%props["key"]
+-            bb.set_tooltip(self.tooltips, txt)
++            bb.set_tooltip_text(txt)
+             bb.connect("clicked", self.on_selecteur_menu_select_plugin,table,instance.id,callback)
+             self.toolbar.insert(bb, 3)
+             bb.show()
+--- a/jbrout/jbrout/winshow.py
++++ b/jbrout/jbrout/winshow.py
+@@ -113,7 +113,6 @@ class WinShow(GladeApp):
+         #======================================================================
+         # put real plugins
+         #======================================================================
+-        self.tooltips = gtk.Tooltips()
+         if isModify:
+             l = JBrout.plugins.request("PhotosProcess", isIcon=True)
+         else:
+@@ -129,7 +128,7 @@ class WinShow(GladeApp):
+             txt = props["label"]
+             if props["key"]:
+                 txt += " (ctrl + %s)" % props["key"]
+-            bb.set_tooltip(self.tooltips, txt)
++            bb.set_tooltip_text(txt)
+             bb.connect("clicked", self.on_selecteur_menu_select_plugin,
+                        callback)
+             self.toolbar1.insert(bb, 3)
diff --git a/jbrout-no-pyexiv2-warning.patch b/jbrout-no-pyexiv2-warning.patch
index 0d740d1..09ddf7c 100644
--- a/jbrout-no-pyexiv2-warning.patch
+++ b/jbrout-no-pyexiv2-warning.patch
@@ -1,21 +1,6 @@
-Index: jbrout/jbrout/jbrout.py
-===================================================================
---- jbrout.orig/jbrout/jbrout.py
-+++ jbrout/jbrout/jbrout.py
-@@ -3117,9 +3117,6 @@ if __name__ == "__main__":
-         psyco.full()
-     except:
-         print "The psyco module does not seem to be installed. It is not necessary, however it can speed up performance."
--    # Print pyexiv2-0.2+ warning if necessary
--    from jbrout.pyexiv import Check
--    Check()
- 
-     try:
-         parser = optparse.OptionParser(usage=USAGE, version=("JBrout "+__version__))
-Index: jbrout/jbrout/jbrout/pyexiv.py
-===================================================================
---- jbrout.orig/jbrout/jbrout/pyexiv.py
-+++ jbrout/jbrout/jbrout/pyexiv.py
+diff -up jbrout/jbrout/jbrout/pyexiv.py.noPyexiv2Warn jbrout/jbrout/jbrout/pyexiv.py
+--- jbrout/jbrout/jbrout/pyexiv.py.noPyexiv2Warn	2011-07-07 10:34:19.000000000 +0200
++++ jbrout/jbrout/jbrout/pyexiv.py	2012-01-14 22:13:06.227033871 +0100
 @@ -269,11 +269,6 @@ def Image(f):
          # pyexiv2 < 0.2
          return Exiv1Metadata(f)
@@ -28,3 +13,16 @@ Index: jbrout/jbrout/jbrout/pyexiv.py
  if __name__ == "__main__":
      t=Image("/home/manatlan/Documents/python/tests_libs_python/TestJPG/p20030830_130202 (copie).jpg")
      #~ t=Image("/home/manatlan/Documents/python/tests_libs_python/TestJPG/p20030830_130202.jpg")
+diff -up jbrout/jbrout/jbrout.py.noPyexiv2Warn jbrout/jbrout/jbrout.py
+--- jbrout/jbrout/jbrout.py.noPyexiv2Warn	2011-07-07 10:34:19.000000000 +0200
++++ jbrout/jbrout/jbrout.py	2012-01-14 22:13:06.227033871 +0100
+@@ -3117,9 +3117,6 @@ if __name__ == "__main__":
+         psyco.full()
+     except:
+         print "The psyco module does not seem to be installed. It is not necessary, however it can speed up performance."
+-    # Print pyexiv2-0.2+ warning if necessary
+-    from jbrout.pyexiv import Check
+-    Check()
+ 
+     try:
+         parser = optparse.OptionParser(usage=USAGE, version=("JBrout "+__version__))
diff --git a/jbrout-purge-no-attribute-attrib.patch b/jbrout-purge-no-attribute-attrib.patch
index 074a08e..aa6abb1 100644
--- a/jbrout-purge-no-attribute-attrib.patch
+++ b/jbrout-purge-no-attribute-attrib.patch
@@ -1,7 +1,6 @@
-Index: jbrout/jbrout/jbrout/db.py
-===================================================================
---- jbrout.orig/jbrout/jbrout/db.py
-+++ jbrout/jbrout/jbrout/db.py
+diff -up jbrout/jbrout/jbrout/db.py.noAttributeAttrib jbrout/jbrout/jbrout/db.py
+--- jbrout/jbrout/jbrout/db.py.noAttributeAttrib	2011-07-07 10:34:19.000000000 +0200
++++ jbrout/jbrout/jbrout/db.py	2012-01-14 22:12:13.400916607 +0100
 @@ -14,6 +14,7 @@
  ##
  
diff --git a/jbrout.spec b/jbrout.spec
index d522467..55373f8 100644
--- a/jbrout.spec
+++ b/jbrout.spec
@@ -4,7 +4,7 @@
 Name:           jbrout
 Version:        0.3.%{revno}
 %if 0%{?svn_checkout}
-Release:        0.4.svn%{revno}%{?dist}
+Release:        0.5.svn%{revno}.MC.1%{?dist}
 %else
 Release:        1%{?dist}
 %endif
@@ -30,6 +30,15 @@ Patch0:         jbrout-purge-no-attribute-attrib.patch
 # Fedora users have no choice about what pyexiv2 to use.
 # Besides pyexiv2 0.3.* is now officially stable.
 Patch1:         jbrout-no-pyexiv2-warning.patch
+# Reformat some files to make Eclipse happy
+Patch2:         0001-Reformatting-to-make-code-PEP8-compatible.patch
+# Finally get rid of bad XML characters!
+Patch3:         0002-Overkill-solution-for-illegal-XML-characters.patch
+# Problems with Iptc.Envelope.CharacterSet
+Patch4:         0003-IPTC-charset-backtrace.patch
+# Gtk.Tooltips has been depreceated a long time ago
+Patch5:			0004-Gtk-Tooltips-depreceated.patch
+
 BuildArch:      noarch
 BuildRoot:      %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
 Requires:       python >= 2.4, python-lxml, pygtk2 >= 2.6
@@ -62,6 +71,11 @@ jBrout is able to :
 %setup -q -n %{name}
 %patch0 -p1 -b .noAttributeAttrib
 %patch1 -p1 -b .noPyexiv2Warn
+%patch2 -p1 -b .EclipseReformat
+%patch3 -p1 -b .illegalXMLchar
+%patch4 -p1 -b .charSet
+%patch5 -p1 -b .GtkTooltip
+
 sh %{SOURCE5}
 install -p %{SOURCE1} jbrout/Makefile
 install -p -m a+rx,u+w %{SOURCE4} jbrout/install-script
@@ -99,6 +113,9 @@ rm -rf $RPM_BUILD_ROOT
 %{_datadir}/applications/jbrout.desktop
 
 %changelog
+* Sat Jan 14 2012 'Matej Cepl <mcepl at redhat.com>' - 0.3.338-0.5.svn338.MC.1
+- EXPERIMENTAL support for pyexiv2 0.3.2 ... produces invalid IPTC tags!!!
+
 * Thu Oct 27 2011 Matěj Cepl <mcepl at redhat.com> - 0.3.338-0.4.svn338
 - fix find cleaning .exe and .dll (closes #749425)
 


More information about the scm-commits mailing list