class SongpressFrame(SDIMainFrame): def __init__(self, res): SDIMainFrame.__init__( self, res, 'MainFrame', 'songpress', 'Skeed', _('song'), 'crd', _('Songpress - Il Canzonatore'), glb.AddPath('img/songpress.ico'), glb.VERSION, _("http://www.skeed.it/songpress"), (_(u"Copyright (c) 2009-{year} Luca Allulli - Skeed\nLocalization:\n{translations}" )).format(year=glb.YEAR, translations="\n".join([ u"- {}: {}".format(glb.languages[x], glb.translators[x]) for x in glb.languages ])), _("Licensed under the terms and conditions of the GNU General Public License, version 2" ), _("Special thanks to:\n * The Pyhton programming language (http://www.python.org)\n * wxWidgets (http://www.wxwidgets.org)\n * wxPython (http://www.wxpython.org)\n * Editra (http://editra.org/) (for the error reporting dialog and... the editor itself!)\n * python-pptx (for PowerPoint export)" ), _import_formats, ) self.pref = Preferences() self.SetDefaultExtension(self.pref.defaultExtension) self.text = Editor(self) dt = SDIDropTarget(self) self.text.SetDropTarget(dt) self.frame.Bind(wx.stc.EVT_STC_UPDATEUI, self.OnUpdateUI, self.text) # Other objects self.previewCanvas = PreviewCanvas(self.frame, self.pref.format, self.pref.notations, self.pref.decorator) self.AddMainPane(self.text) self.AddPane(self.previewCanvas.main_panel, aui.AuiPaneInfo().Right().BestSize(240, 400), _('Preview'), 'preview') if self.previewCanvas.link is not None: self.previewCanvas.main_panel.Bind(wx.adv.EVT_HYPERLINK, self.OnCopyAsImage, self.previewCanvas.link) self.mainToolBar = aui.AuiToolBar(self.frame, wx.ID_ANY, wx.DefaultPosition, agwStyle=aui.AUI_TB_PLAIN_BACKGROUND) self.mainToolBar.SetToolBitmapSize(wx.Size(16, 16)) self.AddTool(self.mainToolBar, 'new', 'img/new.png', _("New"), _("Create a new song")) self.AddTool(self.mainToolBar, 'open', 'img/open.png', _("Open"), _("Open an existing song")) self.AddTool(self.mainToolBar, 'save', 'img/save.png', _("Save"), _("Save song with the current filename")) self.mainToolBar.AddSeparator() self.undoTool = self.AddTool(self.mainToolBar, 'undo', 'img/undo.png', _("Undo"), _("Undo last edit")) self.redoTool = self.AddTool(self.mainToolBar, 'redo', 'img/redo.png', _("Redo"), _("Redo previously undone edit")) self.redoTool = wx.xrc.XRCID('redo') self.mainToolBar.AddSeparator() self.cutTool = self.AddTool(self.mainToolBar, 'cut', 'img/cut.png', _("Cut"), _("Move selected text in the clipboard")) self.copyTool = self.AddTool(self.mainToolBar, 'copy', 'img/copy.png', _("Copy"), _("Copy selected text in the clipboard")) self.copyOnlyTextTool = wx.xrc.XRCID('copyOnlyText') if platform.system() == 'Windows': self.AddTool( self.mainToolBar, 'copyAsImage', 'img/copyAsImage2.png', _("Copy as Image"), _("Copy the whole FORMATTED song (or selected verses) to the clipboard" )) self.pasteTool = self.AddTool( self.mainToolBar, 'paste', 'img/paste.png', _("Paste"), _("Read text from the clipboard and place it at the cursor position" )) self.pasteChordsTool = self.AddTool( self.mainToolBar, 'pasteChords', 'img/pasteChords.png', _("PasteChords"), _("Integrate chords of copied text into current selection")) self.mainToolBar.Realize() self.mainToolBarPane = self.AddPane( self.mainToolBar, aui.AuiPaneInfo().ToolbarPane().Top().Row(1).Position(1), _('Standard'), 'standard') self.formatToolBar = aui.AuiToolBar( self.frame, wx.ID_ANY, agwStyle=aui.AUI_TB_PLAIN_BACKGROUND) self.formatToolBar.SetExtraStyle(aui.AUI_TB_PLAIN_BACKGROUND) self.fontChooser = FontComboBox(self.formatToolBar, -1, self.pref.format.face) self.formatToolBar.AddControl(self.fontChooser) self.frame.Bind(wx.EVT_COMBOBOX, self.OnFontSelected, self.fontChooser) wx.UpdateUIEvent.SetUpdateInterval(500) self.frame.Bind(wx.EVT_UPDATE_UI, self.OnIdle, self.frame) self.frame.Bind(wx.EVT_TEXT_CUT, self.OnTextCutCopy, self.text) self.frame.Bind(wx.EVT_TEXT_COPY, self.OnTextCutCopy, self.text) self.fontChooser.Bind(wx.EVT_TEXT_ENTER, self.OnFontSelected, self.fontChooser) self.fontChooser.Bind(wx.EVT_KILL_FOCUS, self.OnFontSelected, self.fontChooser) self.AddTool(self.formatToolBar, 'title', 'img/title.png', _("Insert title"), _("Insert a command to display song title")) self.AddTool(self.formatToolBar, 'chord', 'img/chord.png', _("Insert chord"), _("Insert square brackets that will host a chord")) self.AddTool(self.formatToolBar, 'chorus', 'img/chorus.png', _("Insert chorus"), _("Insert a couple of commands that will contain chorus")) self.AddTool( self.formatToolBar, 'verseWithCustomLabelOrWithoutLabel', 'img/verse.png', _("Insert verse with custom label or without label"), _("Insert a commands that will display a verse with a custom label" ), ) labelVersesTool = self.formatToolBar.AddToggleTool( # AddToggleTool (agw) or AddTool wx.xrc.XRCID('labelVerses'), wx.Bitmap(wx.Image(glb.AddPath("img/labelVerses.png"))), wx.NullBitmap, True, None, _("Show verse labels"), _("Show or hide verse and chorus labels"), ) self.labelVersesToolId = labelVersesTool.GetId() showChordsIcon = wx.StaticBitmap( self.formatToolBar, -1, wx.Bitmap(wx.Image(glb.AddPath('img/showChords.png')))) self.formatToolBar.AddControl(showChordsIcon) self.showChordsChooser = wx.Slider(self.formatToolBar, -1, 0, 0, 2, wx.DefaultPosition, (100, -1), wx.SL_AUTOTICKS | wx.SL_HORIZONTAL) tt1 = wx.ToolTip(_("Hide or show chords in formatted song")) tt2 = wx.ToolTip(_("Hide or show chords in formatted song")) self.showChordsChooser.SetToolTip(tt1) showChordsIcon.SetToolTip(tt2) self.frame.Bind(wx.EVT_SCROLL, self.OnFontSelected, self.showChordsChooser) self.formatToolBar.AddControl(self.showChordsChooser, "pippo") self.formatToolBar.Realize() self.formatToolBarPane = self.AddPane( self.formatToolBar, aui.AuiPaneInfo().ToolbarPane().Top().Row(1).Position(2), _('Format'), 'format') self.BindMyMenu() self.frame.Bind(EVT_TEXT_CHANGED, self.OnTextChanged) self.exportMenuId = xrc.XRCID('export') self.exportToClipboardAsAVectorImage = xrc.XRCID( 'exportToClipboardAsAVectorImage') self.exportAsEmfMenuId = xrc.XRCID('exportAsEmf') self.cutMenuId = xrc.XRCID('cut') self.copyMenuId = xrc.XRCID('copy') self.copyAsImageMenuId = xrc.XRCID('copyAsImage') self.pasteMenuId = xrc.XRCID('paste') self.pasteChordsMenuId = xrc.XRCID('pasteChords') self.removeChordsMenuId = xrc.XRCID('removeChords') self.labelVersesMenuId = xrc.XRCID('labelVerses') self.noChordsMenuId = xrc.XRCID('noChords') self.oneVerseForEachChordPatternMenuId = xrc.XRCID( 'oneVerseForEachChordPattern') self.wholeSongMenuId = xrc.XRCID('wholeSong') if platform.system() != 'Windows': self.menuBar.GetMenu(0).FindItemById( self.exportMenuId).GetSubMenu().Delete( self.exportToClipboardAsAVectorImage) self.menuBar.GetMenu(1).Delete(self.copyAsImageMenuId) self.menuBar.GetMenu(0).FindItemById( self.exportMenuId).GetSubMenu().Delete(self.exportAsEmfMenuId) self.findReplaceDialog = None self.CheckLabelVerses() self.SetFont() self.text.SetFont(self.pref.editorFace, self.pref.editorSize) self.FinalizePaneInitialization() # Reassign caption value to override caption saved in preferences (it could be another language) self._mgr.GetPane('preview').caption = _('Preview') self._mgr.GetPane('standard').caption = _('Standard') self._mgr.GetPane('format').caption = _('Format') if 'firstTimeEasyKey' in self.pref.notices: msg = _( "You are not a skilled guitarist? Songpress can help you: when you open a song, it can detect if chords are difficult. If this is the case, Songpress will alert you, and offer to transpose your song to the easiest key, automatically.\n\nDo you want to turn this option on?" ) d = wx.MessageDialog(self.frame, msg, _("Songpress"), wx.YES_NO | wx.ICON_QUESTION) if d.ShowModal() == wx.ID_YES: self.pref.autoAdjustEasyKey = True msg = _( "Please take a minute to set up your skill as a guitarist. For each group of chords, tell Songpress how much you like them." ) d = wx.MessageDialog(self.frame, msg, _("Songpress"), wx.OK) d.ShowModal() f = MyPreferencesDialog(self.frame, self.pref, easyChords) f.notebook.SetSelection(1) if f.ShowModal() == wx.ID_OK: self.text.SetFont(self.pref.editorFace, int(self.pref.editorSize)) self.SetDefaultExtension(self.pref.defaultExtension) MyUpdateDialog.check_and_update(self.frame, self.pref) self.frame.Maximize(True) def BindMyMenu(self): """Bind a menu item, by xrc name, to a handler""" def Bind(handler, xrcname): self.Bind(wx.EVT_MENU, handler, xrcname) Bind(self.OnCopyAsImage, 'exportToClipboardAsAVectorImage') Bind(self.OnExportAsSvg, 'exportAsSvg') Bind(self.OnExportAsEmf, 'exportAsEmf') Bind(self.OnExportAsPng, 'exportAsPng') Bind(self.OnExportAsHtml, 'exportAsHtml') Bind(self.OnExportAsPptx, 'exportAsPptx') Bind(self.OnUndo, 'undo') Bind(self.OnRedo, 'redo') Bind(self.OnCut, 'cut') Bind(self.OnCopy, 'copy') Bind(self.OnCopyAsImage, 'copyAsImage') Bind(self.OnCopyOnlyText, 'copyOnlyText') Bind(self.OnPaste, 'paste') Bind(self.OnPasteChords, 'pasteChords') Bind(self.OnFind, 'find') Bind(self.OnFindNext, 'findNext') Bind(self.OnFindPrevious, 'findPrevious') Bind(self.OnReplace, 'replace') Bind(self.OnSelectNextChord, 'selectNextChord') Bind(self.OnSelectPreviousChord, 'selectPreviousChord') Bind(self.OnMoveChordRight, 'moveChordRight') Bind(self.OnMoveChordLeft, 'moveChordLeft') Bind(self.OnRemoveChords, 'removeChords') Bind(self.OnIntegrateChords, 'integrateChords') Bind(self.OnTitle, 'title') Bind(self.OnChord, 'chord') Bind(self.OnChorus, 'chorus') Bind(self.OnVerse, 'verseWithCustomLabelOrWithoutLabel') Bind(self.OnComment, 'comment') Bind(self.OnFormatFont, 'font') Bind(self.OnLabelVerses, 'labelVerses') Bind(self.OnChorusLabel, 'chorusLabel') Bind(self.OnNoChords, 'noChords') Bind(self.OnOneVerseForEachChordPattern, 'oneVerseForEachChordPattern') Bind(self.OnWholeSong, 'wholeSong') Bind(self.OnTranspose, 'transpose') Bind(self.OnSimplifyChords, 'simplifyChords') Bind(self.OnChangeChordNotation, 'changeChordNotation') Bind(self.OnNormalizeChords, 'cleanupChords') Bind(self.OnConvertTabToChordpro, 'convertTabToChordpro') Bind(self.OnRemoveSpuriousBlankLines, 'removeSpuriousBlankLines') Bind(self.OnOptions, 'options') Bind(self.OnGuide, 'guide') Bind(self.OnNewsAndUpdates, 'newsAndUpdates') Bind(self.OnDonate, 'donate') def AddTool(self, toolbar, resource_string, icon_path, label, help): tool = wx.xrc.XRCID(resource_string) toolbar.AddTool(tool, label, wx.Bitmap(wx.Image(glb.AddPath(icon_path))), wx.NullBitmap, wx.ITEM_NORMAL, label, help, None) return tool def New(self): self.text.AutoChangeMode(True) self.text.New() self.text.AutoChangeMode(False) self.UpdateEverything() def Open(self): self.text.AutoChangeMode(True) self.text.Open() self.text.AutoChangeMode(False) self.UpdateEverything() self.AutoAdjust(0, self.text.GetLength()) def Save(self): self.text.Save() self.UpdateEverything() def SavePreferences(self): self.pref.Save() def UpdateUndoRedo(self): self.mainToolBar.EnableTool(self.undoTool, self.text.CanUndo()) self.mainToolBar.EnableTool(self.redoTool, self.text.CanRedo()) def UpdateCutCopyPaste(self): s, e = self.text.GetSelection() self.mainToolBar.EnableTool(self.cutTool, s != e) self.menuBar.Enable(self.cutMenuId, s != e) self.mainToolBar.EnableTool(self.copyTool, s != e) self.menuBar.Enable(self.copyOnlyTextTool, s != e) self.menuBar.Enable(self.copyMenuId, s != e) if platform.system() == 'Windows': cp = self.text.CanPaste() else: # Workaround for weird error in wxGTK cp = True self.mainToolBar.EnableTool(self.pasteTool, cp) self.menuBar.Enable(self.pasteMenuId, cp) self.mainToolBar.EnableTool(self.pasteChordsTool, cp) self.menuBar.Enable(self.pasteChordsMenuId, cp) self.menuBar.Enable(self.removeChordsMenuId, s != e) def UpdateEverything(self): self.UpdateUndoRedo() self.UpdateCutCopyPaste() def TextUpdated(self): self.previewCanvas.Refresh(self.text.GetText()) # self.UpdateEverything() def DrawOnDC(self, dc): decorator = self.pref.decorator if self.pref.labelVerses else SongDecorator( ) r = Renderer(self.pref.format, decorator, self.pref.notations) start, end = self.text.GetSelection() if start == end: w, h = r.Render(self.text.GetText(), dc) else: w, h = r.Render(self.text.GetText(), dc, self.text.LineFromPosition(start), self.text.LineFromPosition(end)) return w, h def AskExportFileName(self, type, ext): """Ask the filename (without saving); return None if user cancels, the file name ow""" leave = False consensus = False while not leave: dlg = wx.FileDialog( self.frame, "Choose a name for the output file", "", os.path.splitext(self.document)[0], "%s files (*.%s)|*.%s|All files (*.*)|*.*" % (type, ext, ext), wx.FD_SAVE) if dlg.ShowModal() == wx.ID_OK: fn = dlg.GetPath() if os.path.isfile(fn): msg = "File \"%s\" already exists. Do you want to overwrite it?" % ( fn, ) d = wx.MessageDialog( self.frame, msg, self.appLongName, wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION) res = d.ShowModal() if res == wx.ID_CANCEL: leave = True consensus = False elif res == wx.ID_NO: leave = False consensus = False else: # wxID_YES leave = True consensus = True else: leave = True consensus = True else: leave = True consensus = False if consensus: return fn else: return None def OnExportAsPng(self, evt): n = self.AskExportFileName(_("PNG image"), "png") if n is not None: dc = wx.MemoryDC(wx.EmptyBitmap(1, 1)) scale = 1 w, h = self.DrawOnDC(dc) b = wx.EmptyBitmap(w * scale, h * scale) dc = wx.MemoryDC(b) dc.SetUserScale(scale, scale) dc.SetBackground(wx.WHITE_BRUSH) dc.Clear() self.DrawOnDC(dc) i = wx.ImageFromBitmap(b) i.SaveFile(n, wx.BITMAP_TYPE_PNG) def OnExportAsHtml(self, evt): n = self.AskExportFileName(_("HTML file"), "html") if n is not None: h = HtmlExporter(self.pref.format) r = Renderer(self.pref.format, h, self.pref.notations) start, end = self.text.GetSelection() if start == end: r.Render(self.text.GetText(), None) else: r.Render(self.text.GetText(), None, self.text.LineFromPosition(start), self.text.LineFromPosition(end)) with open(n, "w", encoding='utf-8') as f: f.write(h.getHtml()) f.close() def OnExportAsSvg(self, evt): n = self.AskExportFileName(_("SVG image"), "svg") if n is not None: dc = wx.MemoryDC(wx.Bitmap(1, 1)) w, h = self.DrawOnDC(dc) dc = wx.SVGFileDC(n, w, h) self.DrawOnDC(dc) def OnExportAsEmf(self, evt): n = self.AskExportFileName(_("Enhanced Metafile"), "emf") if n is not None: dc = wx.msw.MetafileDC(n) self.DrawOnDC(dc) dc.Close() def OnExportAsEps(self, evt): n = self.AskExportFileName(_("EPS image"), "eps") if n is not None: pd = wx.PrintData() pd.SetPaperId(wx.PAPER_NONE) pd.SetPrintMode(wx.PRINT_MODE_FILE) pd.SetFilename(n) dc = wx.PostScriptDC(pd) dc.StartDoc(_("Exporting image as EPS...")) self.DrawOnDC(dc) dc.EndDoc() def OnExportAsPptx(self, evt): try: import songimpress except ImportError: msg = _( "Please install the python-pptx module to use this feature") d = wx.MessageDialog(self.frame, msg, "Songpress", wx.OK | wx.ICON_ERROR) d.ShowModal() return text = replaceTitles(self.text.GetTextOrSelection(), '---') text = removeChordPro(text).strip() if text != '': template_rel = os.path.join('templates', 'slides') template_paths = [ f for f in glb.ListLocalGlobalDir(template_rel) if f[-5:].upper() == '.PPTX' ] template_names = [os.path.split(f)[1][:-5] for f in template_paths] mld = MyListDialog( self.frame, _("Please select a template for your PowerPoint presentation:" ), _("Export as PowerPoint"), template_names, ) if mld.ShowModal() == wx.ID_OK: output_file = self.AskExportFileName(_("PPTX presentation"), "pptx") if output_file is not None: i = mld.GetSelectedIndex() songimpress.to_presentation(text.splitlines(), output_file, template_paths[i]) def OnUpdateUI(self, evt): self.UpdateEverything() evt.Skip() def OnUndo(self, evt): if self.text.CanUndo(): self.text.Undo() self.UpdateUndoRedo() def OnRedo(self, evt): if self.text.CanRedo(): self.text.Redo() self.UpdateUndoRedo() def OnCut(self, evt): self.text.Cut() def OnTextCutCopy(self, evt): self.UpdateCutCopyPaste() evt.Skip() def OnCopy(self, evt): self.text.Copy() def OnCopyOnlyText(self, evt): self.text.CopyOnlyText() def OnCopyAsImage(self, evt): dc = wx.msw.MetafileDC() self.DrawOnDC(dc) m = dc.Close() m.SetClipboard(dc.MaxX(), dc.MaxY()) def OnPaste(self, evt): self.text.Paste() def OnPasteChords(self, evt): self.text.PasteChords() def OnFind(self, evt): self.findReplaceDialog = SongpressFindReplaceDialog(self) def OnFindNext(self, evt): if self.findReplaceDialog != None: self.findReplaceDialog.down = True self.findReplaceDialog.FindNext() def OnFindPrevious(self, evt): if self.findReplaceDialog != None: self.findReplaceDialog.down = False self.findReplaceDialog.FindNext() def OnReplace(self, evt): self.findReplaceDialog = SongpressFindReplaceDialog(self, True) def OnSelectNextChord(self, evt): self.text.SelectNextChord() def OnSelectPreviousChord(self, evt): self.text.SelectPreviousChord() def OnMoveChordRight(self, evt): r = self.text.GetChordUnderCursor() if r is not None: n = self.text.GetLength() s, e, c = r if e < n: self.text.BeginUndoAction() e1 = self.text.PositionAfter(e) self.text.SetSelection(e, e1) l = self.text.GetTextRange(e, e1) self.text.ReplaceSelection('') self.text.SetSelection(s, s) self.text.ReplaceSelection(l) s2 = self.text.PositionAfter(self.text.PositionAfter(s)) self.text.SetSelection(s2, s2) self.text.EndUndoAction() def OnMoveChordLeft(self, evt): r = self.text.GetChordUnderCursor() if r is not None: s, e, c = r if s > 0: self.text.BeginUndoAction() s1 = self.text.PositionBefore(s) e1 = self.text.PositionBefore(e) l = self.text.GetTextRange(s1, s) self.text.SetSelection(e, e) self.text.ReplaceSelection(l) self.text.SetSelection(s1, s) self.text.ReplaceSelection('') s = self.text.PositionAfter(s1) self.text.SetSelection(s, s) self.text.EndUndoAction() def OnRemoveChords(self, evt): self.text.RemoveChordsInSelection() def OnIntegrateChords(self, evt): ln = self.text.GetCurrentLine() if ln < self.text.GetLineCount() - 1: chords = self.text.GetLine(ln).strip("\r\n") text = self.text.GetLine(ln + 1).strip("\r\n") chordpro = integrateChords(chords, text) self.text.SetSelectionStart(self.text.PositionFromLine(ln)) self.text.SetSelectionEnd(self.text.GetLineEndPosition(ln + 1)) self.text.ReplaceSelection(chordpro) def OnFontSelected(self, evt): font = self.fontChooser.GetValue() showChords = self.showChordsChooser.GetValue() self.pref.SetFont(font, showChords) self.SetFont(True) evt.Skip() def OnGuide(self, evt): if platform.system() == 'Windows': helpfile = os.path.join("help", "songpress-%s.chm" % (i18n.getLang(), )) subprocess.Popen("hh " + glb.AddPath(helpfile)) else: wx.LaunchDefaultBrowser(_("http://www.skeed.it/songpress-manual")) def OnIdle(self, evt): try: cp = self.text.CanPaste() self.mainToolBar.EnableTool(self.pasteTool, cp) self.menuBar.Enable(self.pasteMenuId, cp) self.mainToolBar.EnableTool(self.pasteChordsTool, cp) self.menuBar.Enable(self.pasteChordsMenuId, cp) except wx._core.PyDeadObjectError: # When frame is closed, this method may still be executed, generating an exception # because UI elements have been destroyed. Simply ignore it. pass evt.Skip() def OnNewsAndUpdates(self, evt): MyUpdateDialog.check_and_update(self.frame, self.pref, True) def OnDonate(self, evt): wx.LaunchDefaultBrowser(_("http://www.skeed.it/songpress#donate")) def OnFormatFont(self, evt): f = FontFaceDialog(self.frame, wx.ID_ANY, _("Songpress"), self.pref.format, self.pref.decorator, self.pref.decoratorFormat) if f.ShowModal() == wx.ID_OK: self.pref.SetFont(f.GetValue()) self.SetFont() def OnTranspose(self, evt): t = MyTransposeDialog(self.frame, self.pref.notations, self.text.GetTextOrSelection()) if t.ShowModal() == wx.ID_OK: self.text.ReplaceTextOrSelection(t.GetTransposed()) def OnSimplifyChords(self, evt): self.text.AutoChangeMode(True) t = self.text.GetTextOrSelection() notation = autodetectNotation(t, self.pref.notations) count, c, dc, e, de = findEasiestKey(t, self.pref.GetEasyChords(), notation) title = _("Simplify chords") if count > 0 and dc != de: msg = _( "The key of your song, %s, is not the easiest to play (difficulty: %.1f/5.0).\n" ) % (c, 5 * dc) msg += _( "Do you want to transpose the key %s, which is the easiest one (difficulty: %.1f/5.0)?" ) % (e, 5 * de) d = wx.MessageDialog(self.frame, msg, title, wx.YES_NO | wx.ICON_QUESTION) if d.ShowModal() == wx.ID_YES: t = transposeChordPro(translateChord(c, notation), translateChord(e, notation), t, notation) self.text.ReplaceTextOrSelection(t) else: if count > 0: msg = _( "The key of your song, %s, is already the easiest to play (difficulty: %.1f/5.0).\n" ) % (c, 5 * dc) else: msg = _( "Your song or current selection does not contain any chords." ) d = wx.MessageDialog(self.frame, msg, title, wx.OK | wx.ICON_INFORMATION) d.ShowModal() self.text.AutoChangeMode(False) def OnChangeChordNotation(self, evt): t = MyNotationDialog(self.frame, self.pref.notations, self.text.GetTextOrSelection()) if t.ShowModal() == wx.ID_OK: self.text.ReplaceTextOrSelection(t.ChangeChordNotation()) def OnNormalizeChords(self, evt): t = MyNormalizeDialog(self.frame, self.pref.notations, self.text.GetTextOrSelection()) if t.ShowModal() == wx.ID_OK: self.text.ReplaceTextOrSelection(t.NormalizeChords()) def OnConvertTabToChordpro(self, evt): t = self.text.GetTextOrSelection() n = testTabFormat(t, self.pref.notations) if n is not None: self.text.ReplaceTextOrSelection(tab2ChordPro(t, n)) def OnRemoveSpuriousBlankLines(self, evt): self.text.ReplaceTextOrSelection( removeSpuriousLines(self.text.GetTextOrSelection())) def OnOptions(self, evt): f = MyPreferencesDialog(self.frame, self.pref, easyChords) if f.ShowModal() == wx.ID_OK: self.text.SetFont(self.pref.editorFace, int(self.pref.editorSize)) self.SetDefaultExtension(self.pref.defaultExtension) def InsertWithCaret(self, st): s, e = self.text.GetSelection() c = st.find('|') if c != -1: self.text.ReplaceSelection(st[:c] + st[c + 1:]) self.text.SetSelection(s + c, s + c) else: self.text.ReplaceSelection(st) self.text.SetSelection(s + len(st), s + len(st)) def OnTitle(self, evt): self.InsertWithCaret("{title:|}\n\n") def OnChord(self, evt): self.InsertWithCaret("[|]") def OnVerse(self, evt): label = wx.GetTextFromUser( _("Insert a label for verse, or press Cancel if you want to omit label." ), _("Verse label"), "", self.frame, ) self.InsertWithCaret("{Verse:%s}|" % label) def OnChorus(self, evt): default = self.pref.decoratorFormat.GetChorusLabel() label = wx.GetTextFromUser( _("Insert a label for chorus, or press Cancel if you want to omit label." ), _("Chorus label"), default, self.frame, ) if label == default: self.InsertWithCaret("{soc}\n|\n{eoc}\n") else: self.InsertWithCaret("{soc:%s}\n|\n{eoc}\n" % label) def OnComment(self, evt): self.InsertWithCaret("{c:|}") def OnLabelVerses(self, evt): self.pref.labelVerses = not self.pref.labelVerses self.CheckLabelVerses() def OnChorusLabel(self, evt): c = self.pref.decoratorFormat.GetChorusLabel() msg = _("Please enter a label for chorus") d = wx.TextEntryDialog(self.frame, msg, _("Songpress"), c) if d.ShowModal() == wx.ID_OK: c = d.GetValue() self.pref.SetChorusLabel(c) self.previewCanvas.Refresh(self.text.GetText()) def OnNoChords(self, evt): self.pref.format.showChords = 0 self.SetFont(True) def OnOneVerseForEachChordPattern(self, evt): self.pref.format.showChords = 1 self.SetFont(True) def OnWholeSong(self, evt): self.pref.format.showChords = 2 self.SetFont(True) def OnTextChanged(self, evt): self.AutoAdjust(evt.lastPos, evt.currentPos) def AutoAdjust(self, lastPos, currentPos): self.text.AutoChangeMode(True) t = self.text.GetTextRange(lastPos, currentPos) if self.pref.autoAdjustSpuriousLines: if testSpuriousLines(t): msg = _( "It looks like there are spurious blank lines in the song.\n" ) msg += _("Do you want to try to remove them automatically?") title = _("Remove spurious blank lines") d = wx.MessageDialog(self.frame, msg, title, wx.YES_NO | wx.ICON_QUESTION) if d.ShowModal() == wx.ID_YES: self.text.SetSelection(lastPos, currentPos) t = removeSpuriousLines(t) self.text.ReplaceSelection(t) currentPos = self.text.GetCurrentPos() if self.pref.autoAdjustTab2Chordpro: n = testTabFormat(t, self.pref.notations) if n is not None: msg = _( "It looks like your song is in tab format (i.e., chords are above the text).\n" ) msg += _( "Do you want to convert it to ChordPro automatically?") title = _("Convert to ChordPro") d = wx.MessageDialog(self.frame, msg, title, wx.YES_NO | wx.ICON_QUESTION) if d.ShowModal() == wx.ID_YES: self.text.SetSelection(lastPos, currentPos) t = tab2ChordPro(t, n) self.text.ReplaceSelection(t) if self.pref.autoAdjustEasyKey: notation = autodetectNotation(t, self.pref.notations) count, c, dc, e, de = findEasiestKey(t, self.pref.GetEasyChords(), notation) if count > 10 and dc != de: msg = _( "The key of your song, %s, is not the easiest to play (difficulty: %.1f/5.0).\n" ) % (c, 5 * dc) msg += _( "Do you want to transpose the key %s, which is the easiest one (difficulty: %.1f/5.0)?" ) % (e, 5 * de) title = _("Simplify chords") d = wx.MessageDialog(self.frame, msg, title, wx.YES_NO | wx.ICON_QUESTION) if d.ShowModal() == wx.ID_YES: self.text.SetSelection(lastPos, currentPos) t = transposeChordPro(translateChord(c, notation), translateChord(e, notation), t, notation) self.text.ReplaceSelection(t) self.text.AutoChangeMode(False) def SetFont(self, updateFontChooser=True): try: if updateFontChooser: self.fontChooser.SetValue(self.pref.format.face) self.showChordsChooser.SetValue(self.pref.format.showChords) ids = [ self.noChordsMenuId, self.oneVerseForEachChordPatternMenuId, self.wholeSongMenuId ] self.menuBar.Check(ids[self.pref.format.showChords], True) self.previewCanvas.Refresh(self.text.GetText()) except wx._core.PyDeadObjectError: # When frame is closed, this method may still be executed, generating an exception # because UI elements have been destroyed. Simply ignore it. pass def CheckLabelVerses(self): self.formatToolBar.ToggleTool(self.labelVersesToolId, self.pref.labelVerses) self.formatToolBar.Refresh() self.menuBar.Check(self.labelVersesMenuId, self.pref.labelVerses) if self.pref.labelVerses: self.previewCanvas.SetDecorator(self.pref.decorator) else: self.previewCanvas.SetDecorator(SongDecorator()) self.previewCanvas.Refresh(self.text.GetText())