# Created By: Virgil Dupras # Created On: 2009-04-25 # Copyright 2013 Hardcoded Software (http://www.hardcoded.net) # # This software is licensed under the "BSD" License as described in the "LICENSE" file, # which should be included with this package. The terms are also available at # http://www.hardcoded.net/licenses/bsd_license import urllib.parse from PyQt5.QtCore import pyqtSignal, Qt, QRect, QUrl, QModelIndex, QItemSelection from PyQt5.QtWidgets import (QComboBox, QStyledItemDelegate, QStyle, QStyleOptionComboBox, QStyleOptionViewItem, QApplication) from PyQt5.QtGui import QBrush from hscommon.trans import trget from qtlib.tree_model import RefNode, TreeModel tr = trget('ui') HEADERS = [tr("Name"), tr("State")] STATES = [tr("Normal"), tr("Reference"), tr("Excluded")] class DirectoriesDelegate(QStyledItemDelegate): def createEditor(self, parent, option, index): editor = QComboBox(parent); editor.addItems(STATES) return editor def paint(self, painter, option, index): self.initStyleOption(option, index) # No idea why, but this cast is required if we want to have access to the V4 valuess option = QStyleOptionViewItem(option) if (index.column() == 1) and (option.state & QStyle.State_Selected): cboption = QStyleOptionComboBox() cboption.rect = option.rect # On OS X (with Qt4.6.0), adding State_Enabled to the flags causes the whole drawing to # fail (draw nothing), but it's an OS X only glitch. On Windows, it works alright. cboption.state |= QStyle.State_Enabled QApplication.style().drawComplexControl(QStyle.CC_ComboBox, cboption, painter) painter.setBrush(option.palette.text()) rect = QRect(option.rect) rect.setLeft(rect.left()+4) painter.drawText(rect, Qt.AlignLeft, option.text) else: super().paint(painter, option, index) def setEditorData(self, editor, index): value = index.model().data(index, Qt.EditRole) editor.setCurrentIndex(value); editor.showPopup() def setModelData(self, editor, model, index): value = editor.currentIndex() model.setData(index, value, Qt.EditRole) def updateEditorGeometry(self, editor, option, index): editor.setGeometry(option.rect) class DirectoriesModel(TreeModel): def __init__(self, model, view, **kwargs): super().__init__(**kwargs) self.model = model self.model.view = self self.view = view self.view.setModel(self) self.view.selectionModel().selectionChanged[(QItemSelection, QItemSelection)].connect(self.selectionChanged) def _createNode(self, ref, row): return RefNode(self, None, ref, row) def _getChildren(self): return list(self.model) def columnCount(self, parent=QModelIndex()): return 2 def data(self, index, role): if not index.isValid(): return None node = index.internalPointer() ref = node.ref if role == Qt.DisplayRole: if index.column() == 0: return ref.name else: return STATES[ref.state] elif role == Qt.EditRole and index.column() == 1: return ref.state elif role == Qt.ForegroundRole: state = ref.state if state == 1: return QBrush(Qt.blue) elif state == 2: return QBrush(Qt.red) return None def dropMimeData(self, mimeData, action, row, column, parentIndex): # the data in mimeData is urlencoded **in utf-8**!!! What we do is to decode, the mime data # with 'ascii', which works since it's urlencoded. Then, we pass that to urllib. if not mimeData.hasFormat('text/uri-list'): return False data = bytes(mimeData.data('text/uri-list')).decode('ascii') unquoted = urllib.parse.unquote(data) urls = unquoted.split('\r\n') paths = [QUrl(url).toLocalFile() for url in urls if url] for path in paths: self.model.add_directory(path) self.foldersAdded.emit(paths) self.reset() return True def flags(self, index): if not index.isValid(): return Qt.ItemIsEnabled | Qt.ItemIsDropEnabled result = Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDropEnabled if index.column() == 1: result |= Qt.ItemIsEditable return result def headerData(self, section, orientation, role): if orientation == Qt.Horizontal: if role == Qt.DisplayRole and section < len(HEADERS): return HEADERS[section] return None def mimeTypes(self): return ['text/uri-list'] def setData(self, index, value, role): if not index.isValid() or role != Qt.EditRole or index.column() != 1: return False node = index.internalPointer() ref = node.ref ref.state = value return True def supportedDropActions(self): # Normally, the correct action should be ActionLink, but the drop doesn't work. It doesn't # work with ActionMove either. So screw that, and accept anything. return Qt.ActionMask #--- Events def selectionChanged(self, selected, deselected): newNodes = [modelIndex.internalPointer().ref for modelIndex in self.view.selectionModel().selectedRows()] self.model.selected_nodes = newNodes #--- Signals foldersAdded = pyqtSignal(list) #--- model --> view def refresh(self): self.reset() def refresh_states(self): self.refreshData()