Example #1
0
    def __init__(self, options, parent=None):
        """options is a list of strings. Each a comma-delimited field list.

        Eg. ['artist, title', 'album, genre']
        """

        QDialog.__init__(self, parent)
        connect = lambda c, signal, s: self.connect(c, SIGNAL(signal), s)
        self.listbox = ListBox()
        self.listbox.setSelectionMode(self.listbox.ExtendedSelection)

        buttons = ListButtons()

        self.listbox.addItems(options)
        hbox = QHBoxLayout()
        hbox.addWidget(self.listbox, 1)

        hbox.addLayout(buttons)

        okcancel = OKCancel()
        vbox = QVBoxLayout()
        vbox.addLayout(hbox)
        vbox.addLayout(okcancel)
        self.setLayout(vbox)

        connect(buttons, "add", self.addPattern)
        connect(buttons, "edit", self.editItem)
        buttons.duplicate.setVisible(False)
        self.connect(okcancel, SIGNAL('ok'), self.applySettings)
        self.connect(okcancel, SIGNAL('cancel'), self.applySettings)
        self.listbox.connectToListButtons(buttons)
        self.listbox.editButton = buttons.edit
        connect(self.listbox, 'itemDoubleClicked(QListWidgetItem *)',
                self._doubleClicked)
Example #2
0
    def __init__(self, parent=None, status=None):
        QWidget.__init__(self, parent)
        if status is None:
            self._status = {}
            genres = load_genres()
        else:
            self._status = status
            genres = status['genres']

        self.listbox = ListBox()
        self._itemflags = Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled
        [self.listbox.addItem(self._createItem(z)) for z in genres]

        buttons = ListButtons()
        self.listbox.connectToListButtons(buttons)
        self.listbox.setAutoScroll(False)

        self.connect(buttons, SIGNAL('add'), self.add)
        self.connect(buttons, SIGNAL('edit'), self.edit)

        hbox = QHBoxLayout()
        hbox.addWidget(self.listbox, 1)
        hbox.addLayout(buttons, 0)
        self.setLayout(hbox)
Example #3
0
    def __init__(self, parent=None, row=None, files=None, preview_mode=False,
        artwork=True, status=None):

        if status is None:
            status = {'cover_pattern': 'folder'}

        self.status = status
            
        QDialog.__init__(self, parent)
        winsettings('extendedtags', self)
        self.get_fieldlist = []
        self.previewMode = preview_mode

        add = QColor.fromRgb(255, 255, 0)
        edit = QColor.fromRgb(0, 255, 0)
        remove = QColor.fromRgb(255, 0, 0)
        self._colors = {ADD: QBrush(add),
            EDIT: QBrush(edit), REMOVE: QBrush(remove)}

        self.table = QTableWidget(0, 2, self)
        self.table.setVerticalHeader(VerticalHeader())
        self.table.verticalHeader().setVisible(False)
        self.table.setSortingEnabled(True)
        self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table.setHorizontalHeaderLabels([
            translate('Extended Tags', 'Field'),
            translate('Extended Tags', 'Value')])

        header = self.table.horizontalHeader()
        header.setVisible(True)
        header.setSortIndicatorShown (True)
        header.setStretchLastSection (True)
        header.setSortIndicator (0, Qt.AscendingOrder)

        self.piclabel = PicWidget(buttons = True)
        self.connect(self.piclabel, SIGNAL('imageChanged'),
            self._imageChanged)

        if not isinstance(self.piclabel.removepic, QAction):
            self.connect(self.piclabel.removepic, SIGNAL('clicked()'),
                self.removePic)
        else:
            self.connect(self.piclabel.removepic,
                SIGNAL('triggered()'), self.removePic)

        if row >= 0 and files:
            buttons = MoveButtons(files, row)
            self.connect(buttons, SIGNAL('indexChanged'), self._prevnext)
            buttons.setVisible(True)
        else:
            buttons = MoveButtons([], row)
            buttons.setVisible(False)

        self._files = files

        self.okcancel = OKCancel()
        self.okcancel.insertWidget(0, buttons)

        self._reset = QToolButton()
        self._reset.setToolTip(translate('Extended Tags',
                'Resets the selected fields to their original value.'))
        self._reset.setIcon(get_icon('edit-undo', ':/undo.png'))
        self.connect(self._reset, SIGNAL('clicked()'), self.resetFields)

        self.listbuttons = ListButtons()
        self.listbuttons.layout().addWidget(self._reset)
        self.listbuttons.moveup.hide()
        self.listbuttons.movedown.hide()

        listframe = QFrame()
        listframe.setFrameStyle(QFrame.Box)
        hbox = QHBoxLayout()
        hbox.addWidget(self.table, 1)
        hbox.addLayout(self.listbuttons, 0)
        listframe.setLayout(hbox)

        layout = QVBoxLayout()
        if artwork:
            imageframe = QFrame()
            imageframe.setFrameStyle(QFrame.Box)
            vbox = QVBoxLayout()
            vbox.setMargin(0)
            vbox.addWidget(self.piclabel)
            vbox.addStretch()
            vbox.addStrut(0)
            imageframe.setLayout(vbox)

            hbox = QHBoxLayout()
            hbox.addWidget(listframe, 1)
            hbox.addSpacing(4)
            hbox.addWidget(imageframe)
            hbox.addStrut(1)
            layout.addLayout(hbox)
        else:
            layout.addWidget(listframe)

        layout.addLayout(self.okcancel)
        self.setLayout(layout)

        self.connect(self.okcancel, SIGNAL("cancel"), self.closeMe)
        self.connect(self.table,
            SIGNAL("itemDoubleClicked(QTableWidgetItem *)"), self.editField)
        self.connect(self.table,
            SIGNAL("itemSelectionChanged()"), self._checkListBox)
        self.connect(self.okcancel, SIGNAL("ok"), self.okClicked)
        
        clicked = SIGNAL('clicked()')
        self.connect(self.listbuttons, SIGNAL('edit'), self.editField)
        self.connect(self.listbuttons.add, clicked, self.addField)
        self.connect(self.listbuttons.remove, clicked, self.removeField)
        self.connect(self.listbuttons, SIGNAL('duplicate'), self.duplicate)

        self.setMinimumSize(450, 350)

        self.canceled = False
        self.filechanged = False

        if row >= 0 and files:
            self._prevnext(row)
        else:
            self.loadFiles(files)
