コード例 #1
0
ファイル: navigate.py プロジェクト: Al-Caveman/glimpsebrowser
    def _prevnext_cb(elems):
        elem = _find_prevnext(prev, elems)
        word = 'prev' if prev else 'forward'

        if elem is None:
            message.error("No {} links found!".format(word))
            return
        url = elem.resolve_url(baseurl)
        if url is None:
            message.error("No {} links found!".format(word))
            return
        qtutils.ensure_valid(url)

        cur_tabbed_browser = objreg.get('tabbed-browser',
                                        scope='window',
                                        window=win_id)

        if window:
            new_window = mainwindow.MainWindow(
                private=cur_tabbed_browser.is_private)
            new_window.show()
            tabbed_browser = objreg.get('tabbed-browser',
                                        scope='window',
                                        window=new_window.win_id)
            tabbed_browser.tabopen(url, background=False)
        elif tab:
            cur_tabbed_browser.tabopen(url, background=background)
        else:
            browsertab.load_url(url)
コード例 #2
0
 def _load_url_prepare(self,
                       url: QUrl,
                       *,
                       emit_before_load_started: bool = True) -> None:
     qtutils.ensure_valid(url)
     if emit_before_load_started:
         self.before_load_started.emit(url)
コード例 #3
0
    def sizeHint(self, option, index):
        """Override sizeHint of QStyledItemDelegate.

        Return the cell size based on the QTextDocument size, but might not
        work correctly yet.

        Args:
            option: const QStyleOptionViewItem & option
            index: const QModelIndex & index

        Return:
            A QSize with the recommended size.
        """
        value = index.data(Qt.SizeHintRole)
        if value is not None:
            return value
        self._opt = QStyleOptionViewItem(option)
        self.initStyleOption(self._opt, index)
        self._style = self._opt.widget.style()
        self._get_textdoc(index)
        docsize = self._doc.size().toSize()
        size = self._style.sizeFromContents(QStyle.CT_ItemViewItem, self._opt,
                                            docsize, self._opt.widget)
        qtutils.ensure_valid(size)
        return size + QSize(10, 3)
コード例 #4
0
    def resolve(self, query, from_file=False):
        """Resolve a proxy via PAC.

        Args:
            query: QNetworkProxyQuery.
            from_file: Whether the proxy info is coming from a file.

        Return:
            A list of QNetworkProxy objects in order of preference.
        """
        qtutils.ensure_valid(query.url())

        if from_file:
            string_flags = QUrl.PrettyDecoded
        else:
            string_flags = QUrl.RemoveUserInfo
            if query.url().scheme() == 'https':
                string_flags |= QUrl.RemovePath | QUrl.RemoveQuery

        result = self._resolver.call([query.url().toString(string_flags),
                                      query.peerHostName()])
        result_str = result.toString()
        if not result.isString():
            err = "Got strange value from FindProxyForURL: '{}'"
            raise EvalProxyError(err.format(result_str))
        return self._parse_proxy_string(result_str)
コード例 #5
0
def _get_search_url(txt):
    """Get a search engine URL for a text.

    Args:
        txt: Text to search for.

    Return:
        The search URL as a QUrl.
    """
    log.url.debug("Finding search engine for {!r}".format(txt))
    engine, term = _parse_search_term(txt)
    assert term
    if engine is None:
        engine = 'DEFAULT'
    template = config.val.url.searchengines[engine]
    quoted_term = urllib.parse.quote(term, safe='')
    url = qurl_from_user_input(template.format(quoted_term))

    if config.val.url.open_base_url and term in config.val.url.searchengines:
        url = qurl_from_user_input(config.val.url.searchengines[term])
        url.setPath(None)
        url.setFragment(None)
        url.setQuery(None)
    qtutils.ensure_valid(url)
    return url
コード例 #6
0
ファイル: webelem.py プロジェクト: Al-Caveman/glimpsebrowser
    def resolve_url(self, baseurl: QUrl) -> typing.Optional[QUrl]:
        """Resolve the URL in the element's src/href attribute.

        Args:
            baseurl: The URL to base relative URLs on as QUrl.

        Return:
            A QUrl with the absolute URL, or None.
        """
        if baseurl.isRelative():
            raise ValueError("Need an absolute base URL!")

        for attr in ['href', 'src']:
            if attr in self:
                text = self[attr].strip()
                break
        else:
            return None

        url = QUrl(text)
        if not url.isValid():
            return None
        if url.isRelative():
            url = baseurl.resolved(url)
        qtutils.ensure_valid(url)
        return url
