Example #1
0
    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)
Example #2
0
 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)
Example #3
0
    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)
Example #4
0
    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()
Example #5
0
    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()
Example #6
0
    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()
Example #7
0
    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)
Example #8
0
    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
Example #9
0
    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)
Example #10
0
    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
Example #11
0
    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)
Example #12
0
    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)
Example #13
0
    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
Example #14
0
    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)
Example #15
0
    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()
Example #16
0
    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)
Example #17
0
    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)
Example #18
0
    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
Example #19
0
    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)
Example #20
0
    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)
Example #21
0
    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)
Example #22
0
    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
Example #23
0
    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
Example #24
0
    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
Example #25
0
    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)
Example #26
0
    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)
Example #27
0
    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)
Example #28
0
    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)