Beispiel #1
0
class Browser(QWidget):
    """LilyPond documentation browser widget."""
    def __init__(self, dockwidget):
        super(Browser, self).__init__(dockwidget)

        layout = QVBoxLayout(spacing=0)
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)

        self.toolbar = tb = QToolBar()
        self.webview = QWebView(contextMenuPolicy=Qt.CustomContextMenu)
        self.chooser = QComboBox(sizeAdjustPolicy=QComboBox.AdjustToContents)
        self.search = SearchEntry(maximumWidth=200)

        layout.addWidget(self.toolbar)
        layout.addWidget(self.webview)

        ac = dockwidget.actionCollection
        ac.help_back.triggered.connect(self.webview.back)
        ac.help_forward.triggered.connect(self.webview.forward)
        ac.help_home.triggered.connect(self.showHomePage)
        ac.help_print.triggered.connect(self.slotPrint)

        self.webview.page().setNetworkAccessManager(
            lilydoc.network.accessmanager())
        self.webview.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
        self.webview.page().linkClicked.connect(self.openUrl)
        self.webview.page().setForwardUnsupportedContent(True)
        self.webview.page().unsupportedContent.connect(self.slotUnsupported)
        self.webview.urlChanged.connect(self.slotUrlChanged)
        self.webview.customContextMenuRequested.connect(
            self.slotShowContextMenu)

        tb.addAction(ac.help_back)
        tb.addAction(ac.help_forward)
        tb.addSeparator()
        tb.addAction(ac.help_home)
        tb.addAction(ac.help_print)
        tb.addSeparator()
        tb.addWidget(self.chooser)
        tb.addWidget(self.search)

        self.chooser.activated[int].connect(self.showHomePage)
        self.search.textEdited.connect(self.slotSearchChanged)
        self.search.returnPressed.connect(self.slotSearchReturnPressed)
        dockwidget.mainwindow().iconSizeChanged.connect(
            self.updateToolBarSettings)
        dockwidget.mainwindow().toolButtonStyleChanged.connect(
            self.updateToolBarSettings)

        app.settingsChanged.connect(self.readSettings)
        self.readSettings()
        self.loadDocumentation()
        self.showInitialPage()
        app.settingsChanged.connect(self.loadDocumentation)
        app.translateUI(self)

    def readSettings(self):
        s = QSettings()
        s.beginGroup("documentation")
        ws = self.webview.page().settings()
        family = s.value("fontfamily", self.font().family(), str)
        size = s.value("fontsize", 16, int)
        ws.setFontFamily(QWebSettings.StandardFont, family)
        ws.setFontSize(QWebSettings.DefaultFontSize, size)
        fixed = textformats.formatData('editor').font
        ws.setFontFamily(QWebSettings.FixedFont, fixed.family())
        ws.setFontSize(QWebSettings.DefaultFixedFontSize,
                       fixed.pointSizeF() * 96 / 72)

    def keyPressEvent(self, ev):
        if ev.text() == "/":
            self.search.setFocus()
        else:
            super(Browser, self).keyPressEvent(ev)

    def translateUI(self):
        try:
            self.search.setPlaceholderText(_("Search..."))
        except AttributeError:
            pass  # not in Qt 4.6

    def showInitialPage(self):
        """Shows the preferred start page.
        
        If a local documentation instance already has a suitable version,
        just loads it. Otherwise connects to the allLoaded signal, that is
        emitted when all the documentation instances have loaded their version
        information and then shows the start page (if another page wasn't yet
        loaded).
        
        """
        if self.webview.url().isEmpty():
            docs = lilydoc.manager.docs()
            version = lilypondinfo.preferred().version()
            index = -1
            if version:
                for num, doc in enumerate(docs):
                    if doc.version() is not None and doc.version() >= version:
                        index = num  # a suitable documentation is found
                        break
            if index == -1:
                # nothing found (or LilyPond version not available),
                # wait for loading or show the most recent version
                if not lilydoc.manager.loaded():
                    lilydoc.manager.allLoaded.connect(self.showInitialPage)
                    return
                index = len(docs) - 1
            self.chooser.setCurrentIndex(index)
            self.showHomePage()

    def loadDocumentation(self):
        """Puts the available documentation instances in the combobox."""
        i = self.chooser.currentIndex()
        self.chooser.clear()
        for doc in lilydoc.manager.docs():
            v = doc.versionString()
            if doc.isLocal():
                t = _("(local)")
            else:
                t = _("({hostname})").format(hostname=doc.url().host())
            self.chooser.addItem("{0} {1}".format(v or _("<unknown>"), t))
        self.chooser.setCurrentIndex(i)
        if not lilydoc.manager.loaded():
            lilydoc.manager.allLoaded.connect(self.loadDocumentation, -1)
            return

    def updateToolBarSettings(self):
        mainwin = self.parentWidget().mainwindow()
        self.toolbar.setIconSize(mainwin.iconSize())
        self.toolbar.setToolButtonStyle(mainwin.toolButtonStyle())

    def showManual(self):
        """Invoked when the user presses F1."""
        self.slotHomeFrescobaldi()  # TEMP

    def slotUrlChanged(self):
        ac = self.parentWidget().actionCollection
        ac.help_back.setEnabled(self.webview.history().canGoBack())
        ac.help_forward.setEnabled(self.webview.history().canGoForward())

    def openUrl(self, url):
        if url.path().endswith(('.ily', '.lyi', '.ly')):
            self.sourceViewer().showReply(lilydoc.network.get(url))
        else:
            self.webview.load(url)

    def slotUnsupported(self, reply):
        helpers.openUrl(reply.url())

    def slotSearchChanged(self):
        text = self.search.text()
        if not text.startswith(':'):
            self.webview.page().findText(text,
                                         QWebPage.FindWrapsAroundDocument)

    def slotSearchReturnPressed(self):
        text = self.search.text()
        if not text.startswith(':'):
            self.slotSearchChanged()
        else:
            pass  # TODO: implement full doc search

    def sourceViewer(self):
        try:
            return self._sourceviewer
        except AttributeError:
            from . import sourceviewer
            self._sourceviewer = sourceviewer.SourceViewer(self)
            return self._sourceviewer

    def showHomePage(self):
        """Shows the homepage of the LilyPond documentation."""
        i = self.chooser.currentIndex()
        if i < 0:
            i = 0
        doc = lilydoc.manager.docs()[i]

        url = doc.home()
        if doc.isLocal():
            path = url.toLocalFile()
            langs = lilydoc.network.langs()
            if langs:
                for lang in langs:
                    if os.path.exists(path + '.' + lang + '.html'):
                        path += '.' + lang
                        break
            url = QUrl.fromLocalFile(path + '.html')
        self.webview.load(url)

    def slotPrint(self):
        printer = QPrinter()
        dlg = QPrintDialog(printer, self)
        dlg.setWindowTitle(app.caption(_("Print")))
        if dlg.exec_():
            self.webview.print_(printer)

    def slotShowContextMenu(self, pos):
        hit = self.webview.page().currentFrame().hitTestContent(pos)
        menu = QMenu()
        if hit.linkUrl().isValid():
            a = self.webview.pageAction(QWebPage.CopyLinkToClipboard)
            a.setIcon(icons.get("edit-copy"))
            a.setText(_("Copy &Link"))
            menu.addAction(a)
            menu.addSeparator()
            a = menu.addAction(icons.get("window-new"),
                               _("Open Link in &New Window"))
            a.triggered.connect(
                (lambda url: lambda: self.slotNewWindow(url))(hit.linkUrl()))
        else:
            if hit.isContentSelected():
                a = self.webview.pageAction(QWebPage.Copy)
                a.setIcon(icons.get("edit-copy"))
                a.setText(_("&Copy"))
                menu.addAction(a)
                menu.addSeparator()
            a = menu.addAction(icons.get("window-new"),
                               _("Open Document in &New Window"))
            a.triggered.connect((lambda url: lambda: self.slotNewWindow(url))(
                self.webview.url()))
        if menu.actions():
            menu.exec_(self.webview.mapToGlobal(pos))

    def slotNewWindow(self, url):
        helpers.openUrl(url)
