Esempio n. 1
0
    def __init__(self, config, albumpath, title, x, y, w, h):
        ListView.__init__(self, title, x, y, w, h)
        ButtonView.__init__(self, align="center")
        self.AddButton("↑", "Go up")
        self.AddButton("↓", "Go down")
        self.AddButton("c", "Clean name")
        self.AddButton("e", "Edit name")
        #self.AddButton("␣", "Toggle")
        #self.AddButton("↵", "Commit")
        #self.AddButton("␛", "Cancel")

        # elements are a tuple (original path, new path)

        self.cfg = config
        self.fs = Filesystem(self.cfg.music.path)
        self.albumpath = albumpath

        self.nameinput = FileNameInput()
        self.numberinput = TextInput()
        self.cdnuminput = TextInput()
        dialogh = 2 + 3
        self.dialog = Dialog("Rename Song", self.x, self.y + 1, self.w,
                             dialogh)
        self.dialog.AddInput("Song name:", self.nameinput, "Correct name only")
        self.dialog.AddInput("Song number:", self.numberinput,
                             "Song number only")
        self.dialog.AddInput("CD number:", self.cdnuminput,
                             "CD number or nothing")
        self.dialogmode = False
Esempio n. 2
0
    def __init__(self, config, database, title, x, y, w, h):
        ListView.__init__(self, title, x, y, w, h)
        MusicDBTags.__init__(self, config, database)

        self.UpdateView()

        self.dialog = Dialog("Edit Mood", self.x, self.y + 1, self.w,
                             self.h - 1)
        self.dialogmode = False

        self.nameinput = TextInput()
        self.iconinput = TextInput()
        self.colorinput = TextInput()
        self.posxinput = TextInput()
        self.posyinput = TextInput()
        self.varselector = BoolInput()
        self.dialog.AddInput("Name:", self.nameinput, "Visibly for user")
        self.dialog.AddInput("Icon:", self.iconinput, "Unicode char")
        self.dialog.AddInput("U+FE0E:", self.varselector,
                             "Do not replace with emoji")
        self.dialog.AddInput("Color:", self.colorinput,
                             "In HTML notation (#RRGGBB)")
        self.dialog.AddInput("X:", self.posxinput,
                             "X coordinate on grid (positive integer)")
        self.dialog.AddInput("Y:", self.posyinput,
                             "X coordinate on grid (positive integer)")

        self.moodgridcrossref = None
Esempio n. 3
0
    def __init__(self, config, database, title, x, y, w, h):
        ListView.__init__(self, title, x, y, w, h)
        MusicDBTags.__init__(self, config, database)
        self.UpdateView()

        self.dialog     = Dialog("Edit Genre", self.x, self.y+1, self.w, self.h-1)
        self.dialogmode = False
        self.nameinput  = TextInput()
        self.posxinput  = TextInput()
        self.dialog.AddInput("Name:",     self.nameinput, "Visibly for user")
        self.dialog.AddInput("Position:", self.posxinput, "Position in WebUI list (positive integer)")
Esempio n. 4
0
    def __init__(self, config, database, genreview, title, x, y, w, h):
        ListView.__init__(self, title, x, y, w, h)
        MusicDBTags.__init__(self, config, database)

        if type(genreview) != GenreView:
            raise TypeError("genreview must be of type GenreView!")
        self.genreview = genreview
        self.UpdateView()

        self.dialog     = Dialog("Edit Subgenre", self.x, self.y+1, self.w, self.h-1)
        self.dialogmode = False
        self.nameinput  = TextInput()
        self.dialog.AddInput("Name:",  self.nameinput, "Visibly for user")