コード例 #7
0
    def requestStarted(self, job):
        """Handle a request for a glimpse: scheme.

        This method must be reimplemented by all custom URL scheme handlers.
        The request is asynchronous and does not need to be handled right away.

        Args:
            job: QWebEngineUrlRequestJob
        """
        url = job.requestUrl()

        if url.scheme() in ['chrome-error', 'chrome-extension']:
            # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-63378
            job.fail(QWebEngineUrlRequestJob.UrlInvalid)
            return

        if not self._check_initiator(job):
            return

        if job.requestMethod() != b'GET':
            job.fail(QWebEngineUrlRequestJob.RequestDenied)
            return

        assert url.scheme() == 'glimpse'

        log.misc.debug("Got request for {}".format(url.toDisplayString()))
        try:
            mimetype, data = glimpsescheme.data_for_url(url)
        except glimpsescheme.Error as e:
            errors = {
                glimpsescheme.NotFoundError:
                QWebEngineUrlRequestJob.UrlNotFound,
                glimpsescheme.UrlInvalidError:
                QWebEngineUrlRequestJob.UrlInvalid,
                glimpsescheme.RequestDeniedError:
                QWebEngineUrlRequestJob.RequestDenied,
                glimpsescheme.SchemeOSError:
                QWebEngineUrlRequestJob.UrlNotFound,
                glimpsescheme.Error: QWebEngineUrlRequestJob.RequestFailed,
            }
            exctype = type(e)
            log.misc.error("{} while handling glimpse://* URL".format(
                exctype.__name__))
            job.fail(errors[exctype])
        except glimpsescheme.Redirect as e:
            qtutils.ensure_valid(e.url)
            job.redirect(e.url)
        else:
            log.misc.debug("Returning {} data".format(mimetype))

            # We can't just use the QBuffer constructor taking a QByteArray,
            # because that somehow segfaults...
            # https://www.riverbankcomputing.com/pipermail/pyqt/2016-September/038075.html
            buf = QBuffer(parent=self)
            buf.open(QIODevice.WriteOnly)
            buf.write(data)
            buf.seek(0)
            buf.close()
            job.reply(mimetype.encode('ascii'), buf)
コード例 #8
0
 def first_item(self):
     """Return the index of the first child (non-category) in the model."""
     for row, cat in enumerate(self._categories):
         if cat.rowCount() > 0:
             parent = self.index(row, 0)
             index = self.index(0, 0, parent)
             qtutils.ensure_valid(index)
             return index
     return QModelIndex()
コード例 #9
0
 def last_item(self):
     """Return the index of the last child (non-category) in the model."""
     for row, cat in reversed(list(enumerate(self._categories))):
         childcount = cat.rowCount()
         if childcount > 0:
             parent = self.index(row, 0)
             index = self.index(childcount - 1, 0, parent)
             qtutils.ensure_valid(index)
             return index
     return QModelIndex()
コード例 #10
0
 def paintEvent(self, e):
     """Override QLabel::paintEvent to draw elided text."""
     if self._elidemode == Qt.ElideNone:
         super().paintEvent(e)
     else:
         e.accept()
         painter = QPainter(self)
         geom = self.geometry()
         qtutils.ensure_valid(geom)
         painter.drawText(0, 0, geom.width(), geom.height(),
                          int(self.alignment()), self._elided_text)
コード例 #11
0
    def load_url(self, url, newtab):
        """Open a URL, used as a slot.

        Args:
            url: The URL to open as QUrl.
            newtab: True to open URL in a new tab, False otherwise.
        """
        qtutils.ensure_valid(url)
        if newtab or self.widget.currentWidget() is None:
            self.tabopen(url, background=False)
        else:
            self.widget.currentWidget().load_url(url)
