def __init__(self, parent, widget, app): self.widget = widget self.app = app self.syncTimer = None self.lastFindRegexp = None self.lastFindFlags = None self.usingLexer = True wx.Frame.__init__(self, parent, wx.ID_ANY, title = 'Untitled Passage - ' + self.app.NAME, \ size = PassageFrame.DEFAULT_SIZE) # Passage menu passageMenu = wx.Menu() passageMenu.Append(PassageFrame.PASSAGE_EDIT_SELECTION, 'Create &Link From Selection\tCtrl-L') self.Bind(wx.EVT_MENU, self.editSelection, id=PassageFrame.PASSAGE_EDIT_SELECTION) self.outLinksMenu = wx.Menu() self.outLinksMenuTitle = passageMenu.AppendMenu( wx.ID_ANY, 'Outgoing Links', self.outLinksMenu) self.inLinksMenu = wx.Menu() self.inLinksMenuTitle = passageMenu.AppendMenu(wx.ID_ANY, 'Incoming Links', self.inLinksMenu) self.brokenLinksMenu = wx.Menu() self.brokenLinksMenuTitle = passageMenu.AppendMenu( wx.ID_ANY, 'Broken Links', self.brokenLinksMenu) passageMenu.AppendSeparator() passageMenu.Append(wx.ID_SAVE, '&Save Story\tCtrl-S') self.Bind(wx.EVT_MENU, self.widget.parent.parent.save, id=wx.ID_SAVE) passageMenu.Append(PassageFrame.PASSAGE_REBUILD_STORY, '&Rebuild Story\tCtrl-R') self.Bind(wx.EVT_MENU, self.widget.parent.parent.rebuild, id=PassageFrame.PASSAGE_REBUILD_STORY) passageMenu.AppendSeparator() passageMenu.Append(PassageFrame.PASSAGE_FULLSCREEN, '&Fullscreen View\tF12') self.Bind(wx.EVT_MENU, self.openFullscreen, id=PassageFrame.PASSAGE_FULLSCREEN) passageMenu.Append(wx.ID_CLOSE, '&Close Passage\tCtrl-W') self.Bind(wx.EVT_MENU, lambda e: self.Destroy(), id=wx.ID_CLOSE) # Edit menu editMenu = wx.Menu() editMenu.Append(wx.ID_UNDO, '&Undo\tCtrl-Z') self.Bind(wx.EVT_MENU, lambda e: self.bodyInput.Undo(), id=wx.ID_UNDO) editMenu.Append(wx.ID_REDO, '&Redo\tCtrl-Y') self.Bind(wx.EVT_MENU, lambda e: self.bodyInput.Redo(), id=wx.ID_REDO) editMenu.AppendSeparator() editMenu.Append(wx.ID_CUT, 'Cu&t\tCtrl-X') self.Bind(wx.EVT_MENU, lambda e: self.bodyInput.Cut(), id=wx.ID_CUT) editMenu.Append(wx.ID_COPY, '&Copy\tCtrl-C') self.Bind(wx.EVT_MENU, lambda e: self.bodyInput.Copy(), id=wx.ID_COPY) editMenu.Append(wx.ID_PASTE, '&Paste\tCtrl-V') self.Bind(wx.EVT_MENU, lambda e: self.bodyInput.Paste(), id=wx.ID_PASTE) editMenu.Append(wx.ID_SELECTALL, 'Select &All\tCtrl-A') self.Bind(wx.EVT_MENU, lambda e: self.bodyInput.SelectAll(), id=wx.ID_SELECTALL) editMenu.AppendSeparator() editMenu.Append(wx.ID_FIND, '&Find...\tCtrl-F') self.Bind(wx.EVT_MENU, lambda e: self.showSearchFrame(PassageSearchFrame.FIND_TAB), id=wx.ID_FIND) editMenu.Append(PassageFrame.EDIT_FIND_NEXT, 'Find &Next\tCtrl-G') self.Bind(wx.EVT_MENU, self.findNextRegexp, id=PassageFrame.EDIT_FIND_NEXT) if sys.platform == 'darwin': shortcut = 'Ctrl-Shift-H' else: shortcut = 'Ctrl-H' editMenu.Append(wx.ID_REPLACE, '&Replace...\t' + shortcut) self.Bind( wx.EVT_MENU, lambda e: self.showSearchFrame(PassageSearchFrame.REPLACE_TAB), id=wx.ID_REPLACE) # menus self.menus = wx.MenuBar() self.menus.Append(passageMenu, '&Passage') self.menus.Append(editMenu, '&Edit') self.SetMenuBar(self.menus) # controls self.panel = wx.Panel(self) allSizer = wx.BoxSizer(wx.VERTICAL) self.panel.SetSizer(allSizer) # title/tag controls self.topControls = wx.Panel(self.panel) topSizer = wx.FlexGridSizer(3, 2, metrics.size('relatedControls'), metrics.size('relatedControls')) titleLabel = wx.StaticText(self.topControls, style=wx.ALIGN_RIGHT, label=PassageFrame.TITLE_LABEL) self.titleInput = wx.TextCtrl(self.topControls) tagsLabel = wx.StaticText(self.topControls, style=wx.ALIGN_RIGHT, label=PassageFrame.TAGS_LABEL) self.tagsInput = wx.TextCtrl(self.topControls) topSizer.Add(titleLabel, 0, flag=wx.ALL, border=metrics.size('focusRing')) topSizer.Add(self.titleInput, 1, flag=wx.EXPAND | wx.ALL, border=metrics.size('focusRing')) topSizer.Add(tagsLabel, 0, flag=wx.ALL, border=metrics.size('focusRing')) topSizer.Add(self.tagsInput, 1, flag=wx.EXPAND | wx.ALL, border=metrics.size('focusRing')) topSizer.AddGrowableCol(1, 1) self.topControls.SetSizer(topSizer) # body text class Canvoid: def __init__(self, template=None, serial=None): self.points = [] # Defaults if template: self.penColor = template.penColor self.brushColor = template.brushColor else: self.penColor = "BLACK" self.brushColor = "RED" # Override if serial: self.penColor = serial["penColor"] self.brushColor = serial["brushColor"] for point in serial["points"]: self.points.append(wx.Point(point[0], point[1])) # From tuple @staticmethod def initFrom(serial): return Canvoid(serial=serial) def pop(self): self.points.pop() def serialize(self): return { "points": map(wx.Point.Get, self.points), # Map to tuples "brushColor": self.brushColor, "penColor": self.penColor, } class DrawPanelUpdateEvent(wx.PyEvent): pass class DrawPanel(wx.Panel): """Draw a line to a panel.""" CROSS = 5 EVT_UPDATE_ID = wx.NewEventType() EVT_UPDATE = wx.PyEventBinder(EVT_UPDATE_ID, 1) def __init__(self, parent): wx.Panel.__init__(self, parent) self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_LEFT_DOWN, self.OnClick) self.Bind(wx.EVT_MOTION, self.OnMove) self.resetFrom() def resetFrom(self, serial=None): if serial: canvoids = map(Canvoid.initFrom, serial["canvoids"]) else: canvoids = [] self.canvoids = canvoids self.SetBackgroundColour("WHITE") self.currentCanvoid = None self.transientPoint = None self.currentCursor = None self.transientCircle = False if serial: self.Refresh() def serialize(self): return { "canvoids": map(Canvoid.serialize, self.canvoids), } def OnPaint(self, event=None): dc = wx.PaintDC(self) dc.Clear() for canvoid in self.canvoids: dc.SetPen(wx.Pen(canvoid.penColor, 4)) dc.SetBrush(wx.Brush(canvoid.brushColor)) dc.DrawPolygon(canvoid.points) if self.currentCursor: dc.SetPen(wx.Pen("BLACK", 1)) if not self.transientCircle: dc.DrawLine(self.currentCursor.x - DrawPanel.CROSS, self.currentCursor.y + DrawPanel.CROSS, self.currentCursor.x + DrawPanel.CROSS, self.currentCursor.y - DrawPanel.CROSS) dc.DrawLine(self.currentCursor.x - DrawPanel.CROSS, self.currentCursor.y - DrawPanel.CROSS, self.currentCursor.x + DrawPanel.CROSS, self.currentCursor.y + DrawPanel.CROSS) else: dc.SetBrush(wx.Brush("WHITE", wx.TRANSPARENT)) dc.DrawCirclePoint(self.transientPoint, 10) def cap(self): if self.transientPoint: self.currentCanvoid.pop() self.currentCanvoid = None self.transientPoint = None self.transientCircle = False wx.PostEvent( self.GetEventHandler(), DrawPanelUpdateEvent(self.GetId(), DrawPanel.EVT_UPDATE_ID)) def OnClick(self, event=None): position = event.GetPosition() self.currentCursor = position if self.transientCircle: self.cap() else: if not self.currentCanvoid: self.currentCanvoid = Canvoid() self.currentCanvoid.points.append(position) self.canvoids.append(self.currentCanvoid) self.transientPoint = wx.Point(position.x, position.y) self.currentCanvoid.points.append(self.transientPoint) self.Refresh() def OnMove(self, event=None): self.currentCursor = event.GetPosition() if self.transientPoint: def pointDistSq(a, b): c = a - b return c.x * c.x + c.y * c.y def pointCopy(a, b): a.x = b.x a.y = b.y initPoint = self.currentCanvoid.points[0] self.transientCircle = len( self.currentCanvoid.points) > 2 and pointDistSq( initPoint, self.currentCursor) < 100 if self.transientCircle: pointCopy(self.transientPoint, initPoint) else: pointCopy(self.transientPoint, self.currentCursor) self.Refresh() self.drawInput = DrawPanel(self.panel) self.bodyInput = wx.stc.StyledTextCtrl(self.panel, style=wx.TE_PROCESS_TAB | wx.BORDER_SUNKEN) self.bodyInput.SetUseHorizontalScrollBar(False) self.bodyInput.SetMargins(8, 8) self.bodyInput.SetMarginWidth(1, 0) self.bodyInput.SetWrapMode(wx.stc.STC_WRAP_WORD) self.bodyInput.SetSelBackground( True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)) self.bodyInput.SetSelForeground( True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)) # The default keyboard shortcuts for StyledTextCtrl are # nonstandard on Mac OS X if sys.platform == "darwin": # cmd-left/right to move to beginning/end of line self.bodyInput.CmdKeyAssign(wx.stc.STC_KEY_LEFT, wx.stc.STC_SCMOD_CTRL, wx.stc.STC_CMD_HOMEDISPLAY) self.bodyInput.CmdKeyAssign( wx.stc.STC_KEY_LEFT, wx.stc.STC_SCMOD_CTRL | wx.stc.STC_SCMOD_SHIFT, wx.stc.STC_CMD_HOMEDISPLAYEXTEND) self.bodyInput.CmdKeyAssign(wx.stc.STC_KEY_RIGHT, wx.stc.STC_SCMOD_CTRL, wx.stc.STC_CMD_LINEENDDISPLAY) self.bodyInput.CmdKeyAssign( wx.stc.STC_KEY_RIGHT, wx.stc.STC_SCMOD_CTRL | wx.stc.STC_SCMOD_SHIFT, wx.stc.STC_CMD_LINEENDDISPLAYEXTEND) # opt-left/right to move forward/back a word self.bodyInput.CmdKeyAssign(wx.stc.STC_KEY_LEFT, wx.stc.STC_SCMOD_ALT, wx.stc.STC_CMD_WORDLEFT) self.bodyInput.CmdKeyAssign( wx.stc.STC_KEY_LEFT, wx.stc.STC_SCMOD_ALT | wx.stc.STC_SCMOD_SHIFT, wx.stc.STC_CMD_WORDLEFTEXTEND) self.bodyInput.CmdKeyAssign(wx.stc.STC_KEY_RIGHT, wx.stc.STC_SCMOD_ALT, wx.stc.STC_CMD_WORDRIGHT) self.bodyInput.CmdKeyAssign( wx.stc.STC_KEY_RIGHT, wx.stc.STC_SCMOD_ALT | wx.stc.STC_SCMOD_SHIFT, wx.stc.STC_CMD_WORDRIGHTEXTEND) # cmd-delete to delete from the cursor to beginning of line self.bodyInput.CmdKeyAssign(wx.stc.STC_KEY_BACK, wx.stc.STC_SCMOD_CTRL, wx.stc.STC_CMD_DELLINELEFT) # opt-delete to delete the previous/current word self.bodyInput.CmdKeyAssign(wx.stc.STC_KEY_BACK, wx.stc.STC_SCMOD_ALT, wx.stc.STC_CMD_DELWORDLEFT) # cmd-shift-z to redo self.bodyInput.CmdKeyAssign( ord('Z'), wx.stc.STC_SCMOD_CTRL | wx.stc.STC_SCMOD_SHIFT, wx.stc.STC_CMD_REDO) # final layout allSizer.Add(self.topControls, flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, border=metrics.size('windowBorder')) allSizer.Add(self.drawInput, proportion=1, flag=wx.TOP | wx.EXPAND, border=metrics.size('relatedControls')) allSizer.Add(self.bodyInput, proportion=1, flag=wx.TOP | wx.EXPAND, border=metrics.size('relatedControls')) self.lexer = TweeLexer(self.bodyInput, self) self.applyPrefs() self.syncInputs() self.bodyInput.EmptyUndoBuffer() self.updateSubmenus() self.setLexer() # event bindings # we need to do this AFTER setting up initial values self.titleInput.Bind(wx.EVT_TEXT, self.syncPassage) self.tagsInput.Bind(wx.EVT_TEXT, self.syncPassage) self.bodyInput.Bind(wx.stc.EVT_STC_CHANGE, self.syncPassage) self.drawInput.Bind(DrawPanel.EVT_UPDATE, self.syncPassage) self.bodyInput.Bind(wx.stc.EVT_STC_START_DRAG, self.prepDrag) self.Bind(wx.EVT_CLOSE, self.closeFullscreen) self.Bind(wx.EVT_MENU_OPEN, self.updateSubmenus) self.Bind(wx.EVT_UPDATE_UI, self.updateUI) if not re.match('Untitled Passage \d+', self.widget.passage.title): self.bodyInput.SetFocus() self.bodyInput.SetSelection(-1, -1) self.SetIcon(self.app.icon) self.Show(True)
def __init__(self, parent, widget, app): self.widget = widget self.app = app self.syncTimer = None self.lastFindRegexp = None self.lastFindFlags = None self.usingLexer = True wx.Frame.__init__(self, parent, wx.ID_ANY, title = 'Untitled Passage - ' + self.app.NAME, \ size = PassageFrame.DEFAULT_SIZE) # Passage menu passageMenu = wx.Menu() passageMenu.Append(PassageFrame.PASSAGE_EDIT_SELECTION, 'Create &Link From Selection\tCtrl-L') self.Bind(wx.EVT_MENU, self.editSelection, id=PassageFrame.PASSAGE_EDIT_SELECTION) self.outLinksMenu = wx.Menu() self.outLinksMenuTitle = passageMenu.AppendMenu( wx.ID_ANY, 'Outgoing Links', self.outLinksMenu) self.inLinksMenu = wx.Menu() self.inLinksMenuTitle = passageMenu.AppendMenu(wx.ID_ANY, 'Incoming Links', self.inLinksMenu) self.brokenLinksMenu = wx.Menu() self.brokenLinksMenuTitle = passageMenu.AppendMenu( wx.ID_ANY, 'Broken Links', self.brokenLinksMenu) passageMenu.AppendSeparator() passageMenu.Append(wx.ID_SAVE, '&Save Story\tCtrl-S') self.Bind(wx.EVT_MENU, self.widget.parent.parent.save, id=wx.ID_SAVE) passageMenu.Append(PassageFrame.PASSAGE_REBUILD_STORY, '&Rebuild Story\tCtrl-R') self.Bind(wx.EVT_MENU, self.widget.parent.parent.rebuild, id=PassageFrame.PASSAGE_REBUILD_STORY) passageMenu.AppendSeparator() passageMenu.Append(PassageFrame.PASSAGE_FULLSCREEN, '&Fullscreen View\tF12') self.Bind(wx.EVT_MENU, self.openFullscreen, id=PassageFrame.PASSAGE_FULLSCREEN) passageMenu.Append(wx.ID_CLOSE, '&Close Passage\tCtrl-W') self.Bind(wx.EVT_MENU, lambda e: self.Destroy(), id=wx.ID_CLOSE) # Edit menu editMenu = wx.Menu() editMenu.Append(wx.ID_UNDO, '&Undo\tCtrl-Z') self.Bind(wx.EVT_MENU, lambda e: self.bodyInput.Undo(), id=wx.ID_UNDO) editMenu.Append(wx.ID_REDO, '&Redo\tCtrl-Y') self.Bind(wx.EVT_MENU, lambda e: self.bodyInput.Redo(), id=wx.ID_REDO) editMenu.AppendSeparator() editMenu.Append(wx.ID_CUT, 'Cu&t\tCtrl-X') self.Bind(wx.EVT_MENU, lambda e: self.bodyInput.Cut(), id=wx.ID_CUT) editMenu.Append(wx.ID_COPY, '&Copy\tCtrl-C') self.Bind(wx.EVT_MENU, lambda e: self.bodyInput.Copy(), id=wx.ID_COPY) editMenu.Append(wx.ID_PASTE, '&Paste\tCtrl-V') self.Bind(wx.EVT_MENU, lambda e: self.bodyInput.Paste(), id=wx.ID_PASTE) editMenu.Append(wx.ID_SELECTALL, 'Select &All\tCtrl-A') self.Bind(wx.EVT_MENU, lambda e: self.bodyInput.SelectAll(), id=wx.ID_SELECTALL) editMenu.AppendSeparator() editMenu.Append(wx.ID_FIND, '&Find...\tCtrl-F') self.Bind(wx.EVT_MENU, lambda e: self.showSearchFrame(PassageSearchFrame.FIND_TAB), id=wx.ID_FIND) editMenu.Append(PassageFrame.EDIT_FIND_NEXT, 'Find &Next\tCtrl-G') self.Bind(wx.EVT_MENU, self.findNextRegexp, id=PassageFrame.EDIT_FIND_NEXT) if sys.platform == 'darwin': shortcut = 'Ctrl-Shift-H' else: shortcut = 'Ctrl-H' editMenu.Append(wx.ID_REPLACE, '&Replace...\t' + shortcut) self.Bind( wx.EVT_MENU, lambda e: self.showSearchFrame(PassageSearchFrame.REPLACE_TAB), id=wx.ID_REPLACE) # menus self.menus = wx.MenuBar() self.menus.Append(passageMenu, '&Passage') self.menus.Append(editMenu, '&Edit') self.SetMenuBar(self.menus) # controls self.panel = wx.Panel(self) allSizer = wx.BoxSizer(wx.VERTICAL) self.panel.SetSizer(allSizer) # title/tag controls self.topControls = wx.Panel(self.panel) topSizer = wx.FlexGridSizer(3, 2, metrics.size('relatedControls'), metrics.size('relatedControls')) titleLabel = wx.StaticText(self.topControls, style=wx.ALIGN_RIGHT, label=PassageFrame.TITLE_LABEL) self.titleInput = wx.TextCtrl(self.topControls) tagsLabel = wx.StaticText(self.topControls, style=wx.ALIGN_RIGHT, label=PassageFrame.TAGS_LABEL) self.tagsInput = wx.TextCtrl(self.topControls) topSizer.Add(titleLabel, 0, flag=wx.ALL, border=metrics.size('focusRing')) topSizer.Add(self.titleInput, 1, flag=wx.EXPAND | wx.ALL, border=metrics.size('focusRing')) topSizer.Add(tagsLabel, 0, flag=wx.ALL, border=metrics.size('focusRing')) topSizer.Add(self.tagsInput, 1, flag=wx.EXPAND | wx.ALL, border=metrics.size('focusRing')) topSizer.AddGrowableCol(1, 1) self.topControls.SetSizer(topSizer) # body text self.bodyInput = wx.stc.StyledTextCtrl(self.panel, style=wx.TE_PROCESS_TAB | wx.BORDER_SUNKEN) self.bodyInput.SetUseHorizontalScrollBar(False) self.bodyInput.SetMargins(8, 8) self.bodyInput.SetMarginWidth(1, 0) self.bodyInput.SetWrapMode(wx.stc.STC_WRAP_WORD) self.bodyInput.SetSelBackground( True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)) self.bodyInput.SetSelForeground( True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)) # final layout allSizer.Add(self.topControls, flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, border=metrics.size('windowBorder')) allSizer.Add(self.bodyInput, proportion=1, flag=wx.TOP | wx.EXPAND, border=metrics.size('relatedControls')) self.lexer = TweeLexer(self.bodyInput, self) self.applyPrefs() self.syncInputs() self.bodyInput.EmptyUndoBuffer() self.updateSubmenus() self.setLexer() # event bindings # we need to do this AFTER setting up initial values self.titleInput.Bind(wx.EVT_TEXT, self.syncPassage) self.tagsInput.Bind(wx.EVT_TEXT, self.syncPassage) self.bodyInput.Bind(wx.stc.EVT_STC_CHANGE, self.syncPassage) self.bodyInput.Bind(wx.stc.EVT_STC_START_DRAG, self.prepDrag) self.Bind(wx.EVT_CLOSE, self.closeFullscreen) self.Bind(wx.EVT_MENU_OPEN, self.updateSubmenus) self.Bind(wx.EVT_UPDATE_UI, self.updateUI) if not re.match('Untitled Passage \d+', self.widget.passage.title): self.bodyInput.SetFocus() self.bodyInput.SetSelection(-1, -1) self.SetIcon(self.app.icon) self.Show(True)
def __init__(self, parent, widget, app): self.widget = widget self.app = app self.syncTimer = None self.lastFindRegexp = None self.lastFindFlags = None self.usingLexer = self.LEXER_NORMAL self.titleInvalid = False wx.Frame.__init__(self, parent, wx.ID_ANY, title = 'Untitled Passage - ' + self.app.NAME + ' ' + versionString, \ size = PassageFrame.DEFAULT_SIZE) # Passage menu passageMenu = wx.Menu() passageMenu.Append(PassageFrame.PASSAGE_EDIT_SELECTION, 'Create &Link From Selection\tCtrl-L') self.Bind(wx.EVT_MENU, self.editSelection, id=PassageFrame.PASSAGE_EDIT_SELECTION) self.outLinksMenu = wx.Menu() self.outLinksMenuTitle = passageMenu.AppendMenu( wx.ID_ANY, 'Outgoing Links', self.outLinksMenu) self.inLinksMenu = wx.Menu() self.inLinksMenuTitle = passageMenu.AppendMenu(wx.ID_ANY, 'Incoming Links', self.inLinksMenu) self.brokenLinksMenu = wx.Menu() self.brokenLinksMenuTitle = passageMenu.AppendMenu( wx.ID_ANY, 'Broken Links', self.brokenLinksMenu) passageMenu.AppendSeparator() passageMenu.Append(wx.ID_SAVE, '&Save Story\tCtrl-S') self.Bind(wx.EVT_MENU, self.widget.parent.parent.save, id=wx.ID_SAVE) passageMenu.Append(PassageFrame.PASSAGE_VERIFY, '&Verify Passage\tCtrl-E') self.Bind(wx.EVT_MENU, lambda e: (self.widget.verifyPassage(self), self.offerAssistance()),\ id = PassageFrame.PASSAGE_VERIFY) passageMenu.Append(PassageFrame.PASSAGE_TEST_HERE, '&Test Play From Here\tCtrl-T') self.Bind(wx.EVT_MENU, lambda e: self.widget.parent.parent.testBuild(e, startAt = self.widget.passage.title),\ id = PassageFrame.PASSAGE_TEST_HERE) passageMenu.Append(PassageFrame.PASSAGE_REBUILD_STORY, '&Rebuild Story\tCtrl-R') self.Bind(wx.EVT_MENU, self.widget.parent.parent.rebuild, id=PassageFrame.PASSAGE_REBUILD_STORY) passageMenu.AppendSeparator() passageMenu.Append(PassageFrame.PASSAGE_FULLSCREEN, '&Fullscreen View\tF12') self.Bind(wx.EVT_MENU, self.openFullscreen, id=PassageFrame.PASSAGE_FULLSCREEN) passageMenu.Append(wx.ID_CLOSE, '&Close Passage\tCtrl-W') self.Bind(wx.EVT_MENU, lambda e: self.Close(), id=wx.ID_CLOSE) # Edit menu editMenu = wx.Menu() editMenu.Append(wx.ID_UNDO, '&Undo\tCtrl-Z') self.Bind(wx.EVT_MENU, lambda e: self.bodyInput.Undo(), id=wx.ID_UNDO) if sys.platform == 'darwin': shortcut = 'Ctrl-Shift-Z' else: shortcut = 'Ctrl-Y' editMenu.Append(wx.ID_REDO, '&Redo\t' + shortcut) self.Bind(wx.EVT_MENU, lambda e: self.bodyInput.Redo(), id=wx.ID_REDO) editMenu.AppendSeparator() editMenu.Append(wx.ID_CUT, 'Cu&t\tCtrl-X') self.Bind(wx.EVT_MENU, lambda e: self.bodyInput.Cut(), id=wx.ID_CUT) editMenu.Append(wx.ID_COPY, '&Copy\tCtrl-C') self.Bind(wx.EVT_MENU, lambda e: self.bodyInput.Copy(), id=wx.ID_COPY) editMenu.Append(wx.ID_PASTE, '&Paste\tCtrl-V') self.Bind(wx.EVT_MENU, lambda e: self.bodyInput.Paste(), id=wx.ID_PASTE) editMenu.Append(wx.ID_SELECTALL, 'Select &All\tCtrl-A') self.Bind(wx.EVT_MENU, lambda e: self.bodyInput.SelectAll(), id=wx.ID_SELECTALL) editMenu.AppendSeparator() editMenu.Append(wx.ID_FIND, '&Find...\tCtrl-F') self.Bind(wx.EVT_MENU, lambda e: self.showSearchFrame(PassageSearchFrame.FIND_TAB), id=wx.ID_FIND) editMenu.Append(PassageFrame.EDIT_FIND_NEXT, 'Find &Next\tCtrl-G') self.Bind(wx.EVT_MENU, self.findNextRegexp, id=PassageFrame.EDIT_FIND_NEXT) if sys.platform == 'darwin': shortcut = 'Ctrl-Shift-H' else: shortcut = 'Ctrl-H' editMenu.Append(wx.ID_REPLACE, '&Replace...\t' + shortcut) self.Bind( wx.EVT_MENU, lambda e: self.showSearchFrame(PassageSearchFrame.REPLACE_TAB), id=wx.ID_REPLACE) # help menu helpMenu = wx.Menu() helpMenu.Append(PassageFrame.HELP1, 'About Passages') self.Bind(wx.EVT_MENU, lambda e: wx.LaunchDefaultBrowser( 'http://twinery.org/wiki/passage'), id=PassageFrame.HELP1) helpMenu.Append(PassageFrame.HELP2, 'About Text Syntax') self.Bind(wx.EVT_MENU, lambda e: wx.LaunchDefaultBrowser( 'http://twinery.org/wiki/syntax'), id=PassageFrame.HELP2) helpMenu.Append(PassageFrame.HELP3, 'About Links') self.Bind( wx.EVT_MENU, lambda e: wx.LaunchDefaultBrowser('http://twinery.org/wiki/link'), id=PassageFrame.HELP3) helpMenu.Append(PassageFrame.HELP4, 'About Tags') self.Bind( wx.EVT_MENU, lambda e: wx.LaunchDefaultBrowser('http://twinery.org/wiki/tag'), id=PassageFrame.HELP4) # menus self.menus = wx.MenuBar() self.menus.Append(passageMenu, '&Passage') self.menus.Append(editMenu, '&Edit') self.menus.Append(helpMenu, '&Help') self.SetMenuBar(self.menus) # controls self.panel = wx.Panel(self) allSizer = wx.BoxSizer(wx.VERTICAL) self.panel.SetSizer(allSizer) # title/tag controls self.topControls = wx.Panel(self.panel) topSizer = wx.FlexGridSizer(3, 2, metrics.size('relatedControls'), metrics.size('relatedControls')) self.titleLabel = wx.StaticText(self.topControls, style=wx.ALIGN_RIGHT, label=PassageFrame.TITLE_LABEL) self.titleInput = wx.TextCtrl(self.topControls) tagsLabel = wx.StaticText(self.topControls, style=wx.ALIGN_RIGHT, label=PassageFrame.TAGS_LABEL) self.tagsInput = wx.TextCtrl(self.topControls) topSizer.Add(self.titleLabel, 0, flag=wx.ALL, border=metrics.size('focusRing')) topSizer.Add(self.titleInput, 1, flag=wx.EXPAND | wx.ALL, border=metrics.size('focusRing')) topSizer.Add(tagsLabel, 0, flag=wx.ALL, border=metrics.size('focusRing')) topSizer.Add(self.tagsInput, 1, flag=wx.EXPAND | wx.ALL, border=metrics.size('focusRing')) topSizer.AddGrowableCol(1, 1) self.topControls.SetSizer(topSizer) # body text self.bodyInput = wx.stc.StyledTextCtrl(self.panel, style=wx.TE_PROCESS_TAB | wx.BORDER_SUNKEN) self.bodyInput.SetUseHorizontalScrollBar(False) self.bodyInput.SetMargins(8, 8) self.bodyInput.SetMarginWidth(1, 0) self.bodyInput.SetWrapMode(wx.stc.STC_WRAP_WORD) self.bodyInput.SetSelBackground( True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)) self.bodyInput.SetSelForeground( True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)) # The default keyboard shortcuts for StyledTextCtrl are # nonstandard on Mac OS X if sys.platform == "darwin": # cmd-left/right to move to beginning/end of line self.bodyInput.CmdKeyAssign(wx.stc.STC_KEY_LEFT, wx.stc.STC_SCMOD_CTRL, wx.stc.STC_CMD_HOMEDISPLAY) self.bodyInput.CmdKeyAssign( wx.stc.STC_KEY_LEFT, wx.stc.STC_SCMOD_CTRL | wx.stc.STC_SCMOD_SHIFT, wx.stc.STC_CMD_HOMEDISPLAYEXTEND) self.bodyInput.CmdKeyAssign(wx.stc.STC_KEY_RIGHT, wx.stc.STC_SCMOD_CTRL, wx.stc.STC_CMD_LINEENDDISPLAY) self.bodyInput.CmdKeyAssign( wx.stc.STC_KEY_RIGHT, wx.stc.STC_SCMOD_CTRL | wx.stc.STC_SCMOD_SHIFT, wx.stc.STC_CMD_LINEENDDISPLAYEXTEND) # opt-left/right to move forward/back a word self.bodyInput.CmdKeyAssign(wx.stc.STC_KEY_LEFT, wx.stc.STC_SCMOD_ALT, wx.stc.STC_CMD_WORDLEFT) self.bodyInput.CmdKeyAssign( wx.stc.STC_KEY_LEFT, wx.stc.STC_SCMOD_ALT | wx.stc.STC_SCMOD_SHIFT, wx.stc.STC_CMD_WORDLEFTEXTEND) self.bodyInput.CmdKeyAssign(wx.stc.STC_KEY_RIGHT, wx.stc.STC_SCMOD_ALT, wx.stc.STC_CMD_WORDRIGHT) self.bodyInput.CmdKeyAssign( wx.stc.STC_KEY_RIGHT, wx.stc.STC_SCMOD_ALT | wx.stc.STC_SCMOD_SHIFT, wx.stc.STC_CMD_WORDRIGHTEXTEND) # cmd-delete to delete from the cursor to beginning of line self.bodyInput.CmdKeyAssign(wx.stc.STC_KEY_BACK, wx.stc.STC_SCMOD_CTRL, wx.stc.STC_CMD_DELLINELEFT) # opt-delete to delete the previous/current word self.bodyInput.CmdKeyAssign(wx.stc.STC_KEY_BACK, wx.stc.STC_SCMOD_ALT, wx.stc.STC_CMD_DELWORDLEFT) # cmd-shift-z to redo self.bodyInput.CmdKeyAssign( ord('Z'), wx.stc.STC_SCMOD_CTRL | wx.stc.STC_SCMOD_SHIFT, wx.stc.STC_CMD_REDO) # final layout allSizer.Add(self.topControls, flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, border=metrics.size('windowBorder')) allSizer.Add(self.bodyInput, proportion=1, flag=wx.TOP | wx.EXPAND, border=metrics.size('relatedControls')) self.lexer = TweeLexer(self.bodyInput, self) self.applyPrefs() self.syncInputs() self.bodyInput.EmptyUndoBuffer() self.updateSubmenus() self.setLexer() # event bindings # we need to do this AFTER setting up initial values self.titleInput.Bind(wx.EVT_TEXT, self.syncPassage) self.tagsInput.Bind(wx.EVT_TEXT, self.syncPassage) self.bodyInput.Bind(wx.stc.EVT_STC_CHANGE, self.syncPassage) self.bodyInput.Bind(wx.stc.EVT_STC_START_DRAG, self.prepDrag) self.Bind(wx.EVT_CLOSE, self.closeEditor) self.Bind(wx.EVT_MENU_OPEN, self.updateSubmenus) self.Bind(wx.EVT_UPDATE_UI, self.updateUI) if not re.match('Untitled Passage \d+', self.widget.passage.title): self.bodyInput.SetFocus() self.bodyInput.SetSelection(-1, -1) # Hack to force titles (>18 char) to display correctly. # NOTE: stops working if moved above bodyInput code. self.titleInput.SetInsertionPoint(0) self.SetIcon(self.app.icon) self.Show(True)