Esempio n. 5
0
class GenreView(ListView, MusicDBTags):
    def __init__(self, config, database, title, x, y, w, h):
        ListView.__init__(self, title, x, y, w, h)
        MusicDBTags.__init__(self, config, database)
        self.UpdateView()

        self.dialog     = Dialog("Edit Genre", self.x, self.y+1, self.w, self.h-1)
        self.dialogmode = False
        self.nameinput  = TextInput()
        self.posxinput  = TextInput()
        self.dialog.AddInput("Name:",     self.nameinput, "Visibly for user")
        self.dialog.AddInput("Position:", self.posxinput, "Position in WebUI list (positive integer)")


    def UpdateView(self):
        self.SetData(self.GetAllGenres())


    def Draw(self):
        # Only draw the list view when not in dialog mode
        if self.dialogmode == False:
            ListView.Draw(self)


    def onDrawElement(self, element, number, maxwidth):
        # Render Position
        posx = element["posx"]
        posy = element["posy"]
        if posx == None:
            posx = "--"
        else:
            posx = "%2d"%(posx)

        pos    = " (" + posx + ")"

        # Render Name
        width = maxwidth - len(pos)
        name  = element["name"]
        name  = name[:width]
        name  = name.ljust(width)
        return name + "\033[1;30m" + pos


    def onAction(self, element, key):
        if key == "e":      # Edit tag
            name = element["name"]
            posx = element["posx"]

            # Initialize dialog with element
            self.nameinput.SetData(name)
            self.posxinput.SetData(str(posx))

            # show dialog
            self.dialog.oldname = name # trace Tag so that changes can be associated to a specific tag
            self.dialog.Draw()
            self.dialogmode = True

        elif key == "r":    # Remove Tag
            self.DeleteGenre(element["name"])
            self.elements.remove(element)
            self.UpdateView()
            self.Draw()
            return None

        return element


    def HandleKey(self, key):
        if self.dialogmode == True:
            if key == "enter":  # Commit dialog inputs
                self.dialogmode = False
                self.Draw() # show list view instead of dialog

                # Get data from dialog
                name = self.nameinput.GetData()
                posx = self.posxinput.GetData()
                oldname = self.dialog.oldname

                try:
                    posx = int(posx)
                    assert posx >= 0
                except:
                    posx = None # do not update an invalid position

                # Update database with new data
                if oldname == None:
                    self.CreateGenre(name)
                    tagname = name
                    newname = None
                else:
                    tagname = oldname
                    # Was the tag renamed?
                    if oldname != name:
                        newname = name
                    else:
                        newname = None

                self.ModifyGenre(tagname, newname, newposx=posx)
                self.UpdateView()
                self.Draw()


            elif key == "escape":
                self.dialogmode     = False
                self.dialog.oldname = None  # prevent errors by leaving a clean state
                self.Draw() # show list view instead of dialog
                # reject changes

            else:
                self.dialog.HandleKey(key)
        else:
            if key == "a":
                # Add new tag
                self.nameinput.SetData("")
                self.posxinput.SetData("")
                self.dialog.oldname = None    # new tag has no old name
                self.dialog.Draw()
                self.dialogmode = True
            else:
                ListView.HandleKey(self, key)