Beispiel #2
0
class Browser(QWidget):
    """LilyPond documentation browser widget."""
    def __init__(self, dockwidget):
        super(Browser, self).__init__(dockwidget)
        
        layout = QVBoxLayout(spacing=0)
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)
        
        self.toolbar = tb = QToolBar()
        self.webview = QWebView(contextMenuPolicy=Qt.CustomContextMenu)
        self.chooser = QComboBox(sizeAdjustPolicy=QComboBox.AdjustToContents)
        self.search = SearchEntry(maximumWidth=200)
        
        layout.addWidget(self.toolbar)
        layout.addWidget(self.webview)
        
        ac = dockwidget.actionCollection
        ac.help_back.triggered.connect(self.webview.back)
        ac.help_forward.triggered.connect(self.webview.forward)
        ac.help_home.triggered.connect(self.showHomePage)
        ac.help_print.triggered.connect(self.slotPrint)
        
        self.webview.page().setNetworkAccessManager(lilydoc.network.accessmanager())
        self.webview.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
        self.webview.page().linkClicked.connect(self.openUrl)
        self.webview.page().setForwardUnsupportedContent(True)
        self.webview.page().unsupportedContent.connect(self.slotUnsupported)
        self.webview.urlChanged.connect(self.slotUrlChanged)
        self.webview.customContextMenuRequested.connect(self.slotShowContextMenu)
        
        tb.addAction(ac.help_back)
        tb.addAction(ac.help_forward)
        tb.addSeparator()
        tb.addAction(ac.help_home)
        tb.addAction(ac.help_print)
        tb.addSeparator()
        tb.addWidget(self.chooser)
        tb.addWidget(self.search)
        
        self.chooser.activated[int].connect(self.showHomePage)
        self.search.textEdited.connect(self.slotSearchChanged)
        self.search.returnPressed.connect(self.slotSearchReturnPressed)
        dockwidget.mainwindow().iconSizeChanged.connect(self.updateToolBarSettings)
        dockwidget.mainwindow().toolButtonStyleChanged.connect(self.updateToolBarSettings)
        
        app.settingsChanged.connect(self.readSettings)
        self.readSettings()
        self.loadDocumentation()
        self.showInitialPage()
        app.settingsChanged.connect(self.loadDocumentation)
        app.translateUI(self)
    
    def readSettings(self):
        s = QSettings()
        s.beginGroup("documentation")
        ws = self.webview.page().settings()
        family = s.value("fontfamily", self.font().family(), str)
        size = s.value("fontsize", 16, int)
        ws.setFontFamily(QWebSettings.StandardFont, family)
        ws.setFontSize(QWebSettings.DefaultFontSize, size)
        fixed = textformats.formatData('editor').font
        ws.setFontFamily(QWebSettings.FixedFont, fixed.family())
        ws.setFontSize(QWebSettings.DefaultFixedFontSize, fixed.pointSizeF() * 96 / 72)
        
    def keyPressEvent(self, ev):
        if ev.text() == "/":
            self.search.setFocus()
        else:
            super(Browser, self).keyPressEvent(ev)
        
    def translateUI(self):
        try:
            self.search.setPlaceholderText(_("Search..."))
        except AttributeError:
            pass # not in Qt 4.6
    
    def showInitialPage(self):
        """Shows the preferred start page.
        
        If a local documentation instance already has a suitable version,
        just loads it. Otherwise connects to the allLoaded signal, that is
        emitted when all the documentation instances have loaded their version
        information and then shows the start page (if another page wasn't yet
        loaded).
        
        """
        if self.webview.url().isEmpty():
            docs = lilydoc.manager.docs()
            version = lilypondinfo.preferred().version()
            index = -1
            if version:
                for num, doc in enumerate(docs):
                    if doc.version() is not None and doc.version() >= version:
                        index = num # a suitable documentation is found
                        break
            if index == -1:
                # nothing found (or LilyPond version not available),
                # wait for loading or show the most recent version
                if not lilydoc.manager.loaded():
                    lilydoc.manager.allLoaded.connect(self.showInitialPage)
                    return
                index = len(docs) - 1
            self.chooser.setCurrentIndex(index)
            self.showHomePage()
    
    def loadDocumentation(self):
        """Puts the available documentation instances in the combobox."""
        i = self.chooser.currentIndex()
        self.chooser.clear()
        for doc in lilydoc.manager.docs():
            v = doc.versionString()
            if doc.isLocal():
                t = _("(local)")
            else:
                t = _("({hostname})").format(hostname=doc.url().host())
            self.chooser.addItem("{0} {1}".format(v or _("<unknown>"), t))
        self.chooser.setCurrentIndex(i)
        if not lilydoc.manager.loaded():
            lilydoc.manager.allLoaded.connect(self.loadDocumentation, -1)
            return
        
    def updateToolBarSettings(self):
        mainwin = self.parentWidget().mainwindow()
        self.toolbar.setIconSize(mainwin.iconSize())
        self.toolbar.setToolButtonStyle(mainwin.toolButtonStyle())
        
    def showManual(self):
        """Invoked when the user presses F1."""
        self.slotHomeFrescobaldi() # TEMP
        
    def slotUrlChanged(self):
        ac = self.parentWidget().actionCollection
        ac.help_back.setEnabled(self.webview.history().canGoBack())
        ac.help_forward.setEnabled(self.webview.history().canGoForward())
    
    def openUrl(self, url):
        if url.path().endswith(('.ily', '.lyi', '.ly')):
            self.sourceViewer().showReply(lilydoc.network.get(url))
        else:
            self.webview.load(url)
    
    def slotUnsupported(self, reply):
        helpers.openUrl(reply.url())
    
    def slotSearchChanged(self):
        text = self.search.text()
        if not text.startswith(':'):
            self.webview.page().findText(text, QWebPage.FindWrapsAroundDocument)
    
    def slotSearchReturnPressed(self):
        text = self.search.text()
        if not text.startswith(':'):
            self.slotSearchChanged()
        else:
            pass # TODO: implement full doc search
    
    def sourceViewer(self):
        try:
            return self._sourceviewer
        except AttributeError:
            from . import sourceviewer
            self._sourceviewer = sourceviewer.SourceViewer(self)
            return self._sourceviewer
    
    def showHomePage(self):
        """Shows the homepage of the LilyPond documentation."""
        i = self.chooser.currentIndex()
        if i < 0:
            i = 0
        doc = lilydoc.manager.docs()[i]
        
        url = doc.home()
        if doc.isLocal():
            path = url.toLocalFile()
            langs = lilydoc.network.langs()
            if langs:
                for lang in langs:
                    if os.path.exists(path + '.' + lang + '.html'):
                        path += '.' + lang
                        break
            url = QUrl.fromLocalFile(path + '.html')
        self.webview.load(url)
    
    def slotPrint(self):
        printer = QPrinter()
        dlg = QPrintDialog(printer, self)
        dlg.setWindowTitle(app.caption(_("Print")))
        if dlg.exec_():
            self.webview.print_(printer)
    
    def slotShowContextMenu(self, pos):
        hit = self.webview.page().currentFrame().hitTestContent(pos)
        menu = QMenu()
        if hit.linkUrl().isValid():
            a = self.webview.pageAction(QWebPage.CopyLinkToClipboard)
            a.setIcon(icons.get("edit-copy"))
            a.setText(_("Copy &Link"))
            menu.addAction(a)
            menu.addSeparator()
            a = menu.addAction(icons.get("window-new"), _("Open Link in &New Window"))
            a.triggered.connect((lambda url: lambda: self.slotNewWindow(url))(hit.linkUrl()))
        else:
            if hit.isContentSelected():
                a = self.webview.pageAction(QWebPage.Copy)
                a.setIcon(icons.get("edit-copy"))
                a.setText(_("&Copy"))
                menu.addAction(a)
                menu.addSeparator()
            a = menu.addAction(icons.get("window-new"), _("Open Document in &New Window"))
            a.triggered.connect((lambda url: lambda: self.slotNewWindow(url))(self.webview.url()))
        if menu.actions():
            menu.exec_(self.webview.mapToGlobal(pos))
    
    def slotNewWindow(self, url):
        helpers.openUrl(url)