コード例 #12
0
def handler(request, operation, current_url):
    """Scheme handler for glimpse:// URLs.

    Args:
        request: QNetworkRequest to answer to.
        operation: The HTTP operation being done.
        current_url: The page we're on currently.

    Return:
        A QNetworkReply.
    """
    if operation != QNetworkAccessManager.GetOperation:
        return networkreply.ErrorNetworkReply(
            request, "Unsupported request type",
            QNetworkReply.ContentOperationNotPermittedError)

    url = request.url()

    if ((url.scheme(), url.host(), url.path()) ==
            ('glimpse', 'settings', '/set')):
        if current_url != QUrl('glimpse://settings/'):
            log.webview.warning("Blocking malicious request from {} to {}"
                                .format(current_url.toDisplayString(),
                                        url.toDisplayString()))
            return networkreply.ErrorNetworkReply(
                request, "Invalid glimpse://settings request",
                QNetworkReply.ContentAccessDenied)

    try:
        mimetype, data = glimpsescheme.data_for_url(url)
    except glimpsescheme.Error as e:
        errors = {
            glimpsescheme.NotFoundError:
                QNetworkReply.ContentNotFoundError,
            glimpsescheme.UrlInvalidError:
                QNetworkReply.ContentOperationNotPermittedError,
            glimpsescheme.RequestDeniedError:
                QNetworkReply.ContentAccessDenied,
            glimpsescheme.SchemeOSError:
                QNetworkReply.ContentNotFoundError,
            glimpsescheme.Error:
                QNetworkReply.InternalServerError,
        }
        exctype = type(e)
        log.misc.error("{} while handling glimpse://* URL".format(
            exctype.__name__))
        return networkreply.ErrorNetworkReply(request, str(e), errors[exctype])
    except glimpsescheme.Redirect as e:
        qtutils.ensure_valid(e.url)
        return networkreply.RedirectNetworkReply(e.url)

    return networkreply.FixedDataNetworkReply(request, data, mimetype)
コード例 #13
0
    def delete_url(self, url):
        """Remove all history entries with the given url.

        Args:
            url: URL string to delete.
        """
        qurl = QUrl(url)
        qtutils.ensure_valid(qurl)
        self.delete('url', self._format_url(qurl))
        self.completion.delete('url', self._format_completion_url(qurl))
        if self._last_url == url:
            self._last_url = None
        self.url_cleared.emit(qurl)
コード例 #14
0
 def sizeHint(self):
     """Return sizeHint based on the view contents."""
     idx = self.model().last_index()
     bottom = self.visualRect(idx).bottom()
     if bottom != -1:
         margins = self.contentsMargins()
         height = (bottom + margins.top() + margins.bottom() +
                   2 * self.spacing())
         size = QSize(0, height)
     else:
         size = QSize(0, 0)
     qtutils.ensure_valid(size)
     return size
コード例 #15
0
    def _draw_text(self, index):
        """Draw the text of an ItemViewItem.

        This is the main part where we differ from the original implementation
        in Qt: We use a QTextDocument to draw text.

        Args:
            index: The QModelIndex of the item to draw.
        """
        if not self._opt.text:
            return

        text_rect_ = self._style.subElementRect(
            self._style.SE_ItemViewItemText, self._opt, self._opt.widget)
        qtutils.ensure_valid(text_rect_)
        margin = self._style.pixelMetric(QStyle.PM_FocusFrameHMargin,
                                         self._opt, self._opt.widget) + 1
        # remove width padding
        text_rect = text_rect_.adjusted(margin, 0, -margin, 0)
        qtutils.ensure_valid(text_rect)
        # move text upwards a bit
        if index.parent().isValid():
            text_rect.adjust(0, -1, 0, -1)
        else:
            text_rect.adjust(0, -2, 0, -2)
        self._painter.save()
        state = self._opt.state
        if state & QStyle.State_Enabled and state & QStyle.State_Active:
            cg = QPalette.Normal
        elif state & QStyle.State_Enabled:
            cg = QPalette.Inactive
        else:
            cg = QPalette.Disabled

        if state & QStyle.State_Selected:
            self._painter.setPen(self._opt.palette.color(
                cg, QPalette.HighlightedText))
            # This is a dirty fix for the text jumping by one pixel for
            # whatever reason.
            text_rect.adjust(0, -1, 0, 0)
        else:
            self._painter.setPen(self._opt.palette.color(cg, QPalette.Text))

        if state & QStyle.State_Editing:
            self._painter.setPen(self._opt.palette.color(cg, QPalette.Text))
            self._painter.drawRect(text_rect_.adjusted(0, 0, -1, -1))

        self._painter.translate(text_rect.left(), text_rect.top())
        self._get_textdoc(index)
        self._draw_textdoc(text_rect, index.column())
        self._painter.restore()
コード例 #16
0
    def tab_url(self, idx):
        """Get the URL of the tab at the given index.

        Return:
            The tab URL as QUrl.
        """
        tab = self.widget(idx)
        if tab is None:
            url = QUrl()
        else:
            url = tab.url()
        # It's possible for url to be invalid, but the caller will handle that.
        qtutils.ensure_valid(url)
        return url
