def loadPrefs(self): """Loads user preferences into self.config, setting up defaults if none are set.""" self.config = wx.Config('Twine') monoFont = wx.SystemSettings.GetFont(wx.SYS_ANSI_FIXED_FONT) if not self.config.HasEntry('savePath'): self.config.Write('savePath', os.path.expanduser('~')) if not self.config.HasEntry('fsTextColor'): self.config.Write('fsTextColor', '#afcdff') if not self.config.HasEntry('fsBgColor'): self.config.Write('fsBgColor', '#100088') if not self.config.HasEntry('fsFontFace'): self.config.Write('fsFontFace', metrics.face('mono')) if not self.config.HasEntry('fsFontSize'): self.config.WriteInt('fsFontSize', metrics.size('fsEditorBody')) if not self.config.HasEntry('fsLineHeight'): self.config.WriteInt('fsLineHeight', 120) if not self.config.HasEntry('windowedFontFace'): self.config.Write('windowedFontFace', metrics.face('mono')) if not self.config.HasEntry('windowedFontSize'): self.config.WriteInt('windowedFontSize', metrics.size('editorBody')) if not self.config.HasEntry('storyFrameToolbar'): self.config.WriteBool('storyFrameToolbar', True) if not self.config.HasEntry('storyPanelSnap'): self.config.WriteBool('storyPanelSnap', False) if not self.config.HasEntry('fastStoryPanel'): self.config.WriteBool('fastStoryPanel', False)
def loadPrefs (self): """Loads user preferences into self.config, setting up defaults if none are set.""" self.config = wx.Config('Twine') monoFont = wx.SystemSettings.GetFont(wx.SYS_ANSI_FIXED_FONT) if not self.config.HasEntry('savePath'): self.config.Write('savePath', os.path.expanduser('~')) if not self.config.HasEntry('fsTextColor'): self.config.Write('fsTextColor', '#afcdff') if not self.config.HasEntry('fsBgColor'): self.config.Write('fsBgColor', '#100088') if not self.config.HasEntry('fsFontFace'): self.config.Write('fsFontFace', metrics.face('mono')) if not self.config.HasEntry('fsFontSize'): self.config.WriteInt('fsFontSize', metrics.size('fsEditorBody')) if not self.config.HasEntry('fsLineHeight'): self.config.WriteInt('fsLineHeight', 120) if not self.config.HasEntry('windowedFontFace'): self.config.Write('windowedFontFace', metrics.face('mono')) if not self.config.HasEntry('windowedFontSize'): self.config.WriteInt('windowedFontSize', metrics.size('editorBody')) if not self.config.HasEntry('storyFrameToolbar'): self.config.WriteBool('storyFrameToolbar', True) if not self.config.HasEntry('storyPanelSnap'): self.config.WriteBool('storyPanelSnap', False)
def loadPrefs(self): """Loads user preferences into self.config, setting up defaults if none are set.""" sc = self.config = wx.Config('Twine') monoFont = wx.SystemSettings.GetFont(wx.SYS_ANSI_FIXED_FONT) for k,v in { 'savePath' : os.path.expanduser('~'), 'fsTextColor' : '#afcdff', 'fsBgColor' : '#100088', 'fsFontFace' : metrics.face('mono'), 'fsFontSize' : metrics.size('fsEditorBody'), 'fsLineHeight' : 120, 'windowedFontFace' : metrics.face('mono'), 'monospaceFontFace' : metrics.face('mono2'), 'windowedFontSize' : metrics.size('editorBody'), 'monospaceFontSize' : metrics.size('editorBody'), 'flatDesign' : False, 'storyFrameToolbar' : True, 'storyPanelSnap' : False, 'fastStoryPanel' : False, 'imageArrows' : True, 'displayArrows' : True, 'createPassagePrompt' : True, 'importImagePrompt' : True, 'passageWarnings' : True }.iteritems(): if not sc.HasEntry(k): if type(v) == str: sc.Write(k,v) elif type(v) == int: sc.WriteInt(k,v) elif type(v) == bool: sc.WriteBool(k,v)
def __init__(self, parent, storyPanel, app, id=wx.ID_ANY): wx.Dialog.__init__(self, parent, id, title='Story Statistics') self.storyPanel = storyPanel # layout panel = wx.Panel(parent=self) panelSizer = wx.BoxSizer(wx.VERTICAL) panel.SetSizer(panelSizer) # count controls countPanel = wx.Panel(parent=panel) countPanelSizer = wx.FlexGridSizer(5, 2, metrics.size('relatedControls'), metrics.size('relatedControls')) countPanel.SetSizer(countPanelSizer) self.characters = wx.StaticText(countPanel) countPanelSizer.Add(self.characters, flag=wx.ALIGN_RIGHT) countPanelSizer.Add(wx.StaticText(countPanel, label='Characters')) self.words = wx.StaticText(countPanel) countPanelSizer.Add(self.words, flag=wx.ALIGN_RIGHT) countPanelSizer.Add(wx.StaticText(countPanel, label='Words')) self.passages = wx.StaticText(countPanel) countPanelSizer.Add(self.passages, flag=wx.ALIGN_RIGHT) countPanelSizer.Add(wx.StaticText(countPanel, label='Passages')) self.links = wx.StaticText(countPanel) countPanelSizer.Add(self.links, flag=wx.ALIGN_RIGHT) countPanelSizer.Add(wx.StaticText(countPanel, label='Links')) self.brokenLinks = wx.StaticText(countPanel) countPanelSizer.Add(self.brokenLinks, flag=wx.ALIGN_RIGHT) countPanelSizer.Add(wx.StaticText(countPanel, label='Broken Links')) panelSizer.Add(countPanel, flag=wx.ALL | wx.ALIGN_CENTER, border=metrics.size('relatedControls')) okButton = wx.Button(parent=panel, label='OK') okButton.Bind(wx.EVT_BUTTON, lambda e: self.Close()) panelSizer.Add(okButton, flag=wx.ALL | wx.ALIGN_CENTER, border=metrics.size('relatedControls')) panelSizer.Fit(self) size = self.GetSize() if size.width < StatisticsDialog.MIN_WIDTH: size.width = StatisticsDialog.MIN_WIDTH self.SetSize(size) self.count() panelSizer.Layout() self.SetIcon(app.icon) self.Show()
def __init__(self, parent, storyPanel, app, id = wx.ID_ANY): wx.Dialog.__init__(self, parent, id, title = 'Story Statistics') self.storyPanel = storyPanel # layout panel = wx.Panel(parent = self) self.panelSizer = wx.BoxSizer(wx.VERTICAL) panel.SetSizer(self.panelSizer) # count controls countPanel = wx.Panel(parent = panel) countPanelSizer = wx.FlexGridSizer(6, 2, metrics.size('relatedControls'), metrics.size('relatedControls')) countPanel.SetSizer(countPanelSizer) self.characters = wx.StaticText(countPanel) countPanelSizer.Add(self.characters, flag = wx.ALIGN_RIGHT) countPanelSizer.Add(wx.StaticText(countPanel, label = 'Characters')) self.words = wx.StaticText(countPanel) countPanelSizer.Add(self.words, flag = wx.ALIGN_RIGHT) countPanelSizer.Add(wx.StaticText(countPanel, label = 'Words')) self.passages = wx.StaticText(countPanel) countPanelSizer.Add(self.passages, flag = wx.ALIGN_RIGHT) countPanelSizer.Add(wx.StaticText(countPanel, label = 'Passages')) self.links = wx.StaticText(countPanel) countPanelSizer.Add(self.links, flag = wx.ALIGN_RIGHT) countPanelSizer.Add(wx.StaticText(countPanel, label = 'Links')) self.brokenLinks = wx.StaticText(countPanel) countPanelSizer.Add(self.brokenLinks, flag = wx.ALIGN_RIGHT) countPanelSizer.Add(wx.StaticText(countPanel, label = 'Broken Links')) self.variablesCount = wx.StaticText(countPanel) countPanelSizer.Add(self.variablesCount, flag = wx.ALIGN_RIGHT) countPanelSizer.Add(wx.StaticText(countPanel, label = 'Variables Used')) self.panelSizer.Add(countPanel, flag = wx.ALL | wx.ALIGN_CENTER, border = metrics.size('relatedControls')) self.count(panel) okButton = wx.Button(parent = panel, label = 'OK') okButton.Bind(wx.EVT_BUTTON, lambda e: self.Close()) self.panelSizer.Add(okButton, flag = wx.ALL | wx.ALIGN_CENTER, border = metrics.size('relatedControls')) self.panelSizer.Fit(self) size = self.GetSize() if size.width < StatisticsDialog.MIN_WIDTH: size.width = StatisticsDialog.MIN_WIDTH self.SetSize(size) self.panelSizer.Layout() self.SetIcon(app.icon) self.Show()
def __init__ (self, app, parent = None): self.app = app wx.Frame.__init__(self, parent, wx.ID_ANY, title = self.app.NAME + ' Preferences', \ style = wx.MINIMIZE_BOX | wx.CLOSE_BOX | wx.CAPTION | wx.SYSTEM_MENU) panel = wx.Panel(parent = self, id = wx.ID_ANY) borderSizer = wx.BoxSizer(wx.VERTICAL) panel.SetSizer(borderSizer) panelSizer = wx.FlexGridSizer(5, 2, metrics.size('relatedControls'), metrics.size('relatedControls')) borderSizer.Add(panelSizer, flag = wx.ALL, border = metrics.size('windowBorder')) self.editorFont = wx.FontPickerCtrl(panel, style = wx.FNTP_FONTDESC_AS_LABEL) self.editorFont.SetSelectedFont(self.getPrefFont('windowed')) self.editorFont.Bind(wx.EVT_FONTPICKER_CHANGED, lambda e: self.saveFontPref('windowed', \ self.editorFont.GetSelectedFont())) self.fsFont = wx.FontPickerCtrl(panel, style = wx.FNTP_FONTDESC_AS_LABEL) self.fsFont.SetSelectedFont(self.getPrefFont('fs')) self.fsFont.Bind(wx.EVT_FONTPICKER_CHANGED, lambda e: self.saveFontPref('fs', \ self.fsFont.GetSelectedFont())) self.fsTextColor = wx.ColourPickerCtrl(panel) self.fsTextColor.SetColour(self.app.config.Read('fsTextColor')) self.fsTextColor.Bind(wx.EVT_COLOURPICKER_CHANGED, lambda e: self.savePref('fsTextColor', \ self.fsTextColor.GetColour())) self.fsBgColor = wx.ColourPickerCtrl(panel) self.fsBgColor.SetColour(self.app.config.Read('fsBgColor')) self.fsBgColor.Bind(wx.EVT_COLOURPICKER_CHANGED, lambda e: self.savePref('fsBgColor', \ self.fsBgColor.GetColour())) fsLineHeightPanel = wx.Panel(panel) fsLineHeightSizer = wx.BoxSizer(wx.HORIZONTAL) fsLineHeightPanel.SetSizer(fsLineHeightSizer) self.fsLineHeight = wx.ComboBox(fsLineHeightPanel, choices = ('100', '125', '150', '175', '200')) self.fsLineHeight.Bind(wx.EVT_TEXT, lambda e: self.savePref('fsLineHeight', int(self.fsLineHeight.GetValue()))) self.fsLineHeight.SetValue(str(self.app.config.ReadInt('fslineHeight'))) fsLineHeightSizer.Add(self.fsLineHeight, flag = wx.ALIGN_CENTER_VERTICAL) fsLineHeightSizer.Add(wx.StaticText(fsLineHeightPanel, label = '%'), flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(wx.StaticText(panel, label = 'Windowed Editor Font'), flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.editorFont) panelSizer.Add(wx.StaticText(panel, label = 'Fullscreen Editor Font'), flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.fsFont) panelSizer.Add(wx.StaticText(panel, label = 'Fullscreen Editor Text Color'), flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.fsTextColor) panelSizer.Add(wx.StaticText(panel, label = 'Fullscreen Editor Background Color'), \ flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.fsBgColor) panelSizer.Add(wx.StaticText(panel, label = 'Fullscreen Editor Line Spacing'), flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(fsLineHeightPanel, flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Fit(self) borderSizer.Fit(self) self.SetIcon(self.app.icon) self.Show()
def __init__(self, parent, onFind=None, onClose=None): self.findCallback = onFind self.closeCallback = onClose wx.Panel.__init__(self, parent) sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) # find text and label findSizer = wx.BoxSizer(wx.HORIZONTAL) findSizer.Add(wx.StaticText(self, label = 'Find'), flag = wx.BOTTOM | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, \ border = metrics.size('relatedControls'), proportion = 0) self.findField = wx.TextCtrl(self) findSizer.Add(self.findField, proportion = 1, flag = wx.BOTTOM | wx.EXPAND, \ border = metrics.size('relatedControls')) sizer.Add(findSizer, flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border=metrics.size('windowBorder')) # option checkboxes optionSizer = wx.BoxSizer(wx.HORIZONTAL) self.caseCheckbox = wx.CheckBox(self, label='Match Case') self.wholeWordCheckbox = wx.CheckBox(self, label='Whole Word') self.regexpCheckbox = wx.CheckBox(self, label='Regular Expression') optionSizer.Add(self.caseCheckbox, flag=wx.BOTTOM | wx.RIGHT, border=metrics.size('relatedControls')) optionSizer.Add(self.wholeWordCheckbox, flag = wx.BOTTOM | wx.LEFT | wx.RIGHT, \ border = metrics.size('relatedControls')) optionSizer.Add(self.regexpCheckbox, flag = wx.BOTTOM | wx.LEFT, \ border = metrics.size('relatedControls')) sizer.Add(optionSizer, flag = wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, \ border = metrics.size('windowBorder')) # find and close buttons buttonSizer = wx.BoxSizer(wx.HORIZONTAL) self.closeButton = wx.Button(self, label='Close') self.closeButton.Bind(wx.EVT_BUTTON, self.onClose) self.findButton = wx.Button(self, label='Find Next') self.findButton.Bind(wx.EVT_BUTTON, self.onFind) buttonSizer.Add(self.closeButton, flag=wx.TOP | wx.RIGHT, border=metrics.size('buttonSpace')) buttonSizer.Add(self.findButton, flag=wx.TOP, border=metrics.size('buttonSpace')) sizer.Add(buttonSizer, flag = wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT, \ border = metrics.size('windowBorder')) sizer.Fit(self)
def __init__(self, app, parent = None): self.app = app self.parent = parent wx.Frame.__init__(self, parent, wx.ID_ANY, title = parent.title + ' Metadata', \ style = wx.MINIMIZE_BOX | wx.CLOSE_BOX | wx.CAPTION | wx.SYSTEM_MENU) panel = wx.Panel(parent = self, id = wx.ID_ANY) borderSizer = wx.BoxSizer(wx.VERTICAL) panel.SetSizer(borderSizer) panelSizer = wx.FlexGridSizer(8, 1, metrics.size('relatedControls'), metrics.size('relatedControls')) borderSizer.Add(panelSizer, flag = wx.ALL, border = metrics.size('windowBorder')) ctrlset = {} for name, desc in \ [ ("identity", ("What your work identifies as:", "Is it a game, a story, a poem, or something else?\n(This is used for dialogs and error messages only.)", False)), ("description", ("A short description of your work:", "This is inserted in the HTML file's <meta> description tag, used by\nsearch engines and other automated tools.", True)) ]: textlabel = wx.StaticText(panel, label = desc[0]) if desc[2]: textctrl = wx.TextCtrl(panel, size=(200,60), style=wx.TE_MULTILINE) else: textctrl = wx.TextCtrl(panel, size=(200,-1)) textctrl.SetValue(parent.metadata.get(name, '')) textctrl.Bind(wx.EVT_TEXT, lambda e, name=name, textctrl=textctrl: self.saveSetting(name,textctrl.GetValue())) hSizer = wx.BoxSizer(wx.HORIZONTAL) hSizer.Add(textlabel,1,wx.ALIGN_LEFT|wx.ALIGN_TOP) hSizer.Add(textctrl,1,wx.EXPAND) panelSizer.Add(hSizer,flag=wx.ALL|wx.EXPAND) panelSizer.Add(wx.StaticText(panel, label = desc[1])) panelSizer.Add((1,2)) panelSizer.Fit(self) borderSizer.Fit(self) self.SetIcon(self.app.icon) self.Show() self.panelSizer = panelSizer self.borderSizer = borderSizer
def __init__ (self, parent, onFind = None, onClose = None): self.findCallback = onFind self.closeCallback = onClose wx.Panel.__init__(self, parent) sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) # find text and label findSizer = wx.BoxSizer(wx.HORIZONTAL) findSizer.Add(wx.StaticText(self, label = 'Find'), flag = wx.BOTTOM | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, \ border = metrics.size('relatedControls'), proportion = 0) self.findField = wx.TextCtrl(self) findSizer.Add(self.findField, proportion = 1, flag = wx.BOTTOM | wx.EXPAND, \ border = metrics.size('relatedControls')) sizer.Add(findSizer, flag = wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border = metrics.size('windowBorder')) # option checkboxes optionSizer = wx.BoxSizer(wx.HORIZONTAL) self.caseCheckbox = wx.CheckBox(self, label = 'Match Case') self.wholeWordCheckbox = wx.CheckBox(self, label = 'Whole Word') self.regexpCheckbox = wx.CheckBox(self, label = 'Regular Expression') optionSizer.Add(self.caseCheckbox, flag = wx.BOTTOM | wx.RIGHT, border = metrics.size('relatedControls')) optionSizer.Add(self.wholeWordCheckbox, flag = wx.BOTTOM | wx.LEFT | wx.RIGHT, \ border = metrics.size('relatedControls')) optionSizer.Add(self.regexpCheckbox, flag = wx.BOTTOM | wx.LEFT, \ border = metrics.size('relatedControls')) sizer.Add(optionSizer, flag = wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, \ border = metrics.size('windowBorder')) # find and close buttons buttonSizer = wx.BoxSizer(wx.HORIZONTAL) self.closeButton = wx.Button(self, label = 'Close') self.closeButton.Bind(wx.EVT_BUTTON, self.onClose) self.findButton = wx.Button(self, label = 'Find Next') self.findButton.Bind(wx.EVT_BUTTON, self.onFind) buttonSizer.Add(self.closeButton, flag = wx.TOP | wx.RIGHT, border = metrics.size('buttonSpace')) buttonSizer.Add(self.findButton, flag = wx.TOP, border = metrics.size('buttonSpace')) sizer.Add(buttonSizer, flag = wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT, \ border = metrics.size('windowBorder')) sizer.Fit(self)
def __init__(self, app, parent=None): self.app = app wx.Frame.__init__(self, parent, wx.ID_ANY, title = self.app.NAME + ' Preferences', \ style = wx.MINIMIZE_BOX | wx.CLOSE_BOX | wx.CAPTION | wx.SYSTEM_MENU) panel = wx.Panel(parent=self, id=wx.ID_ANY) borderSizer = wx.BoxSizer(wx.VERTICAL) panel.SetSizer(borderSizer) panelSizer = wx.FlexGridSizer(14, 2, metrics.size('relatedControls'), metrics.size('relatedControls')) borderSizer.Add(panelSizer, flag=wx.ALL, border=metrics.size('windowBorder')) self.editorFont = wx.FontPickerCtrl(panel, style=wx.FNTP_FONTDESC_AS_LABEL) self.editorFont.SetSelectedFont(self.getPrefFont('windowed')) self.editorFont.Bind(wx.EVT_FONTPICKER_CHANGED, lambda e: self.saveFontPref('windowed', \ self.editorFont.GetSelectedFont())) self.monoFont = wx.FontPickerCtrl(panel, style=wx.FNTP_FONTDESC_AS_LABEL) self.monoFont.SetSelectedFont(self.getPrefFont('monospace')) self.monoFont.Bind(wx.EVT_FONTPICKER_CHANGED, lambda e: self.saveFontPref('monospace', \ self.monoFont.GetSelectedFont())) self.fsFont = wx.FontPickerCtrl(panel, style=wx.FNTP_FONTDESC_AS_LABEL) self.fsFont.SetSelectedFont(self.getPrefFont('fs')) self.fsFont.Bind(wx.EVT_FONTPICKER_CHANGED, lambda e: self.saveFontPref('fs', \ self.fsFont.GetSelectedFont())) self.fsTextColor = wx.ColourPickerCtrl(panel) self.fsTextColor.SetColour(self.app.config.Read('fsTextColor')) self.fsTextColor.Bind(wx.EVT_COLOURPICKER_CHANGED, lambda e: self.savePref('fsTextColor', \ self.fsTextColor.GetColour())) self.fsBgColor = wx.ColourPickerCtrl(panel) self.fsBgColor.SetColour(self.app.config.Read('fsBgColor')) self.fsBgColor.Bind(wx.EVT_COLOURPICKER_CHANGED, lambda e: self.savePref('fsBgColor', \ self.fsBgColor.GetColour())) fsLineHeightPanel = wx.Panel(panel) fsLineHeightSizer = wx.BoxSizer(wx.HORIZONTAL) fsLineHeightPanel.SetSizer(fsLineHeightSizer) self.fsLineHeight = wx.ComboBox(fsLineHeightPanel, choices=('100', '125', '150', '175', '200')) self.fsLineHeight.Bind( wx.EVT_TEXT, lambda e: self.savePref( 'fsLineHeight', int(self.fsLineHeight.GetValue()))) self.fsLineHeight.SetValue(str( self.app.config.ReadInt('fslineHeight'))) fsLineHeightSizer.Add(self.fsLineHeight, flag=wx.ALIGN_CENTER_VERTICAL) fsLineHeightSizer.Add(wx.StaticText(fsLineHeightPanel, label='%'), flag=wx.ALIGN_CENTER_VERTICAL) def checkbox(self, name, label, panel=panel): setattr(self, name, wx.CheckBox(panel, label=label)) attr = getattr(self, name) attr.Bind(wx.EVT_CHECKBOX, lambda e, name=name, attr=attr: self.savePref( name, attr.GetValue())) attr.SetValue(self.app.config.ReadBool(name)) checkbox(self, "fastStoryPanel", 'Faster but rougher story map display') checkbox(self, "flatDesign", 'Flat Design(TM) mode') checkbox(self, "imageArrows", 'Connector arrows for images and stylesheets') checkbox(self, "displayArrows", 'Connector arrows for <<display>>ed passages') checkbox(self, "createPassagePrompt", 'Offer to create new passages for broken links') checkbox(self, "importImagePrompt", 'Offer to import externally linked images') checkbox(self, "passageWarnings", 'Warn about possible passage code errors') panelSizer.Add(wx.StaticText(panel, label='Normal Font'), flag=wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.editorFont) panelSizer.Add(wx.StaticText(panel, label='Monospace Font'), flag=wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.monoFont) panelSizer.Add(wx.StaticText(panel, label='Fullscreen Editor Font'), flag=wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.fsFont) panelSizer.Add(wx.StaticText(panel, label='Fullscreen Editor Text Color'), flag=wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.fsTextColor) panelSizer.Add(wx.StaticText(panel, label = 'Fullscreen Editor Background Color'), \ flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.fsBgColor) panelSizer.Add(wx.StaticText(panel, label='Fullscreen Editor Line Spacing'), flag=wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(fsLineHeightPanel, flag=wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.fastStoryPanel) # pylint: disable=no-member panelSizer.Add((1, 2)) panelSizer.Add(self.flatDesign) # pylint: disable=no-member panelSizer.Add((1, 2)) panelSizer.Add(self.imageArrows) # pylint: disable=no-member panelSizer.Add((1, 2)) panelSizer.Add(self.displayArrows) # pylint: disable=no-member panelSizer.Add((1, 2)) panelSizer.Add(wx.StaticText(panel, label='When closing a passage:'), flag=wx.ALIGN_CENTER_VERTICAL) panelSizer.Add((1, 2)) panelSizer.Add(self.createPassagePrompt) # pylint: disable=no-member panelSizer.Add((1, 2)) panelSizer.Add(self.importImagePrompt) # pylint: disable=no-member panelSizer.Add((1, 2)) panelSizer.Add(self.passageWarnings) # pylint: disable=no-member panelSizer.Fit(self) borderSizer.Fit(self) self.SetIcon(self.app.icon) self.Show() self.panelSizer = panelSizer self.borderSizer = borderSizer
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.Append(wx.ID_ANY, 'Outgoing Links', self.outLinksMenu) self.inLinksMenu = wx.Menu() self.inLinksMenuTitle = passageMenu.Append(wx.ID_ANY, 'Incoming Links', self.inLinksMenu) self.brokenLinksMenu = wx.Menu() self.brokenLinksMenuTitle = passageMenu.Append(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: wx.Window.FindFocus().Cut(), id = wx.ID_CUT) editMenu.Append(wx.ID_COPY, '&Copy\tCtrl-C') self.Bind(wx.EVT_MENU, lambda e: wx.Window.FindFocus().Copy(), id = wx.ID_COPY) editMenu.Append(wx.ID_PASTE, '&Paste\tCtrl-V') self.Bind(wx.EVT_MENU, lambda e: wx.Window.FindFocus().Paste(), id = wx.ID_PASTE) editMenu.Append(wx.ID_SELECTALL, 'Select &All\tCtrl-A') self.Bind(wx.EVT_MENU, lambda e: wx.Window.FindFocus().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() if self.widget.passage.isStylesheet(): helpMenu.Append(PassageFrame.HELP1, 'About Stylesheets') self.Bind(wx.EVT_MENU, lambda e: wx.LaunchDefaultBrowser('http://twinery.org/wiki/stylesheet'), id = PassageFrame.HELP1) elif self.widget.passage.isScript(): helpMenu.Append(PassageFrame.HELP1, 'About Scripts') self.Bind(wx.EVT_MENU, lambda e: wx.LaunchDefaultBrowser('http://twinery.org/wiki/script'), id = PassageFrame.HELP1) else: 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 Macros') self.Bind(wx.EVT_MENU, lambda e: wx.LaunchDefaultBrowser('http://twinery.org/wiki/macro'), id = PassageFrame.HELP4) helpMenu.Append(PassageFrame.HELP5, 'About Tags') self.Bind(wx.EVT_MENU, lambda e: wx.LaunchDefaultBrowser('http://twinery.org/wiki/tag'), id = PassageFrame.HELP5) # 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.SetTabWidth(4) 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)) self.bodyInput.SetFocus() # 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 = TweeStyler(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)
def __init__(self, parent, widget, app): self.widget = widget self.app = app wx.Frame.__init__(self, parent, wx.ID_ANY, title = self.widget.passage.title + ' - ' + self.app.NAME + ' ' + versionString, \ size = (metrics.size('storySettingsWidth'), metrics.size('storySettingsHeight')), style=wx.DEFAULT_FRAME_STYLE) # menus self.menus = wx.MenuBar() self.SetMenuBar(self.menus) # controls self.panel = wx.lib.scrolledpanel.ScrolledPanel(self) self.panel.SetupScrolling() allSizer = wx.BoxSizer(wx.VERTICAL) self.panel.SetSizer(allSizer) # Read the storysettings definitions for this header self.storySettingsData = self.widget.parent.parent.header.storySettings() if not self.storySettingsData or type(self.storySettingsData) is str: label = self.storySettingsData or "The currently selected story format does not use StorySettings." allSizer.Add(wx.StaticText(self.panel, label = label),flag=wx.ALL|wx.EXPAND, border=metrics.size('windowBorder')) self.storySettingsData = {} self.ctrls = {} for data in self.storySettingsData: ctrlset = [] name = '' if data["type"] == "checkbox": checkbox = wx.CheckBox(self.panel, label = data["label"]) name = data["name"] # Read current value, and default it if it's not present currentValue = self.getSetting(name).lower() if not currentValue: currentValue = data.get('default', 'off') self.saveSetting(name, currentValue) checkbox.SetValue(currentValue not in ["off", "false", '0']) values = data.get("values", ("on","off")) # pylint: disable=unused-variable checkbox.Bind(wx.EVT_CHECKBOX, lambda e, checkbox=checkbox, name=name, values=values: self.saveSetting(name, values[0] if checkbox.GetValue() else values[1] )) allSizer.Add(checkbox,flag=wx.ALL, border=metrics.size('windowBorder')) ctrlset.append(checkbox) elif data["type"] == "text": textlabel = wx.StaticText(self.panel, label = data["label"]) textctrl = wx.TextCtrl(self.panel) name = data["name"] # Read current value currentValue = self.getSetting(name).lower() if not currentValue: currentValue = data.get('default', '') self.saveSetting(name, currentValue) textctrl.SetValue(currentValue or data.get("default",'')) textctrl.Bind(wx.EVT_TEXT, lambda e, name=name, textctrl=textctrl: self.saveSetting(name,textctrl.GetValue())) # Setup sizer for label/textctrl pair hSizer = wx.BoxSizer(wx.HORIZONTAL) hSizer.Add(textlabel,1,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) hSizer.Add(textctrl,1,wx.EXPAND) allSizer.Add(hSizer,flag=wx.ALL|wx.EXPAND, border=metrics.size('windowBorder')) ctrlset += [textlabel, textctrl] else: continue if "desc" in data: desc = wx.StaticText(self.panel, label = data["desc"]) allSizer.Add(desc, 0, flag=wx.LEFT|wx.BOTTOM, border = metrics.size('windowBorder')) ctrlset.append(desc) self.ctrls[name] = ctrlset self.SetIcon(self.app.icon) self.SetTitle(self.title()) self.Layout() self.enableCtrls() self.Show(True)
def cachePaint(self, size): """ Caches the widget so self.paintBuffer is up-to-date. """ def wordWrap(text, lineWidth, gc, lineBreaks=False): """ Returns a list of lines from a string This is somewhat based on the wordwrap function built into wx.lib. (For some reason, GraphicsContext.GetPartialTextExtents() is returning totally wrong numbers but GetTextExtent() works fine.) This assumes that you've already set up the font you want on the GC. It gloms multiple spaces together, but for our purposes that's ok. """ words = re.finditer('\S+\s*', text.replace('\r', '')) lines = '' currentLine = '' for w in words: word = w.group(0) wordWidth = gc.GetTextExtent(currentLine + word)[0] if wordWidth < lineWidth: currentLine += word if '\n' in word: lines += currentLine currentLine = '' else: lines += currentLine + '\n' currentLine = word lines += currentLine return lines.split('\n') # Which set of colors to use flat = self.app.config.ReadBool('flatDesign') colors = PassageWidget.FLAT_COLORS if flat else PassageWidget.COLORS def dim(c, dim, flat=flat): """Lowers a color's alpha if dim is true.""" if isinstance(c, wx.Colour): c = list(c.Get(includeAlpha=True)) elif type(c) is str: c = list(ord(a) for a in c[1:].decode('hex')) else: c = list(c) if len(c) < 4: c.append(255) if dim: a = PassageWidget.FLAT_DIMMED_ALPHA if flat else PassageWidget.DIMMED_ALPHA if not self.app.config.ReadBool('fastStoryPanel'): c[3] *= a else: c[0] *= a c[1] *= a c[2] *= a return wx.Colour(*c) # set up our buffer bitmap = wx.EmptyBitmap(size.width, size.height) self.paintBuffer.SelectObject(bitmap) # switch to a GraphicsContext as necessary gc = self.paintBuffer if self.app.config.ReadBool( 'fastStoryPanel') else wx.GraphicsContext.Create(self.paintBuffer) # text font sizes # wxWindows works with points, so we need to doublecheck on actual pixels titleFontSize = self.parent.toPixels((metrics.size('widgetTitle'), -1), scaleOnly=True)[0] titleFontSize = sorted((metrics.size('fontMin'), titleFontSize, metrics.size('fontMax')))[1] excerptFontSize = sorted((metrics.size('fontMin'), titleFontSize * 0.9, metrics.size('fontMax')))[1] if self.app.config.ReadBool('flatDesign'): titleFont = wx.Font(titleFontSize, wx.SWISS, wx.NORMAL, wx.LIGHT, False, 'Arial') excerptFont = wx.Font(excerptFontSize, wx.SWISS, wx.NORMAL, wx.LIGHT, False, 'Arial') else: titleFont = wx.Font(titleFontSize, wx.SWISS, wx.NORMAL, wx.BOLD, False, 'Arial') excerptFont = wx.Font(excerptFontSize, wx.SWISS, wx.NORMAL, wx.NORMAL, False, 'Arial') titleFontHeight = math.fabs(titleFont.GetPixelSize()[1]) excerptFontHeight = math.fabs(excerptFont.GetPixelSize()[1]) tagBarColor = dim( tuple(i * 256 for i in colorsys.hsv_to_rgb( 0.14 + math.sin(hash("".join(self.passage.tags))) * 0.08, 0.58 if flat else 0.28, 0.88)), self.dimmed) tags = set( self.passage.tags) - (tiddlywiki.TiddlyWiki.INFO_TAGS | self.getHeader().invisiblePassageTags()) # inset for text (we need to know this for layout purposes) inset = titleFontHeight / 3 # frame if self.passage.isAnnotation(): frameColor = colors['frame'] c = wx.Colour(*colors['annotation']) frameInterior = (c, c) else: frameColor = dim(colors['frame'], self.dimmed) frameInterior = (dim(colors['bodyStart'], self.dimmed), dim(colors['bodyEnd'], self.dimmed)) if not flat: gc.SetPen(wx.Pen(frameColor, 1)) if isinstance(gc, wx.GraphicsContext): gc.SetBrush(gc.CreateLinearGradientBrush(0, 0, 0, size.height, \ frameInterior[0], frameInterior[1])) else: gc.GradientFillLinear(wx.Rect(0, 0, size.width - 1, size.height - 1), \ frameInterior[0], frameInterior[1], wx.SOUTH) gc.SetBrush(wx.TRANSPARENT_BRUSH) gc.DrawRectangle(0, 0, size.width - 1, size.height - 1) else: gc.SetPen(wx.Pen(frameInterior[0])) gc.SetBrush(wx.Brush(frameInterior[0])) gc.DrawRectangle(0, 0, size.width, size.height) greek = size.width <= PassageWidget.MIN_GREEKING_SIZE * ( 2 if self.passage.isAnnotation() else 1) # title bar titleBarHeight = PassageWidget.GREEK_HEIGHT * 3 if greek else titleFontHeight + ( 2 * inset) if self.passage.isAnnotation(): titleBarColor = frameInterior[0] else: titleBarColor = dim(self.getTitleColor(), self.dimmed) gc.SetPen(wx.Pen(titleBarColor, 1)) gc.SetBrush(wx.Brush(titleBarColor)) if flat: gc.DrawRectangle(0, 0, size.width, titleBarHeight) else: gc.DrawRectangle(1, 1, size.width - 3, titleBarHeight) if not greek: # draw title # we let clipping prevent writing over the frame if isinstance(gc, wx.GraphicsContext): gc.ResetClip() gc.Clip(inset, inset, size.width - (inset * 2), titleBarHeight - 2) else: gc.DestroyClippingRegion() gc.SetClippingRect( wx.Rect(inset, inset, size.width - (inset * 2), titleBarHeight - 2)) titleTextColor = dim(colors['titleText'], self.dimmed) if isinstance(gc, wx.GraphicsContext): gc.SetFont(titleFont, titleTextColor) else: gc.SetFont(titleFont) gc.SetTextForeground(titleTextColor) if self.passage.title: gc.DrawText(self.passage.title, inset, inset) # draw excerpt if not self.passage.isImage(): excerptTop = inset + titleBarHeight # we split the excerpt by line, then draw them in turn # (we use a library to determine breaks, but have to draw the lines ourselves) if isinstance(gc, wx.GraphicsContext): gc.ResetClip() gc.Clip(inset, inset, size.width - (inset * 2), size.height - (inset * 2) - 1) else: gc.DestroyClippingRegion() gc.SetClippingRect( wx.Rect(inset, inset, size.width - (inset * 2), size.height - (inset * 2) - 1)) if self.passage.isAnnotation(): excerptTextColor = wx.Colour(*colors['annotationText']) else: excerptTextColor = dim(colors['excerptText'], self.dimmed) if isinstance(gc, wx.GraphicsContext): gc.SetFont(excerptFont, excerptTextColor) else: gc.SetFont(excerptFont) gc.SetTextForeground(excerptTextColor) excerptLines = wordWrap(self.passage.text, size.width - (inset * 2), gc, self.passage.isAnnotation()) for line in excerptLines: gc.DrawText(line, inset, excerptTop) excerptTop += excerptFontHeight * PassageWidget.LINE_SPACING \ * min(1.75,max(1,1.75*size.width/260 if (self.passage.isAnnotation() and line) else 1)) if excerptTop + excerptFontHeight > size.height - inset: break if (self.passage.isStoryText() or self.passage.isStylesheet()) and tags: tagBarHeight = excerptFontHeight + (2 * inset) gc.SetPen(wx.Pen(tagBarColor, 1)) gc.SetBrush(wx.Brush(tagBarColor)) gc.DrawRectangle(0, size.height - tagBarHeight - 1, size.width, tagBarHeight + 1) # draw tags tagTextColor = dim(colors['frame'], self.dimmed) if isinstance(gc, wx.GraphicsContext): gc.SetFont(excerptFont, tagTextColor) else: gc.SetFont(excerptFont) gc.SetTextForeground(tagTextColor) text = wordWrap(" ".join(tags), size.width - (inset * 2), gc)[0] gc.DrawText(text, inset * 2, (size.height - tagBarHeight)) else: # greek title gc.SetPen(wx.Pen(colors['titleText'], PassageWidget.GREEK_HEIGHT)) height = inset width = (size.width - inset) / 2 if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(inset, height, width, height) else: gc.DrawLine(inset, height, width, height) height += PassageWidget.GREEK_HEIGHT * 3 # greek body text if not self.passage.isImage(): gc.SetPen(wx.Pen(colors['annotationText'] \ if self.passage.isAnnotation() else colors['greek'], PassageWidget.GREEK_HEIGHT)) chars = len(self.passage.text) while height < size.height - inset and chars > 0: width = size.height - inset if height + (PassageWidget.GREEK_HEIGHT * 2) > size.height - inset: width /= 2 elif chars < 80: width = max(4, width * chars / 80) if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(inset, height, width, height) else: gc.DrawLine(inset, height, width, height) height += PassageWidget.GREEK_HEIGHT * 2 chars -= 80 # greek tags if (self.passage.isStoryText() or self.passage.isStylesheet()) and tags: tagBarHeight = PassageWidget.GREEK_HEIGHT * 3 gc.SetPen(wx.Pen(tagBarColor, 1)) gc.SetBrush(wx.Brush(tagBarColor)) height = size.height - tagBarHeight - 2 width = size.width - 4 gc.DrawRectangle(2, height, width, tagBarHeight) gc.SetPen(wx.Pen(colors['greek'], PassageWidget.GREEK_HEIGHT)) height += inset width = (width - inset * 2) / 2 if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(inset, height, width, height) else: gc.DrawLine(inset, height, width, height) if self.passage.isImage(): if self.bitmap: if isinstance(gc, wx.GraphicsContext): gc.ResetClip() gc.Clip(1, titleBarHeight + 1, size.width - 3, size.height - 3) else: gc.DestroyClippingRegion() gc.SetClippingRect( wx.Rect(1, titleBarHeight + 1, size.width - 3, size.height - 3)) width = size.width height = size.height - titleBarHeight # choose smaller of vertical and horizontal scale factor, to preserve aspect ratio scale = min(width / float(self.bitmap.GetWidth()), height / float(self.bitmap.GetHeight())) img = self.bitmap.ConvertToImage() if scale != 1: img = img.Scale(scale * self.bitmap.GetWidth(), scale * self.bitmap.GetHeight()) # offset image horizontally or vertically, to centre after scaling offsetWidth = (width - img.GetWidth()) / 2 offsetHeight = (height - img.GetHeight()) / 2 if isinstance(gc, wx.GraphicsContext): gc.DrawBitmap(img.ConvertToBitmap(self.bitmap.GetDepth()), 1 + offsetWidth, titleBarHeight + 1 + offsetHeight, img.GetWidth(), img.GetHeight()) else: gc.DrawBitmap(img.ConvertToBitmap(self.bitmap.GetDepth()), 1 + offsetWidth, titleBarHeight + 1 + offsetHeight) if isinstance(gc, wx.GraphicsContext): gc.ResetClip() else: gc.DestroyClippingRegion() # draw a broken link emblem in the bottom right if necessary # fixme: not sure how to do this with transparency def showEmblem(emblem, gc=gc, size=size, inset=inset): emblemSize = emblem.GetSize() emblemPos = [ size.width - (emblemSize[0] + inset), \ size.height - (emblemSize[1] + inset) ] if isinstance(gc, wx.GraphicsContext): gc.DrawBitmap(emblem, emblemPos[0], emblemPos[1], emblemSize[0], emblemSize[1]) else: gc.DrawBitmap(emblem, emblemPos[0], emblemPos[1]) if len(self.getBrokenLinks()): showEmblem(self.brokenEmblem) elif len(self.getIncludedLinks()) or len(self.passage.variableLinks): showEmblem(self.externalEmblem) # finally, draw a selection over ourselves if we're selected if self.selected: color = dim( titleBarColor if flat else wx.SystemSettings_GetColour( wx.SYS_COLOUR_HIGHLIGHT), self.dimmed) if self.app.config.ReadBool('fastStoryPanel'): gc.SetPen(wx.Pen(color, 2 + flat)) else: gc.SetPen(wx.TRANSPARENT_PEN) if isinstance(gc, wx.GraphicsContext): r, g, b = color.Get(False) color = wx.Colour(r, g, b, 64) gc.SetBrush(wx.Brush(color)) else: gc.SetBrush(wx.TRANSPARENT_BRUSH) gc.DrawRectangle(0, 0, size.width, size.height) self.paintBufferBounds = size
def __init__(self, parent, widget, app): self.widget = widget self.app = app self.syncTimer = None self.image = None self.gif = None wx.Frame.__init__(self, parent, wx.ID_ANY, title = 'Untitled Passage - ' + self.app.NAME, \ size = PassageFrame.DEFAULT_SIZE, style=wx.DEFAULT_FRAME_STYLE) # menus self.menus = wx.MenuBar() self.SetMenuBar(self.menus) # controls self.panel = wx.Panel(self) allSizer = wx.BoxSizer(wx.VERTICAL) self.panel.SetSizer(allSizer) # title control 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) self.titleInput.SetValue(self.widget.passage.title) self.SetTitle(self.widget.passage.title + ' - ' + self.app.NAME) 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.AddGrowableCol(1, 1) self.topControls.SetSizer(topSizer) # image pane self.imageScroller = wx.ScrolledWindow(self.panel) self.imageSizer = wx.GridSizer(1, 1) self.imageScroller.SetSizer(self.imageSizer) # image menu passageMenu = wx.Menu() passageMenu.Append(self.IMPORT_IMAGE, '&Replace Image...\tCtrl-O') self.Bind(wx.EVT_MENU, self.replaceImage, id=self.IMPORT_IMAGE) passageMenu.Append(self.SAVE_IMAGE, '&Save Image...') self.Bind(wx.EVT_MENU, self.saveImage, id=self.SAVE_IMAGE) 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(wx.ID_CLOSE, '&Close Image\tCtrl-W') self.Bind(wx.EVT_MENU, lambda e: self.Destroy(), id=wx.ID_CLOSE) # edit menu editMenu = wx.Menu() editMenu.Append(wx.ID_COPY, '&Copy\tCtrl-C') self.Bind(wx.EVT_MENU, self.copyImage, id=wx.ID_COPY) editMenu.Append(wx.ID_PASTE, '&Paste\tCtrl-V') self.Bind(wx.EVT_MENU, self.pasteImage, id=wx.ID_PASTE) # menu bar self.menus = wx.MenuBar() self.menus.Append(passageMenu, '&Image') self.menus.Append(editMenu, '&Edit') self.SetMenuBar(self.menus) # finish allSizer.Add(self.topControls, flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, border=metrics.size('windowBorder')) allSizer.Add(self.imageScroller, proportion=1, flag=wx.TOP | wx.EXPAND, border=metrics.size('relatedControls')) # bindings self.titleInput.Bind(wx.EVT_TEXT, self.syncPassage) self.SetIcon(self.app.icon) self.updateImage() self.Show(True)
def __init__(self, app, parent=None): self.app = app wx.Frame.__init__(self, parent, wx.ID_ANY, title = self.app.NAME + ' Preferences', \ style = wx.MINIMIZE_BOX | wx.CLOSE_BOX | wx.CAPTION | wx.SYSTEM_MENU) panel = wx.Panel(parent=self, id=wx.ID_ANY) borderSizer = wx.BoxSizer(wx.VERTICAL) panel.SetSizer(borderSizer) panelSizer = wx.FlexGridSizer(6, 2, metrics.size('relatedControls'), metrics.size('relatedControls')) borderSizer.Add(panelSizer, flag=wx.ALL, border=metrics.size('windowBorder')) self.editorFont = wx.FontPickerCtrl(panel, style=wx.FNTP_FONTDESC_AS_LABEL) self.editorFont.SetSelectedFont(self.getPrefFont('windowed')) self.editorFont.Bind(wx.EVT_FONTPICKER_CHANGED, lambda e: self.saveFontPref('windowed', \ self.editorFont.GetSelectedFont())) self.fsFont = wx.FontPickerCtrl(panel, style=wx.FNTP_FONTDESC_AS_LABEL) self.fsFont.SetSelectedFont(self.getPrefFont('fs')) self.fsFont.Bind(wx.EVT_FONTPICKER_CHANGED, lambda e: self.saveFontPref('fs', \ self.fsFont.GetSelectedFont())) self.fsTextColor = wx.ColourPickerCtrl(panel) self.fsTextColor.SetColour(self.app.config.Read('fsTextColor')) self.fsTextColor.Bind(wx.EVT_COLOURPICKER_CHANGED, lambda e: self.savePref('fsTextColor', \ self.fsTextColor.GetColour())) self.fsBgColor = wx.ColourPickerCtrl(panel) self.fsBgColor.SetColour(self.app.config.Read('fsBgColor')) self.fsBgColor.Bind(wx.EVT_COLOURPICKER_CHANGED, lambda e: self.savePref('fsBgColor', \ self.fsBgColor.GetColour())) fsLineHeightPanel = wx.Panel(panel) fsLineHeightSizer = wx.BoxSizer(wx.HORIZONTAL) fsLineHeightPanel.SetSizer(fsLineHeightSizer) self.fsLineHeight = wx.ComboBox(fsLineHeightPanel, choices=('100', '125', '150', '175', '200')) self.fsLineHeight.Bind( wx.EVT_TEXT, lambda e: self.savePref( 'fsLineHeight', int(self.fsLineHeight.GetValue()))) self.fsLineHeight.SetValue(str( self.app.config.ReadInt('fslineHeight'))) fsLineHeightSizer.Add(self.fsLineHeight, flag=wx.ALIGN_CENTER_VERTICAL) fsLineHeightSizer.Add(wx.StaticText(fsLineHeightPanel, label='%'), flag=wx.ALIGN_CENTER_VERTICAL) self.fastStoryPanel = wx.CheckBox( panel, label='Faster but rougher story map display') self.fastStoryPanel.Bind(wx.EVT_CHECKBOX, lambda e: self.savePref('fastStoryPanel', \ self.fastStoryPanel.GetValue())) self.fastStoryPanel.SetValue( self.app.config.ReadBool('fastStoryPanel')) panelSizer.Add(wx.StaticText(panel, label='Windowed Editor Font'), flag=wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.editorFont) panelSizer.Add(wx.StaticText(panel, label='Fullscreen Editor Font'), flag=wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.fsFont) panelSizer.Add(wx.StaticText(panel, label='Fullscreen Editor Text Color'), flag=wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.fsTextColor) panelSizer.Add(wx.StaticText(panel, label = 'Fullscreen Editor Background Color'), \ flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.fsBgColor) panelSizer.Add(wx.StaticText(panel, label='Fullscreen Editor Line Spacing'), flag=wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(fsLineHeightPanel, flag=wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.fastStoryPanel) panelSizer.Fit(self) borderSizer.Fit(self) self.SetIcon(self.app.icon) self.Show()
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 wx.Frame.__init__(self, parent, wx.ID_ANY, title = self.widget.passage.title + ' - ' + self.app.NAME + ' ' + versionString, \ size = (metrics.size('storySettingsWidth'), metrics.size('storySettingsHeight')), style=wx.DEFAULT_FRAME_STYLE) # menus self.menus = wx.MenuBar() self.SetMenuBar(self.menus) # controls self.panel = wx.lib.scrolledpanel.ScrolledPanel(self) self.panel.SetupScrolling() allSizer = wx.BoxSizer(wx.VERTICAL) self.panel.SetSizer(allSizer) # Read the storysettings definitions for this header self.storySettingsData = self.widget.parent.parent.header.storySettings() if not self.storySettingsData or type(self.storySettingsData) is str: label = self.storySettingsData or "The currently selected story format does not use StorySettings." allSizer.Add(wx.StaticText(self.panel, label = label),flag=wx.ALL|wx.EXPAND, border=metrics.size('windowBorder')) self.storySettingsData = {} self.ctrls = {} for data in self.storySettingsData: ctrlset = [] name = '' if data["type"] == "checkbox": checkbox = wx.CheckBox(self.panel, label = data["label"]) name = data["name"] # Read current value, and default it if it's not present currentValue = self.getSetting(name).lower() if not currentValue: currentValue = data.get('default', 'off') self.saveSetting(name, currentValue) checkbox.SetValue(currentValue not in ["off", "false", '0']) values = data.get("values", ("on","off")) checkbox.Bind(wx.EVT_CHECKBOX, lambda e, checkbox=checkbox, name=name, values=values: self.saveSetting(name, values[0] if checkbox.GetValue() else values[1] )) allSizer.Add(checkbox,flag=wx.ALL, border=metrics.size('windowBorder')) ctrlset.append(checkbox) elif data["type"] == "text": textlabel = wx.StaticText(self.panel, label = data["label"]) textctrl = wx.TextCtrl(self.panel) name = data["name"] # Read current value currentValue = self.getSetting(name).lower() if not currentValue: currentValue = data.get('default', '') self.saveSetting(name, currentValue) textctrl.SetValue(currentValue or data.get("default",'')) textctrl.Bind(wx.EVT_TEXT, lambda e, name=name, textctrl=textctrl: self.saveSetting(name,textctrl.GetValue())) # Setup sizer for label/textctrl pair hSizer = wx.BoxSizer(wx.HORIZONTAL) hSizer.Add(textlabel,1,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) hSizer.Add(textctrl,1,wx.EXPAND) allSizer.Add(hSizer,flag=wx.ALL|wx.EXPAND, border=metrics.size('windowBorder')) ctrlset += [textlabel, textctrl] else: continue if "desc" in data: desc = wx.StaticText(self.panel, label = data["desc"]) allSizer.Add(desc, 0, flag=wx.LEFT|wx.BOTTOM, border = metrics.size('windowBorder')) ctrlset.append(desc) self.ctrls[name] = ctrlset self.SetIcon(self.app.icon) self.SetTitle(self.title()) self.Layout() self.enableCtrls() self.Show(True)
def __init__(self, app, parent = None): self.app = app wx.Frame.__init__(self, parent, wx.ID_ANY, title = self.app.NAME + ' Preferences', \ style = wx.MINIMIZE_BOX | wx.CLOSE_BOX | wx.CAPTION | wx.SYSTEM_MENU) panel = wx.Panel(parent = self, id = wx.ID_ANY) borderSizer = wx.BoxSizer(wx.VERTICAL) panel.SetSizer(borderSizer) panelSizer = wx.FlexGridSizer(14, 2, metrics.size('relatedControls'), metrics.size('relatedControls')) borderSizer.Add(panelSizer, flag = wx.ALL, border = metrics.size('windowBorder')) self.editorFont = wx.FontPickerCtrl(panel, style = wx.FNTP_FONTDESC_AS_LABEL) self.editorFont.SetSelectedFont(self.getPrefFont('windowed')) self.editorFont.Bind(wx.EVT_FONTPICKER_CHANGED, lambda e: self.saveFontPref('windowed', \ self.editorFont.GetSelectedFont())) self.monoFont = wx.FontPickerCtrl(panel, style = wx.FNTP_FONTDESC_AS_LABEL) self.monoFont.SetSelectedFont(self.getPrefFont('monospace')) self.monoFont.Bind(wx.EVT_FONTPICKER_CHANGED, lambda e: self.saveFontPref('monospace', \ self.monoFont.GetSelectedFont())) self.fsFont = wx.FontPickerCtrl(panel, style = wx.FNTP_FONTDESC_AS_LABEL) self.fsFont.SetSelectedFont(self.getPrefFont('fs')) self.fsFont.Bind(wx.EVT_FONTPICKER_CHANGED, lambda e: self.saveFontPref('fs', \ self.fsFont.GetSelectedFont())) self.fsTextColor = wx.ColourPickerCtrl(panel) self.fsTextColor.SetColour(self.app.config.Read('fsTextColor')) self.fsTextColor.Bind(wx.EVT_COLOURPICKER_CHANGED, lambda e: self.savePref('fsTextColor', \ self.fsTextColor.GetColour())) self.fsBgColor = wx.ColourPickerCtrl(panel) self.fsBgColor.SetColour(self.app.config.Read('fsBgColor')) self.fsBgColor.Bind(wx.EVT_COLOURPICKER_CHANGED, lambda e: self.savePref('fsBgColor', \ self.fsBgColor.GetColour())) fsLineHeightPanel = wx.Panel(panel) fsLineHeightSizer = wx.BoxSizer(wx.HORIZONTAL) fsLineHeightPanel.SetSizer(fsLineHeightSizer) self.fsLineHeight = wx.ComboBox(fsLineHeightPanel, choices = ('100', '125', '150', '175', '200')) self.fsLineHeight.Bind(wx.EVT_TEXT, lambda e: self.savePref('fsLineHeight', int(self.fsLineHeight.GetValue()))) self.fsLineHeight.SetValue(str(self.app.config.ReadInt('fslineHeight'))) fsLineHeightSizer.Add(self.fsLineHeight, flag = wx.ALIGN_CENTER_VERTICAL) fsLineHeightSizer.Add(wx.StaticText(fsLineHeightPanel, label = '%'), flag = wx.ALIGN_CENTER_VERTICAL) def checkbox(self, name, label, panel=panel): setattr(self, name, wx.CheckBox(panel, label=label)) attr = getattr(self, name) attr.Bind(wx.EVT_CHECKBOX, lambda e, name=name, attr=attr: self.savePref(name, attr.GetValue())) attr.SetValue(self.app.config.ReadBool(name)) checkbox(self, "fastStoryPanel", 'Faster but rougher story map display') checkbox(self, "flatDesign", 'Flat Design(TM) mode') checkbox(self, "imageArrows", 'Connector arrows for images and stylesheets') checkbox(self, "displayArrows", 'Connector arrows for <<display>>ed passages') checkbox(self, "createPassagePrompt", 'Offer to create new passages for broken links') checkbox(self, "importImagePrompt", 'Offer to import externally linked images') checkbox(self, "passageWarnings", 'Warn about possible passage code errors') panelSizer.Add(wx.StaticText(panel, label = 'Normal Font'), flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.editorFont) panelSizer.Add(wx.StaticText(panel, label = 'Monospace Font'), flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.monoFont) panelSizer.Add(wx.StaticText(panel, label = 'Fullscreen Editor Font'), flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.fsFont) panelSizer.Add(wx.StaticText(panel, label = 'Fullscreen Editor Text Color'), flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.fsTextColor) panelSizer.Add(wx.StaticText(panel, label = 'Fullscreen Editor Background Color'), \ flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.fsBgColor) panelSizer.Add(wx.StaticText(panel, label = 'Fullscreen Editor Line Spacing'), flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(fsLineHeightPanel, flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.fastStoryPanel) panelSizer.Add((1,2)) panelSizer.Add(self.flatDesign) panelSizer.Add((1,2)) panelSizer.Add(self.imageArrows) panelSizer.Add((1,2)) panelSizer.Add(self.displayArrows) panelSizer.Add((1,2)) panelSizer.Add(wx.StaticText(panel, label = 'When closing a passage:'), flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add((1,2)) panelSizer.Add(self.createPassagePrompt) panelSizer.Add((1,2)) panelSizer.Add(self.importImagePrompt) panelSizer.Add((1,2)) panelSizer.Add(self.passageWarnings) panelSizer.Fit(self) borderSizer.Fit(self) self.SetIcon(self.app.icon) self.Show() self.panelSizer = panelSizer self.borderSizer = borderSizer
def paint (self, gc): """ Paints widget to the graphics context passed. You may give it either a wx.GraphicsContext (anti-aliased drawing) or a wx.PaintDC. """ def wordWrap (text, lineWidth, gc): """ Returns a list of lines from a string This is somewhat based on the wordwrap function built into wx.lib. (For some reason, GraphicsContext.GetPartialTextExtents() is returning totally wrong numbers but GetTextExtent() works fine.) This assumes that you've already set up the font you want on the GC. It gloms multiple spaces together, but for our purposes that's ok. """ words = text.split() lines = [] currentWidth = 0 currentLine = '' for word in words: wordWidth = gc.GetTextExtent(word + ' ')[0] if currentWidth + wordWidth < lineWidth: currentLine += word + ' ' currentWidth += wordWidth else: lines.append(currentLine) currentLine = word + ' ' currentWidth = wordWidth lines.append(currentLine) return lines def dim (c, dim): """Lowers a color's alpha if dim is true.""" if isinstance(c, wx.Color): c = list(c.Get(includeAlpha = True)) if len(c) < 4: c = list(c) c.append(255) if dim: c[3] *= PassageWidget.DIMMED_ALPHA return wx.Color(c[0], c[1], c[2], c[3]) pixPos = self.parent.toPixels(self.pos) pixSize = self.parent.toPixels(self.getSize(), scaleOnly = True) # text font sizes # wxWindows works with points, so we need to doublecheck on actual pixels titleFontSize = self.parent.toPixels((metrics.size('widgetTitle'), -1), scaleOnly = True)[0] titleFontSize = min(titleFontSize, metrics.size('fontMax')) titleFontSize = max(titleFontSize, metrics.size('fontMin')) excerptFontSize = min(titleFontSize * 0.9, metrics.size('fontMax')) excerptFontSize = max(excerptFontSize, metrics.size('fontMin')) titleFont = wx.Font(titleFontSize, wx.SWISS, wx.NORMAL, wx.BOLD, False, 'Arial') excerptFont = wx.Font(excerptFontSize, wx.SWISS, wx.NORMAL, wx.NORMAL, False, 'Arial') titleFontHeight = math.fabs(titleFont.GetPixelSize()[1]) excerptFontHeight = math.fabs(excerptFont.GetPixelSize()[1]) # inset for text (we need to know this for layout purposes) inset = titleFontHeight / 3 # frame frameColor = dim(PassageWidget.COLORS['frame'], self.dimmed) frameInterior = (dim(PassageWidget.COLORS['bodyStart'], self.dimmed), \ dim(PassageWidget.COLORS['bodyEnd'], self.dimmed)) gc.SetPen(wx.Pen(frameColor, 1)) if isinstance(gc, wx.GraphicsContext): gc.SetBrush(gc.CreateLinearGradientBrush(pixPos[0], pixPos[1], \ pixPos[0], pixPos[1] + pixSize[1], \ frameInterior[0], frameInterior[1])) else: gc.GradientFillLinear(wx.Rect(pixPos[0], pixPos[1], pixSize[0] - 1, pixSize[1] - 1), \ frameInterior[0], frameInterior[1], wx.SOUTH) gc.SetBrush(wx.TRANSPARENT_BRUSH) gc.DrawRectangle(pixPos[0], pixPos[1], pixSize[0] - 1, pixSize[1] - 1) if pixSize[0] > PassageWidget.MIN_GREEKING_SIZE: # title bar titleBarHeight = titleFontHeight + (2 * inset) titleBarColor = dim(PassageWidget.COLORS['titleBar'], self.dimmed) gc.SetPen(wx.Pen(titleBarColor, 1)) gc.SetBrush(wx.Brush(titleBarColor)) gc.DrawRectangle(pixPos[0] + 1, pixPos[1] + 1, pixSize[0] - 3, titleBarHeight) # draw title # we let clipping prevent writing over the frame if isinstance(gc, wx.GraphicsContext): gc.ResetClip() gc.Clip(pixPos[0] + inset, pixPos[1] + inset, pixSize[0] - (inset * 2), titleBarHeight - 2) else: gc.DestroyClippingRegion() gc.SetClippingRect(wx.Rect(pixPos[0] + inset, pixPos[1] + inset, pixSize[0] - (inset * 2), titleBarHeight - 2)) titleTextColor = dim(PassageWidget.COLORS['titleText'], self.dimmed) if isinstance(gc, wx.GraphicsContext): gc.SetFont(titleFont, titleTextColor) else: gc.SetFont(titleFont) gc.SetTextForeground(titleTextColor) gc.DrawText(self.passage.title, pixPos[0] + inset, pixPos[1] + inset) # draw excerpt excerptTop = pixPos[1] + inset + titleBarHeight # we split the excerpt by line, then draw them in turn # (we use a library to determine breaks, but have to draw the lines ourselves) if isinstance(gc, wx.GraphicsContext): gc.ResetClip() gc.Clip(pixPos[0] + inset, pixPos[1] + inset, pixSize[0] - (inset * 2), pixSize[1] - (inset * 2)) else: gc.DestroyClippingRegion() gc.SetClippingRect(wx.Rect(pixPos[0] + inset, pixPos[1] + inset, pixSize[0] - (inset * 2), pixSize[1] - (inset * 2))) excerptTextColor = dim(PassageWidget.COLORS['excerptText'], self.dimmed) if isinstance(gc, wx.GraphicsContext): gc.SetFont(excerptFont, excerptTextColor) else: gc.SetFont(excerptFont) gc.SetTextForeground(excerptTextColor) excerptLines = wordWrap(self.passage.text, pixSize[0] - (inset * 2), gc) for line in excerptLines: gc.DrawText(line, pixPos[0] + inset, excerptTop) excerptTop += excerptFontHeight * PassageWidget.LINE_SPACING if excerptTop + excerptFontHeight > (pixPos[1] + pixSize[1]) - inset: break else: # greek title titleBarColor = dim(PassageWidget.COLORS['titleBar'], self.dimmed) gc.SetPen(wx.Pen(titleBarColor, 1)) gc.SetBrush(wx.Brush(titleBarColor)) gc.DrawRectangle(pixPos[0] + 1, pixPos[1] + 1, pixSize[0] - 3, PassageWidget.GREEK_HEIGHT * 3) gc.SetPen(wx.Pen('#ffffff', PassageWidget.GREEK_HEIGHT)) height = pixPos[1] + inset width = pixPos[0] + (pixSize[0] - inset ) / 2 if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(pixPos[0] + inset, height, width, height) else: gc.DrawLine(pixPos[0] + inset, height, width, height) height += PassageWidget.GREEK_HEIGHT * 3 # greek body text gc.SetPen(wx.Pen('#666666', PassageWidget.GREEK_HEIGHT)) while height < (pixPos[1] + pixSize[1]) - inset: width = pixSize[0] - inset if height + (PassageWidget.GREEK_HEIGHT * 2) > (pixPos[1] + pixSize[1]) - inset: width = width / 2 if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(pixPos[0] + inset, height, pixPos[0] + width, height) else: gc.DrawLine(pixPos[0] + inset, height, pixPos[0] + width, height) height += PassageWidget.GREEK_HEIGHT * 2 if isinstance(gc, wx.GraphicsContext): gc.ResetClip() else: gc.DestroyClippingRegion() # draw a broken link emblem in the bottom right if necessary # fixme: not sure how to do this with transparency if len(self.getBrokenLinks()): emblemSize = self.brokenEmblem.GetSize() emblemPos = [ (pixPos[0] + pixSize[0]) - (emblemSize[0] + inset), \ (pixPos[1] + pixSize[1]) - (emblemSize[1] + inset) ] if isinstance(gc, wx.GraphicsContext): gc.DrawBitmap(self.brokenEmblem, emblemPos[0], emblemPos[1], emblemSize[0], emblemSize[1]) else: gc.DrawBitmap(self.brokenEmblem, emblemPos[0], emblemPos[1]) # finally, draw a selection over ourselves if we're selected if self.selected: color = dim(wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT), self.dimmed) gc.SetPen(wx.Pen(color, 2)) if isinstance(gc, wx.GraphicsContext): r, g, b = color.Get() color = wx.Color(r, g, b, 64) gc.SetBrush(wx.Brush(color)) else: gc.SetBrush(wx.TRANSPARENT_BRUSH) gc.DrawRectangle(pixPos[0] + 1, pixPos[1] + 1, pixSize[0] - 2, pixSize[1] - 2)
def __init__ (self, parent, allowIncremental = True, \ onFind = None, onReplace = None, onReplaceAll = None, onClose = None): self.allowIncremental = allowIncremental self.findCallback = onFind self.replaceCallback = onReplace self.replaceAllCallback = onReplaceAll self.closeCallback = onClose wx.Panel.__init__(self, parent) sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) fieldSizer = wx.FlexGridSizer(2, 2) fieldSizer.AddGrowableCol(1, 1) # find text and label fieldSizer.Add(wx.StaticText(self, label = 'Find'), \ flag = wx.BOTTOM | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, \ border = metrics.size('relatedControls'), proportion = 0) self.findField = wx.TextCtrl(self) fieldSizer.Add(self.findField, proportion = 1, flag = wx.BOTTOM | wx.EXPAND, \ border = metrics.size('relatedControls')) # replace text and label fieldSizer.Add(wx.StaticText(self, label = 'Replace With'), \ flag = wx.BOTTOM | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, \ border = metrics.size('relatedControls'), proportion = 0) self.replaceField = wx.TextCtrl(self) fieldSizer.Add(self.replaceField, proportion = 1, flag = wx.BOTTOM | wx.EXPAND | wx.ALIGN_CENTER_VERTICAL, \ border = metrics.size('relatedControls')) sizer.Add(fieldSizer, flag = wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border = metrics.size('windowBorder')) # option checkboxes optionSizer = wx.BoxSizer(wx.HORIZONTAL) self.caseCheckbox = wx.CheckBox(self, label = 'Match Case') self.wholeWordCheckbox = wx.CheckBox(self, label = 'Whole Word') self.regexpCheckbox = wx.CheckBox(self, label = 'Regular Expression') optionSizer.Add(self.caseCheckbox, flag = wx.BOTTOM | wx.TOP | wx.RIGHT, \ border = metrics.size('relatedControls')) optionSizer.Add(self.wholeWordCheckbox, flag = wx.BOTTOM | wx.TOP | wx.LEFT | wx.RIGHT, \ border = metrics.size('relatedControls')) optionSizer.Add(self.regexpCheckbox, flag = wx.BOTTOM | wx.TOP | wx.LEFT, \ border = metrics.size('relatedControls')) sizer.Add(optionSizer, flag = wx.LEFT | wx.RIGHT, border = metrics.size('windowBorder')) # find and close buttons buttonSizer = wx.BoxSizer(wx.HORIZONTAL) self.closeButton = wx.Button(self, label = 'Close') self.closeButton.Bind(wx.EVT_BUTTON, self.onClose) buttonSizer.Add(self.closeButton, flag = wx.TOP | wx.RIGHT, border = metrics.size('buttonSpace')) if allowIncremental: buttonSizer.Add(wx.Panel(self)) self.findButton = wx.Button(self, label = 'Find Next') self.findButton.Bind(wx.EVT_BUTTON, self.onFind) buttonSizer.Add(self.findButton, flag = wx.TOP | wx.LEFT | wx.RIGHT, \ border = metrics.size('buttonSpace')) self.replaceButton = wx.Button(self, label = 'Replace') self.replaceButton.Bind(wx.EVT_BUTTON, self.onReplace) buttonSizer.Add(self.replaceButton, flag = wx.TOP | wx.RIGHT, border = metrics.size('buttonSpace')) self.replaceAllButton = wx.Button(self, label = 'Replace All') self.replaceAllButton.Bind(wx.EVT_BUTTON, self.onReplaceAll) buttonSizer.Add(self.replaceAllButton, flag = wx.TOP, border = metrics.size('buttonSpace')) sizer.Add(buttonSizer, flag = wx.ALIGN_RIGHT | wx.LEFT | wx.RIGHT | wx.BOTTOM, \ border = metrics.size('windowBorder')) sizer.Fit(self)
def cachePaint(self, size): """ Caches the widget so self.paintBuffer is up-to-date. """ def wordWrap(text, lineWidth, gc): """ Returns a list of lines from a string This is somewhat based on the wordwrap function built into wx.lib. (For some reason, GraphicsContext.GetPartialTextExtents() is returning totally wrong numbers but GetTextExtent() works fine.) This assumes that you've already set up the font you want on the GC. It gloms multiple spaces together, but for our purposes that's ok. """ words = text.split() lines = [] currentWidth = 0 currentLine = '' for word in words: wordWidth = gc.GetTextExtent(word + ' ')[0] if currentWidth + wordWidth < lineWidth: currentLine += word + ' ' currentWidth += wordWidth else: lines.append(currentLine) currentLine = word + ' ' currentWidth = wordWidth lines.append(currentLine) return lines def dim(c, dim): """Lowers a color's alpha if dim is true.""" if isinstance(c, wx.Colour): c = list(c.Get(includeAlpha=True)) if len(c) < 4: c = list(c) c.append(255) if dim: c[3] *= PassageWidget.DIMMED_ALPHA return wx.Colour(c[0], c[1], c[2], c[3]) # set up our buffer bitmap = wx.EmptyBitmap(size.width, size.height) self.paintBuffer.SelectObject(bitmap) # switch to a GraphicsContext as necessary if self.app.config.ReadBool('fastStoryPanel'): gc = self.paintBuffer else: gc = wx.GraphicsContext.Create(self.paintBuffer) # text font sizes # wxWindows works with points, so we need to doublecheck on actual pixels titleFontSize = self.parent.toPixels((metrics.size('widgetTitle'), -1), scaleOnly=True)[0] titleFontSize = min(titleFontSize, metrics.size('fontMax')) titleFontSize = max(titleFontSize, metrics.size('fontMin')) excerptFontSize = min(titleFontSize * 0.9, metrics.size('fontMax')) excerptFontSize = max(excerptFontSize, metrics.size('fontMin')) titleFont = wx.Font(titleFontSize, wx.SWISS, wx.NORMAL, wx.BOLD, False, 'Arial') excerptFont = wx.Font(excerptFontSize, wx.SWISS, wx.NORMAL, wx.NORMAL, False, 'Arial') titleFontHeight = math.fabs(titleFont.GetPixelSize()[1]) excerptFontHeight = math.fabs(excerptFont.GetPixelSize()[1]) # inset for text (we need to know this for layout purposes) inset = titleFontHeight / 3 # frame frameColor = dim(PassageWidget.COLORS['frame'], self.dimmed) frameInterior = (dim(PassageWidget.COLORS['bodyStart'], self.dimmed), \ dim(PassageWidget.COLORS['bodyEnd'], self.dimmed)) gc.SetPen(wx.Pen(frameColor, 1)) if isinstance(gc, wx.GraphicsContext): gc.SetBrush(gc.CreateLinearGradientBrush(0, 0, 0, size.height, \ frameInterior[0], frameInterior[1])) else: gc.GradientFillLinear(wx.Rect(0, 0, size.width - 1, size.height - 1), \ frameInterior[0], frameInterior[1], wx.SOUTH) gc.SetBrush(wx.TRANSPARENT_BRUSH) gc.DrawRectangle(0, 0, size.width - 1, size.height - 1) if size.width > PassageWidget.MIN_GREEKING_SIZE: # title bar titleBarHeight = titleFontHeight + (2 * inset) titleBarColor = dim(PassageWidget.COLORS['titleBar'], self.dimmed) gc.SetPen(wx.Pen(titleBarColor, 1)) gc.SetBrush(wx.Brush(titleBarColor)) gc.DrawRectangle(1, 1, size.width - 3, titleBarHeight) # draw title # we let clipping prevent writing over the frame if isinstance(gc, wx.GraphicsContext): gc.ResetClip() gc.Clip(inset, inset, size.width - (inset * 2), titleBarHeight - 2) else: gc.DestroyClippingRegion() gc.SetClippingRect( wx.Rect(inset, inset, size.width - (inset * 2), titleBarHeight - 2)) titleTextColor = dim(PassageWidget.COLORS['titleText'], self.dimmed) if isinstance(gc, wx.GraphicsContext): gc.SetFont(titleFont, titleTextColor) else: gc.SetFont(titleFont) gc.SetTextForeground(titleTextColor) gc.DrawText(self.passage.title, inset, inset) # draw excerpt excerptTop = inset + titleBarHeight # we split the excerpt by line, then draw them in turn # (we use a library to determine breaks, but have to draw the lines ourselves) if isinstance(gc, wx.GraphicsContext): gc.ResetClip() gc.Clip(inset, inset, size.width - (inset * 2), size.height - (inset * 2)) else: gc.DestroyClippingRegion() gc.SetClippingRect( wx.Rect(inset, inset, size.width - (inset * 2), size.height - (inset * 2))) excerptTextColor = dim(PassageWidget.COLORS['excerptText'], self.dimmed) if isinstance(gc, wx.GraphicsContext): gc.SetFont(excerptFont, excerptTextColor) else: gc.SetFont(excerptFont) gc.SetTextForeground(excerptTextColor) excerptLines = wordWrap(self.passage.text, size.width - (inset * 2), gc) for line in excerptLines: gc.DrawText(line, inset, excerptTop) excerptTop += excerptFontHeight * PassageWidget.LINE_SPACING if excerptTop + excerptFontHeight > size.height - inset: break else: # greek title titleBarColor = dim(PassageWidget.COLORS['titleBar'], self.dimmed) gc.SetPen(wx.Pen(titleBarColor, 1)) gc.SetBrush(wx.Brush(titleBarColor)) gc.DrawRectangle(1, 1, size.width - 3, PassageWidget.GREEK_HEIGHT * 3) gc.SetPen(wx.Pen('#ffffff', PassageWidget.GREEK_HEIGHT)) height = inset width = (size.width - inset) / 2 if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(inset, height, width, height) else: gc.DrawLine(inset, height, width, height) height += PassageWidget.GREEK_HEIGHT * 3 # greek body text gc.SetPen(wx.Pen('#666666', PassageWidget.GREEK_HEIGHT)) while height < size.height - inset: width = size.height - inset if height + (PassageWidget.GREEK_HEIGHT * 2) > size.height - inset: width = width / 2 if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(inset, height, width, height) else: gc.DrawLine(inset, height, width, height) height += PassageWidget.GREEK_HEIGHT * 2 if isinstance(gc, wx.GraphicsContext): gc.ResetClip() else: gc.DestroyClippingRegion() # draw a broken link emblem in the bottom right if necessary # fixme: not sure how to do this with transparency if len(self.getBrokenLinks()): emblemSize = self.brokenEmblem.GetSize() emblemPos = [ size.width - (emblemSize[0] + inset), \ size.height - (emblemSize[1] + inset) ] if isinstance(gc, wx.GraphicsContext): gc.DrawBitmap(self.brokenEmblem, emblemPos[0], emblemPos[1], emblemSize[0], emblemSize[1]) else: gc.DrawBitmap(self.brokenEmblem, emblemPos[0], emblemPos[1]) # finally, draw a selection over ourselves if we're selected if self.selected: color = dim(wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT), self.dimmed) gc.SetPen(wx.Pen(color, 2)) if isinstance(gc, wx.GraphicsContext): r, g, b = color.Get() color = wx.Colour(r, g, b, 64) gc.SetBrush(wx.Brush(color)) else: gc.SetBrush(wx.TRANSPARENT_BRUSH) gc.DrawRectangle(1, 1, size.width - 2, size.height - 2) self.paintBufferBounds = size
def cachePaint(self, size): """ Caches the widget so self.paintBuffer is up-to-date. """ def wordWrap(text, lineWidth, gc, lineBreaks = False): """ Returns a list of lines from a string This is somewhat based on the wordwrap function built into wx.lib. (For some reason, GraphicsContext.GetPartialTextExtents() is returning totally wrong numbers but GetTextExtent() works fine.) This assumes that you've already set up the font you want on the GC. It gloms multiple spaces together, but for our purposes that's ok. """ words = re.finditer('\S+\s*', text.replace('\r','')) lines = '' currentLine = '' for w in words: word = w.group(0) wordWidth = gc.GetTextExtent(currentLine + word)[0] if wordWidth < lineWidth: currentLine += word if '\n' in word: lines += currentLine currentLine = '' else: lines += currentLine + '\n' currentLine = word lines += currentLine return lines.split('\n') def dim(c, dim): """Lowers a color's alpha if dim is true.""" if isinstance(c, wx.Colour): c = list(c.Get(includeAlpha = True)) if len(c) < 4: c = list(c) c.append(255) if dim: a = PassageWidget.DIMMED_ALPHA if not self.app.config.ReadBool('fastStoryPanel'): c[3] *= a else: c[0] *= a c[1] *= a c[2] *= a return wx.Colour(*c) # set up our buffer bitmap = wx.EmptyBitmap(size.width, size.height) self.paintBuffer.SelectObject(bitmap) # switch to a GraphicsContext as necessary if self.app.config.ReadBool('fastStoryPanel'): gc = self.paintBuffer else: gc = wx.GraphicsContext.Create(self.paintBuffer) # text font sizes # wxWindows works with points, so we need to doublecheck on actual pixels titleFontSize = self.parent.toPixels((metrics.size('widgetTitle'), -1), scaleOnly = True)[0] titleFontSize = min(titleFontSize, metrics.size('fontMax')) titleFontSize = max(titleFontSize, metrics.size('fontMin')) excerptFontSize = min(titleFontSize * 0.9, metrics.size('fontMax')) excerptFontSize = max(excerptFontSize, metrics.size('fontMin')) titleFont = wx.Font(titleFontSize, wx.SWISS, wx.NORMAL, wx.BOLD, False, 'Arial') excerptFont = wx.Font(excerptFontSize, wx.SWISS, wx.NORMAL, wx.NORMAL, False, 'Arial') titleFontHeight = math.fabs(titleFont.GetPixelSize()[1]) excerptFontHeight = math.fabs(excerptFont.GetPixelSize()[1]) tagBarColor = dim( tuple(i*256 for i in colorsys.hsv_to_rgb(0.14 + math.sin(hash("".join(self.passage.tags)))*0.08, 0.28, 0.88)), self.dimmed) # inset for text (we need to know this for layout purposes) inset = titleFontHeight / 3 # frame if self.passage.isAnnotation(): frameColor = PassageWidget.COLORS['frame'] c = wx.Colour(*PassageWidget.COLORS['annotation']) frameInterior = (c,c) else: frameColor = dim(PassageWidget.COLORS['frame'], self.dimmed) frameInterior = (dim(PassageWidget.COLORS['bodyStart'], self.dimmed), \ dim(PassageWidget.COLORS['bodyEnd'], self.dimmed)) gc.SetPen(wx.Pen(frameColor, 1)) if isinstance(gc, wx.GraphicsContext): gc.SetBrush(gc.CreateLinearGradientBrush(0, 0, 0, size.height, \ frameInterior[0], frameInterior[1])) else: gc.GradientFillLinear(wx.Rect(0, 0, size.width - 1, size.height - 1), \ frameInterior[0], frameInterior[1], wx.SOUTH) gc.SetBrush(wx.TRANSPARENT_BRUSH) gc.DrawRectangle(0, 0, size.width - 1, size.height - 1) if size.width > PassageWidget.MIN_GREEKING_SIZE * (2 if self.passage.isAnnotation() else 1): # title bar titleBarHeight = titleFontHeight + (2 * inset) if self.passage.isAnnotation(): titleBarColor = frameInterior[0] else: titleBarColor = dim(PassageWidget.COLORS[self.getTitleColorIndex()], self.dimmed) gc.SetPen(wx.Pen(titleBarColor, 1)) gc.SetBrush(wx.Brush(titleBarColor)) gc.DrawRectangle(1, 1, size.width - 3, titleBarHeight) # draw title # we let clipping prevent writing over the frame if isinstance(gc, wx.GraphicsContext): gc.ResetClip() gc.Clip(inset, inset, size.width - (inset * 2), titleBarHeight - 2) else: gc.DestroyClippingRegion() gc.SetClippingRect(wx.Rect(inset, inset, size.width - (inset * 2), titleBarHeight - 2)) titleTextColor = dim(PassageWidget.COLORS['titleText'], self.dimmed) if isinstance(gc, wx.GraphicsContext): gc.SetFont(titleFont, titleTextColor) else: gc.SetFont(titleFont) gc.SetTextForeground(titleTextColor) if self.passage.title: gc.DrawText(self.passage.title, inset, inset) # draw excerpt if not self.passage.isImage(): excerptTop = inset + titleBarHeight # we split the excerpt by line, then draw them in turn # (we use a library to determine breaks, but have to draw the lines ourselves) if isinstance(gc, wx.GraphicsContext): gc.ResetClip() gc.Clip(inset, inset, size.width - (inset * 2), size.height - (inset * 2)-1) else: gc.DestroyClippingRegion() gc.SetClippingRect(wx.Rect(inset, inset, size.width - (inset * 2), size.height - (inset * 2)-1)) if self.passage.isAnnotation(): excerptTextColor = wx.Colour(*PassageWidget.COLORS['annotationText']) else: excerptTextColor = dim(PassageWidget.COLORS['excerptText'], self.dimmed) if isinstance(gc, wx.GraphicsContext): gc.SetFont(excerptFont, excerptTextColor) else: gc.SetFont(excerptFont) gc.SetTextForeground(excerptTextColor) excerptLines = wordWrap(self.passage.text, size.width - (inset * 2), gc, self.passage.isAnnotation()) for line in excerptLines: gc.DrawText(line, inset, excerptTop) excerptTop += excerptFontHeight * PassageWidget.LINE_SPACING \ * min(1.75,max(1,1.75*size.width/260 if (self.passage.isAnnotation() and line) else 1)) if excerptTop + excerptFontHeight > size.height - inset: break if (self.passage.isStoryText() and self.passage.tags) or \ (self.passage.isStylesheet() and len(self.passage.tags) > 1): tagBarHeight = excerptFontHeight + (2 * inset) gc.SetPen(wx.Pen(tagBarColor, 1)) gc.SetBrush(wx.Brush(tagBarColor)) gc.DrawRectangle(0, size.height-tagBarHeight-1, size.width, tagBarHeight+1) # draw tags tagTextColor = dim(PassageWidget.COLORS['excerptText'], self.dimmed) if isinstance(gc, wx.GraphicsContext): gc.SetFont(excerptFont, tagTextColor) else: gc.SetFont(excerptFont) gc.SetTextForeground(tagTextColor) text = wordWrap(" ".join(a for a in self.passage.tags if a not in tiddlywiki.TiddlyWiki.INFO_TAGS), size.width - (inset * 2), gc)[0] gc.DrawText(text, inset*2, (size.height-tagBarHeight)) else: # greek title titleBarHeight = PassageWidget.GREEK_HEIGHT*3 titleBarColor = dim(PassageWidget.COLORS[self.getTitleColorIndex()], self.dimmed) gc.SetPen(wx.Pen(titleBarColor, 1)) gc.SetBrush(wx.Brush(titleBarColor)) gc.DrawRectangle(1, 1, size.width - 3, PassageWidget.GREEK_HEIGHT * 3) gc.SetPen(wx.Pen('#ffffff', PassageWidget.GREEK_HEIGHT)) height = inset width = (size.width - inset) / 2 if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(inset, height, width, height) else: gc.DrawLine(inset, height, width, height) height += PassageWidget.GREEK_HEIGHT * 3 # greek body text if not self.passage.isImage(): gc.SetPen(wx.Pen(self.COLORS['annotationText'] \ if self.passage.isAnnotation() else '#666666', PassageWidget.GREEK_HEIGHT)) chars = len(self.passage.text) while height < size.height - inset and chars > 0: width = size.height - inset if height + (PassageWidget.GREEK_HEIGHT * 2) > size.height - inset: width /= 2 elif chars < 80: width = max(4, width * chars / 80) if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(inset, height, width, height) else: gc.DrawLine(inset, height, width, height) height += PassageWidget.GREEK_HEIGHT * 2 chars -= 80 # greek tags if (self.passage.isStoryText() and self.passage.tags) or \ (self.passage.isStylesheet() and len(self.passage.tags) > 1) : tagBarHeight = PassageWidget.GREEK_HEIGHT*3 gc.SetPen(wx.Pen(tagBarColor, 1)) gc.SetBrush(wx.Brush(tagBarColor)) height = size.height-tagBarHeight-2 width = size.width-4 gc.DrawRectangle(2, height, width, tagBarHeight) gc.SetPen(wx.Pen('#666666', PassageWidget.GREEK_HEIGHT)) height += inset width = (width-inset*2)/2 if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(inset, height, width, height) else: gc.DrawLine(inset, height, width, height) if self.passage.isImage(): if self.bitmap: if isinstance(gc, wx.GraphicsContext): gc.ResetClip() gc.Clip(1, titleBarHeight + 1, size.width - 3, size.height - 3) else: gc.DestroyClippingRegion() gc.SetClippingRect(wx.Rect(1, titleBarHeight + 1, size.width - 3, size.height - 3)) width = size.width height = size.height - titleBarHeight # choose smaller of vertical and horizontal scale factor, to preserve aspect ratio scale = min(width/float(self.bitmap.GetWidth()), height/float(self.bitmap.GetHeight())); img = self.bitmap.ConvertToImage(); if scale != 1: img = img.Scale(scale*self.bitmap.GetWidth(),scale*self.bitmap.GetHeight()); # offset image horizontally or vertically, to centre after scaling offsetWidth = (width - img.GetWidth())/2 offsetHeight = (height - img.GetHeight())/2 if isinstance(gc, wx.GraphicsContext): gc.DrawBitmap(img.ConvertToBitmap(self.bitmap.GetDepth()), 1 + offsetWidth, titleBarHeight + 1 + offsetHeight, img.GetWidth(), img.GetHeight()) else: gc.DrawBitmap(img.ConvertToBitmap(self.bitmap.GetDepth()), 1 + offsetWidth, titleBarHeight + 1 + offsetHeight) if isinstance(gc, wx.GraphicsContext): gc.ResetClip() else: gc.DestroyClippingRegion() # draw a broken link emblem in the bottom right if necessary # fixme: not sure how to do this with transparency def showEmblem(emblem, gc=gc, size=size, inset=inset): emblemSize = emblem.GetSize() emblemPos = [ size.width - (emblemSize[0] + inset), \ size.height - (emblemSize[1] + inset) ] if isinstance(gc, wx.GraphicsContext): gc.DrawBitmap(emblem, emblemPos[0], emblemPos[1], emblemSize[0], emblemSize[1]) else: gc.DrawBitmap(emblem, emblemPos[0], emblemPos[1]) if len(self.getBrokenLinks()): showEmblem(self.brokenEmblem) elif len(self.getExternalLinks()): showEmblem(self.externalEmblem) # finally, draw a selection over ourselves if we're selected if self.selected: color = dim(wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT), self.dimmed) gc.SetPen(wx.Pen(color, 2)) if isinstance(gc, wx.GraphicsContext): r, g, b = color.Get(False) color = wx.Colour(r, g, b, 64) gc.SetBrush(wx.Brush(color)) else: gc.SetBrush(wx.TRANSPARENT_BRUSH) gc.DrawRectangle(1, 1, size.width - 2, size.height - 2) self.paintBufferBounds = size
def __init__(self, app, parent = None): self.app = app wx.Frame.__init__(self, parent, wx.ID_ANY, title = self.app.NAME + ' Preferences', \ style = wx.MINIMIZE_BOX | wx.CLOSE_BOX | wx.CAPTION | wx.SYSTEM_MENU) panel = wx.Panel(parent = self, id = wx.ID_ANY) borderSizer = wx.BoxSizer(wx.VERTICAL) panel.SetSizer(borderSizer) panelSizer = wx.FlexGridSizer(8, 2, metrics.size('relatedControls'), metrics.size('relatedControls')) borderSizer.Add(panelSizer, flag = wx.ALL, border = metrics.size('windowBorder')) self.editorFont = wx.FontPickerCtrl(panel, style = wx.FNTP_FONTDESC_AS_LABEL) self.editorFont.SetSelectedFont(self.getPrefFont('windowed')) self.editorFont.Bind(wx.EVT_FONTPICKER_CHANGED, lambda e: self.saveFontPref('windowed', \ self.editorFont.GetSelectedFont())) self.monoFont = wx.FontPickerCtrl(panel, style = wx.FNTP_FONTDESC_AS_LABEL) self.monoFont.SetSelectedFont(self.getPrefFont('monospace')) self.monoFont.Bind(wx.EVT_FONTPICKER_CHANGED, lambda e: self.saveFontPref('monospace', \ self.monoFont.GetSelectedFont())) self.fsFont = wx.FontPickerCtrl(panel, style = wx.FNTP_FONTDESC_AS_LABEL) self.fsFont.SetSelectedFont(self.getPrefFont('fs')) self.fsFont.Bind(wx.EVT_FONTPICKER_CHANGED, lambda e: self.saveFontPref('fs', \ self.fsFont.GetSelectedFont())) self.fsTextColor = wx.ColourPickerCtrl(panel) self.fsTextColor.SetColour(self.app.config.Read('fsTextColor')) self.fsTextColor.Bind(wx.EVT_COLOURPICKER_CHANGED, lambda e: self.savePref('fsTextColor', \ self.fsTextColor.GetColour())) self.fsBgColor = wx.ColourPickerCtrl(panel) self.fsBgColor.SetColour(self.app.config.Read('fsBgColor')) self.fsBgColor.Bind(wx.EVT_COLOURPICKER_CHANGED, lambda e: self.savePref('fsBgColor', \ self.fsBgColor.GetColour())) fsLineHeightPanel = wx.Panel(panel) fsLineHeightSizer = wx.BoxSizer(wx.HORIZONTAL) fsLineHeightPanel.SetSizer(fsLineHeightSizer) self.fsLineHeight = wx.ComboBox(fsLineHeightPanel, choices = ('100', '125', '150', '175', '200')) self.fsLineHeight.Bind(wx.EVT_TEXT, lambda e: self.savePref('fsLineHeight', int(self.fsLineHeight.GetValue()))) self.fsLineHeight.SetValue(str(self.app.config.ReadInt('fslineHeight'))) fsLineHeightSizer.Add(self.fsLineHeight, flag = wx.ALIGN_CENTER_VERTICAL) fsLineHeightSizer.Add(wx.StaticText(fsLineHeightPanel, label = '%'), flag = wx.ALIGN_CENTER_VERTICAL) self.fastStoryPanel = wx.CheckBox(panel, label = 'Faster but rougher story map display') self.fastStoryPanel.Bind(wx.EVT_CHECKBOX, lambda e: self.savePref('fastStoryPanel', \ self.fastStoryPanel.GetValue())) self.fastStoryPanel.SetValue(self.app.config.ReadBool('fastStoryPanel')) self.imageArrows = wx.CheckBox(panel, label = 'Connector arrows for images and stylesheets') self.imageArrows.Bind(wx.EVT_CHECKBOX, lambda e: self.savePref('imageArrows', \ self.imageArrows.GetValue())) self.imageArrows.SetValue(self.app.config.ReadBool('imageArrows')) panelSizer.Add(wx.StaticText(panel, label = 'Normal Font'), flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.editorFont) panelSizer.Add(wx.StaticText(panel, label = 'Monospace Font'), flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.monoFont) panelSizer.Add(wx.StaticText(panel, label = 'Fullscreen Editor Font'), flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.fsFont) panelSizer.Add(wx.StaticText(panel, label = 'Fullscreen Editor Text Color'), flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.fsTextColor) panelSizer.Add(wx.StaticText(panel, label = 'Fullscreen Editor Background Color'), \ flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.fsBgColor) panelSizer.Add(wx.StaticText(panel, label = 'Fullscreen Editor Line Spacing'), flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(fsLineHeightPanel, flag = wx.ALIGN_CENTER_VERTICAL) panelSizer.Add(self.fastStoryPanel) panelSizer.Add((1,2)) panelSizer.Add(self.imageArrows) panelSizer.Fit(self) borderSizer.Fit(self) self.SetIcon(self.app.icon) self.Show() self.panelSizer = panelSizer self.borderSizer = borderSizer
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: wx.Window.FindFocus().Cut(), id = wx.ID_CUT) editMenu.Append(wx.ID_COPY, '&Copy\tCtrl-C') self.Bind(wx.EVT_MENU, lambda e: wx.Window.FindFocus().Copy(), id = wx.ID_COPY) editMenu.Append(wx.ID_PASTE, '&Paste\tCtrl-V') self.Bind(wx.EVT_MENU, lambda e: wx.Window.FindFocus().Paste(), id = wx.ID_PASTE) editMenu.Append(wx.ID_SELECTALL, 'Select &All\tCtrl-A') self.Bind(wx.EVT_MENU, lambda e: wx.Window.FindFocus().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() if (self.widget.passage.isStylesheet()): helpMenu.Append(PassageFrame.HELP1, 'About Stylesheets') self.Bind(wx.EVT_MENU, lambda e: wx.LaunchDefaultBrowser('http://twinery.org/wiki/stylesheet'), id = PassageFrame.HELP1) elif (self.widget.passage.isScript()): helpMenu.Append(PassageFrame.HELP1, 'About Scripts') self.Bind(wx.EVT_MENU, lambda e: wx.LaunchDefaultBrowser('http://twinery.org/wiki/script'), id = PassageFrame.HELP1) else: 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 Macros') self.Bind(wx.EVT_MENU, lambda e: wx.LaunchDefaultBrowser('http://twinery.org/wiki/macro'), id = PassageFrame.HELP4) helpMenu.Append(PassageFrame.HELP5, 'About Tags') self.Bind(wx.EVT_MENU, lambda e: wx.LaunchDefaultBrowser('http://twinery.org/wiki/tag'), id = PassageFrame.HELP5) # 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.SetTabWidth(4) 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)) self.bodyInput.SetFocus() # 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 = TweeStyler(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)
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.image = None self.gif = None wx.Frame.__init__(self, parent, wx.ID_ANY, title = 'Untitled Passage - ' + self.app.NAME + ' ' + versionString, \ size = PassageFrame.DEFAULT_SIZE, style=wx.DEFAULT_FRAME_STYLE) # controls self.panel = wx.Panel(self) allSizer = wx.BoxSizer(wx.VERTICAL) self.panel.SetSizer(allSizer) # title control 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) self.titleInput.SetValue(self.widget.passage.title) self.SetTitle(self.title()) 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.AddGrowableCol(1, 1) self.topControls.SetSizer(topSizer) # image pane self.imageScroller = wx.ScrolledWindow(self.panel) self.imageSizer = wx.GridSizer(1,1) self.imageScroller.SetSizer(self.imageSizer) # image menu passageMenu = wx.Menu() passageMenu.Append(self.IMPORT_IMAGE, '&Replace Image...\tCtrl-O') self.Bind(wx.EVT_MENU, self.replaceImage, id = self.IMPORT_IMAGE) passageMenu.Append(self.SAVE_IMAGE, '&Save Image...') self.Bind(wx.EVT_MENU, self.saveImage, id = self.SAVE_IMAGE) 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(wx.ID_CLOSE, '&Close Image\tCtrl-W') self.Bind(wx.EVT_MENU, lambda e: self.Destroy(), id = wx.ID_CLOSE) # edit menu editMenu = wx.Menu() editMenu.Append(wx.ID_COPY, '&Copy\tCtrl-C') self.Bind(wx.EVT_MENU, self.copyImage, id = wx.ID_COPY) editMenu.Append(wx.ID_PASTE, '&Paste\tCtrl-V') self.Bind(wx.EVT_MENU, self.pasteImage, id = wx.ID_PASTE) # menu bar self.menus = wx.MenuBar() self.menus.Append(passageMenu, '&Image') self.menus.Append(editMenu, '&Edit') self.SetMenuBar(self.menus) # finish allSizer.Add(self.topControls, flag = wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, border = metrics.size('windowBorder')) allSizer.Add(self.imageScroller, proportion = 1, flag = wx.TOP | wx.EXPAND, border = metrics.size('relatedControls')) # bindings self.titleInput.Bind(wx.EVT_TEXT, self.syncPassage) self.SetIcon(self.app.icon) self.updateImage() 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 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)