Esempio n. 6
0
class SubGenreView(ListView, MusicDBTags):
    def __init__(self, config, database, genreview, title, x, y, w, h):
        ListView.__init__(self, title, x, y, w, h)
        MusicDBTags.__init__(self, config, database)

        if type(genreview) != GenreView:
            raise TypeError("genreview must be of type GenreView!")
        self.genreview = genreview
        self.UpdateView()

        self.dialog     = Dialog("Edit Subgenre", self.x, self.y+1, self.w, self.h-1)
        self.dialogmode = False
        self.nameinput  = TextInput()
        self.dialog.AddInput("Name:",  self.nameinput, "Visibly for user")


    def UpdateView(self):
        # Only show subgenres of the selected genre
        genre     = self.genreview.GetSelectedData()
        subgenres = self.GetAllSubgenres()
        elements  = [ subgenre for subgenre in subgenres if subgenre["parentid"] == genre["id"] ]

        self.SetData(elements)


    def Draw(self):
        # Only draw the list view when not in dialog mode
        if self.dialogmode == False:
            ListView.Draw(self)


    def onDrawElement(self, element, number, maxwidth):
        name  = element["name"]
        name  = name[:maxwidth]
        name  = name.ljust(maxwidth)
        return name


    def onAction(self, element, key):
        if key == "e":      # Edit tag
            name = element["name"]

            # Initialize dialog with element
            self.nameinput.SetData(name)

            # show dialog
            parent     = self.genreview.GetSelectedData()
            parentname = parent["name"]

            self.dialog.oldname    = name # trace Tag so that changes can be associated to a specific tag
            self.dialog.parentname = parentname
            self.dialog.Draw()
            self.dialogmode = True

        elif key == "r":    # Remove Tag
            self.DeleteSubgenre(element["name"])
            self.elements.remove(element)
            self.UpdateView()
            self.Draw()
            return None

        return element


    def HandleKey(self, key):
        if self.dialogmode == True:
            if key == "enter":  # Commit dialog inputs
                self.dialogmode = False
                self.Draw() # show list view instead of dialog

                # Get data from dialog
                name       = self.nameinput.GetData()
                oldname    = self.dialog.oldname
                parentname = self.dialog.parentname

                # Update database with new data
                if oldname == None:
                    self.CreateSubgenre(name, parentname)
                    tagname = name
                    newname = None
                else:
                    tagname = oldname
                    # Was the tag renamed?
                    if oldname != name:
                        newname = name
                    else:
                        newname = None

                self.ModifySubgenre(tagname, newname)
                self.UpdateView()
                self.Draw()


            elif key == "escape":
                self.dialogmode        = False
                self.dialog.oldname    = None  # prevent errors by leaving a clean state
                self.dialog.parentname = None
                self.Draw() # show list view instead of dialog
                # reject changes

            else:
                self.dialog.HandleKey(key)
        else:
            if key == "a":
                # Add new tag
                self.nameinput.SetData("")

                parent     = self.genreview.GetSelectedData()
                parentname = parent["name"]

                self.dialog.oldname    = None    # new tag has no old name
                self.dialog.parentname = parentname
                self.dialog.Draw()
                self.dialogmode = True
            else:
                ListView.HandleKey(self, key)