コード例 #17
0
ファイル: debug.py プロジェクト: Al-Caveman/glimpsebrowser
 def connect_log_slot(obj):
     """Helper function to connect all signals to a logging slot."""
     metaobj = obj.metaObject()
     for i in range(metaobj.methodCount()):
         meta_method = metaobj.method(i)
         qtutils.ensure_valid(meta_method)
         if meta_method.methodType() == QMetaMethod.Signal:
             name = bytes(meta_method.name()).decode('ascii')
             if name != 'destroyed':
                 signal = getattr(obj, name)
                 try:
                     signal.connect(functools.partial(
                         log_slot, obj, signal))
                 except TypeError:  # pragma: no cover
                     pass
コード例 #18
0
    def _draw_icon(self, layouts, opt, p):
        """Draw the tab icon.

        Args:
            layouts: The layouts from _tab_layout.
            opt: QStyleOption
            p: QPainter
        """
        qtutils.ensure_valid(layouts.icon)
        icon_mode = (QIcon.Normal if opt.state & QStyle.State_Enabled
                     else QIcon.Disabled)
        icon_state = (QIcon.On if opt.state & QStyle.State_Selected
                      else QIcon.Off)
        icon = opt.icon.pixmap(opt.iconSize, icon_mode, icon_state)
        self._style.drawItemPixmap(p, layouts.icon, Qt.AlignCenter, icon)
コード例 #19
0
    def _tab_layout(self, opt):
        """Compute the text/icon rect from the opt rect.

        This is based on Qt's QCommonStylePrivate::tabLayout
        (qtbase/src/widgets/styles/qcommonstyle.cpp) as we can't use the
        private implementation.

        Args:
            opt: QStyleOptionTab

        Return:
            A Layout object with two QRects.
        """
        padding = config.cache['tabs.padding']
        indicator_padding = config.cache['tabs.indicator.padding']

        text_rect = QRect(opt.rect)
        if not text_rect.isValid():
            # This happens sometimes according to crash reports, but no idea
            # why...
            return None

        text_rect.adjust(padding.left, padding.top, -padding.right,
                         -padding.bottom)

        indicator_width = config.cache['tabs.indicator.width']
        if indicator_width == 0:
            indicator_rect = QRect()
        else:
            indicator_rect = QRect(opt.rect)
            qtutils.ensure_valid(indicator_rect)
            indicator_rect.adjust(padding.left + indicator_padding.left,
                                  padding.top + indicator_padding.top,
                                  0,
                                  -(padding.bottom + indicator_padding.bottom))
            indicator_rect.setWidth(indicator_width)

            text_rect.adjust(indicator_width + indicator_padding.left +
                             indicator_padding.right, 0, 0, 0)

        icon_rect = self._get_icon_rect(opt, text_rect)
        if icon_rect.isValid():
            text_rect.adjust(
                icon_rect.width() + TabBarStyle.ICON_PADDING, 0, 0, 0)

        text_rect = self._style.visualRect(opt.direction, opt.rect, text_rect)
        return Layouts(text=text_rect, icon=icon_rect,
                       indicator=indicator_rect)
コード例 #20
0
    def get_by_qurl(self, url):
        """Look up a quickmark by QUrl, returning its name.

        Takes O(n) time, where n is the number of quickmarks.
        Use a name instead where possible.
        """
        qtutils.ensure_valid(url)
        urlstr = url.toString(QUrl.RemovePassword | QUrl.FullyEncoded)

        try:
            index = list(self.marks.values()).index(urlstr)
            key = list(self.marks.keys())[index]
        except ValueError:
            raise DoesNotExistError(
                "Quickmark for '{}' not found!".format(urlstr))
        return key
コード例 #21
0
def test_ensure_valid(obj, raising, exc_reason, exc_str):
    """Test ensure_valid.

    Args:
        obj: The object to test with.
        raising: Whether QtValueError is expected to be raised.
        exc_reason: The expected .reason attribute of the exception.
        exc_str: The expected string of the exception.
    """
    if raising:
        with pytest.raises(qtutils.QtValueError) as excinfo:
            qtutils.ensure_valid(obj)
        assert excinfo.value.reason == exc_reason
        assert str(excinfo.value) == exc_str
    else:
        qtutils.ensure_valid(obj)
