def __init__(self, parent, id, title,  *args, **kwargs):
        wx.Frame.__init__(self, parent, id, title, *args, **kwargs)
        self.frameTitlePrefix = title
        self.simpleView = False

        # Image handling
        self.initImageHandling()
        # Load config from file
        self.loadConfigFromFile()

        # Setting up the menu
        filemenu = wx.Menu()
        menuOpenF = filemenu.Append(wx.ID_OPEN, "Open &File"," Open an image file to view and browse")
        menuOpenD = filemenu.Append(wx.ID_ANY, "Open &Dir"," Open a directory to browse images")
        menuOpenU = filemenu.Append(wx.ID_ANY, "Open &URL"," Open an image file from a web URL (http://...)")
        menuAbout = filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
        menuExit  = filemenu.Append(wx.ID_EXIT,"E&xit"," Terminate the program")
        settmenu = wx.Menu()
        menuGUIview = settmenu.AppendCheckItem(wx.ID_ANY, "Show simple GUI")
        menuGUIview.Check(self.simpleView)
        menuOpenR = settmenu.AppendCheckItem(wx.ID_ANY, "Browse directories recursively")
        menuOpenR.Check(self.browseRecursively)
        menuDlTmp = settmenu.AppendCheckItem(wx.ID_ANY, "Download URL images to ~/.mdviewer (instead of TMP)")
        menuDlTmp.Check(self.downloadURLstoHOME)
        menupicontacts = settmenu.Append(wx.ID_ANY,"Set Picasa &contacts.xml path","Set filepath to Picasa's contacts.xml (if not default or could not be found)")
        menuesavecfg = settmenu.Append(wx.ID_ANY,"Save &configuration now","Save configuration to file")

        # Creating the menubar
        menuBar = wx.MenuBar()
        menuBar.Append(filemenu,"&File")
        menuBar.Append(settmenu,"&Settings")
        self.SetMenuBar(menuBar)

        # Events for menus
        self.Bind(wx.EVT_MENU, self.OnOpenFile, menuOpenF)
        self.Bind(wx.EVT_MENU, self.OnOpenDir, menuOpenD)
        self.Bind(wx.EVT_MENU, self.OnOpenURL, menuOpenU)
        self.Bind(wx.EVT_MENU, self.GUISimpleView, menuGUIview)
        self.Bind(wx.EVT_MENU, self.OnOpenRec, menuOpenR)
        self.Bind(wx.EVT_MENU, self.OpenURLTmpHome, menuDlTmp)
        self.Bind(wx.EVT_MENU, self.saveConfigToFile, menuesavecfg)
        self.Bind(wx.EVT_MENU, self.setPicasaContacts, menupicontacts)
        self.Bind(wx.EVT_MENU, self.OnExit, menuExit)
        self.Bind(wx.EVT_MENU, self.OnAbout, menuAbout)

        if self.simpleView == True:
          # Define sizer
          sizer = wx.BoxSizer(wx.VERTICAL)
          self.imagepanel  = ImagePanel(self, size=(400,300))
          self.privmdpanel = PrivateMDPanel(self)
          sizer.Add(self.imagepanel,  4, wx.EXPAND)
          sizer.Add(self.privmdpanel, 1, wx.EXPAND)
          self.SetSizer(sizer)
        else:
          # Define splitter
          self.splitNS  = ProportionalSplitter(self,         -1, 0.66)
          self.splitNWO = ProportionalSplitter(self.splitNS, -1, 0.75)
          self.splitSWO = ProportionalSplitter(self.splitNS, -1, 0.60)
          # Define content
          self.imagepanel  = ImagePanel(self.splitNWO, size=(200,150))
          self.mappanel    = MapPanel(self.splitNWO)
          self.privmdpanel = PrivateMDPanel(self.splitSWO)
          self.mdtreepanel = MDTreePanel(self.splitSWO)
          # Define splitting
          self.splitNS.SplitHorizontally(self.splitNWO, self.splitSWO)
          self.splitNWO.SplitVertically(self.imagepanel, self.mappanel)
          self.splitSWO.SplitVertically(self.privmdpanel, self.mdtreepanel)

          # Setup map
          self.mappanel.create_map()

        # Events for GUI elements
        self.imagepanel.Bind(wx.EVT_KEY_UP, self.OnKeyUp)

        # Show
        self.Show()