Esempio n. 7
0
    def ShowImportDialog(self, albumpath):
        self.cli.ShowCursor(False)
        self.cli.ClearScreen()
        self.cli.SetColor("1;30", "40")
        self.cli.SetCursor(1, 1)
        self.cli.PrintText("Press Ctrl-D to exit without any changes.")
        self.cli.SetCursor(1, 2)
        self.cli.PrintText(
            "\033[1;31m✘\033[1;30m marks invalid data, \033[1;32m✔\033[1;30m marks valid data."
        )
        self.cli.SetCursor(1, 3)
        self.cli.PrintText("MusicDB file naming scheme:")
        self.cli.SetCursor(3, 4)
        self.cli.PrintText(
            "{artistname}/{albumrelease} - {albumname}/{songnumber} {songname}.{extension}"
        )
        self.cli.SetCursor(3, 5)
        self.cli.PrintText(
            "{artistname}/{albumrelease} - {albumname}/{cdnumber}-{songnumber} {songname}.{extension}"
        )

        # Calculate the positions of the UI element
        headh = 5  # n lines high headline
        pathh = 2
        dialogx = 1
        dialogy = 2 + headh
        dialogw = self.maxw - 2
        dialogh = 9
        listx = 1
        listy = dialogy + dialogh + 1
        listw = self.maxw - 2
        listh = self.maxh - (headh + dialogh + pathh + 3 * 2)
        pathx = dialogx + 1
        pathw = dialogw - 2
        pathy = listy + listh + 1

        # List Views
        songview = SongView(self.cfg, albumpath, "New Songs", listx, listy,
                            listw, listh)
        albumdialog = Dialog("Album Import", dialogx, dialogy, dialogw,
                             dialogh)
        albumdialog.RemoveButton("Cancel")
        albumdialog.RemoveButton("Commit")

        artistinput = FileNameInput()
        nameinput = FileNameInput()
        releaseinput = TextInput()
        origininput = TextInput()
        artworkinput = BoolInput()
        musicaiinput = BoolInput()
        lyricsinput = BoolInput()

        albumdialog.AddInput("Artist name:", artistinput,
                             "Correct name of the album artist")
        albumdialog.AddInput("Album name:", nameinput,
                             "Correct name of the album (no release year)")
        albumdialog.AddInput("Release date:", releaseinput,
                             "Year with 4 digits like \"2017\"")
        albumdialog.AddInput(
            "Origin:", origininput,
            "\"iTunes\", \"bandcamp\", \"CD\", \"internet\", \"music163\"")
        albumdialog.AddInput("Import artwork:", artworkinput,
                             "Import the artwork to MusicDB")
        albumdialog.AddInput("Import lyrics:", lyricsinput,
                             "Try to import lyrics from file")
        albumdialog.AddInput("Predict genre:", musicaiinput,
                             "Runs MusicAI to predict the song genres")

        # Initialize dialog
        albumdirname = os.path.split(albumpath)[1]
        artistdirname = os.path.split(albumpath)[0]
        metadata = self.GetAlbumMetadata(albumpath)
        if metadata:
            origin = str(metadata["origin"])
        else:
            origin = "Internet"
        analresult = self.fs.AnalyseAlbumDirectoryName(albumdirname)
        if analresult:
            release = str(analresult["release"])
            albumname = analresult["name"]
        else:
            # suggest the release date from meta data - the user can change when it's wrong
            if metadata:
                release = str(metadata["releaseyear"])
            else:
                release = "20??"
            albumname = albumdirname

        artistinput.SetData(artistdirname)
        nameinput.SetData(albumname)
        releaseinput.SetData(release)
        origininput.SetData(origin)
        artworkinput.SetData(True)

        if self.cfg.debug.disableai:
            musicaiinput.SetData(False)
        else:
            musicaiinput.SetData(True)

        if metadata["lyrics"]:
            lyricsinput.SetData(True)
        else:
            lyricsinput.SetData(False)

        # Initialize list
        songview.UpdateUI()

        # Buttons
        buttons = ButtonView(align="middle")
        buttons.AddButton("↹", "Select list")
        buttons.AddButton("W", "Rename files Write to database")

        # Composition
        tabgroup = TabGroup()
        tabgroup.AddPane(albumdialog)
        tabgroup.AddPane(songview)

        # Draw once
        buttons.Draw(0, self.maxh - 2, self.maxw)

        while True:
            artistname = artistinput.GetData()
            albumname = nameinput.GetData()
            releasedate = releaseinput.GetData()
            origin = origininput.GetData()
            artwork = artworkinput.GetData()
            lyrics = lyricsinput.GetData()
            musicai = musicaiinput.GetData()

            # Show everything
            songview.Draw()
            albumdialog.Draw()
            self.ShowArtistValidation(artistname, pathx, pathy, pathw)
            self.ShowAlbumValidation(artistname, albumname, releasedate, pathx,
                                     pathy + 1, pathw)
            self.cli.FlushScreen()

            # Handle keys
            key = self.cli.GetKey()
            if key == "Ctrl-D":
                return None

            elif key == "W":
                # Returns a dicrionary
                data = {}
                data["oldalbumpath"] = albumpath
                data["artistname"] = artistname
                data["albumname"] = albumname
                data["releasedate"] = releasedate
                data["origin"] = origin
                data["runartwork"] = artwork
                data["runlyrics"] = lyrics
                data["runmusicai"] = musicai
                data["songs"] = songview.GetData()
                return data

            else:
                tabgroup.HandleKey(key)
