def offerAssistance(self): """ Offer to fulfill certain incomplete tasks evident from the state of the passage text. (Technically, none of this needs to be on passageFrame instead of passageWidget.) """ # Offer to create passage for broken links if self.app.config.ReadBool('createPassagePrompt'): brokens = links = filter(lambda text: TweeLexer.linkStyle(text) == TweeLexer.BAD_LINK, self.widget.getBrokenLinks()) if brokens : if len(brokens) > 1: brokenmsg = 'create ' + str(len(brokens)) + ' new passages to match these broken links?' else: brokenmsg = 'create the passage "' + brokens[0] + '"?' dialog = wx.MessageDialog(self, 'Do you want to ' + brokenmsg, 'Create Passages', \ wx.ICON_QUESTION | wx.YES_NO | wx.CANCEL | wx.YES_DEFAULT) check = dialog.ShowModal() if check == wx.ID_YES: for title in brokens: self.widget.parent.newWidget(title = title, pos = self.widget.parent.toPixels (self.widget.pos)) elif check == wx.ID_CANCEL: return # Offer to import external images if self.app.config.ReadBool('importImagePrompt'): regex = tweeregex.EXTERNAL_IMAGE_REGEX externalimages = re.finditer(regex, self.widget.passage.text) check = None downloadedurls = {} storyframe = self.widget.parent.parent for img in externalimages: if not check: dialog = wx.MessageDialog(self, 'Do you want to import the image files linked\nin this passage into the story file?', 'Import Images', \ wx.ICON_QUESTION | wx.YES_NO | wx.CANCEL | wx.YES_DEFAULT); check = dialog.ShowModal() if check == wx.ID_NO: break elif check == wx.ID_CANCEL: return # Download the image if it's at an absolute URL imgurl = img.group(4) or img.group(7) if not imgurl: continue # If we've downloaded it before, don't do it again if imgurl not in downloadedurls: # Internet image, or local image? if any(imgurl.startswith(t) for t in ['http://', 'https://', 'ftp://']): imgpassagename = storyframe.importImageURL(imgurl, showdialog=False) else: imgpassagename = storyframe.importImageFile(storyframe.getLocalDir()+os.sep+imgurl, showdialog=False) if not imgpassagename: continue downloadedurls[imgurl] = imgpassagename # Replace all found images for old, new in downloadedurls.iteritems(): self.widget.passage.text = re.sub(regex.replace(tweeregex.IMAGE_FILENAME_REGEX, re.escape(old)), lambda m: m.group(0).replace(old, new), self.widget.passage.text) self.bodyInput.SetText(self.widget.passage.text)
def updateSubmenus(self, event=None): """ Updates our passage menus. This should be called sparingly, i.e. not during a UI update event, as it is doing a bunch of removing and adding of items. """ # separate outgoing and broken links outgoing = [] incoming = [] broken = [] # Remove externals links = filter(lambda text: TweeLexer.linkStyle(text) == TweeLexer.BAD_LINK, self.widget.passage.links) for link in links: if len(link) > 0: if link in self.widget.parent.widgetDict: outgoing.append(link) elif not self.widget.parent.includedPassageExists(link): broken.append(link) # incoming links for widget in self.widget.parent.widgetDict.itervalues(): if self.widget.passage.title in widget.passage.links and len(widget.passage.title) > 0: incoming.append(widget.passage.title) # repopulate the menus def populate(menu, links): for item in menu.GetMenuItems(): menu.DeleteItem(item) if len(links): for link in links: item = menu.Append(-1, link) self.Bind(wx.EVT_MENU, self.openOtherEditor, item) else: item = menu.Append(wx.ID_ANY, "(None)") item.Enable(False) outTitle = "Outgoing Links" if len(outgoing) > 0: outTitle += " (" + str(len(outgoing)) + ")" self.outLinksMenuTitle.SetText(outTitle) populate(self.outLinksMenu, outgoing) inTitle = "Incoming Links" if len(incoming) > 0: inTitle += " (" + str(len(incoming)) + ")" self.inLinksMenuTitle.SetText(inTitle) populate(self.inLinksMenu, incoming) brokenTitle = "Broken Links" if len(broken) > 0: brokenTitle += " (" + str(len(broken)) + ")" self.brokenLinksMenuTitle.SetText(brokenTitle) populate(self.brokenLinksMenu, broken)
def update(self): """ Update the lists of all passages linked/displayed by this one. Returns internal links and <<choice>>/<<actions>> macros. """ if not self.isStoryText() and not self.isAnnotation() and not self.isStylesheet(): self.displays = [] self.links = [] self.images = [] self.macros = [] return # <<display>> self.displays = list(set(re.findall(r'\<\<display\s+[\'"]?(.+?)[\'"]?\s?\>\>', self.text, re.IGNORECASE))) macros = set() # other macros (including shorthand <<display>>) for m in re.finditer(tweeregex.MACRO_REGEX, self.text): # Exclude shorthand <<print>> if m.group(1) and m.group(1)[0] != '$': macros.add(m.group(1)) self.macros = list(macros) # avoid duplicates by collecting links in a set links = set() # Regular hyperlinks (also matches wiki-style links inside macros) for m in re.finditer(tweeregex.LINK_REGEX, self.text): # Exclude external links link = m.group(2) or m.group(1) if TweeLexer.linkStyle(link) != TweeLexer.EXTERNAL: links.add(m.group(2) or m.group(1)) # Include images for m in re.finditer(tweeregex.IMAGE_REGEX, self.text): if m.group(5): links.add(m.group(5)) # <<choice passage_name [link_text]>> for block in re.findall(r'\<\<choice\s+(.*?)\s?\>\>', self.text): item = re.match(r'(?:"([^"]*)")|(?:\'([^\']*)\')|([^"\'\[\s]\S*)', block) if item: links.add(''.join(item.groups(''))) # <<actions '' ''>> for block in re.findall(r'\<\<actions\s+(.*?)\s?\>\>', self.text): links.update(re.findall(r'[\'"](.*?)[\'"]', block)) self.links = list(links) # Images images = set() for block in re.finditer(tweeregex.IMAGE_REGEX, self.text): images.add(block.group(4)) self.images = list(images)
def updateSubmenus(self, event = None): """ Updates our passage menus. This should be called sparingly, i.e. not during a UI update event, as it is doing a bunch of removing and adding of items. """ # separate outgoing and broken links outgoing = [] incoming = [] broken = [] # Remove externals for link in self.widget.passage.links: if len(link) > 0 and TweeLexer.linkStyle(link) == TweeLexer.BAD_LINK: if link in self.widget.parent.widgetDict: outgoing.append(link) elif not self.widget.parent.includedPassageExists(link): broken.append(link) # incoming links for widget in self.widget.parent.widgetDict.values(): if self.widget.passage.title in widget.passage.links \ and len(widget.passage.title) > 0: incoming.append(widget.passage.title) # repopulate the menus def populate(menu, links): for item in menu.GetMenuItems(): menu.DeleteItem(item) if len(links): for link in links: item = menu.Append(-1, link) self.Bind(wx.EVT_MENU, self.openOtherEditor, item) else: item = menu.Append(wx.ID_ANY, '(None)') item.Enable(False) outTitle = 'Outgoing Links' if len(outgoing) > 0: outTitle += ' (' + str(len(outgoing)) + ')' self.outLinksMenuTitle.SetText(outTitle) populate(self.outLinksMenu, outgoing) inTitle = 'Incoming Links' if len(incoming) > 0: inTitle += ' (' + str(len(incoming)) + ')' self.inLinksMenuTitle.SetText(inTitle) populate(self.inLinksMenu, incoming) brokenTitle = 'Broken Links' if len(broken) > 0: brokenTitle += ' (' + str(len(broken)) + ')' self.brokenLinksMenuTitle.SetText(brokenTitle) populate(self.brokenLinksMenu, broken)
def update(self): """ Update the lists of all passages linked/displayed by this one. Returns internal links and <<choice>>/<<actions>> macros. """ if not self.isStoryText() and not self.isAnnotation() and not self.isStylesheet(): self.displays = [] self.links = [] self.images = [] self.macros = [] return images = set() macros = set() links = set() # <<display>> self.displays = list(set(re.findall(r'\<\<display\s+[\'"]?(.+?)[\'"]?\s?\>\>', self.text, re.IGNORECASE))) macros = set() # other macros (including shorthand <<display>>) for m in re.finditer(tweeregex.MACRO_REGEX, self.text): # Exclude shorthand <<print>> if m.group(1) and m.group(1)[0] != '$': macros.add(m.group(1)) self.macros = list(macros) # Regular hyperlinks (also matches wiki-style links inside macros) for m in re.finditer(tweeregex.LINK_REGEX, self.text): # Exclude external links link = m.group(2) or m.group(1) if TweeLexer.linkStyle(link) != TweeLexer.EXTERNAL: links.add(m.group(2) or m.group(1)) # Include images for m in re.finditer(tweeregex.IMAGE_REGEX, self.text): if m.group(5): links.add(m.group(5)) # HTML data-passage links for m in re.finditer(tweeregex.HTML_REGEX, self.text): attrs = m.group(2) if attrs: dataPassage = re.search(r"""data-passage\s*=\s*(?:([^<>'"=`\s]+)|'((?:[^'\\]*\\.)*[^'\\]*)'|"((?:[^"\\]*\\.)*[^"\\]*)")""", attrs) if dataPassage: theSet = images if m.group(1) == "img" else links theSet.add(dataPassage.group(1) or dataPassage.group(2) or dataPassage.group(3)) # <<choice passage_name [link_text]>> for block in re.findall(r'\<\<choice\s+(.*?)\s?\>\>', self.text): item = re.match(r'(?:"([^"]*)")|(?:\'([^\']*)\')|([^"\'\[\s]\S*)', block) if item: links.add(''.join(item.groups(''))) # <<actions '' ''>> for block in re.findall(r'\<\<actions\s+(.*?)\s?\>\>', self.text): links.update(re.findall(r'[\'"](.*?)[\'"]', block)) self.links = list(links) # Images for block in re.finditer(tweeregex.IMAGE_REGEX, self.text): images.add(block.group(4)) self.images = list(images)
def addLink(link): style = TweeLexer.linkStyle(link) if style == TweeLexer.PARAM: variableLinks.add(link) elif style != TweeLexer.EXTERNAL: links.add(link)
def addLink(link): style = TweeLexer.linkStyle(link) if style == TweeLexer.PARAM: variableLinks.add(link) elif style != TweeLexer.EXTERNAL: links.add(link)
def update(self): """ Update the lists of all passages linked/displayed by this one. Returns internal links and <<choice>>/<<actions>> macros. """ if not self.isStoryText() and not self.isAnnotation( ) and not self.isStylesheet(): self.displays = [] self.links = [] self.images = [] self.macros = [] return # <<display>> self.displays = list( set( re.findall(r'\<\<display\s+[\'"]?(.+?)[\'"]?\s?\>\>', self.text, re.IGNORECASE))) macros = set() # other macros (including shorthand <<display>>) for m in re.finditer(tweeregex.MACRO_REGEX, self.text): # Exclude shorthand <<print>> if m.group(1) and m.group(1)[0] != '$': macros.add(m.group(1)) self.macros = list(macros) # avoid duplicates by collecting links in a set links = set() # Regular hyperlinks (also matches wiki-style links inside macros) for m in re.finditer(tweeregex.LINK_REGEX, self.text): # Exclude external links link = m.group(2) or m.group(1) if TweeLexer.linkStyle(link) != TweeLexer.EXTERNAL: links.add(m.group(2) or m.group(1)) # Include images for m in re.finditer(tweeregex.IMAGE_REGEX, self.text): if m.group(5): links.add(m.group(5)) # <<choice passage_name [link_text]>> for block in re.findall(r'\<\<choice\s+(.*?)\s?\>\>', self.text): item = re.match(r'(?:"([^"]*)")|(?:\'([^\']*)\')|([^"\'\[\s]\S*)', block) if item: links.add(''.join(item.groups(''))) # <<actions '' ''>> for block in re.findall(r'\<\<actions\s+(.*?)\s?\>\>', self.text): links.update(re.findall(r'[\'"](.*?)[\'"]', block)) self.links = list(links) # Images images = set() for block in re.finditer(tweeregex.IMAGE_REGEX, self.text): images.add(block.group(4)) self.images = list(images)
def offerAssistance(self): """ Offer to fulfill certain incomplete tasks evident from the state of the passage text. (Technically, none of this needs to be on passageFrame instead of passageWidget.) """ # Offer to create passage for broken links if self.app.config.ReadBool('createPassagePrompt'): brokens = [link for link in self.widget.getBrokenLinks() if TweeLexer.linkStyle(link) == TweeLexer.BAD_LINK] if brokens : if len(brokens) > 1: brokenmsg = 'create ' + str(len(brokens)) + ' new passages to match these broken links?' else: brokenmsg = 'create the passage "' + brokens[0] + '"?' dialog = wx.MessageDialog(self, 'Do you want to ' + brokenmsg, 'Create Passages', \ wx.ICON_QUESTION | wx.YES_NO | wx.CANCEL | wx.YES_DEFAULT) check = dialog.ShowModal() if check == wx.ID_YES: for title in brokens: self.widget.parent.newWidget(title = title, pos = self.widget.parent.toPixels (self.widget.pos)) elif check == wx.ID_CANCEL: return False # Offer to import external images if self.app.config.ReadBool('importImagePrompt'): regex = tweeregex.EXTERNAL_IMAGE_REGEX externalimages = re.finditer(regex, self.widget.passage.text) check = None downloadedurls = {} storyframe = self.widget.parent.parent for img in externalimages: if not check: dialog = wx.MessageDialog(self, 'Do you want to import the image files linked\nin this passage into the story file?', 'Import Images', \ wx.ICON_QUESTION | wx.YES_NO | wx.CANCEL | wx.YES_DEFAULT) check = dialog.ShowModal() if check == wx.ID_NO: break elif check == wx.ID_CANCEL: return False # Download the image if it's at an absolute URL imgurl = img.group(4) or img.group(7) if not imgurl: continue # If we've downloaded it before, don't do it again if imgurl not in downloadedurls: # Internet image, or local image? if isURL(imgurl): imgpassagename = storyframe.importImageURL(imgurl, showdialog=False) else: imgpassagename = storyframe.importImageFile(storyframe.getLocalDir()+os.sep+imgurl, showdialog=False) if not imgpassagename: continue downloadedurls[imgurl] = imgpassagename # Replace all found images for old, new in downloadedurls.items(): self.widget.passage.text = re.sub(regex.replace(tweeregex.IMAGE_FILENAME_REGEX, re.escape(old)), lambda m: m.group(0).replace(old, new), self.widget.passage.text) if self.bodyInput.GetText() != self.widget.passage.text: self.bodyInput.SetText(self.widget.passage.text) # If it's StoryIncludes, update the links if self.widget.passage.title == "StoryIncludes": self.widget.parent.refreshIncludedPassageList() return True