class MainWindow(wx.Frame):
    def __init__(self, parent, id, title,  *args, **kwargs):
        wx.Frame.__init__(self, parent, id, title, *args, **kwargs)
        self.frameTitlePrefix = title
        self.simpleView = False

        # Image handling
        self.initImageHandling()
        # Load config from file
        self.loadConfigFromFile()

        # Setting up the menu
        filemenu = wx.Menu()
        menuOpenF = filemenu.Append(wx.ID_OPEN, "Open &File"," Open an image file to view and browse")
        menuOpenD = filemenu.Append(wx.ID_ANY, "Open &Dir"," Open a directory to browse images")
        menuOpenU = filemenu.Append(wx.ID_ANY, "Open &URL"," Open an image file from a web URL (http://...)")
        menuAbout = filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
        menuExit  = filemenu.Append(wx.ID_EXIT,"E&xit"," Terminate the program")
        settmenu = wx.Menu()
        menuGUIview = settmenu.AppendCheckItem(wx.ID_ANY, "Show simple GUI")
        menuGUIview.Check(self.simpleView)
        menuOpenR = settmenu.AppendCheckItem(wx.ID_ANY, "Browse directories recursively")
        menuOpenR.Check(self.browseRecursively)
        menuDlTmp = settmenu.AppendCheckItem(wx.ID_ANY, "Download URL images to ~/.mdviewer (instead of TMP)")
        menuDlTmp.Check(self.downloadURLstoHOME)
        menupicontacts = settmenu.Append(wx.ID_ANY,"Set Picasa &contacts.xml path","Set filepath to Picasa's contacts.xml (if not default or could not be found)")
        menuesavecfg = settmenu.Append(wx.ID_ANY,"Save &configuration now","Save configuration to file")

        # Creating the menubar
        menuBar = wx.MenuBar()
        menuBar.Append(filemenu,"&File")
        menuBar.Append(settmenu,"&Settings")
        self.SetMenuBar(menuBar)

        # Events for menus
        self.Bind(wx.EVT_MENU, self.OnOpenFile, menuOpenF)
        self.Bind(wx.EVT_MENU, self.OnOpenDir, menuOpenD)
        self.Bind(wx.EVT_MENU, self.OnOpenURL, menuOpenU)
        self.Bind(wx.EVT_MENU, self.GUISimpleView, menuGUIview)
        self.Bind(wx.EVT_MENU, self.OnOpenRec, menuOpenR)
        self.Bind(wx.EVT_MENU, self.OpenURLTmpHome, menuDlTmp)
        self.Bind(wx.EVT_MENU, self.saveConfigToFile, menuesavecfg)
        self.Bind(wx.EVT_MENU, self.setPicasaContacts, menupicontacts)
        self.Bind(wx.EVT_MENU, self.OnExit, menuExit)
        self.Bind(wx.EVT_MENU, self.OnAbout, menuAbout)

        if self.simpleView == True:
          # Define sizer
          sizer = wx.BoxSizer(wx.VERTICAL)
          self.imagepanel  = ImagePanel(self, size=(400,300))
          self.privmdpanel = PrivateMDPanel(self)
          sizer.Add(self.imagepanel,  4, wx.EXPAND)
          sizer.Add(self.privmdpanel, 1, wx.EXPAND)
          self.SetSizer(sizer)
        else:
          # Define splitter
          self.splitNS  = ProportionalSplitter(self,         -1, 0.66)
          self.splitNWO = ProportionalSplitter(self.splitNS, -1, 0.75)
          self.splitSWO = ProportionalSplitter(self.splitNS, -1, 0.60)
          # Define content
          self.imagepanel  = ImagePanel(self.splitNWO, size=(200,150))
          self.mappanel    = MapPanel(self.splitNWO)
          self.privmdpanel = PrivateMDPanel(self.splitSWO)
          self.mdtreepanel = MDTreePanel(self.splitSWO)
          # Define splitting
          self.splitNS.SplitHorizontally(self.splitNWO, self.splitSWO)
          self.splitNWO.SplitVertically(self.imagepanel, self.mappanel)
          self.splitSWO.SplitVertically(self.privmdpanel, self.mdtreepanel)

          # Setup map
          self.mappanel.create_map()

        # Events for GUI elements
        self.imagepanel.Bind(wx.EVT_KEY_UP, self.OnKeyUp)

        # Show
        self.Show()

    def initImageHandling(self):
        self.dirname = os.getcwd()
        self.filename = ''
        self.filelist = []
        self.currentfileid = 0
        self.image_metadata = {}
        self.picasa_faces = None
        self.picasaContactsFile = None
        self.lastImageLoadedDir = ''
        self.browseRecursively = False
        self.downloaddir = None
        self.tempdir = None
        self.downloadURLstoHOME = False
        
    def OnAbout(self,e):
        dlg = wx.MessageDialog(self, " Photo Private Metadata Viewer \n by Benjamin Henne \n<*****@*****.**>\n", "About photo private_metadata viewer", wx.OK)
        dlg.ShowModal()
        dlg.Destroy()

    def OnExit(self,e):
        self.Close(True)

    def OnOpenFile(self,e):
        dlg = wx.FileDialog(self, "Choose an image file", self.dirname, "", "*.*", wx.OPEN)
        if dlg.ShowModal() == wx.ID_OK:
            self.ImplOpenDirFile(os.path.dirname(os.path.abspath(dlg.GetDirectory())+'/'), os.path.basename(dlg.GetFilename()), type='file')
        dlg.Destroy()
        
    def OnOpenDir(self,e):
        dlg = wx.DirDialog(self, "Choose a directory containing images", self.dirname)
        if dlg.ShowModal() == wx.ID_OK:
            self.ImplOpenDirFile(os.path.dirname(os.path.abspath(dlg.GetPath())+'/'), '', type='file')
        dlg.Destroy()
        
    def OnOpenURL(self, e):
        dlg = wx.TextEntryDialog(self, "Open File from web URL:", "Open URL")
        dlg.SetValue("http://")
        if dlg.ShowModal() == wx.ID_OK:
            value = dlg.GetValue()
            if not (value.startswith('http://') or value.startswith('https://')):
                value = 'http://'+value
            self.ImplOpenDirFile(os.path.dirname(value), os.path.basename(value), type='URL')
        dlg.Destroy()

    def setPicasaContacts(self, e):
        dlg = PicasaContactsDialog(self, "Choose your Picasa contacts.xml storing face names")
        if self.picasaContactsFile is not None:
            dlg.SetValue(self.picasaContactsFile)
        if dlg.ShowModal() == wx.ID_OK:
            value = dlg.GetValue()
            if value != '':
                self.picasaContactsFile = value
            else:
                self.picasaContactsFile = None
        dlg.Destroy()
        self.loadImage(picasaReload=True)

    def OnOpenRec(self, e):
        self.browseRecursively = not self.browseRecursively
        self.ImplOpenDirFile(self.dirname, self.filename)

    def OpenURLTmpHome(self, e):
        self.downloadURLstoHOME = not self.downloadURLstoHOME
        self.downloaddir = None

    def GUISimpleView(self, e):
        self.simpleView = not self.simpleView
        dlg = wx.MessageDialog(self, "You have to 1) save configuration and 2) restart the viewer to apply changes to GUI.", "Have to restart", wx.OK | wx.ICON_INFORMATION)
        dlg.ShowModal()
        dlg.Destroy()

    def OnKeyUp(self, event):
        keycode = event.GetKeyCode()
        if keycode == wx.WXK_ESCAPE:
            ret  = wx.MessageBox('Are you sure to quit?', 'Question', 
                wx.YES_NO | wx.NO_DEFAULT, self)
            if ret == wx.YES:
                self.Close()
        elif (keycode == wx.WXK_RIGHT) or (keycode == wx.WXK_NUMPAD_RIGHT) or (keycode == 83):
            self.ImplNextImage()
        elif (keycode == wx.WXK_LEFT) or (keycode == wx.WXK_NUMPAD_LEFT) or (keycode == 65):
            self.ImplPreviousImage()
        event.Skip()

    def loadConfigFromFile(self):
        home = os.getenv('HOME') or os.getenv('USERPROFILE')
        cfgfile = home+'/.mdviewer/'+'mdviewer.cfg'
        if os.path.isfile(cfgfile):
            import ConfigParser
            c = ConfigParser.SafeConfigParser({ 'browserecursively': str(self.browseRecursively),
                                                'dltohome': str(self.downloadURLstoHOME),
                                                'picasaini': str(self.picasaContactsFile),
                                                'simpleview': str(self.simpleView),
                                                })
            c.read(cfgfile)                                    
            self.browseRecursively = c.getboolean('mdviewer', 'browserecursively')
            self.downloadURLstoHOME = c.getboolean('mdviewer', 'dltohome')
            self.picasaContactsFile = c.get('mdviewer', 'picasaini')
            self.simpleView = c.getboolean('mdviewer', 'simpleview')

    def saveConfigToFile(self, e):
        home = os.getenv('HOME') or os.getenv('USERPROFILE')
        dir = home+'/.mdviewer/'
        if not os.path.isdir(dir):
            os.mkdir(dir)
        import ConfigParser
        c = ConfigParser.SafeConfigParser()
        c.add_section('mdviewer')
        c.set('mdviewer', 'browserecursively', str(self.browseRecursively))
        c.set('mdviewer', 'dltohome', str(self.downloadURLstoHOME))
        c.set('mdviewer', 'picasaini', str(self.picasaContactsFile))
        c.set('mdviewer', 'simpleview', str(self.simpleView))
        cfile = open(dir+'mdviewer.cfg', 'wb')
        c.write(cfile)
        cfile.close()

    def getJpgs(self, dir, recursive):
        list = []
        if os.path.isdir(dir):
            if recursive == False:
                for file in os.listdir(dir):
                    if file.lower().endswith('.jpg'):
                        list.append(os.path.join(dir, file))
            else:
                for path, paths, files in os.walk(dir):
                    for file in files:
                        if file.lower().endswith('.jpg'):
                            list.append(os.path.join(path, file))
        return sorted(list)

    def loadImage(self, picasaReload=False):
        #TODO: try+except IOError
    
        def shortenedfilename(flen=30, dlen=50):
            base = self.filename
            dir = self.dirname
            if len(base) > flen:
                base = base[0:flen/2-2]+'[..]'+base[-flen/2-2:]
            if len(dir) > dlen - len(base):
                dir = dir[0:(dlen - len(base))/2-2]+'[..]'+dir[-(dlen - len(base))/2-2:]
            return '%s/%s' % (dir, base)
        
        self.image_metadata = {}
        if self.filename != '':
            # picasa metadata from .picasa.ini
            if self.dirname != self.lastImageLoadedDir or picasaReload == True:
                    if os.path.isfile(os.path.join(self.dirname, '.picasa.ini')):
                        self.picasa_faces = picasa_faces.picasa_faces.PicasaIniFaces(self.dirname, picasa_faces.picasa_faces.PicasaContacts(self.picasaContactsFile))
            # image metadata from image file
            self.md = private_metadata.PrivateMetadata(self.fullname)
            try:
                self.md.read()
            except IOError as strerror:
                raise IOError('Could not read metadata from file: %s' % strerror)
            self.parseMetadata(self.md, self.picasa_faces)
            self.imagepanel.setImage(self.fullname, rects=self.image_rects)
        else:
            placeholder = (wx.EmptyImage(*self.GetSize()))
            placeholder.ConvertColourToAlpha(0,0,0)
            self.imagepanel.setImage(placeholder)
        self.privmdpanel.text.SetValue(str(self.image_metadata))
        frameTitleSuffix = ': %s' % shortenedfilename() if self.filename != '' else ''
        self.SetTitle(self.frameTitlePrefix+frameTitleSuffix)
        self.lastImageLoadedDir = self.dirname
        if self.simpleView == False:
            self.mdtree = private_metadata.MetadataTree(self.md, nsprefix=False)
            self.mdtreepanel.setMetadata(self.mdtree)
            self.privmdpanel.text.SetValue(str(self.image_metadata))
            if 'WGS84' in self.image_metadata:
                loc = self.image_metadata['WGS84']
                self.mappanel.greyedOverlay = None
                self.mappanel.points = [[loc[0], loc[1]]]
                self.mappanel.point_colours = ['red'] * len(self.mappanel.points)
                #TODO: read location accuracy from GPSDOP and draw rectangle/circle
                #    ! we cannot calculate any accuracy rectangle by any DOP
                #self.mappanel.rectangles = [[0, 0, 1, 1]]
                #self.mappanel.rectangle_colours = ['red']
                self.mappanel.center = self.mappanel.points[0]
                self.mappanel.Refresh()
            else:
                self.mappanel.greyedOverlay = [ 50, 80 ]
                self.mappanel.points = []
                self.mappanel.rectangles = []
                self.mappanel.Refresh()


    def parseMetadata(self, md, picasa_faces):
        rects = []
        people = []
        if picasa_faces is not None and self.filename in picasa_faces.faces and \
           picasa_faces.faces[self.filename] is not None and len(picasa_faces.faces[self.filename]) > 0:
                for i in picasa_faces.faces[self.filename]:
                    people.append(i[0])
                    rects.append([ i[0], i[1][0], i[1][1], i[1][2], i[1][3], 'blue' ])
        mpri = md.privateMetadata['people:mpri'].value
        if mpri is not None and len(mpri) > 0:
            for i in mpri:
                people.append(i[1])
                if i[0] & private_metadata.MdMPRI.RECTANGLE:
                    rects.append([ i[1], i[2], i[3], i[4], i[5], 'red' ])
        mwgrs = md.privateMetadata['people:mwgrs'].value
        if mwgrs is not None and len(mwgrs) > 0:
            for i in mwgrs:
                people.append(i[1])
                if i[0] & private_metadata.MdMwgRs.RECTANGLE:
                    rects.append([ i[1], i[2]-i[4]/2, i[3]-i[5]/2, i[4], i[5], 'green' ])
                if i[0] & private_metadata.MdMwgRs.CIRCLE:
                    # use diameter as width and height
                    rects.append([ i[1], i[2]-i[4]/2, i[3]-i[4]/2, i[4], i[4], 'green' ])
                if i[0] & private_metadata.MdMwgRs.POINT:
                    d = 0.04
                    rects.append([ i[1], i[2]-d/2, i[3]-d/2, d, d, 'green' ])
        mediaproppl = md.privateMetadata['people:mediapro'].value
        if mediaproppl is not None:
            people.append(mediaproppl)
        iptc4extPII = md.privateMetadata['people:iptc4ext'].value
        if iptc4extPII is not None:
            people.append(iptc4extPII)

        if len(rects) > 0:
            self.image_rects = rects
        else:
            self.image_rects = None
        self.image_metadata['people'] = sorted(people)
	if md.privateMetadata['location:wgs84'].value is not None:
		self.image_metadata['WGS84'] = md.privateMetadata['location:wgs84'].value

    def ImplOpenDirFile(self, directory, filename, type='file'):
            if type == 'file':
                self.ImplNextImage = self.ImplNextImageLocal
                self.ImplPreviousImage = self.ImplPreviousImageLocal
            elif type == 'URL':
                self.ImplNextImage = self.ImplNextImageLocal
                self.ImplPreviousImage = self.ImplPreviousImageLocal
                #self.ImplNextImage = lambda *x, **xx: None # we can override this to browse image only, e.g. via Flickr API
                #self.ImplNextImage = lambda *x, **xx: None # or any other api, if using service specifiv url. browse only using
                # IOString instead of downloading image files. Or download, but browse images at service ...
                if self.downloaddir == None:
                    home = os.getenv('HOME') or os.getenv('USERPROFILE')
                    if self.downloadURLstoHOME == True and home is not None:
                        userdir = home+'/.mdviewer/'
                        if not os.path.isdir(userdir):
                            os.mkdir(userdir, 0755)
                        self.downloaddir = userdir
                    else:
                        if self.tempdir == None:
                            import tempfile
                            self.tempdir = tempfile.mkdtemp()
                        self.downloaddir = self.tempdir
                import urllib2
                try:
                    dlfile = open('%s/%s' % (self.downloaddir, filename), 'wb')
                    dlfile.write(urllib2.urlopen('%s/%s' % (directory, filename)).read())
                    dlfile.close()
                    directory = self.downloaddir
                except IOError:
                    sys.stderr.write('URL not found: %s/%s' % (directory, filename))
                    directory = self.dirname
                    filename = self.filename
            self.dirname = directory
            self.filename = filename
            self.fullname = os.path.join(self.dirname, self.filename)
            self.filelist = self.getJpgs(self.dirname, self.browseRecursively)
            if len(self.filelist) > 0:
                if self.filename == '':
                    self.currentfileid = 0
                    self.filename = os.path.basename(self.filelist[self.currentfileid])
                    self.dirname = os.path.dirname(self.filelist[self.currentfileid])
                    self.fullname = os.path.join(self.dirname, self.filename)
                else:
                    self.currentfileid = self.filelist.index(self.fullname)

            self.loadImage()

    def ImplNextImage(self):
        pass
        
    def ImplPreviousImage(self):
        pass

    def ImplNextImageLocal(self):
        try:
            self.currentfileid = (self.currentfileid + 1) % len(self.filelist)
            self.filename = os.path.basename(self.filelist[self.currentfileid])
            self.dirname = os.path.dirname(self.filelist[self.currentfileid])
            self.fullname = os.path.join(self.dirname, self.filename)
            self.loadImage()
        except ZeroDivisionError:
            pass

    def ImplPreviousImageLocal(self):
        try:
            self.currentfileid = (self.currentfileid - 1) % len(self.filelist)
            self.filename = os.path.basename(self.filelist[self.currentfileid])
            self.dirname = os.path.dirname(self.filelist[self.currentfileid])
            self.fullname = os.path.join(self.dirname, self.filename)
            self.loadImage()
        except ZeroDivisionError:
            pass