Example #4
0
class ExTags(QDialog):
    """A dialog that shows you the tags in a file

    In addition, any attached cover art is shown."""
    def __init__(self, parent=None, row=None, files=None, preview_mode=False,
        artwork=True, status=None):

        if status is None:
            status = {'cover_pattern': 'folder'}

        self.status = status
            
        QDialog.__init__(self, parent)
        winsettings('extendedtags', self)
        self.get_fieldlist = []
        self.previewMode = preview_mode

        add = QColor.fromRgb(255, 255, 0)
        edit = QColor.fromRgb(0, 255, 0)
        remove = QColor.fromRgb(255, 0, 0)
        self._colors = {ADD: QBrush(add),
            EDIT: QBrush(edit), REMOVE: QBrush(remove)}

        self.table = QTableWidget(0, 2, self)
        self.table.setVerticalHeader(VerticalHeader())
        self.table.verticalHeader().setVisible(False)
        self.table.setSortingEnabled(True)
        self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table.setHorizontalHeaderLabels([
            translate('Extended Tags', 'Field'),
            translate('Extended Tags', 'Value')])

        header = self.table.horizontalHeader()
        header.setVisible(True)
        header.setSortIndicatorShown (True)
        header.setStretchLastSection (True)
        header.setSortIndicator (0, Qt.AscendingOrder)

        self.piclabel = PicWidget(buttons = True)
        self.connect(self.piclabel, SIGNAL('imageChanged'),
            self._imageChanged)

        if not isinstance(self.piclabel.removepic, QAction):
            self.connect(self.piclabel.removepic, SIGNAL('clicked()'),
                self.removePic)
        else:
            self.connect(self.piclabel.removepic,
                SIGNAL('triggered()'), self.removePic)

        if row >= 0 and files:
            buttons = MoveButtons(files, row)
            self.connect(buttons, SIGNAL('indexChanged'), self._prevnext)
            buttons.setVisible(True)
        else:
            buttons = MoveButtons([], row)
            buttons.setVisible(False)

        self._files = files

        self.okcancel = OKCancel()
        self.okcancel.insertWidget(0, buttons)

        self._reset = QToolButton()
        self._reset.setToolTip(translate('Extended Tags',
                'Resets the selected fields to their original value.'))
        self._reset.setIcon(get_icon('edit-undo', ':/undo.png'))
        self.connect(self._reset, SIGNAL('clicked()'), self.resetFields)

        self.listbuttons = ListButtons()
        self.listbuttons.layout().addWidget(self._reset)
        self.listbuttons.moveup.hide()
        self.listbuttons.movedown.hide()

        listframe = QFrame()
        listframe.setFrameStyle(QFrame.Box)
        hbox = QHBoxLayout()
        hbox.addWidget(self.table, 1)
        hbox.addLayout(self.listbuttons, 0)
        listframe.setLayout(hbox)

        layout = QVBoxLayout()
        if artwork:
            imageframe = QFrame()
            imageframe.setFrameStyle(QFrame.Box)
            vbox = QVBoxLayout()
            vbox.setMargin(0)
            vbox.addWidget(self.piclabel)
            vbox.addStretch()
            vbox.addStrut(0)
            imageframe.setLayout(vbox)

            hbox = QHBoxLayout()
            hbox.addWidget(listframe, 1)
            hbox.addSpacing(4)
            hbox.addWidget(imageframe)
            hbox.addStrut(1)
            layout.addLayout(hbox)
        else:
            layout.addWidget(listframe)

        layout.addLayout(self.okcancel)
        self.setLayout(layout)

        self.connect(self.okcancel, SIGNAL("cancel"), self.closeMe)
        self.connect(self.table,
            SIGNAL("itemDoubleClicked(QTableWidgetItem *)"), self.editField)
        self.connect(self.table,
            SIGNAL("itemSelectionChanged()"), self._checkListBox)
        self.connect(self.okcancel, SIGNAL("ok"), self.okClicked)
        
        clicked = SIGNAL('clicked()')
        self.connect(self.listbuttons, SIGNAL('edit'), self.editField)
        self.connect(self.listbuttons.add, clicked, self.addField)
        self.connect(self.listbuttons.remove, clicked, self.removeField)
        self.connect(self.listbuttons, SIGNAL('duplicate'), self.duplicate)

        self.setMinimumSize(450, 350)

        self.canceled = False
        self.filechanged = False

        if row >= 0 and files:
            self._prevnext(row)
        else:
            self.loadFiles(files)

    def addField(self):
        win = EditField(parent=self, field_list=self.get_fieldlist)
        win.setModal(True)
        win.show()
        self.connect(win, SIGNAL("donewithmyshit"), self.editFieldBuddy)

    def _checkListBox(self):
        if self.table.rowCount() <= 0:
            self.table.setEnabled(False)
            self.listbuttons.edit.setEnabled(False)
            self.listbuttons.remove.setEnabled(False)
            self.listbuttons.duplicate.setEnabled(False)
            self._reset.setEnabled(False)
        else:
            self.table.setEnabled(True)
            self._reset.setEnabled(True)
            if len(self.table.selectedIndexes()) / 2 > 1:
                self.listbuttons.edit.setEnabled(False)
                self.listbuttons.duplicate.setEnabled(False)
            else:
                self.listbuttons.edit.setEnabled(True)
                self.listbuttons.remove.setEnabled(True)
                self.listbuttons.duplicate.setEnabled(True)
        self.table.resizeColumnToContents(0)
    
    def closeEvent(self,event):
        self.piclabel.close()
        QDialog.closeEvent(self,event)

    def closeMe(self):
        self.canceled = True
        self.close()

    def _deletePressed(self, item):
        if self.table.deletePressed:
            self.table.deletePressed = False
            self.removeField()

    def duplicate(self):
        self.editField(True)

    def editField(self, duplicate=False):
        """Opens a dialog to edit the currently selected Field.

        If duplicate is True the Edit Field dialog will be populated
        with the currently selected field's values. The new field'll then
        be added to the field list."""
        row = self.table.currentRow()
        if row != -1:
            prevtag = self.get_field(row)
            if duplicate is True:
                win = EditField(prevtag, self, self.get_fieldlist, edit=False)
            else:
                win = EditField(prevtag, self, self.get_fieldlist)
            win.setModal(True)
            win.show()

            #Have to check for truth, because this method is
            #called by the doubleclicked signal.
            if duplicate is True:
                buddy = partial(self.editFieldBuddy, duplicate=True)
            else:
                buddy = self.editFieldBuddy
            self.connect(win, SIGNAL("donewithmyshit"), buddy)

    def editFieldBuddy(self, tag, value, prevtag = None, duplicate=False):
        rowcount = self.table.rowCount()
        if prevtag is not None:
            if duplicate:
                row = rowcount
                self._settag(rowcount, tag, value, ADD,
                    self.previewMode, True)
            else:
                if tag == prevtag[0]:
                    row = self.table.currentRow()
                    self._settag(row, tag, value, EDIT,
                        self.previewMode, True)
                    if row + 1 < rowcount:
                        self.table.selectRow(row + 1)
                else:
                    cur_item = self.table.item(self.table.currentRow(),0)
                    self.resetFields([cur_item])
                    self.table.setCurrentItem(cur_item,
                        QItemSelectionModel.ClearAndSelect)
                    self.table.selectRow(self.table.row(cur_item))
                    self.removeField()
                    valitem = self._settag(rowcount, tag,
                        value, ADD, self.previewMode, True)
                    cur_item.linked = [valitem]
        else:
            self._settag(rowcount, tag, value, ADD, self.previewMode, True)
        self._checkListBox()
        self.filechanged = True
        self.table.clearSelection()

    def get_field(self, row, status = None):
        getitem = self.table.item
        item = getitem(row, 0)
        tag = unicode(item.text())
        try:
            value = unicode(getitem(row, 1).text())
        except AttributeError:
            value = unicode(self.table.cellWidget(row, 1).currentText())
        if status:
            return (tag, value, item.status)
        else:
            return (tag, value)

    def _imageChanged(self):
        self.filechanged = True

    def loadSettings(self):
        cparser = PuddleConfig()
        self.get_fieldlist = gettaglist()
        get = lambda k,v : cparser.get('extendedtags', k, v, True)
        add = QColor.fromRgb(*get('add', [255, 255, 0]))
        edit = QColor.fromRgb(*get('edit', [0, 255,0]))
        remove = QColor.fromRgb(*get('remove', [255, 0, 0]))

        self._colors = {ADD: QBrush(add),
            EDIT: QBrush(edit), REMOVE: QBrush(remove)}

        item = self.table.item
        for row in xrange(self.table.rowCount()):
            field_item = self.get_item(row, 0)
            field_item.statusColors = self._colors
            field_item.status = field_item.status
            
            val_item = self.get_item(row, 1)
            val_item.statusColors = self._colors
            val_item.status = val_item.status

    def listtotag(self):
        get_field = self.get_field
        tags = {}
        lowered = {}
        listitems = [get_field(row, True) for row
            in xrange(self.table.rowCount())]

        for field, val, status in listitems:
            if status != REMOVE:
                if val == KEEP:
                    continue
                l_field = field.lower()
                if l_field in lowered:
                    tags[lowered[l_field]].append(val)
                else:
                    lowered[l_field] = field
                    tags[field] = [z.strip() for z in val.split('\\') if z.strip()]
            else:
                if field.lower() not in lowered:
                    tags[field] = []
                    lowered[field.lower()] = field
        return tags

    def loadFiles(self, audios):
        if self.filechanged:
            self.save()
        self.filechanged = False
        self.table.clearContents()
        self.table.setRowCount(0)
        self.piclabel.lastfilename = audios[0].filepath
        self.piclabel.setEnabled(False)
        self.piclabel.setImages(None)

        if len(audios) == 1:
            audio = audios[0]
            self.setWindowTitle(audios[0].filepath)
            self._loadsingle(audio)
        else:
            self.setWindowTitle(
                translate('Extended Tags', 'Different files.'))
            
            from puddlestuff.tagmodel import status
            k = status['table'].model().taginfo[0]
            common, numvalues, imagetags = commontags(audios)
            images = common['__image']
            del(common['__image'])
            previews = set(audios[0].preview)
            italics = set(audios[0].equal_fields())
            self.piclabel.currentFile = audios[0]
            self.piclabel.filePattern = self.status['cover_pattern']

            for audio in audios[1:]:
                previews = previews.intersection(audio.preview)
                italics = italics.intersection(audio.equal_fields())

            row = 0

            for field, values in common.iteritems():
                if field in italics:
                    preview = UNCHANGED
                #field in italics => field in previews.
                elif field in previews:
                    preview = BOLD
                else:
                    preview = UNCHANGED
                if numvalues[field] != len(audios):
                    self._settag(row, field, values, multi=True)
                    row += 1
                else:
                    if isinstance(values, basestring):
                        self._settag(row, field, values, None, preview)
                        row += 1
                    else:
                        for v in values:
                            self._settag(row, field, v, None, preview)
                            row += 1

            self.piclabel.setImageTags(imagetags)
            if images:
                self.piclabel.setEnabled(True)
                self.piclabel.setImages(images)
            else:
                self.piclabel.setImages(None)
                self.piclabel.setEnabled(True)
                if images == 0:
                    self.piclabel.context = 'Cover Varies'
                    self.piclabel.removepic.setEnabled(True)
        self._checkListBox()

    def _loadsingle(self, tags):
        items = []
        d = tags.usertags.copy()
        italics = tags.equal_fields()
        self.piclabel.currentFile = tags
        self.piclabel.filePattern = self.status['cover_pattern']

        for key, val in sorted(d.items()):
            if key in italics:
                preview = UNCHANGED
            elif key in tags.preview:
                preview = BOLD
            else:
                preview = UNCHANGED
            if isinstance(val, basestring):
                items.append([key, val, None, preview])
            else:
                [items.append([key, z, None, preview]) for z in val]

        [self._settag(i, *item) for i, item in enumerate(items)]

        self.piclabel.lastfilename = tags.filepath
        if not tags.library:
            self.piclabel.setImageTags(tags.IMAGETAGS)
            if tags.IMAGETAGS:
                if '__image' in tags.preview:
                    images = tags.preview['__image']
                else:
                    images = tags.images
                self.piclabel.setEnabled(True)
                if images:
                    self.piclabel.setImages(deepcopy(images))
                else:
                    self.piclabel.setImages(None)
        self._checkListBox()
        self.setWindowTitle(tags[PATH])

    def okClicked(self):
        self.save()
        self.close()

    def _prevnext(self, row):
        if self.filechanged:
            self.save()
        self.loadFiles([self._files[row]])
        self.emit(SIGNAL('rowChanged'), row)

    def get_item(self, row, column=None):
        if column is None: #Assume QModelIndex passed
            column = row.column()
            row = row.row()
        item = self.table.item(row, column)
        if item is None:
            item = self.table.cellWidget(row, column)
        return item

    def removeField(self):
        tb = self.table
        tb.setSortingEnabled(False)
        to_remove = {}
        rows = []
        for index in self.table.selectedIndexes():
            row = index.row()
            item = self.get_item(index)
            if item.status == ADD:
                to_remove[row] = item
            rows.append(row)
            item.status = REMOVE
            item.status = REMOVE
        [tb.removeRow(tb.row(z)) for z in to_remove.values()]
        tb.setSortingEnabled(True)
        self.filechanged = True
        self._checkListBox()
        if rows:
            row = max(rows)
            self.table.clearSelection()
            if row + 1 < self.table.rowCount():
                self.table.selectRow(row + 1)

    def resetFields(self, items=None):
        box = self.table
        to_remove = {} #Stores row: item values so that only one item
                       #gets removed per row.

        max_row = -1
        for index in box.selectedIndexes():
            row = index.row()
            item = self.table.item(row, index.column())
            if item is None:    
                item = self.table.cellWidget(row, index.column())
            for i in item.linked:
                try:
                    to_remove[box.row(i)] = i
                except RuntimeError:
                    pass
            item.reset()
            if row > max_row:
                max_row = row
            if item.status == REMOVE:
                to_remove[row] = item

        self.table.clearSelection()
        if max_row != -1 and max_row + 1 < self.table.rowCount():
            self.table.selectRow(max_row + 1)

        for item in to_remove.values():
            self.table.removeRow(self.table.row(item))
        self._checkListBox()

    def removePic(self):
        if self.piclabel.context == 'Cover Varies':
            self.piclabel.context = 'No Images'
            self.piclabel.removepic.setEnabled(False)
            if not isinstance(self.piclabel.removepic, QAction):
                self.disconnect(self.piclabel.removepic, SIGNAL('clicked()'),
                    self.removePic)
            else:
                self.discconnect(self.piclabel.removepic,
                    SIGNAL('triggered()'), self.removePic)
            
            
            self.piclabel.setImages(None)

    def save(self):
        
        if not self.filechanged:
            table = self.table
            for row in range(table.rowCount()):
                combo = table.cellWidget(row, 1)
                if combo is not None and combo.currentIndex() != 0:
                    self.filechanged = True
                    break

        if not self.filechanged:
            return

        tags = self.listtotag()
        if self.piclabel.context != 'Cover Varies':
            if not self.piclabel.images:
                tags['__image'] = []
            else:
                tags["__image"] = self.piclabel.images
        newtags = [z for z in tags if z not in self.get_fieldlist]
        if newtags and newtags != ['__image']:
            settaglist(newtags + self.get_fieldlist)
        self.emit(SIGNAL('extendedtags'), tags)

    def _settag(self, row, field, value, status=None, preview=False,
                check=False, multi=False):

        tb = self.table
        tb.setSortingEnabled(False)
        if row >= tb.rowCount():
            tb.insertRow(row)
            field_item = StatusWidgetItem(field, status,
                self._colors, preview)
            tb.setItem(row, 0, field_item)
            if not multi and (len(value) == 1 or isinstance(value, basestring)):
                valitem = StatusWidgetItem(to_string(value), status, self._colors, preview)
                tb.setItem(row, 1, valitem)
            else:
                valitem = StatusWidgetCombo(value, status, self._colors, preview)
                tb.setCellWidget(row, 1, valitem)
        else:
            field_item = tb.item(row, 0)
            field_item.setText(field)
            field_item.status = status
            
            val_item = self.get_item(row, 1)
            val_item.setText(value)
            val_item.status = status

        if check:
            lowered_tag = field.lower()
            for row in xrange(tb.rowCount()):
                item = tb.item(row, 0)
                text = unicode(item.text())
                if text != field and text.lower() == lowered_tag:
                    item.setText(field)
                    if item.status not in [ADD, REMOVE]:
                        item.status = EDIT
                        try:
                            tb.item(row, 1).status = EDIT
                        except AttributeError:
                            tb.cellWidget(row, 1).status = EDIT

        tb.setSortingEnabled(True)
        return field_item