コード例 #22
0
def incdec_number(url, incdec, count=1, segments=None):
    """Find a number in the url and increment or decrement it.

    Args:
        url: The current url
        incdec: Either 'increment' or 'decrement'
        count: The number to increment or decrement by
        segments: A set of URL segments to search. Valid segments are:
                  'host', 'port', 'path', 'query', 'anchor'.
                  Default: {'path', 'query'}

    Return:
        The new url with the number incremented/decremented.

    Raises IncDecError if the url contains no number.
    """
    if not url.isValid():
        raise InvalidUrlError(url)

    if segments is None:
        segments = {'path', 'query'}
    valid_segments = {'host', 'port', 'path', 'query', 'anchor'}
    if segments - valid_segments:
        extra_elements = segments - valid_segments
        raise IncDecError("Invalid segments: {}".format(
            ', '.join(extra_elements)), url)

    # Make a copy of the QUrl so we don't modify the original
    url = QUrl(url)
    # We're searching the last number so we walk the url segments backwards
    for segment, getter, setter in reversed(_URL_SEGMENTS):
        if segment not in segments:
            continue

        # Get the last number in a string not preceded by regex '%' or '%.'
        match = re.fullmatch(r'(.*\D|^)(?<!%)(?<!%.)(0*)(\d+)(.*)',
                             getter(url))
        if not match:
            continue

        setter(url, _get_incdec_value(match, incdec, url, count))
        qtutils.ensure_valid(url)

        return url

    raise IncDecError("No number found in URL!", url)
コード例 #23
0
    def delete_cur_item(self, index):
        """Delete the row at the given index."""
        qtutils.ensure_valid(index)
        parent = index.parent()
        cat = self._cat_from_idx(parent)
        assert cat, "CompletionView sent invalid index for deletion"
        if not cat.delete_func:
            raise cmdutils.CommandError("Cannot delete this item.")

        data = [
            cat.data(cat.index(index.row(), i))
            for i in range(cat.columnCount())
        ]
        cat.delete_func(data)

        self.beginRemoveRows(parent, index.row(), index.row())
        cat.removeRow(index.row(), QModelIndex())
        self.endRemoveRows()
コード例 #24
0
    def tabSizeHint(self, index: int) -> QSize:
        """Override tabSizeHint to customize qb's tab size.

        https://wiki.python.org/moin/PyQt/Customising%20tab%20bars

        Args:
            index: The index of the tab.

        Return:
            A QSize.
        """
        if self.count() == 0:
            # This happens on startup on macOS.
            # We return it directly rather than setting `size' because we don't
            # want to ensure it's valid in this special case.
            return QSize()

        height = self._minimum_tab_height()
        if self.vertical:
            confwidth = str(config.cache['tabs.width'])
            if confwidth.endswith('%'):
                main_window = objreg.get('main-window', scope='window',
                                         window=self._win_id)
                perc = int(confwidth.rstrip('%'))
                width = main_window.width() * perc / 100
            else:
                width = int(confwidth)
            size = QSize(width, height)
        else:
            if config.cache['tabs.pinned.shrink'] and self._tab_pinned(index):
                # Give pinned tabs the minimum size they need to display their
                # titles, let Qt handle scaling it down if we get too small.
                width = self.minimumTabSizeHint(index, ellipsis=False).width()
            else:
                # Request as much space as possible so we fill the tabbar, let
                # Qt shrink us down. If for some reason (tests, bugs)
                # self.width() gives 0, use a sane min of 10 px
                width = max(self.width(), 10)
                max_width = config.cache['tabs.max_width']
                if max_width > 0:
                    width = min(max_width, width)
            size = QSize(width, height)
        qtutils.ensure_valid(size)
        return size
コード例 #25
0
    def _on_data_changed(self, idx, *, webengine):
        """Called when a downloader's data changed.

        Args:
            start: The first changed index as int.
            end: The last changed index as int, or -1 for all indices.
            webengine: If given, the QtNetwork download length is added to the
                      index.
        """
        if idx == -1:
            start_index = self.index(0, 0)
            end_index = self.last_index()
        else:
            if webengine:
                idx += len(self._qtnetwork_manager.downloads)
            start_index = self.index(idx, 0)
            end_index = self.index(idx, 0)
            qtutils.ensure_valid(start_index)
            qtutils.ensure_valid(end_index)
        self.dataChanged.emit(start_index, end_index)