Beispiel #3
0
class HelpWidget(QWidget):
    def __init__(self):
        super().__init__()  # no parent; we are a standalone window
        # Set window title to translated value
        self.setWindowTitle(
            _TR("title of help viewer window", "PPQT Help Viewer"))
        # Initialize saved geometry, see showEvent()
        self.last_shape = None
        # Initialize find string, see find_action().
        self.find_text = None
        self.view = QWebView()
        hb = QHBoxLayout()
        hb.addWidget(self.view)
        self.setLayout(hb)
        # Look for the help text file and load it if found.
        paths.notify_me(self.path_change)
        self.help_url = None
        self.path_change()  # force a load now

    # Slot to receive the pathChanged signal from the paths module. That
    # signal is given when any of the standard paths are changed. Use the
    # extras path to (re)load the help file.
    #
    # This is also called from __init__ to do the initial load, and from
    # keyEvent processing to implement Back when the history stack is empty.
    #
    # The file we need to load is not actually ppqt2help.html but the
    # file sphinx/index.html. QWebView only shows the nice trimmings if we
    # open that.

    def path_change(self, code='extras'):
        if code == 'extras':
            extras_path = paths.get_extras_path()
            help_path = os.path.join(extras_path, 'ppqt2help.html')
            sphinx_path = os.path.join(extras_path, 'sphinx', 'index.html')
            if os.access(help_path, os.R_OK) and os.access(
                    sphinx_path, os.R_OK):
                # the help file exists. Save a QUrl describing it, and load it.
                self.help_url = QUrl.fromLocalFile(sphinx_path)
                self.view.load(self.help_url)
            else:
                self.help_url = None
                self.view.setHtml(DEFAULT_HTML)

    def closeEvent(self, event):
        self.last_shape = self.saveGeometry()
        event.ignore()
        self.hide()
        return super().closeEvent(event)

    def showEvent(self, event):
        if self.last_shape:  # is not None
            self.restoreGeometry(self.last_shape)

    def keyPressEvent(self, event):
        kkey = int(int(event.modifiers()) & C.KEYPAD_MOD_CLEAR) | int(
            event.key())
        if kkey == C.CTL_F:
            event.accept()
            self.find_action()
        elif kkey == C.CTL_G:
            event.accept()
            self.find_next_action()
        elif kkey == C.CTL_SHFT_G:
            event.accept()
            self.find_prior_action()
        elif kkey in C.KEYS_WEB_BACK:
            event.accept()
            if self.view.history().canGoBack():
                self.view.history().back()
            elif self.help_url is not None:
                self.view.load(self.help_url)
            else:  # try to reload the default text
                self.path_change()
        elif kkey in C.KEYS_WEB_FORWARD:
            if self.view.history().canGoForward():
                self.view.history().forward()
            else:
                utilities.beep()
        else:
            super().keyPressEvent(event)

    # Methods for a basic Find operation. Other modules e.g. noteview have a
    # custom Edit menu with Find/Next as menu actions. Here we do not support
    # any editing actions, so support Find/Next/Prior only via keystrokes.
    #
    # Use the simple find dialog in utilities to get a string to look for.
    # Initialize the dialog with up to 40 chars of the current selection. If
    # the user clicks Cancel in the Find dialog, self.find_text is None; if
    # the user just clears the input area and clicks OK the find_text is an
    # empty string. In either case do nothing.
    #
    # Following a successful find, the found text is selected in the view, so
    # if you do ^f again without disturbing that selection, that text is back
    # in the dialog to be used or replaced. So ^f+Enter is the same as ^g.

    FIND_NORMAL = QWebPage.FindWrapsAroundDocument
    FIND_PRIOR = QWebPage.FindWrapsAroundDocument | QWebPage.FindBackward

    def find_action(self):
        prep_text = self.view.selectedText()[:40]
        self.find_text = utilities.get_find_string(
            _TR('Help viewer find dialog', 'Text to find'), self, prep_text)
        if self.find_text:  # is not None nor an empty string
            self._do_find(self.FIND_NORMAL)

    # For ^g Find Next, if there is no active find-text pretend that ^f was
    # hit. If there was a prior search, repeat the search.
    def find_next_action(self):
        if self.find_text:  # is not None nor an empty string
            self._do_find(self.FIND_NORMAL)
        else:
            self.find_action()

    # for ^G Find Prior, same as for ^g but backward.
    def find_prior_action(self):
        if self.find_text:
            self._do_find(self.FIND_PRIOR)
        else:
            self.find_action()

    # The actual search, factored out of the above actions. Because the
    # search wraps around, if it fails, it fails, and that's that.

    def _do_find(self, find_flags):
        if not self.view.page().findText(self.find_text, find_flags):
            utilities.beep()