Esempio n. 8
0
class SongView(ListView, ButtonView):
    def __init__(self, config, albumpath, title, x, y, w, h):
        ListView.__init__(self, title, x, y, w, h)
        ButtonView.__init__(self, align="center")
        self.AddButton("↑", "Go up")
        self.AddButton("↓", "Go down")
        self.AddButton("c", "Clean name")
        self.AddButton("e", "Edit name")
        #self.AddButton("␣", "Toggle")
        #self.AddButton("↵", "Commit")
        #self.AddButton("␛", "Cancel")

        # elements are a tuple (original path, new path)

        self.cfg = config
        self.fs = Filesystem(self.cfg.music.path)
        self.albumpath = albumpath

        self.nameinput = FileNameInput()
        self.numberinput = TextInput()
        self.cdnuminput = TextInput()
        dialogh = 2 + 3
        self.dialog = Dialog("Rename Song", self.x, self.y + 1, self.w,
                             dialogh)
        self.dialog.AddInput("Song name:", self.nameinput, "Correct name only")
        self.dialog.AddInput("Song number:", self.numberinput,
                             "Song number only")
        self.dialog.AddInput("CD number:", self.cdnuminput,
                             "CD number or nothing")
        self.dialogmode = False

    def FindSongs(self):
        files = self.fs.GetFiles(self.albumpath, self.cfg.music.ignoresongs)
        songs = []
        # Only take audio files into account - ignore images and booklets
        for f in files:
            extension = self.fs.GetFileExtension(f)
            if extension in ["mp3", "flac", "m4a", "aac"]:
                songs.append((f, f))
        return songs

    def CleanFileNames(self):
        for index, element in enumerate(self.elements):
            origpath = element[0]
            path = element[1]
            directory = self.fs.GetDirectory(path)
            filename = self.fs.GetFileName(path)
            extension = self.fs.GetFileExtension(path)
            seg = self.FileNameSegments(filename)

            newfilename = filename[seg["number"]:seg["gap"]]
            newfilename += filename[seg["name"]:]
            newfilename = unicodedata.normalize("NFC", newfilename)

            newpath = os.path.join(directory, newfilename + "." + extension)
            self.elements[index] = (origpath, newpath)

    # no path, no file extension!
    # returns indices of name segments
    def FileNameSegments(self, filename):
        seg = {}

        # Start of song number
        m = re.search("\d", filename)
        if m:
            seg["number"] = m.start()
        else:
            seg["number"] = 0

        # End of song number (1 space is necessary)
        m = re.search("\s", filename[seg["number"]:])
        if m:
            seg["gap"] = seg["number"] + 1 + m.start()
        else:
            seg["gap"] = seg["number"] + 1

        # Find start of song name
        m = re.search("\w", filename[seg["gap"]:])
        if m:
            seg["name"] = seg["gap"] + m.start()
        else:
            seg["name"] = seg["gap"]

        return seg

    def UpdateUI(self):
        newsongs = self.FindSongs()
        self.SetData(newsongs)

    def onDrawElement(self, element, number, maxwidth):
        oldpath = element[0]
        path = element[1]
        width = maxwidth
        filename = self.fs.GetFileName(path)
        extension = self.fs.GetFileExtension(path)
        analresult = self.fs.AnalyseSongFileName(filename + "." + extension)

        # Render validation
        if not analresult:
            validation = "\033[1;31m ✘ "
        else:
            validation = "\033[1;32m ✔ "
        width -= 3

        # Render file name
        renderedname = ""
        width -= len(filename)
        seg = self.FileNameSegments(filename)
        renderedname += "\033[1;31m\033[4m" + filename[
            0:seg["number"]] + "\033[24m"
        renderedname += "\033[1;34m" + filename[seg["number"]:seg["gap"]]
        renderedname += "\033[1;31m\033[4m" + filename[
            seg["gap"]:seg["name"]] + "\033[24m"
        renderedname += "\033[1;34m" + filename[seg["name"]:]

        # Render file extension
        fileextension = "." + extension
        fileextension = fileextension[:width]
        fileextension = fileextension.ljust(width)
        return validation + "\033[1;34m" + renderedname + "\033[1;30m" + fileextension

    def Draw(self):
        if self.dialogmode == True:
            pass
        else:
            ListView.Draw(self)
            x = self.x + 1
            y = self.y + self.h - 1
            w = self.w - 2
            ButtonView.Draw(self, x, y, w)

    def HandleKey(self, key):
        if self.dialogmode == True:
            if key == "enter":  # Commit dialog inputs
                songname = self.nameinput.GetData()
                songnumber = self.numberinput.GetData()
                cdnumber = self.cdnuminput.GetData()

                element = self.dialog.oldelement
                path = element[
                    1]  # the editable path is 1, 0 is the original path
                directory = self.fs.GetDirectory(path)
                extension = self.fs.GetFileExtension(path)

                if len(songnumber) == 1:
                    songnumber = "0" + songnumber
                if cdnumber:
                    songnumber = cdnumber + "-" + songnumber

                newpath = os.path.join(
                    directory, songnumber + " " + songname + "." + extension)
                self.SetSelectedData((element[0], newpath))

                self.dialogmode = False
                self.Draw()  # show list view instead of dialog

            elif key == "escape":
                self.dialogmode = False
                self.dialog.oldname = None  # prevent errors by leaving a clean state
                self.Draw()  # show list view instead of dialog
                # reject changes

            else:
                self.dialog.HandleKey(key)

        else:
            if key == "up" or key == "down":
                ListView.HandleKey(self, key)

            elif key == "c":
                self.CleanFileNames()

            elif key == "e":  # edit name
                element = self.GetSelectedData()
                editpath = element[1]
                filename = self.fs.GetFileName(editpath)
                seg = self.FileNameSegments(filename)
                songnumber = filename[seg["number"]:seg["gap"]].strip()
                songname = filename[seg["name"]:].strip()

                if "-" in songnumber:
                    cdnumber = songnumber.split("-")[0].strip()
                    songnumber = songnumber.split("-")[1].strip()
                else:
                    cdnumber = ""

                self.nameinput.SetData(songname)
                self.numberinput.SetData(songnumber)
                self.cdnuminput.SetData(cdnumber)

                self.dialog.oldelement = element
                self.dialog.Draw()
                self.dialogmode = True