Example #5
0
    def __init__(self,
                 parent=None,
                 row=None,
                 files=None,
                 preview_mode=False,
                 artwork=True,
                 status=None):

        if status is None:
            status = {'cover_pattern': 'folder'}

        self.status = status

        QDialog.__init__(self, parent)
        winsettings('extendedtags', self)
        self.get_fieldlist = []
        self.previewMode = preview_mode

        add = QColor.fromRgb(255, 255, 0)
        edit = QColor.fromRgb(0, 255, 0)
        remove = QColor.fromRgb(255, 0, 0)
        self._colors = {
            ADD: QBrush(add),
            EDIT: QBrush(edit),
            REMOVE: QBrush(remove)
        }

        self.table = QTableWidget(0, 2, self)
        self.table.setVerticalHeader(VerticalHeader())
        self.table.verticalHeader().setVisible(False)
        self.table.setSortingEnabled(True)
        self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table.setHorizontalHeaderLabels([
            translate('Extended Tags', 'Field'),
            translate('Extended Tags', 'Value')
        ])

        header = self.table.horizontalHeader()
        header.setVisible(True)
        header.setSortIndicatorShown(True)
        header.setStretchLastSection(True)
        header.setSortIndicator(0, Qt.AscendingOrder)

        self.piclabel = PicWidget(buttons=True)
        self.connect(self.piclabel, SIGNAL('imageChanged'), self._imageChanged)

        if not isinstance(self.piclabel.removepic, QAction):
            self.connect(self.piclabel.removepic, SIGNAL('clicked()'),
                         self.removePic)
        else:
            self.connect(self.piclabel.removepic, SIGNAL('triggered()'),
                         self.removePic)

        if row >= 0 and files:
            buttons = MoveButtons(files, row)
            self.connect(buttons, SIGNAL('indexChanged'), self._prevnext)
            buttons.setVisible(True)
        else:
            buttons = MoveButtons([], row)
            buttons.setVisible(False)

        self._files = files

        self.okcancel = OKCancel()
        self.okcancel.insertWidget(0, buttons)

        self._reset = QToolButton()
        self._reset.setToolTip(
            translate('Extended Tags',
                      'Resets the selected fields to their original value.'))
        self._reset.setIcon(get_icon('edit-undo', ':/undo.png'))
        self.connect(self._reset, SIGNAL('clicked()'), self.resetFields)

        self.listbuttons = ListButtons()
        self.listbuttons.layout().addWidget(self._reset)
        self.listbuttons.moveup.hide()
        self.listbuttons.movedown.hide()

        listframe = QFrame()
        listframe.setFrameStyle(QFrame.Box)
        hbox = QHBoxLayout()
        hbox.addWidget(self.table, 1)
        hbox.addLayout(self.listbuttons, 0)
        listframe.setLayout(hbox)

        layout = QVBoxLayout()
        if artwork:
            imageframe = QFrame()
            imageframe.setFrameStyle(QFrame.Box)
            vbox = QVBoxLayout()
            vbox.setMargin(0)
            vbox.addWidget(self.piclabel)
            vbox.addStretch()
            vbox.addStrut(0)
            imageframe.setLayout(vbox)

            hbox = QHBoxLayout()
            hbox.addWidget(listframe, 1)
            hbox.addSpacing(4)
            hbox.addWidget(imageframe)
            hbox.addStrut(1)
            layout.addLayout(hbox)
        else:
            layout.addWidget(listframe)

        layout.addLayout(self.okcancel)
        self.setLayout(layout)

        self.connect(self.okcancel, SIGNAL("cancel"), self.closeMe)
        self.connect(self.table,
                     SIGNAL("itemDoubleClicked(QTableWidgetItem *)"),
                     self.editField)
        self.connect(self.table, SIGNAL("itemSelectionChanged()"),
                     self._checkListBox)
        self.connect(self.okcancel, SIGNAL("ok"), self.okClicked)

        clicked = SIGNAL('clicked()')
        self.connect(self.listbuttons, SIGNAL('edit'), self.editField)
        self.connect(self.listbuttons.add, clicked, self.addField)
        self.connect(self.listbuttons.remove, clicked, self.removeField)
        self.connect(self.listbuttons, SIGNAL('duplicate'), self.duplicate)

        self.setMinimumSize(450, 350)

        self.canceled = False
        self.filechanged = False

        if row >= 0 and files:
            self._prevnext(row)
        else:
            self.loadFiles(files)
