def backspace(self, siteId, evt): siteName, index = iconsites.splitSeriesSiteId(siteId) topIcon = self.topLevelParent() redrawRegion = comn.AccumRects(topIcon.hierRect()) win = self.window if index == 0: if siteName == "targets0": return if siteName == "values" and not hasattr(self.sites, 'targets1'): # This is the only '=' in the assignment, convert it to a tuple argIcons = [tgtSite.att for tgtSite in self.sites.targets0] numTargets = len(argIcons) argIcons += [valueSite.att for valueSite in self.sites.values] newTuple = listicons.TupleIcon(window=win, noParens=True) for i, arg in enumerate(argIcons): if arg is not None: self.replaceChild(None, self.siteOf(arg)) newTuple.insertChild(arg, "argIcons", i) parent = self.parent() if parent is None: win.replaceTop(self, newTuple) else: # I don't think this is possible, remove if print never appears print("Assign icon has parent?????") parentSite = parent.siteOf(self) parent.replaceChild(newTuple, parentSite) cursorSite = iconsites.makeSeriesSiteId('argIcons', numTargets) win.cursor.setToIconSite(newTuple, cursorSite) else: # Merge lists around '=' to convert it to ',' #... This should do the same as backspaceComma and insert entry icon topIcon = self.topLevelParent() redrawRegion = comn.AccumRects(topIcon.hierRect()) if siteName == "values": removetgtGrpIdx = len(self.tgtLists) - 1 srcSite = "targets%d" % removetgtGrpIdx destSite = "values" destIdx = 0 cursorIdx = len(getattr(self.sites, srcSite)) - 1 else: srcSite = siteName removetgtGrpIdx = int(siteName[7:]) destSite = siteName[:7] + str(removetgtGrpIdx - 1) destIdx = len(getattr(self.sites, destSite)) cursorIdx = destIdx - 1 argIcons = [s.att for s in getattr(self.sites, srcSite)] for i, arg in enumerate(argIcons): self.replaceChild(None, self.siteOf(arg)) self.insertChild(arg, destSite, destIdx + i) self.removeTargetGroup(removetgtGrpIdx) cursorSite = iconsites.makeSeriesSiteId(destSite, cursorIdx) win.cursor.setToIconSite( *icon.rightmostFromSite(self, cursorSite)) else: # Cursor is on comma input. Delete if empty or previous site is empty listicons.backspaceComma(self, siteId, evt) return redrawRegion.add(win.layoutDirtyIcons(filterRedundantParens=False)) win.refresh(redrawRegion.get()) win.undo.addBoundary()
def _iconsBetween(self, ic, site): """Find the icons that should be selected by shift-select between the current anchor (self.anchorIc and Pos, or self.anchorLine) and cursor at ic, site.""" siteX, siteY = ic.posOfSite(site) siteType = ic.typeOf(site) if siteType in ("attrIn", "attrOut"): siteY -= icon.ATTR_SITE_OFFSET elif siteType in ("seqIn", "seqOut"): seqInX, seqInY = ic.posOfSite("seqIn") seqOutX, seqOutY = ic.posOfSite("seqOut") siteY = (seqInY + seqOutY) // 2 if self.anchorIc is not None: anchorX, anchorY = self.anchorIc.posOfSite(self.anchorSite) anchorSiteType = self.anchorIc.typeOf(self.anchorSite) if anchorSiteType in ("attrIn", "attrOut"): anchorY -= icon.ATTR_SITE_OFFSET elif anchorSiteType in ("seqIn", "seqOut"): seqInX, seqInY = self.anchorIc.posOfSite("seqIn") seqOutX, seqOutY = self.anchorIc.posOfSite("seqOut") anchorY = (seqInY + seqOutY) // 2 if anchorX < siteX: anchorX += 4 # Avoid overlapped icons and sites siteX -= 3 elif siteX < anchorX: siteX += 4 anchorX -= 3 selectRect = comn.AccumRects( (anchorX, anchorY - 3, anchorX, anchorY + 3)) selectRect.add((siteX, siteY - 3, siteX, siteY + 3)) else: if self.anchorLine[0] == self.anchorLine[2]: anchorX = self.anchorLine[0] if anchorX < siteX: self.anchorLine = comn.offsetRect(self.anchorLine, 4, 0) siteX -= 3 elif siteX < anchorX: siteX += 4 icon.moveRect(self.anchorLine, (-3, 0)) selectRect = comn.AccumRects(self.anchorLine) selectRect.add((siteX, siteY, siteX, siteY)) # Get the icons that would be covered by a rectangular selection of the box iconsToSelect = self.window.findIconsInRegion(selectRect.get()) iconsToSelect = [ ic for ic in iconsToSelect if ic.inRectSelect(selectRect.get()) ] # If the selection spans multiple statements, change to statement-level selection topIcons = {ic.topLevelParent() for ic in iconsToSelect} if len(topIcons) <= 1: return iconsToSelect else: return self._seqIconsBetween(topIcons)
def selectToCursor(self, ic, site, direction): """Modify selection based on cursor movement (presumably with Shift key held)""" selectedIcons = set(self.window.selectedIcons()) redrawRect = comn.AccumRects() if len(selectedIcons) == 0 and self.type == "icon": # This is a new cursor-based selection self.anchorIc = self.icon self.anchorSite = self.site elif self.anchorIc is not None or self.anchorLine is not None: # There is a current recorded selection, but verify that it is valid by # matching it with the current actual selection lastSelIcons = set( self._iconsBetween(self.lastSelIc, self.lastSelSite)) diffIcons = selectedIcons.difference(lastSelIcons) if len({i for i in diffIcons if not isinstance(i, icon.BlockEnd)}): self.anchorIc = None self.anchorLine = None if self.anchorIc is None and self.anchorLine is None: # The current selection is not valid, assume it was made via mouse. Choose # an anchor line as the farthest side of the selection rectangle from the # cursor using the direction of arrow motion to choose horiz/vert selectedRect = comn.AccumRects() for i in selectedIcons: selectedRect.add(i.selectionRect()) left, top, right, bottom = selectedRect.get() left += 3 # Inset rectangle to avoid overlaps and protruding sites right -= 3 top += 1 bottom -= 1 siteX, siteY = ic.posOfSite(site) if direction in ("Left", "Right"): anchorX = right if abs(siteX - left) <= abs(siteX - right) else left self.anchorLine = (anchorX, top, anchorX, bottom) else: anchorY = bottom if abs(siteY - top) <= abs(siteY - bottom) else top self.anchorLine = (left, anchorY, right, anchorY) select = set(self._iconsBetween(ic, site)) erase = selectedIcons.difference(select) select = select.difference(selectedIcons) for i in erase: redrawRect.add(i.hierRect()) i.select(False) for i in select: redrawRect.add(i.hierRect()) i.select() self.window.refresh(redrawRect.get()) self.setToIconSite(ic, site) self.lastSelIc = ic self.lastSelSite = site
def backspace(self, siteId, evt): win = self.window redrawRegion = comn.AccumRects(self.topLevelParent().hierRect()) if siteId == 'attrIcon': # Cursor is on attribute site of right paren. Re-open the paren # On backspace from the outside right paren, reopen the list entryicon.reopenParen(self) redrawRegion.add(win.layoutDirtyIcons(filterRedundantParens=False)) win.refresh(redrawRegion.get()) return else: # Cursor is on the argument site: remove the parens unless an attribute is # attached to the parens, in which case, don't delete, just select attrIcon = self.childAt('attrIcon') if attrIcon: win.unselectAll() toSelect = list(attrIcon.traverse()) if siteId == 'argIcon': toSelect.append(self) for i in toSelect: win.select(i) win.refresh(redrawRegion.get()) return parent = self.parent() content = self.childAt('argIcon') if parent is None: if content is None: # Open paren was the only thing left of the statement. Remove if self.prevInSeq() is not None: cursorIc = self.prevInSeq() cursorSite = 'seqOut' elif self.nextInSeq() is not None: cursorIc = self.nextInSeq() cursorSite = 'seqIn' else: cursorIc = None pos = self.pos() win.removeIcons([self]) if cursorIc is None: win.cursor.setToWindowPos(pos) else: win.cursor.setToIconSite(cursorIc, cursorSite) else: # Open paren on top level had content self.replaceChild(None, 'argIcon') win.replaceTop(self, content) topNode = reorderexpr.reorderArithExpr(content) win.cursor.setToBestCoincidentSite(topNode, 'output') else: # Open paren had a parent. Remove by attaching content to parent parentSite = parent.siteOf(self) if content is None: parent.replaceChild(None, parentSite, leavePlace=True) win.cursor.setToIconSite(parent, parentSite) else: parent.replaceChild(content, parentSite) win.cursor.setToIconSite(parent, parentSite) reorderexpr.reorderArithExpr(content) redrawRegion.add(win.layoutDirtyIcons(filterRedundantParens=True)) win.refresh(redrawRegion.get())
def arrowKeyWithSelection(self, evt, selectedIcons): """Process arrow key pressed with no cursor but selected icons.""" direction = evt.keysym shiftPressed = evt.state & python_g.SHIFT_MASK if not shiftPressed: self.window.unselectAll() selectedRects = comn.AccumRects() for ic in selectedIcons: selectedRects.add(ic.selectionRect()) selectedRect = selectedRects.get() l, t, r, b = selectedRect l -= 10 t -= 10 r += 10 b += 10 searchRect = l, t, r, b cursorSites = [] for ic in self.window.findIconsInRegion(searchRect): snapLists = ic.snapLists(forCursor=True) for ic, (x, y), name in snapLists.get("input", []): cursorSites.append((x, y, ic, name)) for ic, (x, y), name in snapLists.get("attrIn", []): cursorSites.append((x, y - icon.ATTR_SITE_OFFSET, ic, name)) outSites = snapLists.get("output", []) if len(outSites) > 0: cursorSites.append((*outSites[0][1], ic, "output")) if len(cursorSites) == 0: return # It is possible to have icons with no viable cursor sites selLeft, selTop, selRight, selBottom = selectedRect bestDist = None for siteData in cursorSites: x, y = siteData[:2] if direction == "Left": # distance from left edge dist = abs(x - selLeft) + max(0, selBottom - y) + max( 0, y - selTop) elif direction == "Right": # distance from right edge dist = abs(x - selRight) + max(0, selBottom - y) + max( 0, y - selTop) elif direction == "Up": # distance from top edge dist = abs(y - selTop) + max(0, selLeft - x) + max( 0, x - selRight) elif direction == "Down": # distance from bottom edge dist = abs(y - selBottom) + max(0, selLeft - x) + max( 0, x - selRight) if bestDist is None or dist < bestDist: bestSiteData = siteData bestDist = dist x, y, ic, site = bestSiteData if site == "output": parent = ic.parent() if parent is not None: self.setToIconSite(parent, parent.siteOf(ic)) return self.moveToIconSite(ic, site, evt)
def backspace(self, siteId, evt): win = self.window if siteId == 'indexIcon': # Cursor is on the index site. Try to remove brackets if self.hasSite('upperIcon') and self.childAt('upperIcon') or \ self.hasSite('stepIcon') and self.childAt('stepIcon') or \ self.hasSite('attrIcon') and self.childAt('attrIcon'): # Can't remove brackets: select the icon and its children win._select(self, op='hier') elif not self.childAt('indexIcon'): # Icon is empty, remove parent = self.parent() win.removeIcons([self]) if parent is not None: win.cursor.setToIconSite(parent, 'attrIcon') else: # Icon has a single argument and it's in the first slot: unwrap # the bracket from around it. redrawRegion = comn.AccumRects( self.topLevelParent().hierRect()) parent = self.parent() content = self.childAt('indexIcon') if parent is None: # The icon was on the top level: replace it with its content self.replaceChild(None, 'indexIcon') win.replaceTop(self, content) win.cursor.setToBestCoincidentSite(content, 'output') else: # The icon has a parent, but since the subscript icon sits on # an attribute site we can't attach, so create an entry icon # and make the content a pending argument to it. parentSite = parent.siteOf(self) win.entryIcon = entryicon.EntryIcon(parent, parentSite, window=win) parent.replaceChild(win.entryIcon, parentSite) win.entryIcon.setPendingArg(content) win.cursor.setToEntryIcon() win.redisplayChangedEntryIcon(evt, redrawRegion.get()) return redrawRegion.add( win.layoutDirtyIcons(filterRedundantParens=False)) win.refresh(redrawRegion.get()) return elif siteId == 'attrIcon': # The cursor is on the attr site, remove the end bracket redrawRegion = comn.AccumRects(self.topLevelParent().hierRect()) entryicon.reopenParen(self) redrawRegion.add(win.layoutDirtyIcons(filterRedundantParens=False)) win.refresh(redrawRegion.get()) return # Site is after a colon. Try to remove it redrawRegion = comn.AccumRects(self.topLevelParent().hierRect()) if siteId == 'upperIcon': # Remove first colon mergeSite1 = 'indexIcon' mergeSite2 = 'upperIcon' else: # Remove second colon mergeSite1 = 'upperIcon' mergeSite2 = 'stepIcon' mergeIcon1 = self.childAt(mergeSite1) mergeIcon2 = self.childAt(mergeSite2) if mergeIcon2 is None: # Site after colon is empty (no need to merge) cursorIc, cursorSite = icon.rightmostFromSite(self, mergeSite1) elif mergeIcon1 is None: # Site before colon is empty, move the icon after the colon to it self.replaceChild(None, mergeSite2) self.replaceChild(mergeIcon2, mergeSite1) cursorIc, cursorSite = self, mergeSite1 else: # Both sites are occupied merge the two expressions rightmostIc, rightmostSite = icon.rightmostSite(mergeIcon1) if rightmostIc.typeOf(rightmostSite) == 'input': # An empty input on the right allows merge without inserting entry icon self.replaceChild(None, mergeSite2) rightmostIc.replaceChild(mergeIcon2, rightmostSite) reorderexpr.reorderArithExpr(mergeIcon1) cursorIc, cursorSite = rightmostIc, rightmostSite else: lowestIc, lowestSite = iconsites.lowestCoincidentSite( self, mergeSite2) if lowestIc.childAt(lowestSite): # Can't merge the two expressions: insert an entry icon self.replaceChild(None, mergeSite2) win.entryIcon = entryicon.EntryIcon('', window=win) win.entryIcon.setPendingArg(mergeIcon2) rightmostIc.replaceChild(win.entryIcon, rightmostSite) cursorIc = cursorSite = None else: # Empty site right of colon, merge left side in to that and reorder self.replaceChild(None, mergeSite2) self.replaceChild(mergeIcon2, mergeSite1) lowestIc.replaceChild(mergeIcon1, lowestSite) reorderexpr.reorderArithExpr(lowestIc) cursorIc, cursorSite = rightmostIc, rightmostSite # If there is a step site that wasn't part of the merge, shift it. if self.hasSite('stepIcon') and mergeSite2 != 'stepIcon': moveIcon = self.childAt('stepIcon') self.replaceChild(moveIcon, 'upperIcon') # Remove the colon (colonectomy) if self.hasSite('stepIcon'): self.changeNumSubscripts(2) else: self.changeNumSubscripts(1) # Place the cursor or new entry icon, and redraw if win.entryIcon is None: win.cursor.setToIconSite(cursorIc, cursorSite) redrawRegion.add(win.layoutDirtyIcons()) win.refresh(redrawRegion.get()) else: win.cursor.setToEntryIcon() win.redisplayChangedEntryIcon(evt, redrawRegion.get())
def backspace(self, siteId, evt): siteName, index = iconsites.splitSeriesSiteId(siteId) win = self.window if siteName == "values" and index == 0: # Cursor is on first input site. Remove icon and replace with cursor text = self.op + '=' valueIcons = [ s.att for s in self.sites.values if s.att is not None ] targetIcon = self.childAt("targetIcon") if len(valueIcons) in (0, 1): # Zero or one argument, convert to entry icon (with pending arg if # there was an argument) attached to name icon redrawRegion = comn.AccumRects( self.topLevelParent().hierRect()) if self.parent() is not None: print('AugmentedAssign has parent?????') return if targetIcon is None: win.entryIcon = entryicon.EntryIcon(initialString=text, window=win) win.replaceTop(self, win.entryIcon) else: win.entryIcon = entryicon.EntryIcon(initialString=text, window=win) win.replaceTop(self, targetIcon) targetIcon.replaceChild(win.entryIcon, 'attrIcon') if len(valueIcons) == 1: win.entryIcon.setPendingArg(valueIcons[0]) else: # Multiple remaining arguments: convert to tuple with entry icon as # first element redrawRegion = comn.AccumRects( self.topLevelParent().hierRect()) valueIcons = [ s.att for s in self.sites.values if s.att is not None ] newTuple = listicons.TupleIcon(window=win, noParens=True) if targetIcon is None: win.entryIcon = entryicon.EntryIcon(initialString=text, window=win) newTuple.replaceChild(win.entryIcon, "argIcons_0") else: win.entryIcon = entryicon.EntryIcon(initialString=text, window=win) targetIcon.replaceChild(win.entryIcon, 'attrIcon') newTuple.replaceChild(targetIcon, 'argIcons_0') for i, arg in enumerate(valueIcons): if i == 0: win.entryIcon.setPendingArg(arg) else: self.replaceChild(None, self.siteOf(arg)) newTuple.insertChild(arg, "argIcons", i) win.replaceTop(self, newTuple) win.cursor.setToEntryIcon() win.redisplayChangedEntryIcon(evt, redrawRegion.get()) elif siteName == "values": # Cursor is on comma input. Delete if empty or previous site is empty prevSite = iconsites.makeSeriesSiteId(siteName, index - 1) childAtCursor = self.childAt(siteId) if childAtCursor and self.childAt(prevSite): cursors.beep() return topIcon = self.topLevelParent() redrawRegion = comn.AccumRects(topIcon.hierRect()) if not self.childAt(prevSite): self.removeEmptySeriesSite(prevSite) win.cursor.setToIconSite(self, prevSite) else: rightmostIcon = icon.findLastAttrIcon(self.childAt(prevSite)) rightmostIcon, rightmostSite = icon.rightmostSite( rightmostIcon) self.removeEmptySeriesSite(siteId) win.cursor.setToIconSite(rightmostIcon, rightmostSite) redrawRegion.add(win.layoutDirtyIcons(filterRedundantParens=False)) win.refresh(redrawRegion.get()) win.undo.addBoundary()