コード例 #26
0
 def _draw_focus_rect(self):
     """Draw the focus rectangle of an ItemViewItem."""
     state = self._opt.state
     if not state & QStyle.State_HasFocus:
         return
     o = self._opt
     o.rect = self._style.subElementRect(
         self._style.SE_ItemViewItemFocusRect, self._opt, self._opt.widget)
     o.state |= QStyle.State_KeyboardFocusChange | QStyle.State_Item
     qtutils.ensure_valid(o.rect)
     if state & QStyle.State_Enabled:
         cg = QPalette.Normal
     else:
         cg = QPalette.Disabled
     if state & QStyle.State_Selected:
         role = QPalette.Highlight
     else:
         role = QPalette.Window
     o.backgroundColor = self._opt.palette.color(cg, role)
     self._style.drawPrimitive(QStyle.PE_FrameFocusRect, o, self._painter,
                               self._opt.widget)
コード例 #27
0
ファイル: sessions.py プロジェクト: Al-Caveman/glimpsebrowser
    def _save_tab(self, tab, active):
        """Get a dict with data for a single tab.

        Args:
            tab: The WebView to save.
            active: Whether the tab is currently active.
        """
        data = {'history': []}
        if active:
            data['active'] = True
        for idx, item in enumerate(tab.history):
            qtutils.ensure_valid(item)
            item_data = self._save_tab_item(tab, idx, item)
            if item.url().scheme() == 'glimpse' and item.url().host() == 'back':
                # don't add glimpse://back to the session file
                if item_data.get('active', False) and data['history']:
                    # mark entry before glimpse://back as active
                    data['history'][-1]['active'] = True
            else:
                data['history'].append(item_data)
        return data
コード例 #28
0
    def update_for_url(self, url: QUrl) -> typing.Set[str]:
        """Update settings customized for the given tab.

        Return:
            A set of settings which actually changed.
        """
        qtutils.ensure_valid(url)
        changed_settings = set()
        for values in config.instance:
            if not values.opt.supports_pattern:
                continue

            value = values.get_for_url(url, fallback=False)

            changed = self._update_setting(values.opt.name, value)
            if changed:
                log.config.debug("Changed for {}: {} = {}".format(
                    url.toDisplayString(), values.opt.name, value))
                changed_settings.add(values.opt.name)

        return changed_settings
コード例 #29
0
def fuzzy_url(urlstr, cwd=None, relative=False, do_search=True,
              force_search=False):
    """Get a QUrl based on a user input which is URL or search term.

    Args:
        urlstr: URL to load as a string.
        cwd: The current working directory, or None.
        relative: Whether to resolve relative files.
        do_search: Whether to perform a search on non-URLs.
        force_search: Whether to force a search even if the content can be
                      interpreted as a URL or a path.

    Return:
        A target QUrl to a search page or the original URL.
    """
    urlstr = urlstr.strip()
    path = get_path_if_valid(urlstr, cwd=cwd, relative=relative,
                             check_exists=True)

    if not force_search and path is not None:
        url = QUrl.fromLocalFile(path)
    elif force_search or (do_search and not is_url(urlstr)):
        # probably a search term
        log.url.debug("URL is a fuzzy search term")
        try:
            url = _get_search_url(urlstr)
        except ValueError:  # invalid search engine
            url = qurl_from_user_input(urlstr)
    else:  # probably an address
        log.url.debug("URL is a fuzzy address")
        url = qurl_from_user_input(urlstr)
    log.url.debug("Converting fuzzy term {!r} to URL -> {}".format(
        urlstr, url.toDisplayString()))
    if do_search and config.val.url.auto_search != 'never' and urlstr:
        qtutils.ensure_valid(url)
    else:
        if not url.isValid():
            raise InvalidUrlError(url)
    return url
コード例 #30
0
    def lessThan(self, lindex, rindex):
        """Custom sorting implementation.

        Prefers all items which start with self._pattern. Other than that, uses
        normal Python string sorting.

        Args:
            lindex: The QModelIndex of the left item (*left* < right)
            rindex: The QModelIndex of the right item (left < *right*)

        Return:
            True if left < right, else False
        """
        qtutils.ensure_valid(lindex)
        qtutils.ensure_valid(rindex)

        left = self.srcmodel.data(lindex)
        right = self.srcmodel.data(rindex)

        if left is None or right is None:  # pragma: no cover
            log.completion.warning("Got unexpected None value, "
                                   "left={!r} right={!r} "
                                   "lindex={!r} rindex={!r}"
                                   .format(left, right, lindex, rindex))
            return False

        leftstart = left.startswith(self._pattern)
        rightstart = right.startswith(self._pattern)

        if leftstart and not rightstart:
            return True
        elif rightstart and not leftstart:
            return False
        elif self._sort:
            return left < right
        else:
            return False