Example #6
0
class ExTags(QDialog):
    """A dialog that shows you the tags in a file

    In addition, any attached cover art is shown."""
    def __init__(self,
                 parent=None,
                 row=None,
                 files=None,
                 preview_mode=False,
                 artwork=True,
                 status=None):

        if status is None:
            status = {'cover_pattern': 'folder'}

        self.status = status

        QDialog.__init__(self, parent)
        winsettings('extendedtags', self)
        self.get_fieldlist = []
        self.previewMode = preview_mode

        add = QColor.fromRgb(255, 255, 0)
        edit = QColor.fromRgb(0, 255, 0)
        remove = QColor.fromRgb(255, 0, 0)
        self._colors = {
            ADD: QBrush(add),
            EDIT: QBrush(edit),
            REMOVE: QBrush(remove)
        }

        self.table = QTableWidget(0, 2, self)
        self.table.setVerticalHeader(VerticalHeader())
        self.table.verticalHeader().setVisible(False)
        self.table.setSortingEnabled(True)
        self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table.setHorizontalHeaderLabels([
            translate('Extended Tags', 'Field'),
            translate('Extended Tags', 'Value')
        ])

        header = self.table.horizontalHeader()
        header.setVisible(True)
        header.setSortIndicatorShown(True)
        header.setStretchLastSection(True)
        header.setSortIndicator(0, Qt.AscendingOrder)

        self.piclabel = PicWidget(buttons=True)
        self.connect(self.piclabel, SIGNAL('imageChanged'), self._imageChanged)

        if not isinstance(self.piclabel.removepic, QAction):
            self.connect(self.piclabel.removepic, SIGNAL('clicked()'),
                         self.removePic)
        else:
            self.connect(self.piclabel.removepic, SIGNAL('triggered()'),
                         self.removePic)

        if row >= 0 and files:
            buttons = MoveButtons(files, row)
            self.connect(buttons, SIGNAL('indexChanged'), self._prevnext)
            buttons.setVisible(True)
        else:
            buttons = MoveButtons([], row)
            buttons.setVisible(False)

        self._files = files

        self.okcancel = OKCancel()
        self.okcancel.insertWidget(0, buttons)

        self._reset = QToolButton()
        self._reset.setToolTip(
            translate('Extended Tags',
                      'Resets the selected fields to their original value.'))
        self._reset.setIcon(get_icon('edit-undo', ':/undo.png'))
        self.connect(self._reset, SIGNAL('clicked()'), self.resetFields)

        self.listbuttons = ListButtons()
        self.listbuttons.layout().addWidget(self._reset)
        self.listbuttons.moveup.hide()
        self.listbuttons.movedown.hide()

        listframe = QFrame()
        listframe.setFrameStyle(QFrame.Box)
        hbox = QHBoxLayout()
        hbox.addWidget(self.table, 1)
        hbox.addLayout(self.listbuttons, 0)
        listframe.setLayout(hbox)

        layout = QVBoxLayout()
        if artwork:
            imageframe = QFrame()
            imageframe.setFrameStyle(QFrame.Box)
            vbox = QVBoxLayout()
            vbox.setMargin(0)
            vbox.addWidget(self.piclabel)
            vbox.addStretch()
            vbox.addStrut(0)
            imageframe.setLayout(vbox)

            hbox = QHBoxLayout()
            hbox.addWidget(listframe, 1)
            hbox.addSpacing(4)
            hbox.addWidget(imageframe)
            hbox.addStrut(1)
            layout.addLayout(hbox)
        else:
            layout.addWidget(listframe)

        layout.addLayout(self.okcancel)
        self.setLayout(layout)

        self.connect(self.okcancel, SIGNAL("cancel"), self.closeMe)
        self.connect(self.table,
                     SIGNAL("itemDoubleClicked(QTableWidgetItem *)"),
                     self.editField)
        self.connect(self.table, SIGNAL("itemSelectionChanged()"),
                     self._checkListBox)
        self.connect(self.okcancel, SIGNAL("ok"), self.okClicked)

        clicked = SIGNAL('clicked()')
        self.connect(self.listbuttons, SIGNAL('edit'), self.editField)
        self.connect(self.listbuttons.add, clicked, self.addField)
        self.connect(self.listbuttons.remove, clicked, self.removeField)
        self.connect(self.listbuttons, SIGNAL('duplicate'), self.duplicate)

        self.setMinimumSize(450, 350)

        self.canceled = False
        self.filechanged = False

        if row >= 0 and files:
            self._prevnext(row)
        else:
            self.loadFiles(files)

    def addField(self):
        win = EditField(parent=self, field_list=self.get_fieldlist)
        win.setModal(True)
        win.show()
        self.connect(win, SIGNAL("donewithmyshit"), self.editFieldBuddy)

    def _checkListBox(self):
        if self.table.rowCount() <= 0:
            self.table.setEnabled(False)
            self.listbuttons.edit.setEnabled(False)
            self.listbuttons.remove.setEnabled(False)
            self.listbuttons.duplicate.setEnabled(False)
            self._reset.setEnabled(False)
        else:
            self.table.setEnabled(True)
            self._reset.setEnabled(True)
            if len(self.table.selectedIndexes()) / 2 > 1:
                self.listbuttons.edit.setEnabled(False)
                self.listbuttons.duplicate.setEnabled(False)
            else:
                self.listbuttons.edit.setEnabled(True)
                self.listbuttons.remove.setEnabled(True)
                self.listbuttons.duplicate.setEnabled(True)
        self.table.resizeColumnToContents(0)

    def closeEvent(self, event):
        self.piclabel.close()
        QDialog.closeEvent(self, event)

    def closeMe(self):
        self.canceled = True
        self.close()

    def _deletePressed(self, item):
        if self.table.deletePressed:
            self.table.deletePressed = False
            self.removeField()

    def duplicate(self):
        self.editField(True)

    def editField(self, duplicate=False):
        """Opens a dialog to edit the currently selected Field.

        If duplicate is True the Edit Field dialog will be populated
        with the currently selected field's values. The new field'll then
        be added to the field list."""
        row = self.table.currentRow()
        if row != -1:
            prevtag = self.get_field(row)
            if duplicate is True:
                win = EditField(prevtag, self, self.get_fieldlist, edit=False)
            else:
                win = EditField(prevtag, self, self.get_fieldlist)
            win.setModal(True)
            win.show()

            #Have to check for truth, because this method is
            #called by the doubleclicked signal.
            if duplicate is True:
                buddy = partial(self.editFieldBuddy, duplicate=True)
            else:
                buddy = self.editFieldBuddy
            self.connect(win, SIGNAL("donewithmyshit"), buddy)

    def editFieldBuddy(self, tag, value, prevtag=None, duplicate=False):
        rowcount = self.table.rowCount()
        if prevtag is not None:
            if duplicate:
                row = rowcount
                self._settag(rowcount, tag, value, ADD, self.previewMode, True)
            else:
                if tag == prevtag[0]:
                    row = self.table.currentRow()
                    self._settag(row, tag, value, EDIT, self.previewMode, True)
                    if row + 1 < rowcount:
                        self.table.selectRow(row + 1)
                else:
                    cur_item = self.table.item(self.table.currentRow(), 0)
                    self.resetFields([cur_item])
                    self.table.setCurrentItem(
                        cur_item, QItemSelectionModel.ClearAndSelect)
                    self.table.selectRow(self.table.row(cur_item))
                    self.removeField()
                    valitem = self._settag(rowcount, tag, value, ADD,
                                           self.previewMode, True)
                    cur_item.linked = [valitem]
        else:
            self._settag(rowcount, tag, value, ADD, self.previewMode, True)
        self._checkListBox()
        self.filechanged = True
        self.table.clearSelection()

    def get_field(self, row, status=None):
        getitem = self.table.item
        item = getitem(row, 0)
        tag = unicode(item.text())
        try:
            value = unicode(getitem(row, 1).text())
        except AttributeError:
            value = unicode(self.table.cellWidget(row, 1).currentText())
        if status:
            return (tag, value, item.status)
        else:
            return (tag, value)

    def _imageChanged(self):
        self.filechanged = True

    def loadSettings(self):
        cparser = PuddleConfig()
        self.get_fieldlist = gettaglist()
        get = lambda k, v: cparser.get('extendedtags', k, v, True)
        add = QColor.fromRgb(*get('add', [255, 255, 0]))
        edit = QColor.fromRgb(*get('edit', [0, 255, 0]))
        remove = QColor.fromRgb(*get('remove', [255, 0, 0]))

        self._colors = {
            ADD: QBrush(add),
            EDIT: QBrush(edit),
            REMOVE: QBrush(remove)
        }

        item = self.table.item
        for row in xrange(self.table.rowCount()):
            field_item = self.get_item(row, 0)
            field_item.statusColors = self._colors
            field_item.status = field_item.status

            val_item = self.get_item(row, 1)
            val_item.statusColors = self._colors
            val_item.status = val_item.status

    def listtotag(self):
        get_field = self.get_field
        tags = {}
        lowered = {}
        listitems = [
            get_field(row, True) for row in xrange(self.table.rowCount())
        ]

        for field, val, status in listitems:
            if status != REMOVE:
                if val == KEEP:
                    continue
                l_field = field.lower()
                if l_field in lowered:
                    tags[lowered[l_field]].append(val)
                else:
                    lowered[l_field] = field
                    tags[field] = [
                        z.strip() for z in val.split('\\') if z.strip()
                    ]
            else:
                if field.lower() not in lowered:
                    tags[field] = []
                    lowered[field.lower()] = field
        return tags

    def loadFiles(self, audios):
        if self.filechanged:
            self.save()
        self.filechanged = False
        self.table.clearContents()
        self.table.setRowCount(0)
        self.piclabel.lastfilename = audios[0].filepath
        self.piclabel.setEnabled(False)
        self.piclabel.setImages(None)

        if len(audios) == 1:
            audio = audios[0]
            self.setWindowTitle(audios[0].filepath)
            self._loadsingle(audio)
        else:
            self.setWindowTitle(translate('Extended Tags', 'Different files.'))

            from puddlestuff.tagmodel import status
            k = status['table'].model().taginfo[0]
            common, numvalues, imagetags = commontags(audios)
            images = common['__image']
            del (common['__image'])
            previews = set(audios[0].preview)
            italics = set(audios[0].equal_fields())
            self.piclabel.currentFile = audios[0]
            self.piclabel.filePattern = self.status['cover_pattern']

            for audio in audios[1:]:
                previews = previews.intersection(audio.preview)
                italics = italics.intersection(audio.equal_fields())

            row = 0

            for field, values in common.iteritems():
                if field in italics:
                    preview = UNCHANGED
                #field in italics => field in previews.
                elif field in previews:
                    preview = BOLD
                else:
                    preview = UNCHANGED
                if numvalues[field] != len(audios):
                    self._settag(row, field, values, multi=True)
                    row += 1
                else:
                    if isinstance(values, basestring):
                        self._settag(row, field, values, None, preview)
                        row += 1
                    else:
                        for v in values:
                            self._settag(row, field, v, None, preview)
                            row += 1

            self.piclabel.setImageTags(imagetags)
            if images:
                self.piclabel.setEnabled(True)
                self.piclabel.setImages(images)
            else:
                self.piclabel.setImages(None)
                self.piclabel.setEnabled(True)
                if images == 0:
                    self.piclabel.context = 'Cover Varies'
                    self.piclabel.removepic.setEnabled(True)
        self._checkListBox()

    def _loadsingle(self, tags):
        items = []
        d = tags.usertags.copy()
        italics = tags.equal_fields()
        self.piclabel.currentFile = tags
        self.piclabel.filePattern = self.status['cover_pattern']

        for key, val in sorted(d.items()):
            if key in italics:
                preview = UNCHANGED
            elif key in tags.preview:
                preview = BOLD
            else:
                preview = UNCHANGED
            if isinstance(val, basestring):
                items.append([key, val, None, preview])
            else:
                [items.append([key, z, None, preview]) for z in val]

        [self._settag(i, *item) for i, item in enumerate(items)]

        self.piclabel.lastfilename = tags.filepath
        if not tags.library:
            self.piclabel.setImageTags(tags.IMAGETAGS)
            if tags.IMAGETAGS:
                if '__image' in tags.preview:
                    images = tags.preview['__image']
                else:
                    images = tags.images
                self.piclabel.setEnabled(True)
                if images:
                    self.piclabel.setImages(deepcopy(images))
                else:
                    self.piclabel.setImages(None)
        self._checkListBox()
        self.setWindowTitle(tags[PATH])

    def okClicked(self):
        self.save()
        self.close()

    def _prevnext(self, row):
        if self.filechanged:
            self.save()
        self.loadFiles([self._files[row]])
        self.emit(SIGNAL('rowChanged'), row)

    def get_item(self, row, column=None):
        if column is None:  #Assume QModelIndex passed
            column = row.column()
            row = row.row()
        item = self.table.item(row, column)
        if item is None:
            item = self.table.cellWidget(row, column)
        return item

    def removeField(self):
        tb = self.table
        tb.setSortingEnabled(False)
        to_remove = {}
        rows = []
        for index in self.table.selectedIndexes():
            row = index.row()
            item = self.get_item(index)
            if item.status == ADD:
                to_remove[row] = item
            rows.append(row)
            item.status = REMOVE
            item.status = REMOVE
        [tb.removeRow(tb.row(z)) for z in to_remove.values()]
        tb.setSortingEnabled(True)
        self.filechanged = True
        self._checkListBox()
        if rows:
            row = max(rows)
            self.table.clearSelection()
            if row + 1 < self.table.rowCount():
                self.table.selectRow(row + 1)

    def resetFields(self, items=None):
        box = self.table
        to_remove = {}  #Stores row: item values so that only one item
        #gets removed per row.

        max_row = -1
        for index in box.selectedIndexes():
            row = index.row()
            item = self.table.item(row, index.column())
            if item is None:
                item = self.table.cellWidget(row, index.column())
            for i in item.linked:
                try:
                    to_remove[box.row(i)] = i
                except RuntimeError:
                    pass
            item.reset()
            if row > max_row:
                max_row = row
            if item.status == REMOVE:
                to_remove[row] = item

        self.table.clearSelection()
        if max_row != -1 and max_row + 1 < self.table.rowCount():
            self.table.selectRow(max_row + 1)

        for item in to_remove.values():
            self.table.removeRow(self.table.row(item))
        self._checkListBox()

    def removePic(self):
        if self.piclabel.context == 'Cover Varies':
            self.piclabel.context = 'No Images'
            self.piclabel.removepic.setEnabled(False)
            if not isinstance(self.piclabel.removepic, QAction):
                self.disconnect(self.piclabel.removepic, SIGNAL('clicked()'),
                                self.removePic)
            else:
                self.discconnect(self.piclabel.removepic,
                                 SIGNAL('triggered()'), self.removePic)

            self.piclabel.setImages(None)

    def save(self):

        if not self.filechanged:
            table = self.table
            for row in range(table.rowCount()):
                combo = table.cellWidget(row, 1)
                if combo is not None and combo.currentIndex() != 0:
                    self.filechanged = True
                    break

        if not self.filechanged:
            return

        tags = self.listtotag()
        if self.piclabel.context != 'Cover Varies':
            if not self.piclabel.images:
                tags['__image'] = []
            else:
                tags["__image"] = self.piclabel.images
        newtags = [z for z in tags if z not in self.get_fieldlist]
        if newtags and newtags != ['__image']:
            settaglist(newtags + self.get_fieldlist)
        self.emit(SIGNAL('extendedtags'), tags)

    def _settag(self,
                row,
                field,
                value,
                status=None,
                preview=False,
                check=False,
                multi=False):

        tb = self.table
        tb.setSortingEnabled(False)
        if row >= tb.rowCount():
            tb.insertRow(row)
            field_item = StatusWidgetItem(field, status, self._colors, preview)
            tb.setItem(row, 0, field_item)
            if not multi and (len(value) == 1
                              or isinstance(value, basestring)):
                valitem = StatusWidgetItem(to_string(value), status,
                                           self._colors, preview)
                tb.setItem(row, 1, valitem)
            else:
                valitem = StatusWidgetCombo(value, status, self._colors,
                                            preview)
                tb.setCellWidget(row, 1, valitem)
        else:
            field_item = tb.item(row, 0)
            field_item.setText(field)
            field_item.status = status

            val_item = self.get_item(row, 1)
            val_item.setText(value)
            val_item.status = status

        if check:
            lowered_tag = field.lower()
            for row in xrange(tb.rowCount()):
                item = tb.item(row, 0)
                text = unicode(item.text())
                if text != field and text.lower() == lowered_tag:
                    item.setText(field)
                    if item.status not in [ADD, REMOVE]:
                        item.status = EDIT
                        try:
                            tb.item(row, 1).status = EDIT
                        except AttributeError:
                            tb.cellWidget(row, 1).status = EDIT

        tb.setSortingEnabled(True)
        return field_item