def _createFunc(self,wrapperNames,wrappersToReturn,parent,token): wrapper = Wrapper(self.nameToElement[token]) if parent is not None: assert parent.element.id in wrapper.element.parents wrapper.parent = parent if token in wrapperNames: wrappersToReturn[wrapperNames.index(token)] = wrapper return wrapper
def createWrapper(id): """Create a wrapper to be inserted in the browser. If the wrapper should contain all of its element's contents, create a BrowserWrapper, that will load the contents """ element = levels.real[id] if id in cDict: # wrapper should contain only a part of its element's contents wrapper = Wrapper(element) wrapper.setContents([createWrapper(cid) for cid in cDict[id]]) return wrapper elif element.isFile() or len(element.contents) == 0: return Wrapper(element) else: return bnodes.BrowserWrapper(element) # a wrapper that will load all its contents when needed
def createWrapper(id): """Create a wrapper to be inserted in the browser. If the wrapper should contain all of its element's contents, create a BrowserWrapper, that will load the contents """ element = levels.real[id] if id in cDict: # wrapper should contain only a part of its element's contents wrapper = Wrapper(element) wrapper.setContents([createWrapper(cid) for cid in cDict[id]]) return wrapper elif element.isFile() or len(element.contents) == 0: return Wrapper(element) else: return bnodes.BrowserWrapper( element ) # a wrapper that will load all its contents when needed
def _buildTree(wrappers, seqs, boundingSeq, toplevelIds): """Build a tree over the part of *wrappers* specified by *boundingSeq* (on over all wrappers if *boundingSeq* is None). *seqs* is the SequenceDict, *toplevelIds* are the elements that may be used at the highest level. """ # Root nodes are not necessarily added in correct order. Insert them indexed by the start point of their # sequence and at the end sort them by the start point. roots = {} while True: # Choose the longest sequence (bounded to boundingSeq and only from elements within toplevelIds) # The second restriction increases performance and avoids that we accidentally choose a child when # its parent has the same sequence (this happens only for single children). longestSeq, element = seqs.longest(toplevelIds,boundingSeq) if longestSeq is None: # All wrappers in *boundingSeq* are covered break boundedSeq = longestSeq.bounded(Sequence(0,len(wrappers)-1)) if boundedSeq is None or len(boundedSeq) == 0: # Skip sequences that are completely out of range (this may happen due to preWrapper/postWrapper) seqs.remove(longestSeq) continue # Create a wrapper for the tree assert element.isContainer() wrapper = Wrapper(element) childContents = _buildTree(wrappers, seqs, boundedSeq, element.contents.ids) wrapper.setContents(childContents) findPositions(wrapper,wrapper.contents) # Add the wrapper to roots and remove the sequence from all sequences roots[boundedSeq.asTuple()] = wrapper seqs.remove(longestSeq) # Finally add elements that have not been covered by any container uncovered = [] for i in boundingSeq.asRange() if boundingSeq is not None else range(len(wrappers)): if not any(startIndex <= i <= endIndex for (startIndex, endIndex), root in roots.items()): uncovered.append(i) for i in uncovered: roots[(i,i)] = wrappers[i] # Sort root nodes by start point return [roots[key] for key in sorted(roots.keys())]
def _buildWrappersFromUrls(self, urls): """Build wrappers for the given urls and if possible add containers. In other words: convert a flat playlist to a tree playlist. """ files = [Wrapper(element) for element in self.level.collect(urls)] wrappers = treebuilder.buildTree(self.level, files) for i in range(len(wrappers)): while wrappers[i].getContentsCount() == 1: wrappers[i] = wrappers[i].contents[0] return wrappers
def insertUrlsAtOffset(self, offset, urls, updateBackend='always'): """Insert the given paths at the given offset.""" wrappers = [Wrapper(element) for element in self.level.collect(urls)] file = self.root.fileAtOffset(offset, allowFileCount=True) if file is None: parent = self.root position = self.root.getContentsCount() else: parent = file.parent position = parent.index(file) self.insert(parent, position, wrappers, updateBackend)
def _createFunc(parent, token): """This is used as createFunc-argument for Level.createWrappers.""" if token.startswith('EXT:'): url = urllib.parse.unquote(token[4:]) # remove "EXT:" url = urls.URL(url) try: element = self.level.collect(url) except OSError as e: raise ValueError(str(e)) else: element = self.level.collect(int(token)) return Wrapper(element, parent=parent)
def dropMimeData(self, mimeData, action, row, column, parentIndex): # Compute drop position parent = self.data(parentIndex, Qt.EditRole) if parent.isFile(): # Drop onto a file => drop behind it position = parent.parent.index(parent) + 1 parent = parent.parent elif row == -1: # no specific position: insert at the beginning or end if self.dndTarget is not None and self.dndTarget.isExpanded(parentIndex): position = 0 else: position = parent.getContentsCount() else: position = row # Handle internal moves separately if action == Qt.MoveAction and isinstance(self.dndSource, QtWidgets.QTreeView) \ and isinstance(self.dndSource.model(), PlaylistModel) \ and self.dndSource.model().backend == self.backend: return self.move(list(mimeData.wrappers()), parent, position) self.stack.beginMacro(self.tr("Drop elements")) # Create wrappers if mimeData.hasFormat(config.options.gui.mime): # Do not simply copy wrappers from other levels as they might be invalid on real level if hasattr(mimeData, 'level') and mimeData.level == self.level: wrappers = [wrapper.copy() for wrapper in mimeData.wrappers()] else: wrappers = [Wrapper(self.level.collect(wrapper.element.id)) for wrapper in mimeData.fileWrappers()] else: urls = utils.files.collectAsList(mimeData.urls()) wrappers = [Wrapper(element) for element in self.level.collect(urls)] if len(wrappers) == 0: return True if self.insert(parent, position, wrappers): self.stack.endMacro() return True else: return False # macro has been aborted
def move(self, wrappers, parent, position): """Move *wrappers* at *position* into *parent*. If the current song is moved, keep the player unchanged (contrary to removing and inserting the wrappers again). Return False if the move is not possible because *parent* or one of its ancestors is in *wrappers* and True otherwise. """ # Abort if the drop target or one of its ancestors would be moved if any(p in wrappers for p in parent.getParents(includeSelf=True)): return False self.stack.beginMacro(self.tr("Move elements")) # First change the backend # We use a special command to really move songs within the backend (contrary to removing and # inserting them). This keeps the status of the current song intact even if it is moved. self.stack.push(PlaylistMoveInBackendCommand(self, wrappers, parent, position)) # Remove wrappers. # This might change the insert position for several reasons: # - we might remove wrappers within parent and before the insert position, # - we might remove wrappers making a parent node empty, # - after any removal adjacent wrappers may be glued # While we could easily calculate a new position taking the first effect into account, this is # difficult for the other ones. Therefore we mark the insert position with a special wrapper. # We use the super()-methods when we don't want fancy stuff like glueing # We use _dontGlueAway to avoid the following problems: Assume the playlist contains the containers # A, B, A. If we now move B into one of the A-containers, both A-containers are glued. This might # delete our insert parent! Similarly nodes behind the before position might be glued with nodes # behind it. marker = Wrapper(wrappers[0].element) super().insert(parent, position, [marker]) self._dontGlueAway = [parent, marker] self.removeWrappers(wrappers, updateBackend='never') position = parent.index(marker) super().removeMany([(parent, position, position)]) self._dontGlueAway = None self.insert(parent, position, wrappers, updateBackend='never') self.stack.endMacro() return True
def setElement(self, element): """Shortcut: Display the given element (inside a Wrapper).""" self.setNode(Wrapper(element))