Esempio n. 9
0
 def HandleKey(self, key):
     # Replace slash with DIVISION SLASH
     if key == "/":
         key = "∕"
     TextInput.HandleKey(self, key)
Esempio n. 10
0
 def __init__(self, x=0, y=0, w=0):
     TextInput.__init__(self, x, y, w)
Esempio n. 11
0
class MoodView(ListView, MusicDBTags):
    def __init__(self, config, database, title, x, y, w, h):
        ListView.__init__(self, title, x, y, w, h)
        MusicDBTags.__init__(self, config, database)

        self.UpdateView()

        self.dialog = Dialog("Edit Mood", self.x, self.y + 1, self.w,
                             self.h - 1)
        self.dialogmode = False

        self.nameinput = TextInput()
        self.iconinput = TextInput()
        self.colorinput = TextInput()
        self.posxinput = TextInput()
        self.posyinput = TextInput()
        self.varselector = BoolInput()
        self.dialog.AddInput("Name:", self.nameinput, "Visibly for user")
        self.dialog.AddInput("Icon:", self.iconinput, "Unicode char")
        self.dialog.AddInput("U+FE0E:", self.varselector,
                             "Do not replace with emoji")
        self.dialog.AddInput("Color:", self.colorinput,
                             "In HTML notation (#RRGGBB)")
        self.dialog.AddInput("X:", self.posxinput,
                             "X coordinate on grid (positive integer)")
        self.dialog.AddInput("Y:", self.posyinput,
                             "X coordinate on grid (positive integer)")

        self.moodgridcrossref = None

    def UpdateView(self):
        self.SetData(self.GetAllMoods())

    def onDrawElement(self, element, number, maxwidth):
        # prints the following information
        # Icon Name Position Type

        width = maxwidth  # trace left width during rendering. Start with maximum

        # Render Icon
        if element["icontype"] == 1:  # unicode icon
            icon = element["icon"]
        else:
            icon = "?"

        # there may be a modifier "\xef\xb8\x8e" at the end to prevent some silly browser
        #  to replace the Unicode characters with ugly emoji images that totally f**k up the design
        if len(icon) > 1:
            icon = icon[0]

        icon += " "
        width -= 2

        # Render icon type (currently, only type 1 is supported by MusicDB)
        if element["icontype"] == 1:  # Unicode
            icontype = " type:txt"
        elif element["icontype"] == 2:  # HTML tag
            icontype = " type:htm"
        elif element["icontype"] == 3:  # png image
            icontype = " type:png"
        elif element["icontype"] == 4:  # svg graphic
            icontype = " type:svg"

        width -= len(icontype)

        # Render Icon Color
        if element["color"]:
            color = HTMLColorToANSI(element["color"])
        else:
            color = HTMLColorToANSI("#CCCCCC")

        # Render Position
        posx = element["posx"]
        posy = element["posy"]
        if posx == None:
            posx = "--"
        else:
            posx = "%2d" % (posx)
        if posy == None:
            posy = "--"
        else:
            posy = "%2d" % (posy)

        pos = " (" + posx + ";" + posy + ")"
        width -= len(pos)

        # Render Name
        name = element["name"]
        name = name[:width]
        name = name.ljust(width)
        return color + icon + "\033[1;34m" + name + "\033[1;30m" + pos + icontype

    def Draw(self):
        # Only draw the list view when not in dialog mode
        if self.dialogmode == False:
            ListView.Draw(self)

    def onAction(self, element, key):
        if key == "e":  # Edit tag
            name = element["name"]
            posx = element["posx"]
            posy = element["posy"]

            if element["icon"] == None:
                icon = ""
            else:
                icon = element["icon"][0]

            # Check if variant selector 15 is append to the Unicode character
            if len(element["icon"]) > 1:
                varsec15 = True
            else:
                varsec15 = False

            # Check if there is a color defined
            if element["color"]:
                color = element["color"]
            else:
                color = ""

            # Initialize dialog with element
            self.nameinput.SetData(name)
            self.iconinput.SetData(icon)
            self.varselector.SetData(varsec15)
            self.colorinput.SetData(color)
            self.posxinput.SetData(str(posx))
            self.posyinput.SetData(str(posy))

            # show dialog
            self.dialog.oldname = element[
                "name"]  # trace Tag so that changes can be associated to a specific tag
            self.dialog.Draw()
            self.dialogmode = True

        elif key == "r":  # Remove Tag
            self.DeleteMood(element["name"])
            self.elements.remove(element)
            self.UpdateView()
            self.Draw()
            # Synchronize new state with mood grid
            self.moodgridcrossref.UpdateView()
            self.moodgridcrossref.Draw()
            return None

        return element

    def HandleKey(self, key):
        if self.dialogmode == True:
            if key == "enter":  # Commit dialog inputs
                self.dialogmode = False
                self.Draw()  # show list view instead of dialog

                # Get data from dialog
                name = self.nameinput.GetData()
                icon = self.iconinput.GetData()
                posx = self.posxinput.GetData()
                posy = self.posyinput.GetData()

                try:
                    posx = int(posx)
                    assert posx >= 0
                except:
                    posx = None  # do not update an invalid position
                try:
                    posy = int(posy)
                    assert posy >= 0
                except:
                    posy = None  # do not update an invalid position

                if self.varselector.GetData() == True:
                    icon += b"\xef\xb8\x8e".decode()
                color = self.colorinput.GetData()

                if len(color) != 7 or color[0] != "#":
                    color = None
                oldname = self.dialog.oldname

                # Update database with new data
                if oldname == None:
                    self.CreateMood(name)
                    tagname = name
                    newname = None
                else:
                    tagname = oldname
                    # Was the tag renamed?
                    if oldname != name:
                        newname = name
                    else:
                        newname = None

                self.ModifyMood(tagname, newname, icon, color, posx, posy)
                self.UpdateView()
                self.Draw()
                # Synchronize new state with mood grid
                self.moodgridcrossref.UpdateView()
                self.moodgridcrossref.Draw()

            elif key == "escape":
                self.dialogmode = False
                self.dialog.oldname = None  # prevent errors by leaving a clean state
                self.Draw()  # show list view instead of dialog
                # reject changes

            else:
                self.dialog.HandleKey(key)
        else:
            if key == "a":
                # Add new tag
                self.nameinput.SetData("")
                self.iconinput.SetData("")
                self.varselector.SetData(False)
                self.colorinput.SetData("")
                self.dialog.oldname = None  # new tag has no old name
                self.dialog.Draw()
                self.dialogmode = True
            else:
                ListView.HandleKey(self, key)