Beispiel #4
0
class HelpWidget(QWidget) :

    def __init__( self ) :
        super().__init__() # no parent; we are a standalone window
        # Set window title to translated value
        self.setWindowTitle(
            _TR( "title of help viewer window", "PPQT Help Viewer" ) )
        # Initialize saved geometry, see showEvent()
        self.last_shape = None
        # Initialize find string, see find_action().
        self.find_text = None
        self.view = QWebView()
        hb = QHBoxLayout()
        hb.addWidget(self.view)
        self.setLayout(hb)
        # Look for the help text file and load it if found.
        paths.notify_me( self.path_change )
        self.help_url = None
        self.path_change( ) # force a load now

    # Slot to receive the pathChanged signal from the paths module. That
    # signal is given when any of the standard paths are changed. Use the
    # extras path to (re)load the help file.
    #
    # This is also called from __init__ to do the initial load, and from
    # keyEvent processing to implement Back when the history stack is empty.
    #
    # The file we need to load is not actually ppqt2help.html but the
    # file sphinx/index.html. QWebView only shows the nice trimmings if we
    # open that.

    def path_change( self, code='extras' ) :
        if code == 'extras' :
            extras_path = paths.get_extras_path()
            help_path = os.path.join( extras_path , 'ppqt2help.html' )
            sphinx_path = os.path.join( extras_path, 'sphinx', 'index.html' )
            if os.access( help_path, os.R_OK ) and os.access( sphinx_path, os.R_OK ) :
                # the help file exists. Save a QUrl describing it, and load it.
                self.help_url = QUrl.fromLocalFile( sphinx_path )
                self.view.load( self.help_url )
            else :
                self.help_url = None
                self.view.setHtml( DEFAULT_HTML )

    def closeEvent( self, event ) :
        self.last_shape = self.saveGeometry()
        event.ignore()
        self.hide()
        return super().closeEvent(event)

    def showEvent( self, event ) :
        if self.last_shape : # is not None
            self.restoreGeometry( self.last_shape )

    def keyPressEvent( self, event ) :
        kkey = int( int(event.modifiers()) & C.KEYPAD_MOD_CLEAR) | int(event.key() )
        if kkey == C.CTL_F :
            event.accept()
            self.find_action()
        elif kkey == C.CTL_G :
            event.accept()
            self.find_next_action()
        elif kkey == C.CTL_SHFT_G :
            event.accept()
            self.find_prior_action()
        elif kkey in C.KEYS_WEB_BACK :
            event.accept()
            if self.view.history().canGoBack() :
                self.view.history().back()
            elif self.help_url is not None:
                self.view.load( self.help_url )
            else: # try to reload the default text
                self.path_change()
        elif kkey in C.KEYS_WEB_FORWARD :
            if self.view.history().canGoForward() :
                self.view.history().forward()
            else:
                utilities.beep()
        else :
            super().keyPressEvent(event)

    # Methods for a basic Find operation. Other modules e.g. noteview have a
    # custom Edit menu with Find/Next as menu actions. Here we do not support
    # any editing actions, so support Find/Next/Prior only via keystrokes.
    #
    # Use the simple find dialog in utilities to get a string to look for.
    # Initialize the dialog with up to 40 chars of the current selection. If
    # the user clicks Cancel in the Find dialog, self.find_text is None; if
    # the user just clears the input area and clicks OK the find_text is an
    # empty string. In either case do nothing.
    #
    # Following a successful find, the found text is selected in the view, so
    # if you do ^f again without disturbing that selection, that text is back
    # in the dialog to be used or replaced. So ^f+Enter is the same as ^g.

    FIND_NORMAL = QWebPage.FindWrapsAroundDocument
    FIND_PRIOR = QWebPage.FindWrapsAroundDocument | QWebPage.FindBackward


    def find_action(self):
        prep_text = self.view.selectedText()[:40]
        self.find_text = utilities.get_find_string(
            _TR('Help viewer find dialog','Text to find'),
            self, prep_text)
        if self.find_text : # is not None nor an empty string
            self._do_find( self.FIND_NORMAL )

    # For ^g Find Next, if there is no active find-text pretend that ^f was
    # hit. If there was a prior search, repeat the search.
    def find_next_action(self):
        if self.find_text : # is not None nor an empty string
            self._do_find( self.FIND_NORMAL )
        else :
            self.find_action()

    # for ^G Find Prior, same as for ^g but backward.
    def find_prior_action(self):
        if self.find_text :
            self._do_find( self.FIND_PRIOR )
        else :
            self.find_action()

    # The actual search, factored out of the above actions. Because the
    # search wraps around, if it fails, it fails, and that's that.

    def _do_find( self, find_flags ) :
        if not self.view.page().findText( self.find_text, find_flags ):
            utilities.beep()