2014-08-06 23:51:44 +02:00
|
|
|
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
2014-06-19 09:04:37 +02:00
|
|
|
|
2017-05-09 21:37:03 +02:00
|
|
|
# Copyright 2014-2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
2014-04-17 09:44:26 +02:00
|
|
|
#
|
|
|
|
# This file is part of qutebrowser.
|
|
|
|
#
|
|
|
|
# qutebrowser is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# qutebrowser is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
2014-05-27 15:56:44 +02:00
|
|
|
"""Command dispatcher for TabbedBrowser."""
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2014-04-29 18:00:22 +02:00
|
|
|
import os
|
2015-11-24 18:04:10 +01:00
|
|
|
import os.path
|
2015-06-17 07:57:38 +02:00
|
|
|
import shlex
|
2014-09-28 22:13:14 +02:00
|
|
|
import functools
|
2017-09-18 09:41:12 +02:00
|
|
|
import typing
|
2014-04-29 18:00:22 +02:00
|
|
|
|
2017-07-11 23:41:08 +02:00
|
|
|
from PyQt5.QtWidgets import QApplication, QTabBar, QDialog
|
2016-08-24 15:17:09 +02:00
|
|
|
from PyQt5.QtCore import Qt, QUrl, QEvent, QUrlQuery
|
2016-02-03 20:27:11 +01:00
|
|
|
from PyQt5.QtGui import QKeyEvent
|
2016-07-12 12:54:11 +02:00
|
|
|
from PyQt5.QtPrintSupport import QPrintDialog, QPrintPreviewDialog
|
2014-09-15 17:59:54 +02:00
|
|
|
import pygments
|
|
|
|
import pygments.lexers
|
|
|
|
import pygments.formatters
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2015-06-18 07:01:30 +02:00
|
|
|
from qutebrowser.commands import userscripts, cmdexc, cmdutils, runners
|
2017-07-04 15:33:58 +02:00
|
|
|
from qutebrowser.config import config, configdata
|
2016-09-04 21:09:15 +02:00
|
|
|
from qutebrowser.browser import (urlmarks, browsertab, inspector, navigate,
|
2016-11-01 17:50:29 +01:00
|
|
|
webelem, downloads)
|
2015-05-13 07:29:17 +02:00
|
|
|
from qutebrowser.keyinput import modeman
|
2014-12-13 17:28:50 +01:00
|
|
|
from qutebrowser.utils import (message, usertypes, log, qtutils, urlutils,
|
2017-09-18 09:41:12 +02:00
|
|
|
objreg, utils, debug)
|
2015-04-28 16:50:42 +02:00
|
|
|
from qutebrowser.utils.usertypes import KeyMode
|
2015-06-09 07:57:27 +02:00
|
|
|
from qutebrowser.misc import editor, guiprocess
|
2017-03-03 14:31:28 +01:00
|
|
|
from qutebrowser.completion.models import urlmodel, miscmodels
|
2017-09-18 09:41:12 +02:00
|
|
|
from qutebrowser.mainwindow import mainwindow
|
2014-04-17 09:44:26 +02:00
|
|
|
|
|
|
|
|
2014-05-27 15:12:43 +02:00
|
|
|
class CommandDispatcher:
|
2014-04-17 09:44:26 +02:00
|
|
|
|
|
|
|
"""Command dispatcher for TabbedBrowser.
|
|
|
|
|
|
|
|
Contains all commands which are related to the current tab.
|
|
|
|
|
|
|
|
We can't simply add these commands to BrowserTab directly and use
|
2014-05-17 22:38:07 +02:00
|
|
|
currentWidget() for TabbedBrowser.cmd because at the time
|
2014-04-17 09:44:26 +02:00
|
|
|
cmdutils.register() decorators are run, currentWidget() will return None.
|
|
|
|
|
|
|
|
Attributes:
|
2014-05-21 15:37:18 +02:00
|
|
|
_editor: The ExternalEditor object.
|
2014-09-28 22:13:14 +02:00
|
|
|
_win_id: The window ID the CommandDispatcher is associated with.
|
2015-05-18 21:31:54 +02:00
|
|
|
_tabbed_browser: The TabbedBrowser used.
|
2014-04-17 09:44:26 +02:00
|
|
|
"""
|
|
|
|
|
2015-05-18 21:31:54 +02:00
|
|
|
def __init__(self, win_id, tabbed_browser):
|
2014-09-28 22:13:14 +02:00
|
|
|
self._win_id = win_id
|
2015-05-18 21:31:54 +02:00
|
|
|
self._tabbed_browser = tabbed_browser
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2014-09-23 23:31:17 +02:00
|
|
|
def __repr__(self):
|
2014-09-26 15:48:24 +02:00
|
|
|
return utils.get_repr(self)
|
2014-09-23 23:31:17 +02:00
|
|
|
|
2017-04-24 21:08:00 +02:00
|
|
|
def _new_tabbed_browser(self, private):
|
2015-05-18 21:31:54 +02:00
|
|
|
"""Get a tabbed-browser from a new window."""
|
2017-04-24 21:08:00 +02:00
|
|
|
new_window = mainwindow.MainWindow(private=private)
|
2015-05-18 21:31:54 +02:00
|
|
|
new_window.show()
|
|
|
|
return new_window.tabbed_browser
|
2014-09-28 22:13:14 +02:00
|
|
|
|
2014-09-24 20:21:43 +02:00
|
|
|
def _count(self):
|
|
|
|
"""Convenience method to get the widget count."""
|
2015-05-18 21:31:54 +02:00
|
|
|
return self._tabbed_browser.count()
|
2014-09-24 20:21:43 +02:00
|
|
|
|
|
|
|
def _set_current_index(self, idx):
|
|
|
|
"""Convenience method to set the current widget index."""
|
2016-08-03 08:57:05 +02:00
|
|
|
cmdutils.check_overflow(idx, 'int')
|
2016-08-03 09:07:12 +02:00
|
|
|
self._tabbed_browser.setCurrentIndex(idx)
|
2014-09-24 20:21:43 +02:00
|
|
|
|
|
|
|
def _current_index(self):
|
|
|
|
"""Convenience method to get the current widget index."""
|
2015-05-18 21:31:54 +02:00
|
|
|
return self._tabbed_browser.currentIndex()
|
2014-09-24 20:21:43 +02:00
|
|
|
|
|
|
|
def _current_url(self):
|
|
|
|
"""Convenience method to get the current url."""
|
2015-01-04 20:13:25 +01:00
|
|
|
try:
|
2015-05-18 21:31:54 +02:00
|
|
|
return self._tabbed_browser.current_url()
|
2015-01-04 20:13:25 +01:00
|
|
|
except qtutils.QtValueError as e:
|
|
|
|
msg = "Current URL is invalid"
|
|
|
|
if e.reason:
|
|
|
|
msg += " ({})".format(e.reason)
|
|
|
|
msg += "!"
|
|
|
|
raise cmdexc.CommandError(msg)
|
2014-09-24 20:21:43 +02:00
|
|
|
|
2015-05-22 00:17:22 +02:00
|
|
|
def _current_title(self):
|
|
|
|
"""Convenience method to get the current title."""
|
2015-07-12 02:28:31 +02:00
|
|
|
return self._current_widget().title()
|
2015-05-22 00:17:22 +02:00
|
|
|
|
2014-09-03 11:32:56 +02:00
|
|
|
def _current_widget(self):
|
|
|
|
"""Get the currently active widget from a command."""
|
2015-05-18 21:31:54 +02:00
|
|
|
widget = self._tabbed_browser.currentWidget()
|
2014-09-03 11:32:56 +02:00
|
|
|
if widget is None:
|
|
|
|
raise cmdexc.CommandError("No WebView available yet!")
|
|
|
|
return widget
|
|
|
|
|
2016-08-02 21:26:13 +02:00
|
|
|
def _open(self, url, tab=False, background=False, window=False,
|
2017-07-01 21:13:06 +02:00
|
|
|
related=False, private=None):
|
2014-09-22 21:59:21 +02:00
|
|
|
"""Helper function to open a page.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
url: The URL to open as QUrl.
|
|
|
|
tab: Whether to open in a new tab.
|
|
|
|
background: Whether to open in the background.
|
2014-09-28 22:41:43 +02:00
|
|
|
window: Whether to open in a new window
|
2017-04-24 21:08:00 +02:00
|
|
|
private: If opening a new window, open it in private browsing mode.
|
|
|
|
If not given, inherit the current window's mode.
|
2014-09-22 21:59:21 +02:00
|
|
|
"""
|
2014-11-18 19:49:31 +01:00
|
|
|
urlutils.raise_cmdexc_if_invalid(url)
|
2015-05-18 21:31:54 +02:00
|
|
|
tabbed_browser = self._tabbed_browser
|
2017-05-10 22:44:05 +02:00
|
|
|
cmdutils.check_exclusive((tab, background, window, private), 'tbwp')
|
2017-05-29 06:37:28 +02:00
|
|
|
if window and private is None:
|
2017-04-24 21:08:00 +02:00
|
|
|
private = self._tabbed_browser.private
|
|
|
|
|
2017-05-10 07:11:44 +02:00
|
|
|
if window or private:
|
2017-04-24 21:08:00 +02:00
|
|
|
tabbed_browser = self._new_tabbed_browser(private)
|
2014-09-28 22:41:43 +02:00
|
|
|
tabbed_browser.tabopen(url)
|
2014-09-22 21:59:21 +02:00
|
|
|
elif tab:
|
2017-07-01 21:13:06 +02:00
|
|
|
tabbed_browser.tabopen(url, background=False, related=related)
|
2014-09-22 21:59:21 +02:00
|
|
|
elif background:
|
2017-07-01 21:13:06 +02:00
|
|
|
tabbed_browser.tabopen(url, background=True, related=related)
|
2014-09-22 21:59:21 +02:00
|
|
|
else:
|
|
|
|
widget = self._current_widget()
|
|
|
|
widget.openurl(url)
|
|
|
|
|
2014-09-24 19:53:31 +02:00
|
|
|
def _cntwidget(self, count=None):
|
|
|
|
"""Return a widget based on a count/idx.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
count: The tab index, or None.
|
|
|
|
|
|
|
|
Return:
|
|
|
|
The current widget if count is None.
|
|
|
|
The widget with the given tab ID if count is given.
|
|
|
|
None if no widget was found.
|
|
|
|
"""
|
|
|
|
if count is None:
|
2015-05-18 21:31:54 +02:00
|
|
|
return self._tabbed_browser.currentWidget()
|
2014-09-24 20:21:43 +02:00
|
|
|
elif 1 <= count <= self._count():
|
2014-09-24 19:53:31 +02:00
|
|
|
cmdutils.check_overflow(count + 1, 'int')
|
2015-05-18 21:31:54 +02:00
|
|
|
return self._tabbed_browser.widget(count - 1)
|
2014-09-24 19:53:31 +02:00
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
2017-06-20 12:45:36 +02:00
|
|
|
def _tab_focus_last(self, *, show_error=True):
|
2014-08-03 00:39:39 +02:00
|
|
|
"""Select the tab which was last focused."""
|
2014-09-24 06:41:51 +02:00
|
|
|
try:
|
2014-09-29 07:17:01 +02:00
|
|
|
tab = objreg.get('last-focused-tab', scope='window',
|
|
|
|
window=self._win_id)
|
2014-09-24 06:41:51 +02:00
|
|
|
except KeyError:
|
2017-06-19 15:48:17 +02:00
|
|
|
if not show_error:
|
|
|
|
return
|
2014-08-26 19:10:14 +02:00
|
|
|
raise cmdexc.CommandError("No last focused tab!")
|
2015-05-18 21:31:54 +02:00
|
|
|
idx = self._tabbed_browser.indexOf(tab)
|
2014-08-03 00:39:39 +02:00
|
|
|
if idx == -1:
|
2014-08-26 19:10:14 +02:00
|
|
|
raise cmdexc.CommandError("Last focused tab vanished!")
|
2014-09-24 20:21:43 +02:00
|
|
|
self._set_current_index(idx)
|
2014-08-03 00:39:39 +02:00
|
|
|
|
2016-10-18 12:35:37 +02:00
|
|
|
def _get_selection_override(self, prev, next_, opposite):
|
2014-10-07 20:30:31 +02:00
|
|
|
"""Helper function for tab_close to get the tab to select.
|
|
|
|
|
|
|
|
Args:
|
2016-10-18 12:35:37 +02:00
|
|
|
prev: Force selecting the tab before the current tab.
|
|
|
|
next_: Force selecting the tab after the current tab.
|
2015-03-31 20:49:29 +02:00
|
|
|
opposite: Force selecting the tab in the opposite direction of
|
2017-06-16 18:10:04 +02:00
|
|
|
what's configured in 'tabs.select_on_remove'.
|
2014-10-07 20:30:31 +02:00
|
|
|
|
|
|
|
Return:
|
|
|
|
QTabBar.SelectLeftTab, QTabBar.SelectRightTab, or None if no change
|
|
|
|
should be made.
|
|
|
|
"""
|
2016-10-18 12:35:37 +02:00
|
|
|
cmdutils.check_exclusive((prev, next_, opposite), 'pno')
|
|
|
|
if prev:
|
2014-10-07 20:30:31 +02:00
|
|
|
return QTabBar.SelectLeftTab
|
2016-10-18 12:35:37 +02:00
|
|
|
elif next_:
|
2014-10-07 20:30:31 +02:00
|
|
|
return QTabBar.SelectRightTab
|
|
|
|
elif opposite:
|
2017-06-12 13:19:56 +02:00
|
|
|
conf_selection = config.val.tabs.select_on_remove
|
2014-10-07 20:30:31 +02:00
|
|
|
if conf_selection == QTabBar.SelectLeftTab:
|
|
|
|
return QTabBar.SelectRightTab
|
|
|
|
elif conf_selection == QTabBar.SelectRightTab:
|
|
|
|
return QTabBar.SelectLeftTab
|
|
|
|
elif conf_selection == QTabBar.SelectPreviousTab:
|
|
|
|
raise cmdexc.CommandError(
|
2017-06-16 18:10:04 +02:00
|
|
|
"-o is not supported with 'tabs.select_on_remove' set to "
|
2016-10-18 12:35:37 +02:00
|
|
|
"'last-used'!")
|
2015-11-25 18:47:36 +01:00
|
|
|
else: # pragma: no cover
|
2017-07-04 15:43:54 +02:00
|
|
|
raise ValueError("Invalid select_on_remove value "
|
2015-11-25 18:47:36 +01:00
|
|
|
"{!r}!".format(conf_selection))
|
2014-10-07 20:30:31 +02:00
|
|
|
return None
|
|
|
|
|
2016-11-10 00:28:31 +01:00
|
|
|
def _tab_close(self, tab, prev=False, next_=False, opposite=False):
|
2016-11-08 04:45:07 +01:00
|
|
|
"""Helper function for tab_close be able to handle message.async.
|
2014-05-17 22:38:07 +02:00
|
|
|
|
|
|
|
Args:
|
2017-05-14 09:21:51 +02:00
|
|
|
tab: Tab object to select be closed.
|
2016-10-18 12:35:37 +02:00
|
|
|
prev: Force selecting the tab before the current tab.
|
|
|
|
next_: Force selecting the tab after the current tab.
|
2015-03-31 20:49:29 +02:00
|
|
|
opposite: Force selecting the tab in the opposite direction of
|
2017-06-16 18:10:04 +02:00
|
|
|
what's configured in 'tabs.select_on_remove'.
|
2014-05-17 22:38:07 +02:00
|
|
|
count: The tab index to close, or None
|
|
|
|
"""
|
2015-05-18 21:31:54 +02:00
|
|
|
tabbar = self._tabbed_browser.tabBar()
|
2016-10-18 12:35:37 +02:00
|
|
|
selection_override = self._get_selection_override(prev, next_,
|
2014-10-07 20:30:31 +02:00
|
|
|
opposite)
|
2016-11-09 23:50:41 +01:00
|
|
|
|
2014-10-07 20:30:31 +02:00
|
|
|
if selection_override is None:
|
2015-05-18 21:31:54 +02:00
|
|
|
self._tabbed_browser.close_tab(tab)
|
2014-10-07 20:30:31 +02:00
|
|
|
else:
|
|
|
|
old_selection_behavior = tabbar.selectionBehaviorOnRemove()
|
|
|
|
tabbar.setSelectionBehaviorOnRemove(selection_override)
|
2015-05-18 21:31:54 +02:00
|
|
|
self._tabbed_browser.close_tab(tab)
|
2014-10-07 20:30:31 +02:00
|
|
|
tabbar.setSelectionBehaviorOnRemove(old_selection_behavior)
|
2014-05-17 22:38:07 +02:00
|
|
|
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
|
|
|
@cmdutils.argument('count', count=True)
|
2016-11-16 08:18:08 +01:00
|
|
|
def tab_close(self, prev=False, next_=False, opposite=False,
|
2016-11-22 06:57:00 +01:00
|
|
|
force=False, count=None):
|
2014-05-17 22:38:07 +02:00
|
|
|
"""Close the current/[count]th tab.
|
2016-11-11 17:10:46 +01:00
|
|
|
|
2014-05-17 22:38:07 +02:00
|
|
|
Args:
|
2016-11-10 00:28:31 +01:00
|
|
|
prev: Force selecting the tab before the current tab.
|
|
|
|
next_: Force selecting the tab after the current tab.
|
2015-03-31 20:49:29 +02:00
|
|
|
opposite: Force selecting the tab in the opposite direction of
|
2017-06-16 18:10:04 +02:00
|
|
|
what's configured in 'tabs.select_on_remove'.
|
2016-11-16 08:18:08 +01:00
|
|
|
force: Avoid confirmation for pinned tabs.
|
2014-05-17 22:38:07 +02:00
|
|
|
count: The tab index to close, or None
|
|
|
|
"""
|
2014-09-24 19:53:31 +02:00
|
|
|
tab = self._cntwidget(count)
|
2014-05-17 22:38:07 +02:00
|
|
|
if tab is None:
|
|
|
|
return
|
2016-11-10 00:28:31 +01:00
|
|
|
close = functools.partial(self._tab_close, tab, prev,
|
2017-05-11 22:28:26 +02:00
|
|
|
next_, opposite)
|
2016-11-07 22:28:05 +01:00
|
|
|
|
2017-07-13 04:45:14 +02:00
|
|
|
self._tabbed_browser.tab_close_prompt_if_pinned(tab, force, close)
|
2016-11-06 15:52:23 +01:00
|
|
|
|
2016-11-07 21:25:05 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
name='tab-pin')
|
2016-11-06 15:52:23 +01:00
|
|
|
@cmdutils.argument('count', count=True)
|
2017-05-12 22:09:22 +02:00
|
|
|
def tab_pin(self, count=None):
|
2017-05-17 06:03:33 +02:00
|
|
|
"""Pin/Unpin the current/[count]th tab.
|
2016-11-07 08:02:25 +01:00
|
|
|
|
2017-07-23 01:20:27 +02:00
|
|
|
Pinning a tab shrinks it to the size of its title text.
|
2017-05-22 07:11:13 +02:00
|
|
|
Attempting to close a pinned tab will cause a confirmation,
|
|
|
|
unless --force is passed.
|
|
|
|
|
2016-11-07 08:02:25 +01:00
|
|
|
Args:
|
2016-11-07 21:12:34 +01:00
|
|
|
count: The tab index to pin or unpin, or None
|
2016-11-07 08:02:25 +01:00
|
|
|
"""
|
2016-11-06 15:52:23 +01:00
|
|
|
tab = self._cntwidget(count)
|
|
|
|
if tab is None:
|
|
|
|
return
|
|
|
|
|
2017-05-22 04:49:10 +02:00
|
|
|
to_pin = not tab.data.pinned
|
2017-06-29 06:15:49 +02:00
|
|
|
self._tabbed_browser.set_tab_pinned(tab, to_pin)
|
2016-11-06 15:52:23 +01:00
|
|
|
|
2014-09-23 21:35:08 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', name='open',
|
2016-05-10 23:04:52 +02:00
|
|
|
maxsplit=0, scope='window')
|
2016-09-20 12:52:52 +02:00
|
|
|
@cmdutils.argument('url', completion=urlmodel.url)
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.argument('count', count=True)
|
2017-07-01 21:13:06 +02:00
|
|
|
def openurl(self, url=None, related=False,
|
2017-04-24 21:08:00 +02:00
|
|
|
bg=False, tab=False, window=False, count=None, secure=False,
|
|
|
|
private=False):
|
2014-05-15 16:27:34 +02:00
|
|
|
"""Open a URL in the current/[count]th tab.
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2016-08-06 22:03:50 +02:00
|
|
|
If the URL contains newlines, each line gets opened in its own tab.
|
|
|
|
|
2014-04-17 09:44:26 +02:00
|
|
|
Args:
|
2014-09-13 00:22:27 +02:00
|
|
|
url: The URL to open.
|
|
|
|
bg: Open in a new background tab.
|
|
|
|
tab: Open in a new tab.
|
2014-09-28 22:41:43 +02:00
|
|
|
window: Open in a new window.
|
2017-07-01 21:13:06 +02:00
|
|
|
related: If opening a new tab, position the tab as related to the
|
|
|
|
current one (like clicking on a link).
|
2014-04-17 09:44:26 +02:00
|
|
|
count: The tab index to open the URL in, or None.
|
2017-03-25 08:44:36 +01:00
|
|
|
secure: Force HTTPS.
|
2017-04-24 21:08:00 +02:00
|
|
|
private: Open a new window in private browsing mode.
|
2014-04-17 09:44:26 +02:00
|
|
|
"""
|
2015-02-27 08:10:00 +01:00
|
|
|
if url is None:
|
2017-07-02 12:07:27 +02:00
|
|
|
urls = [config.val.url.default_page]
|
2015-02-27 08:10:00 +01:00
|
|
|
else:
|
2016-08-09 16:20:02 +02:00
|
|
|
urls = self._parse_url_input(url)
|
2016-11-26 12:17:23 +01:00
|
|
|
|
2016-08-06 22:03:50 +02:00
|
|
|
for i, cur_url in enumerate(urls):
|
2017-03-25 08:44:36 +01:00
|
|
|
if secure:
|
|
|
|
cur_url.setScheme('https')
|
2016-08-06 22:03:50 +02:00
|
|
|
if not window and i > 0:
|
|
|
|
tab = False
|
|
|
|
bg = True
|
2017-05-10 07:11:44 +02:00
|
|
|
|
|
|
|
if tab or bg or window or private:
|
2017-07-01 21:13:06 +02:00
|
|
|
self._open(cur_url, tab, bg, window, related=related,
|
2017-04-24 21:08:00 +02:00
|
|
|
private=private)
|
2014-09-02 21:54:07 +02:00
|
|
|
else:
|
2016-08-06 22:03:50 +02:00
|
|
|
curtab = self._cntwidget(count)
|
|
|
|
if curtab is None:
|
|
|
|
if count is None:
|
|
|
|
# We want to open a URL in the current tab, but none
|
|
|
|
# exists yet.
|
|
|
|
self._tabbed_browser.tabopen(cur_url)
|
2016-08-09 16:20:02 +02:00
|
|
|
else:
|
|
|
|
# Explicit count with a tab that doesn't exist.
|
|
|
|
return
|
2016-11-07 08:02:25 +01:00
|
|
|
elif curtab.data.pinned:
|
2016-11-06 15:52:23 +01:00
|
|
|
message.info("Tab is pinned!")
|
2016-08-06 22:03:50 +02:00
|
|
|
else:
|
|
|
|
curtab.openurl(cur_url)
|
|
|
|
|
2016-08-09 16:20:02 +02:00
|
|
|
def _parse_url(self, url, *, force_search=False):
|
2016-08-06 22:03:50 +02:00
|
|
|
"""Parse a URL or quickmark or search query.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
url: The URL to parse.
|
|
|
|
force_search: Whether to force a search even if the content can be
|
|
|
|
interpreted as a URL or a path.
|
|
|
|
|
|
|
|
Return:
|
2016-08-09 19:23:38 +02:00
|
|
|
A URL that can be opened.
|
|
|
|
"""
|
2016-08-06 22:03:50 +02:00
|
|
|
try:
|
|
|
|
return objreg.get('quickmark-manager').get(url)
|
|
|
|
except urlmarks.Error:
|
|
|
|
try:
|
|
|
|
return urlutils.fuzzy_url(url, force_search=force_search)
|
|
|
|
except urlutils.InvalidUrlError as e:
|
|
|
|
# We don't use cmdexc.CommandError here as this can be
|
|
|
|
# called async from edit_url
|
2016-09-14 20:52:32 +02:00
|
|
|
message.error(str(e))
|
2016-08-09 16:20:02 +02:00
|
|
|
return None
|
|
|
|
|
|
|
|
def _parse_url_input(self, url):
|
|
|
|
"""Parse a URL or newline-separated list of URLs.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
url: The URL or list to parse.
|
2016-08-06 22:03:50 +02:00
|
|
|
|
2016-08-09 16:20:02 +02:00
|
|
|
Return:
|
2016-08-09 19:23:38 +02:00
|
|
|
A list of URLs that can be opened.
|
|
|
|
"""
|
2017-10-17 17:19:37 +02:00
|
|
|
if isinstance(url, QUrl):
|
|
|
|
yield url
|
|
|
|
return
|
|
|
|
|
2016-08-09 16:20:02 +02:00
|
|
|
force_search = False
|
|
|
|
urllist = [u for u in url.split('\n') if u.strip()]
|
|
|
|
if (len(urllist) > 1 and not urlutils.is_url(urllist[0]) and
|
|
|
|
urlutils.get_path_if_valid(urllist[0], check_exists=True)
|
|
|
|
is None):
|
|
|
|
urllist = [url]
|
|
|
|
force_search = True
|
|
|
|
for cur_url in urllist:
|
|
|
|
parsed = self._parse_url(cur_url, force_search=force_search)
|
|
|
|
if parsed is not None:
|
|
|
|
yield parsed
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', name='reload',
|
2016-05-10 19:51:11 +02:00
|
|
|
scope='window')
|
|
|
|
@cmdutils.argument('count', count=True)
|
2015-04-20 19:29:29 +02:00
|
|
|
def reloadpage(self, force=False, count=None):
|
2014-04-17 09:44:26 +02:00
|
|
|
"""Reload the current/[count]th tab.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
count: The tab index to reload, or None.
|
2014-12-19 15:27:22 +01:00
|
|
|
force: Bypass the page cache.
|
2014-04-17 09:44:26 +02:00
|
|
|
"""
|
2014-09-24 19:53:31 +02:00
|
|
|
tab = self._cntwidget(count)
|
2014-04-17 09:44:26 +02:00
|
|
|
if tab is not None:
|
2016-06-14 13:39:51 +02:00
|
|
|
tab.reload(force=force)
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
|
|
|
@cmdutils.argument('count', count=True)
|
2015-04-20 19:29:29 +02:00
|
|
|
def stop(self, count=None):
|
2014-04-17 09:44:26 +02:00
|
|
|
"""Stop loading in the current/[count]th tab.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
count: The tab index to stop, or None.
|
|
|
|
"""
|
2014-09-24 19:53:31 +02:00
|
|
|
tab = self._cntwidget(count)
|
2014-04-17 09:44:26 +02:00
|
|
|
if tab is not None:
|
|
|
|
tab.stop()
|
|
|
|
|
2017-02-06 12:54:13 +01:00
|
|
|
def _print_preview(self, tab):
|
|
|
|
"""Show a print preview."""
|
|
|
|
def print_callback(ok):
|
|
|
|
if not ok:
|
|
|
|
message.error("Printing failed!")
|
|
|
|
|
2017-02-06 13:42:31 +01:00
|
|
|
tab.printing.check_preview_support()
|
2017-03-02 18:41:28 +01:00
|
|
|
diag = QPrintPreviewDialog(tab)
|
2017-02-06 12:54:13 +01:00
|
|
|
diag.setAttribute(Qt.WA_DeleteOnClose)
|
|
|
|
diag.setWindowFlags(diag.windowFlags() | Qt.WindowMaximizeButtonHint |
|
|
|
|
Qt.WindowMinimizeButtonHint)
|
|
|
|
diag.paintRequested.connect(functools.partial(
|
|
|
|
tab.printing.to_printer, callback=print_callback))
|
|
|
|
diag.exec_()
|
|
|
|
|
|
|
|
def _print_pdf(self, tab, filename):
|
|
|
|
"""Print to the given PDF file."""
|
2017-02-06 13:42:31 +01:00
|
|
|
tab.printing.check_pdf_support()
|
2017-02-06 12:54:13 +01:00
|
|
|
filename = os.path.expanduser(filename)
|
|
|
|
directory = os.path.dirname(filename)
|
|
|
|
if directory and not os.path.exists(directory):
|
|
|
|
os.mkdir(directory)
|
2017-02-06 13:09:39 +01:00
|
|
|
tab.printing.to_pdf(filename)
|
2017-02-06 12:54:13 +01:00
|
|
|
log.misc.debug("Print to file: {}".format(filename))
|
|
|
|
|
|
|
|
def _print(self, tab):
|
|
|
|
"""Print with a QPrintDialog."""
|
|
|
|
def print_callback(ok):
|
|
|
|
"""Called when printing finished."""
|
|
|
|
if not ok:
|
|
|
|
message.error("Printing failed!")
|
|
|
|
diag.deleteLater()
|
|
|
|
|
2017-07-11 23:41:08 +02:00
|
|
|
def do_print():
|
|
|
|
"""Called when the dialog was closed."""
|
|
|
|
tab.printing.to_printer(diag.printer(), print_callback)
|
|
|
|
|
2017-03-02 18:41:28 +01:00
|
|
|
diag = QPrintDialog(tab)
|
2017-09-20 10:39:39 +02:00
|
|
|
if utils.is_mac:
|
2017-07-11 23:41:08 +02:00
|
|
|
# For some reason we get a segfault when using open() on macOS
|
|
|
|
ret = diag.exec_()
|
|
|
|
if ret == QDialog.Accepted:
|
|
|
|
do_print()
|
|
|
|
else:
|
|
|
|
diag.open(do_print)
|
2017-02-06 12:54:13 +01:00
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', name='print',
|
2016-05-10 19:51:11 +02:00
|
|
|
scope='window')
|
|
|
|
@cmdutils.argument('count', count=True)
|
2016-07-12 02:07:54 +02:00
|
|
|
@cmdutils.argument('pdf', flag='f', metavar='file')
|
2016-07-11 03:42:43 +02:00
|
|
|
def printpage(self, preview=False, count=None, *, pdf=None):
|
2014-04-17 09:44:26 +02:00
|
|
|
"""Print the current/[count]th tab.
|
|
|
|
|
|
|
|
Args:
|
2014-09-13 00:22:27 +02:00
|
|
|
preview: Show preview instead of printing.
|
2014-04-17 09:44:26 +02:00
|
|
|
count: The tab index to print, or None.
|
2016-07-11 03:42:43 +02:00
|
|
|
pdf: The file path to write the PDF to.
|
2014-04-17 09:44:26 +02:00
|
|
|
"""
|
2014-09-24 19:53:31 +02:00
|
|
|
tab = self._cntwidget(count)
|
2016-07-12 12:54:11 +02:00
|
|
|
if tab is None:
|
|
|
|
return
|
|
|
|
|
|
|
|
try:
|
|
|
|
if pdf:
|
|
|
|
tab.printing.check_pdf_support()
|
2014-09-02 21:54:07 +02:00
|
|
|
else:
|
2016-07-12 12:54:11 +02:00
|
|
|
tab.printing.check_printer_support()
|
2017-02-06 09:51:11 +01:00
|
|
|
if preview:
|
|
|
|
tab.printing.check_preview_support()
|
2016-07-12 12:54:11 +02:00
|
|
|
except browsertab.WebTabError as e:
|
|
|
|
raise cmdexc.CommandError(e)
|
|
|
|
|
|
|
|
if preview:
|
2017-02-06 12:54:13 +01:00
|
|
|
self._print_preview(tab)
|
2016-07-12 12:54:11 +02:00
|
|
|
elif pdf:
|
2017-02-06 12:54:13 +01:00
|
|
|
self._print_pdf(tab, pdf)
|
2016-07-12 12:54:11 +02:00
|
|
|
else:
|
2017-02-06 12:54:13 +01:00
|
|
|
self._print(tab)
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2014-10-06 17:58:40 +02:00
|
|
|
def tab_clone(self, bg=False, window=False):
|
2014-09-27 23:13:11 +02:00
|
|
|
"""Duplicate the current tab.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
bg: Open in a background tab.
|
2014-10-06 17:58:40 +02:00
|
|
|
window: Open in a new window.
|
2014-09-27 23:13:11 +02:00
|
|
|
|
|
|
|
Return:
|
|
|
|
The new QWebView.
|
|
|
|
"""
|
2015-11-26 20:11:53 +01:00
|
|
|
cmdutils.check_exclusive((bg, window), 'bw')
|
2015-02-13 17:30:36 +01:00
|
|
|
curtab = self._current_widget()
|
2015-05-18 21:31:54 +02:00
|
|
|
cur_title = self._tabbed_browser.page_title(self._current_index())
|
2017-02-08 11:38:39 +01:00
|
|
|
try:
|
|
|
|
history = curtab.history.serialize()
|
|
|
|
except browsertab.WebTabError as e:
|
|
|
|
raise cmdexc.CommandError(e)
|
|
|
|
|
2015-03-08 14:54:42 +01:00
|
|
|
# The new tab could be in a new tabbed_browser (e.g. because of
|
2017-07-04 15:43:54 +02:00
|
|
|
# tabs.tabs_are_windows being set)
|
2015-05-18 21:31:54 +02:00
|
|
|
if window:
|
2017-04-24 21:08:00 +02:00
|
|
|
new_tabbed_browser = self._new_tabbed_browser(
|
|
|
|
private=self._tabbed_browser.private)
|
2015-05-18 21:31:54 +02:00
|
|
|
else:
|
|
|
|
new_tabbed_browser = self._tabbed_browser
|
2017-05-12 22:26:22 +02:00
|
|
|
newtab = new_tabbed_browser.tabopen(background=bg)
|
2015-03-08 14:54:42 +01:00
|
|
|
new_tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
|
|
|
window=newtab.win_id)
|
|
|
|
idx = new_tabbed_browser.indexOf(newtab)
|
2017-02-08 11:38:39 +01:00
|
|
|
|
2015-03-08 14:54:42 +01:00
|
|
|
new_tabbed_browser.set_page_title(idx, cur_title)
|
2017-06-13 15:18:10 +02:00
|
|
|
if config.val.tabs.favicons.show:
|
2015-10-10 12:47:02 +02:00
|
|
|
new_tabbed_browser.setTabIcon(idx, curtab.icon())
|
2017-06-12 13:19:56 +02:00
|
|
|
if config.val.tabs.tabs_are_windows:
|
2016-04-14 16:26:00 +02:00
|
|
|
new_tabbed_browser.window().setWindowIcon(curtab.icon())
|
2017-02-08 11:38:39 +01:00
|
|
|
|
2016-07-04 16:12:58 +02:00
|
|
|
newtab.data.keep_icon = True
|
2017-02-08 11:38:39 +01:00
|
|
|
newtab.history.deserialize(history)
|
2016-09-06 15:15:59 +02:00
|
|
|
newtab.zoom.set_factor(curtab.zoom.factor())
|
2017-06-29 06:15:49 +02:00
|
|
|
new_tabbed_browser.set_tab_pinned(newtab, curtab.data.pinned)
|
2016-06-14 15:42:32 +02:00
|
|
|
return newtab
|
2014-09-27 23:13:11 +02:00
|
|
|
|
2017-10-03 20:05:00 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
|
|
|
@cmdutils.argument('index', completion=miscmodels.buffer)
|
|
|
|
def tab_take(self, index):
|
|
|
|
"""Take a tab from another window.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
index: The [win_id/]index of the tab to take. Or a substring
|
|
|
|
in which case the closest match will be taken.
|
|
|
|
"""
|
|
|
|
tabbed_browser, tab = self._resolve_buffer_index(index)
|
|
|
|
|
|
|
|
if tabbed_browser is self._tabbed_browser:
|
|
|
|
raise cmdexc.CommandError("Can't take a tab from the same window")
|
|
|
|
|
|
|
|
self._open(tab.url(), tab=True)
|
|
|
|
tabbed_browser.close_tab(tab, add_undo=False)
|
|
|
|
|
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
|
|
|
@cmdutils.argument('win_id', completion=miscmodels.window)
|
2017-10-04 04:35:40 +02:00
|
|
|
def tab_give(self, win_id: int = None):
|
|
|
|
"""Give the current tab to a new or existing window if win_id given.
|
|
|
|
|
|
|
|
If no win_id is given, the tab will get detached into a new window.
|
2017-10-03 20:05:00 +02:00
|
|
|
|
|
|
|
Args:
|
|
|
|
win_id: The window ID of the window to give the current tab to.
|
|
|
|
"""
|
|
|
|
if win_id == self._win_id:
|
|
|
|
raise cmdexc.CommandError("Can't give a tab to the same window")
|
|
|
|
|
2017-10-17 09:14:59 +02:00
|
|
|
if win_id is None:
|
2017-10-04 04:35:40 +02:00
|
|
|
if self._count() < 2:
|
|
|
|
raise cmdexc.CommandError("Cannot detach from a window with "
|
|
|
|
"only one tab")
|
|
|
|
|
|
|
|
tabbed_browser = self._new_tabbed_browser(
|
2017-10-17 09:14:59 +02:00
|
|
|
private=self._tabbed_browser.private)
|
|
|
|
else:
|
2017-10-21 23:22:45 +02:00
|
|
|
if win_id not in objreg.window_registry:
|
|
|
|
raise cmdexc.CommandError(
|
|
|
|
"There's no window with id {}!".format(win_id))
|
|
|
|
|
2017-10-17 09:14:59 +02:00
|
|
|
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
|
|
|
window=win_id)
|
|
|
|
|
2017-10-03 20:05:00 +02:00
|
|
|
tabbed_browser.tabopen(self._current_url())
|
|
|
|
self._tabbed_browser.close_tab(self._current_widget(), add_undo=False)
|
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
deprecated='Use :tab-give instead!')
|
2017-10-17 10:28:28 +02:00
|
|
|
def tab_detach(self):
|
|
|
|
"""Deprecated way to detach a tab."""
|
|
|
|
self.tab_give()
|
|
|
|
|
2014-10-06 17:58:40 +02:00
|
|
|
def _back_forward(self, tab, bg, window, count, forward):
|
2014-09-27 23:13:27 +02:00
|
|
|
"""Helper function for :back/:forward."""
|
2016-06-14 13:28:28 +02:00
|
|
|
history = self._current_widget().history
|
2015-11-16 19:03:07 +01:00
|
|
|
# Catch common cases before e.g. cloning tab
|
2016-06-14 13:28:28 +02:00
|
|
|
if not forward and not history.can_go_back():
|
2014-10-06 19:47:35 +02:00
|
|
|
raise cmdexc.CommandError("At beginning of history.")
|
2016-06-14 13:28:28 +02:00
|
|
|
elif forward and not history.can_go_forward():
|
2014-10-06 19:47:35 +02:00
|
|
|
raise cmdexc.CommandError("At end of history.")
|
2015-11-16 19:03:07 +01:00
|
|
|
|
2014-10-06 17:58:40 +02:00
|
|
|
if tab or bg or window:
|
|
|
|
widget = self.tab_clone(bg, window)
|
2014-09-27 23:13:27 +02:00
|
|
|
else:
|
|
|
|
widget = self._current_widget()
|
2015-11-16 19:03:07 +01:00
|
|
|
|
2017-07-10 15:58:11 +02:00
|
|
|
try:
|
2014-09-27 23:13:27 +02:00
|
|
|
if forward:
|
2017-07-10 00:28:47 +02:00
|
|
|
widget.history.forward(count)
|
2014-09-27 23:13:27 +02:00
|
|
|
else:
|
2017-07-10 00:28:47 +02:00
|
|
|
widget.history.back(count)
|
2017-07-10 15:58:11 +02:00
|
|
|
except browsertab.WebTabError as e:
|
|
|
|
raise cmdexc.CommandError(e)
|
2014-09-27 23:13:27 +02:00
|
|
|
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
|
|
|
@cmdutils.argument('count', count=True)
|
2015-04-20 19:29:29 +02:00
|
|
|
def back(self, tab=False, bg=False, window=False, count=1):
|
2014-04-17 09:44:26 +02:00
|
|
|
"""Go back in the history of the current tab.
|
|
|
|
|
|
|
|
Args:
|
2014-09-27 23:13:27 +02:00
|
|
|
tab: Go back in a new tab.
|
|
|
|
bg: Go back in a background tab.
|
2014-10-06 17:58:40 +02:00
|
|
|
window: Go back in a new window.
|
2014-04-17 09:44:26 +02:00
|
|
|
count: How many pages to go back.
|
|
|
|
"""
|
2014-10-06 17:58:40 +02:00
|
|
|
self._back_forward(tab, bg, window, count, forward=False)
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
|
|
|
@cmdutils.argument('count', count=True)
|
2015-04-20 19:29:29 +02:00
|
|
|
def forward(self, tab=False, bg=False, window=False, count=1):
|
2014-04-17 09:44:26 +02:00
|
|
|
"""Go forward in the history of the current tab.
|
|
|
|
|
|
|
|
Args:
|
2014-09-27 23:13:27 +02:00
|
|
|
tab: Go forward in a new tab.
|
2014-10-06 17:58:40 +02:00
|
|
|
bg: Go forward in a background tab.
|
|
|
|
window: Go forward in a new window.
|
2014-04-17 09:44:26 +02:00
|
|
|
count: How many pages to go forward.
|
|
|
|
"""
|
2014-10-06 17:58:40 +02:00
|
|
|
self._back_forward(tab, bg, window, count, forward=True)
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2016-08-08 14:09:28 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2016-05-11 08:01:25 +02:00
|
|
|
@cmdutils.argument('where', choices=['prev', 'next', 'up', 'increment',
|
|
|
|
'decrement'])
|
2016-08-16 03:09:15 +02:00
|
|
|
@cmdutils.argument('count', count=True)
|
|
|
|
def navigate(self, where: str, tab=False, bg=False, window=False, count=1):
|
2014-09-22 21:13:42 +02:00
|
|
|
"""Open typical prev/next links or navigate using the URL path.
|
2014-08-03 00:33:39 +02:00
|
|
|
|
2014-09-22 20:57:50 +02:00
|
|
|
This tries to automatically click on typical _Previous Page_ or
|
|
|
|
_Next Page_ links using some heuristics.
|
2014-09-02 21:54:07 +02:00
|
|
|
|
2014-09-22 21:13:42 +02:00
|
|
|
Alternatively it can navigate by changing the current URL.
|
|
|
|
|
2014-09-02 21:54:07 +02:00
|
|
|
Args:
|
2014-09-22 20:57:50 +02:00
|
|
|
where: What to open.
|
2014-05-01 15:27:32 +02:00
|
|
|
|
2014-09-22 20:57:50 +02:00
|
|
|
- `prev`: Open a _previous_ link.
|
|
|
|
- `next`: Open a _next_ link.
|
2014-09-22 21:13:42 +02:00
|
|
|
- `up`: Go up a level in the current URL.
|
2014-09-22 21:51:09 +02:00
|
|
|
- `increment`: Increment the last number in the URL.
|
|
|
|
- `decrement`: Decrement the last number in the URL.
|
2014-08-03 00:33:39 +02:00
|
|
|
|
2014-09-13 00:22:27 +02:00
|
|
|
tab: Open in a new tab.
|
2014-10-06 17:58:40 +02:00
|
|
|
bg: Open in a background tab.
|
|
|
|
window: Open in a new window.
|
2016-08-16 11:36:55 +02:00
|
|
|
count: For `increment` and `decrement`, the number to change the
|
|
|
|
URL by. For `up`, the number of levels to go up in the URL.
|
2014-08-03 00:33:39 +02:00
|
|
|
"""
|
2016-04-12 04:36:37 +02:00
|
|
|
# save the pre-jump position in the special ' mark
|
|
|
|
self.set_mark("'")
|
|
|
|
|
2014-10-07 20:39:02 +02:00
|
|
|
cmdutils.check_exclusive((tab, bg, window), 'tbw')
|
2014-09-22 21:08:11 +02:00
|
|
|
widget = self._current_widget()
|
2016-04-25 19:05:20 +02:00
|
|
|
url = self._current_url().adjusted(QUrl.RemoveFragment)
|
2016-06-15 14:07:05 +02:00
|
|
|
|
2016-08-05 17:12:44 +02:00
|
|
|
handlers = {
|
|
|
|
'prev': functools.partial(navigate.prevnext, prev=True),
|
|
|
|
'next': functools.partial(navigate.prevnext, prev=False),
|
|
|
|
'up': navigate.path_up,
|
|
|
|
'decrement': functools.partial(navigate.incdec,
|
|
|
|
inc_or_dec='decrement'),
|
|
|
|
'increment': functools.partial(navigate.incdec,
|
|
|
|
inc_or_dec='increment'),
|
|
|
|
}
|
|
|
|
|
|
|
|
try:
|
|
|
|
if where in ['prev', 'next']:
|
|
|
|
handler = handlers[where]
|
|
|
|
handler(browsertab=widget, win_id=self._win_id, baseurl=url,
|
|
|
|
tab=tab, background=bg, window=window)
|
|
|
|
elif where in ['up', 'increment', 'decrement']:
|
2016-08-16 03:09:15 +02:00
|
|
|
new_url = handlers[where](url, count)
|
2017-07-01 21:13:06 +02:00
|
|
|
self._open(new_url, tab, bg, window, related=True)
|
2016-08-05 17:12:44 +02:00
|
|
|
else: # pragma: no cover
|
|
|
|
raise ValueError("Got called with invalid value {} for "
|
|
|
|
"`where'.".format(where))
|
|
|
|
except navigate.Error as e:
|
|
|
|
raise cmdexc.CommandError(e)
|
2014-05-01 15:27:32 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.argument('count', count=True)
|
2016-05-10 22:03:10 +02:00
|
|
|
def scroll_px(self, dx: int, dy: int, count=1):
|
2015-05-15 18:59:46 +02:00
|
|
|
"""Scroll the current tab by 'count * dx/dy' pixels.
|
2014-04-17 09:44:26 +02:00
|
|
|
|
|
|
|
Args:
|
|
|
|
dx: How much to scroll in x-direction.
|
2016-04-12 04:31:49 +02:00
|
|
|
dy: How much to scroll in y-direction.
|
2014-04-17 09:44:26 +02:00
|
|
|
count: multiplier
|
|
|
|
"""
|
2014-09-02 21:54:07 +02:00
|
|
|
dx *= count
|
|
|
|
dy *= count
|
2014-05-14 23:29:18 +02:00
|
|
|
cmdutils.check_overflow(dx, 'int')
|
|
|
|
cmdutils.check_overflow(dy, 'int')
|
2016-07-26 13:19:07 +02:00
|
|
|
self._current_widget().scroller.delta(dx, dy)
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.argument('count', count=True)
|
2016-05-11 08:01:25 +02:00
|
|
|
def scroll(self, direction: typing.Union[str, int], count=1):
|
2015-05-15 18:59:46 +02:00
|
|
|
"""Scroll the current tab in the given direction.
|
|
|
|
|
2017-03-14 16:58:33 +01:00
|
|
|
Note you can use `:run-with-count` to have a keybinding with a bigger
|
|
|
|
scroll increment.
|
|
|
|
|
2015-05-15 18:59:46 +02:00
|
|
|
Args:
|
|
|
|
direction: In which direction to scroll
|
|
|
|
(up/down/left/right/top/bottom).
|
|
|
|
count: multiplier
|
|
|
|
"""
|
2016-06-14 17:32:36 +02:00
|
|
|
tab = self._current_widget()
|
|
|
|
funcs = {
|
2016-07-26 13:19:07 +02:00
|
|
|
'up': tab.scroller.up,
|
|
|
|
'down': tab.scroller.down,
|
|
|
|
'left': tab.scroller.left,
|
|
|
|
'right': tab.scroller.right,
|
|
|
|
'top': tab.scroller.top,
|
|
|
|
'bottom': tab.scroller.bottom,
|
|
|
|
'page-up': tab.scroller.page_up,
|
|
|
|
'page-down': tab.scroller.page_down,
|
2015-05-15 18:59:46 +02:00
|
|
|
}
|
|
|
|
try:
|
2016-06-14 17:32:36 +02:00
|
|
|
func = funcs[direction]
|
2015-05-15 18:59:46 +02:00
|
|
|
except KeyError:
|
2016-06-14 17:32:36 +02:00
|
|
|
expected_values = ', '.join(sorted(funcs))
|
2015-05-15 18:59:46 +02:00
|
|
|
raise cmdexc.CommandError("Invalid value {!r} for direction - "
|
|
|
|
"expected one of: {}".format(
|
2015-11-03 22:36:08 +01:00
|
|
|
direction, expected_values))
|
2015-05-15 18:59:46 +02:00
|
|
|
|
2016-07-12 22:05:32 +02:00
|
|
|
if direction in ['top', 'bottom']:
|
2016-06-14 17:32:36 +02:00
|
|
|
func()
|
|
|
|
else:
|
|
|
|
func(count=count)
|
2015-05-15 18:59:46 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.argument('count', count=True)
|
2016-05-10 18:58:23 +02:00
|
|
|
@cmdutils.argument('horizontal', flag='x')
|
2017-10-03 22:44:21 +02:00
|
|
|
def scroll_to_perc(self, perc: float = None, horizontal=False, count=None):
|
2014-09-02 21:54:07 +02:00
|
|
|
"""Scroll to a specific percentage of the page.
|
2014-08-03 00:33:39 +02:00
|
|
|
|
|
|
|
The percentage can be given either as argument or as count.
|
|
|
|
If no percentage is given, the page is scrolled to the end.
|
2014-04-17 09:44:26 +02:00
|
|
|
|
|
|
|
Args:
|
|
|
|
perc: Percentage to scroll.
|
2014-09-13 00:22:27 +02:00
|
|
|
horizontal: Scroll horizontally instead of vertically.
|
2014-04-17 09:44:26 +02:00
|
|
|
count: Percentage to scroll.
|
|
|
|
"""
|
2016-04-12 04:36:37 +02:00
|
|
|
# save the pre-jump position in the special ' mark
|
|
|
|
self.set_mark("'")
|
|
|
|
|
2015-11-16 19:05:05 +01:00
|
|
|
if perc is None and count is None:
|
|
|
|
perc = 100
|
2016-06-29 00:12:44 +02:00
|
|
|
elif count is not None:
|
2015-11-16 19:05:05 +01:00
|
|
|
perc = count
|
|
|
|
|
2016-06-14 17:32:36 +02:00
|
|
|
if horizontal:
|
|
|
|
x = perc
|
|
|
|
y = None
|
2015-11-16 19:05:05 +01:00
|
|
|
else:
|
2016-06-14 17:32:36 +02:00
|
|
|
x = None
|
|
|
|
y = perc
|
|
|
|
|
2016-07-26 13:19:07 +02:00
|
|
|
self._current_widget().scroller.to_perc(x, y)
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.argument('count', count=True)
|
2016-05-11 08:01:25 +02:00
|
|
|
@cmdutils.argument('top_navigate', metavar='ACTION',
|
|
|
|
choices=('prev', 'decrement'))
|
|
|
|
@cmdutils.argument('bottom_navigate', metavar='ACTION',
|
|
|
|
choices=('next', 'increment'))
|
2016-05-10 22:03:10 +02:00
|
|
|
def scroll_page(self, x: float, y: float, *,
|
2017-04-13 18:00:36 +02:00
|
|
|
top_navigate: str = None, bottom_navigate: str = None,
|
2015-05-29 21:18:44 +02:00
|
|
|
count=1):
|
2014-04-17 09:44:26 +02:00
|
|
|
"""Scroll the frame page-wise.
|
|
|
|
|
|
|
|
Args:
|
2014-08-03 00:33:39 +02:00
|
|
|
x: How many pages to scroll to the right.
|
|
|
|
y: How many pages to scroll down.
|
2015-05-31 15:02:09 +02:00
|
|
|
bottom_navigate: :navigate action (next, increment) to run when
|
|
|
|
scrolling down at the bottom of the page.
|
|
|
|
top_navigate: :navigate action (prev, decrement) to run when
|
|
|
|
scrolling up at the top of the page.
|
2014-04-17 09:44:26 +02:00
|
|
|
count: multiplier
|
|
|
|
"""
|
2016-06-14 17:32:36 +02:00
|
|
|
tab = self._current_widget()
|
2016-07-08 10:05:46 +02:00
|
|
|
if not tab.url().isValid():
|
2017-02-05 00:13:11 +01:00
|
|
|
# See https://github.com/qutebrowser/qutebrowser/issues/701
|
2015-05-29 23:49:48 +02:00
|
|
|
return
|
|
|
|
|
2016-07-26 13:19:07 +02:00
|
|
|
if bottom_navigate is not None and tab.scroller.at_bottom():
|
2015-05-29 21:18:44 +02:00
|
|
|
self.navigate(bottom_navigate)
|
|
|
|
return
|
2016-07-26 13:19:07 +02:00
|
|
|
elif top_navigate is not None and tab.scroller.at_top():
|
2015-05-29 21:18:44 +02:00
|
|
|
self.navigate(top_navigate)
|
2015-05-29 18:35:15 +02:00
|
|
|
return
|
|
|
|
|
2016-07-05 11:14:04 +02:00
|
|
|
try:
|
2016-07-26 13:19:07 +02:00
|
|
|
tab.scroller.delta_page(count * x, count * y)
|
2016-07-05 11:14:04 +02:00
|
|
|
except OverflowError:
|
|
|
|
raise cmdexc.CommandError(
|
|
|
|
"Numeric argument is too large for internal int "
|
|
|
|
"representation.")
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2016-09-06 18:16:32 +02:00
|
|
|
def _yank_url(self, what):
|
|
|
|
"""Helper method for yank() to get the URL to copy."""
|
|
|
|
assert what in ['url', 'pretty-url'], what
|
|
|
|
flags = QUrl.RemovePassword
|
2016-09-22 14:25:48 +02:00
|
|
|
if what == 'pretty-url':
|
|
|
|
flags |= QUrl.DecodeReserved
|
|
|
|
else:
|
2016-09-06 18:16:32 +02:00
|
|
|
flags |= QUrl.FullyEncoded
|
|
|
|
url = QUrl(self._current_url())
|
2016-09-29 11:02:30 +02:00
|
|
|
url_query = QUrlQuery()
|
2017-11-06 08:50:03 +01:00
|
|
|
url_query_str = urlutils.query_string(url)
|
2016-09-29 17:45:12 +02:00
|
|
|
if '&' not in url_query_str and ';' in url_query_str:
|
2016-09-29 11:02:30 +02:00
|
|
|
url_query.setQueryDelimiters('=', ';')
|
2016-09-29 17:45:12 +02:00
|
|
|
url_query.setQuery(url_query_str)
|
2016-09-06 18:16:32 +02:00
|
|
|
for key in dict(url_query.queryItems()):
|
2017-07-02 12:07:27 +02:00
|
|
|
if key in config.val.url.yank_ignored_parameters:
|
2016-09-06 18:16:32 +02:00
|
|
|
url_query.removeQueryItem(key)
|
|
|
|
url.setQuery(url_query)
|
|
|
|
return url.toString(flags)
|
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2016-08-06 03:55:51 +02:00
|
|
|
@cmdutils.argument('what', choices=['selection', 'url', 'pretty-url',
|
|
|
|
'title', 'domain'])
|
|
|
|
def yank(self, what='url', sel=False, keep=False):
|
|
|
|
"""Yank something to the clipboard or primary selection.
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2014-05-18 08:14:11 +02:00
|
|
|
Args:
|
2016-08-06 03:55:51 +02:00
|
|
|
what: What to yank.
|
2016-08-07 02:08:04 +02:00
|
|
|
|
|
|
|
- `url`: The current URL.
|
|
|
|
- `pretty-url`: The URL in pretty decoded form.
|
|
|
|
- `title`: The current page's title.
|
|
|
|
- `domain`: The current scheme, domain, and port number.
|
|
|
|
- `selection`: The selection under the cursor.
|
|
|
|
|
2014-09-13 00:22:27 +02:00
|
|
|
sel: Use the primary selection instead of the clipboard.
|
2016-08-07 02:08:04 +02:00
|
|
|
keep: Stay in visual mode after yanking the selection.
|
2014-05-18 08:14:11 +02:00
|
|
|
"""
|
2016-08-06 03:55:51 +02:00
|
|
|
if what == 'title':
|
2015-05-18 21:31:54 +02:00
|
|
|
s = self._tabbed_browser.page_title(self._current_index())
|
2016-08-06 03:55:51 +02:00
|
|
|
elif what == 'domain':
|
2015-06-04 13:20:39 +02:00
|
|
|
port = self._current_url().port()
|
|
|
|
s = '{}://{}{}'.format(self._current_url().scheme(),
|
|
|
|
self._current_url().host(),
|
|
|
|
':' + str(port) if port > -1 else '')
|
2016-08-06 03:55:51 +02:00
|
|
|
elif what in ['url', 'pretty-url']:
|
2016-09-06 18:16:32 +02:00
|
|
|
s = self._yank_url(what)
|
2016-08-07 02:08:04 +02:00
|
|
|
what = 'URL' # For printing
|
2016-08-06 03:55:51 +02:00
|
|
|
elif what == 'selection':
|
|
|
|
caret = self._current_widget().caret
|
|
|
|
s = caret.selection()
|
2016-08-07 02:15:51 +02:00
|
|
|
if not caret.has_selection() or not s:
|
2016-09-14 20:52:32 +02:00
|
|
|
message.info("Nothing to yank")
|
2016-08-06 03:55:51 +02:00
|
|
|
return
|
2016-08-07 02:15:51 +02:00
|
|
|
else: # pragma: no cover
|
|
|
|
raise ValueError("Invalid value {!r} for `what'.".format(what))
|
2016-02-03 20:27:11 +01:00
|
|
|
|
2016-05-08 22:06:00 +02:00
|
|
|
if sel and utils.supports_selection():
|
2014-05-19 11:56:51 +02:00
|
|
|
target = "primary selection"
|
|
|
|
else:
|
2016-02-03 20:27:11 +01:00
|
|
|
sel = False
|
2014-05-19 11:56:51 +02:00
|
|
|
target = "clipboard"
|
2016-02-03 20:27:11 +01:00
|
|
|
|
|
|
|
utils.set_clipboard(s, selection=sel)
|
2016-08-06 03:55:51 +02:00
|
|
|
if what != 'selection':
|
2016-09-14 20:52:32 +02:00
|
|
|
message.info("Yanked {} to {}: {}".format(what, target, s))
|
2016-08-06 03:55:51 +02:00
|
|
|
else:
|
2016-09-14 20:52:32 +02:00
|
|
|
message.info("{} {} yanked to {}".format(
|
2016-08-06 03:55:51 +02:00
|
|
|
len(s), "char" if len(s) == 1 else "chars", target))
|
|
|
|
if not keep:
|
2016-11-10 07:19:45 +01:00
|
|
|
modeman.leave(self._win_id, KeyMode.caret, "yank selected",
|
|
|
|
maybe=True)
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
|
|
|
@cmdutils.argument('count', count=True)
|
2015-04-20 19:29:29 +02:00
|
|
|
def zoom_in(self, count=1):
|
2014-05-15 16:27:34 +02:00
|
|
|
"""Increase the zoom level for the current tab.
|
2014-04-17 09:44:26 +02:00
|
|
|
|
|
|
|
Args:
|
2014-08-03 00:33:39 +02:00
|
|
|
count: How many steps to zoom in.
|
2014-04-17 09:44:26 +02:00
|
|
|
"""
|
2014-09-03 11:32:56 +02:00
|
|
|
tab = self._current_widget()
|
2015-06-24 23:06:55 +02:00
|
|
|
try:
|
2016-06-15 13:02:24 +02:00
|
|
|
perc = tab.zoom.offset(count)
|
2015-06-24 23:06:55 +02:00
|
|
|
except ValueError as e:
|
|
|
|
raise cmdexc.CommandError(e)
|
2017-06-19 13:16:32 +02:00
|
|
|
message.info("Zoom level: {}%".format(int(perc)), replace=True)
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
|
|
|
@cmdutils.argument('count', count=True)
|
2015-04-20 19:29:29 +02:00
|
|
|
def zoom_out(self, count=1):
|
2014-05-15 16:27:34 +02:00
|
|
|
"""Decrease the zoom level for the current tab.
|
2014-04-17 09:44:26 +02:00
|
|
|
|
|
|
|
Args:
|
2014-08-03 00:33:39 +02:00
|
|
|
count: How many steps to zoom out.
|
2014-04-17 09:44:26 +02:00
|
|
|
"""
|
2014-09-03 11:32:56 +02:00
|
|
|
tab = self._current_widget()
|
2015-06-24 23:06:55 +02:00
|
|
|
try:
|
2016-06-15 13:02:24 +02:00
|
|
|
perc = tab.zoom.offset(-count)
|
2015-06-24 23:06:55 +02:00
|
|
|
except ValueError as e:
|
|
|
|
raise cmdexc.CommandError(e)
|
2017-06-19 13:16:32 +02:00
|
|
|
message.info("Zoom level: {}%".format(int(perc)), replace=True)
|
2014-04-29 18:00:22 +02:00
|
|
|
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
|
|
|
@cmdutils.argument('count', count=True)
|
2017-06-26 21:51:35 +02:00
|
|
|
def zoom(self, zoom=None, count=None):
|
2014-08-03 00:33:39 +02:00
|
|
|
"""Set the zoom level for the current tab.
|
|
|
|
|
2016-08-02 17:29:35 +02:00
|
|
|
The zoom can be given as argument or as [count]. If neither is
|
|
|
|
given, the zoom is set to the default zoom. If both are given,
|
|
|
|
use [count].
|
2014-05-09 14:20:26 +02:00
|
|
|
|
|
|
|
Args:
|
2014-08-03 00:33:39 +02:00
|
|
|
zoom: The zoom percentage to set.
|
|
|
|
count: The zoom percentage to set.
|
2014-05-09 14:20:26 +02:00
|
|
|
"""
|
2017-06-26 21:51:35 +02:00
|
|
|
if zoom is not None:
|
|
|
|
try:
|
|
|
|
zoom = int(zoom.rstrip('%'))
|
|
|
|
except ValueError:
|
|
|
|
raise cmdexc.CommandError("zoom: Invalid int value {}"
|
|
|
|
.format(zoom))
|
|
|
|
|
2016-08-02 17:29:35 +02:00
|
|
|
level = count if count is not None else zoom
|
|
|
|
if level is None:
|
2017-06-13 12:37:32 +02:00
|
|
|
level = config.val.zoom.default
|
2014-09-03 11:32:56 +02:00
|
|
|
tab = self._current_widget()
|
2015-06-24 23:06:55 +02:00
|
|
|
|
|
|
|
try:
|
2016-06-15 13:02:24 +02:00
|
|
|
tab.zoom.set_factor(float(level) / 100)
|
|
|
|
except ValueError:
|
|
|
|
raise cmdexc.CommandError("Can't zoom {}%!".format(level))
|
2017-06-19 13:16:32 +02:00
|
|
|
message.info("Zoom level: {}%".format(int(level)), replace=True)
|
2014-05-09 14:20:26 +02:00
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2017-05-11 23:30:45 +02:00
|
|
|
def tab_only(self, prev=False, next_=False, force=False):
|
2014-10-07 20:56:47 +02:00
|
|
|
"""Close all tabs except for the current one.
|
|
|
|
|
|
|
|
Args:
|
2016-10-18 12:55:58 +02:00
|
|
|
prev: Keep tabs before the current.
|
|
|
|
next_: Keep tabs after the current.
|
2017-05-11 23:30:45 +02:00
|
|
|
force: Avoid confirmation for pinned tabs.
|
2014-10-07 20:56:47 +02:00
|
|
|
"""
|
2016-10-18 12:55:58 +02:00
|
|
|
cmdutils.check_exclusive((prev, next_), 'pn')
|
2015-05-18 21:31:54 +02:00
|
|
|
cur_idx = self._tabbed_browser.currentIndex()
|
2014-10-07 20:56:47 +02:00
|
|
|
assert cur_idx != -1
|
|
|
|
|
2017-05-13 01:56:07 +02:00
|
|
|
def _to_close(i):
|
2017-05-11 23:30:45 +02:00
|
|
|
"""Helper method to check if a tab should be closed or not."""
|
2017-05-13 01:56:07 +02:00
|
|
|
return not (i == cur_idx or
|
|
|
|
(prev and i < cur_idx) or
|
|
|
|
(next_ and i > cur_idx))
|
2017-05-11 23:30:45 +02:00
|
|
|
|
|
|
|
# Check to see if we are closing any pinned tabs
|
|
|
|
if not force:
|
|
|
|
for i, tab in enumerate(self._tabbed_browser.widgets()):
|
|
|
|
if _to_close(i) and tab.data.pinned:
|
2017-07-13 04:45:14 +02:00
|
|
|
self._tabbed_browser.tab_close_prompt_if_pinned(
|
|
|
|
tab,
|
|
|
|
force,
|
2017-05-13 01:56:07 +02:00
|
|
|
lambda: self.tab_only(
|
|
|
|
prev=prev, next_=next_, force=True))
|
2017-05-11 23:30:45 +02:00
|
|
|
return
|
|
|
|
|
2017-11-06 19:32:10 +01:00
|
|
|
first_tab = True
|
2015-05-18 21:31:54 +02:00
|
|
|
for i, tab in enumerate(self._tabbed_browser.widgets()):
|
2017-05-11 23:30:45 +02:00
|
|
|
if _to_close(i):
|
2017-11-06 19:32:10 +01:00
|
|
|
self._tabbed_browser.close_tab(tab, new_undo=first_tab)
|
|
|
|
first_tab = False
|
2014-05-17 22:38:07 +02:00
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2014-05-17 23:22:10 +02:00
|
|
|
def undo(self):
|
2017-11-06 19:32:10 +01:00
|
|
|
"""Re-open the last closed tab or tabs."""
|
2014-09-27 22:56:50 +02:00
|
|
|
try:
|
2015-05-18 21:31:54 +02:00
|
|
|
self._tabbed_browser.undo()
|
2014-09-27 22:56:50 +02:00
|
|
|
except IndexError:
|
2014-08-26 19:10:14 +02:00
|
|
|
raise cmdexc.CommandError("Nothing to undo!")
|
2014-05-17 22:38:07 +02:00
|
|
|
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
|
|
|
@cmdutils.argument('count', count=True)
|
2015-04-20 19:29:29 +02:00
|
|
|
def tab_prev(self, count=1):
|
2014-08-03 00:33:39 +02:00
|
|
|
"""Switch to the previous tab, or switch [count] tabs back.
|
2014-05-17 22:38:07 +02:00
|
|
|
|
|
|
|
Args:
|
|
|
|
count: How many tabs to switch back.
|
|
|
|
"""
|
2016-04-25 18:53:35 +02:00
|
|
|
if self._count() == 0:
|
|
|
|
# Running :tab-prev after last tab was closed
|
2017-02-05 00:13:11 +01:00
|
|
|
# See https://github.com/qutebrowser/qutebrowser/issues/1448
|
2016-04-25 18:53:35 +02:00
|
|
|
return
|
2014-09-24 20:21:43 +02:00
|
|
|
newidx = self._current_index() - count
|
2014-05-17 22:38:07 +02:00
|
|
|
if newidx >= 0:
|
2014-09-24 20:21:43 +02:00
|
|
|
self._set_current_index(newidx)
|
2017-06-12 13:19:56 +02:00
|
|
|
elif config.val.tabs.wrap:
|
2014-09-24 20:21:43 +02:00
|
|
|
self._set_current_index(newidx % self._count())
|
2014-05-17 22:38:07 +02:00
|
|
|
else:
|
2014-08-26 19:10:14 +02:00
|
|
|
raise cmdexc.CommandError("First tab")
|
2014-05-17 22:38:07 +02:00
|
|
|
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
|
|
|
@cmdutils.argument('count', count=True)
|
2015-04-20 19:29:29 +02:00
|
|
|
def tab_next(self, count=1):
|
2014-08-03 00:33:39 +02:00
|
|
|
"""Switch to the next tab, or switch [count] tabs forward.
|
2014-05-17 22:38:07 +02:00
|
|
|
|
|
|
|
Args:
|
|
|
|
count: How many tabs to switch forward.
|
|
|
|
"""
|
2016-04-25 18:53:35 +02:00
|
|
|
if self._count() == 0:
|
|
|
|
# Running :tab-next after last tab was closed
|
2017-02-05 00:13:11 +01:00
|
|
|
# See https://github.com/qutebrowser/qutebrowser/issues/1448
|
2016-04-25 18:53:35 +02:00
|
|
|
return
|
2014-09-24 20:21:43 +02:00
|
|
|
newidx = self._current_index() + count
|
|
|
|
if newidx < self._count():
|
|
|
|
self._set_current_index(newidx)
|
2017-06-12 13:19:56 +02:00
|
|
|
elif config.val.tabs.wrap:
|
2014-09-24 20:21:43 +02:00
|
|
|
self._set_current_index(newidx % self._count())
|
2014-05-17 22:38:07 +02:00
|
|
|
else:
|
2014-08-26 19:10:14 +02:00
|
|
|
raise cmdexc.CommandError("Last tab")
|
2014-05-17 22:38:07 +02:00
|
|
|
|
2017-10-03 19:33:23 +02:00
|
|
|
def _resolve_buffer_index(self, index):
|
2017-10-17 09:33:20 +02:00
|
|
|
"""Resolve a buffer index to the tabbedbrowser and tab.
|
2016-02-18 07:01:30 +01:00
|
|
|
|
|
|
|
Args:
|
2017-10-03 19:33:23 +02:00
|
|
|
index: The [win_id/]index of the tab to be selected. Or a substring
|
2016-02-18 07:01:30 +01:00
|
|
|
in which case the closest match will be focused.
|
|
|
|
"""
|
2017-10-03 19:33:23 +02:00
|
|
|
index_parts = index.split('/', 1)
|
2016-02-18 07:01:30 +01:00
|
|
|
|
2017-10-03 19:33:23 +02:00
|
|
|
try:
|
|
|
|
for part in index_parts:
|
|
|
|
int(part)
|
|
|
|
except ValueError:
|
|
|
|
model = miscmodels.buffer()
|
|
|
|
model.set_pattern(index)
|
|
|
|
if model.count() > 0:
|
|
|
|
index = model.data(model.first_item())
|
|
|
|
index_parts = index.split('/', 1)
|
|
|
|
else:
|
|
|
|
raise cmdexc.CommandError(
|
|
|
|
"No matching tab for: {}".format(index))
|
2016-02-18 07:01:30 +01:00
|
|
|
|
|
|
|
if len(index_parts) == 2:
|
|
|
|
win_id = int(index_parts[0])
|
|
|
|
idx = int(index_parts[1])
|
|
|
|
elif len(index_parts) == 1:
|
|
|
|
idx = int(index_parts[0])
|
|
|
|
active_win = objreg.get('app').activeWindow()
|
|
|
|
if active_win is None:
|
|
|
|
# Not sure how you enter a command without an active window...
|
|
|
|
raise cmdexc.CommandError(
|
|
|
|
"No window specified and couldn't find active window!")
|
|
|
|
win_id = active_win.win_id
|
|
|
|
|
|
|
|
if win_id not in objreg.window_registry:
|
|
|
|
raise cmdexc.CommandError(
|
|
|
|
"There's no window with id {}!".format(win_id))
|
|
|
|
|
|
|
|
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
|
|
|
window=win_id)
|
|
|
|
if not 0 < idx <= tabbed_browser.count():
|
|
|
|
raise cmdexc.CommandError(
|
|
|
|
"There's no tab with index {}!".format(idx))
|
|
|
|
|
2017-10-03 19:33:23 +02:00
|
|
|
return (tabbed_browser, tabbed_browser.widget(idx-1))
|
|
|
|
|
2017-11-05 20:41:43 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
maxsplit=0)
|
2017-10-03 19:33:23 +02:00
|
|
|
@cmdutils.argument('index', completion=miscmodels.buffer)
|
|
|
|
@cmdutils.argument('count', count=True)
|
|
|
|
def buffer(self, index=None, count=None):
|
|
|
|
"""Select tab by index or url/title best match.
|
|
|
|
|
|
|
|
Focuses window if necessary when index is given. If both index and
|
|
|
|
count are given, use count.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
index: The [win_id/]index of the tab to focus. Or a substring
|
|
|
|
in which case the closest match will be focused.
|
|
|
|
count: The tab index to focus, starting with 1.
|
|
|
|
"""
|
|
|
|
if count is None and index is None:
|
|
|
|
raise cmdexc.CommandError("buffer: Either a count or the argument "
|
|
|
|
"index must be specified.")
|
|
|
|
|
|
|
|
if count is not None:
|
|
|
|
index = str(count)
|
|
|
|
|
|
|
|
tabbed_browser, tab = self._resolve_buffer_index(index)
|
|
|
|
|
|
|
|
window = tabbed_browser.window()
|
2016-02-18 07:01:30 +01:00
|
|
|
window.activateWindow()
|
|
|
|
window.raise_()
|
2017-10-03 19:33:23 +02:00
|
|
|
tabbed_browser.setCurrentWidget(tab)
|
2016-02-18 07:01:30 +01:00
|
|
|
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2016-05-11 08:01:25 +02:00
|
|
|
@cmdutils.argument('index', choices=['last'])
|
2016-11-23 08:34:21 +01:00
|
|
|
@cmdutils.argument('count', count=True)
|
2017-10-09 21:20:47 +02:00
|
|
|
def tab_focus(self, index: typing.Union[str, int] = None, count=None):
|
2014-05-17 22:38:07 +02:00
|
|
|
"""Select the tab given as argument/[count].
|
|
|
|
|
2015-08-11 22:01:18 +02:00
|
|
|
If neither count nor index are given, it behaves like tab-next.
|
2016-08-02 17:50:21 +02:00
|
|
|
If both are given, use count.
|
2015-08-11 22:01:18 +02:00
|
|
|
|
2014-05-17 22:38:07 +02:00
|
|
|
Args:
|
2014-08-03 00:39:39 +02:00
|
|
|
index: The tab index to focus, starting with 1. The special value
|
2016-08-02 17:50:21 +02:00
|
|
|
`last` focuses the last focused tab (regardless of count).
|
|
|
|
Negative indices count from the end, such that -1 is the
|
|
|
|
last tab.
|
2014-08-03 00:33:39 +02:00
|
|
|
count: The tab index to focus, starting with 1.
|
2014-05-17 22:38:07 +02:00
|
|
|
"""
|
2017-06-16 12:28:56 +02:00
|
|
|
index = count if count is not None else index
|
|
|
|
|
2017-06-19 15:48:17 +02:00
|
|
|
if index == 'last':
|
2014-08-03 00:39:39 +02:00
|
|
|
self._tab_focus_last()
|
|
|
|
return
|
2017-06-19 15:48:17 +02:00
|
|
|
elif index == self._current_index() + 1:
|
|
|
|
self._tab_focus_last(show_error=False)
|
|
|
|
return
|
|
|
|
elif index is None:
|
2015-08-11 10:13:00 +02:00
|
|
|
self.tab_next()
|
|
|
|
return
|
2016-11-23 08:34:21 +01:00
|
|
|
|
|
|
|
if index < 0:
|
2016-04-09 03:05:47 +02:00
|
|
|
index = self._count() + index + 1
|
2016-08-02 17:29:35 +02:00
|
|
|
|
|
|
|
if 1 <= index <= self._count():
|
|
|
|
self._set_current_index(index - 1)
|
2014-05-17 22:38:07 +02:00
|
|
|
else:
|
2014-08-26 19:10:14 +02:00
|
|
|
raise cmdexc.CommandError("There's no tab with index {}!".format(
|
2016-08-02 17:29:35 +02:00
|
|
|
index))
|
2014-05-17 22:38:07 +02:00
|
|
|
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2016-07-30 05:06:40 +02:00
|
|
|
@cmdutils.argument('index', choices=['+', '-'])
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.argument('count', count=True)
|
2017-10-09 21:20:47 +02:00
|
|
|
def tab_move(self, index: typing.Union[str, int] = None, count=None):
|
2016-07-30 05:06:40 +02:00
|
|
|
"""Move the current tab according to the argument and [count].
|
|
|
|
|
|
|
|
If neither is given, move it to the first position.
|
2014-05-17 22:38:07 +02:00
|
|
|
|
|
|
|
Args:
|
2016-07-30 05:06:40 +02:00
|
|
|
index: `+` or `-` to move relative to the current tab by
|
|
|
|
count, or a default of 1 space.
|
|
|
|
A tab index to move to that index.
|
|
|
|
count: If moving relatively: Offset.
|
|
|
|
If moving absolutely: New position (default: 0). This
|
|
|
|
overrides the index argument, if given.
|
2014-05-17 22:38:07 +02:00
|
|
|
"""
|
2016-07-30 05:06:40 +02:00
|
|
|
if index in ['+', '-']:
|
2015-11-26 17:44:34 +01:00
|
|
|
# relative moving
|
2016-07-30 05:06:40 +02:00
|
|
|
new_idx = self._current_index()
|
2015-11-26 17:44:34 +01:00
|
|
|
delta = 1 if count is None else count
|
2016-07-30 05:06:40 +02:00
|
|
|
if index == '-':
|
|
|
|
new_idx -= delta
|
|
|
|
elif index == '+': # pragma: no branch
|
|
|
|
new_idx += delta
|
2015-12-02 06:20:41 +01:00
|
|
|
|
2017-06-12 13:19:56 +02:00
|
|
|
if config.val.tabs.wrap:
|
2015-12-02 06:20:41 +01:00
|
|
|
new_idx %= self._count()
|
2016-07-30 05:06:40 +02:00
|
|
|
else:
|
|
|
|
# absolute moving
|
|
|
|
if count is not None:
|
|
|
|
new_idx = count - 1
|
|
|
|
elif index is not None:
|
|
|
|
new_idx = index - 1 if index >= 0 else index + self._count()
|
|
|
|
else:
|
|
|
|
new_idx = 0
|
2015-11-26 17:44:34 +01:00
|
|
|
|
2014-09-24 20:21:43 +02:00
|
|
|
if not 0 <= new_idx < self._count():
|
2014-08-26 19:10:14 +02:00
|
|
|
raise cmdexc.CommandError("Can't move tab to position {}!".format(
|
2015-11-26 16:37:54 +01:00
|
|
|
new_idx + 1))
|
2015-11-26 17:44:34 +01:00
|
|
|
|
2014-09-24 20:21:43 +02:00
|
|
|
cur_idx = self._current_index()
|
2014-05-17 22:38:07 +02:00
|
|
|
cmdutils.check_overflow(cur_idx, 'int')
|
|
|
|
cmdutils.check_overflow(new_idx, 'int')
|
2016-11-30 15:23:35 +01:00
|
|
|
self._tabbed_browser.tabBar().moveTab(cur_idx, new_idx)
|
2014-05-17 22:38:07 +02:00
|
|
|
|
2015-06-17 07:57:38 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
2016-08-05 18:04:14 +02:00
|
|
|
maxsplit=0, no_replace_variables=True)
|
2015-06-17 07:57:38 +02:00
|
|
|
def spawn(self, cmdline, userscript=False, verbose=False, detach=False):
|
2014-07-28 01:40:25 +02:00
|
|
|
"""Spawn a command in a shell.
|
2014-05-03 14:25:22 +02:00
|
|
|
|
|
|
|
Args:
|
2016-03-25 11:09:03 +01:00
|
|
|
userscript: Run the command as a userscript. You can use an
|
|
|
|
absolute path, or store the userscript in one of those
|
|
|
|
locations:
|
|
|
|
- `~/.local/share/qutebrowser/userscripts`
|
|
|
|
(or `$XDG_DATA_DIR`)
|
|
|
|
- `/usr/share/qutebrowser/userscripts`
|
2015-06-11 20:19:31 +02:00
|
|
|
verbose: Show notifications when the command started/exited.
|
2015-05-31 16:25:25 +02:00
|
|
|
detach: Whether the command should be detached from qutebrowser.
|
2015-06-17 07:57:38 +02:00
|
|
|
cmdline: The commandline to execute.
|
2014-05-03 14:25:22 +02:00
|
|
|
"""
|
2017-07-23 21:11:33 +02:00
|
|
|
cmdutils.check_exclusive((userscript, detach), 'ud')
|
2015-06-17 07:57:38 +02:00
|
|
|
try:
|
|
|
|
cmd, *args = shlex.split(cmdline)
|
|
|
|
except ValueError as e:
|
|
|
|
raise cmdexc.CommandError("Error while splitting command: "
|
|
|
|
"{}".format(e))
|
2015-06-18 07:01:30 +02:00
|
|
|
|
|
|
|
args = runners.replace_variables(self._win_id, args)
|
|
|
|
|
2015-06-17 07:57:38 +02:00
|
|
|
log.procs.debug("Executing {} with args {}, userscript={}".format(
|
|
|
|
cmd, args, userscript))
|
2015-01-15 22:28:04 +01:00
|
|
|
if userscript:
|
2015-08-27 20:28:16 +02:00
|
|
|
# ~ expansion is handled by the userscript module.
|
2015-12-20 18:38:29 +01:00
|
|
|
self._run_userscript(cmd, *args, verbose=verbose)
|
2015-01-15 22:28:04 +01:00
|
|
|
else:
|
2015-08-27 20:28:16 +02:00
|
|
|
cmd = os.path.expanduser(cmd)
|
2016-09-14 20:52:32 +02:00
|
|
|
proc = guiprocess.GUIProcess(what='command', verbose=verbose,
|
2015-06-09 07:57:27 +02:00
|
|
|
parent=self._tabbed_browser)
|
2015-05-31 16:25:25 +02:00
|
|
|
if detach:
|
2015-06-09 07:57:27 +02:00
|
|
|
proc.start_detached(cmd, args)
|
2015-05-31 16:25:25 +02:00
|
|
|
else:
|
|
|
|
proc.start(cmd, args)
|
2015-05-31 16:32:21 +02:00
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2014-05-09 13:09:37 +02:00
|
|
|
def home(self):
|
|
|
|
"""Open main startpage in current tab."""
|
2017-10-17 17:19:37 +02:00
|
|
|
self.openurl(config.val.url.start_pages[0])
|
2014-05-09 13:09:37 +02:00
|
|
|
|
2015-12-20 18:38:29 +01:00
|
|
|
def _run_userscript(self, cmd, *args, verbose=False):
|
2015-06-29 17:48:30 +02:00
|
|
|
"""Run a userscript given as argument.
|
2014-07-29 01:45:42 +02:00
|
|
|
|
|
|
|
Args:
|
|
|
|
cmd: The userscript to run.
|
|
|
|
args: Arguments to pass to the userscript.
|
2015-06-11 20:19:31 +02:00
|
|
|
verbose: Show notifications when the command started/exited.
|
2014-07-29 01:45:42 +02:00
|
|
|
"""
|
2015-01-04 20:16:15 +01:00
|
|
|
env = {
|
|
|
|
'QUTE_MODE': 'command',
|
|
|
|
}
|
|
|
|
|
|
|
|
idx = self._current_index()
|
|
|
|
if idx != -1:
|
2015-05-18 21:31:54 +02:00
|
|
|
env['QUTE_TITLE'] = self._tabbed_browser.page_title(idx)
|
2015-01-04 20:16:15 +01:00
|
|
|
|
2016-07-05 11:58:31 +02:00
|
|
|
tab = self._tabbed_browser.currentWidget()
|
2016-07-05 20:11:42 +02:00
|
|
|
if tab is not None and tab.caret.has_selection():
|
|
|
|
env['QUTE_SELECTED_TEXT'] = tab.caret.selection()
|
2016-09-01 22:45:14 +02:00
|
|
|
try:
|
|
|
|
env['QUTE_SELECTED_HTML'] = tab.caret.selection(html=True)
|
|
|
|
except browsertab.UnsupportedOperationError:
|
|
|
|
pass
|
2016-07-05 20:11:42 +02:00
|
|
|
|
2016-07-07 18:03:37 +02:00
|
|
|
# FIXME:qtwebengine: If tab is None, run_async will fail!
|
2015-01-04 20:16:15 +01:00
|
|
|
|
|
|
|
try:
|
2015-05-18 21:31:54 +02:00
|
|
|
url = self._tabbed_browser.current_url()
|
2015-01-04 20:16:15 +01:00
|
|
|
except qtutils.QtValueError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
env['QUTE_URL'] = url.toString(QUrl.FullyEncoded)
|
|
|
|
|
2016-07-05 13:30:13 +02:00
|
|
|
try:
|
|
|
|
userscripts.run_async(tab, cmd, *args, win_id=self._win_id,
|
|
|
|
env=env, verbose=verbose)
|
2016-09-30 09:26:43 +02:00
|
|
|
except userscripts.Error as e:
|
2016-07-05 13:30:13 +02:00
|
|
|
raise cmdexc.CommandError(e)
|
2014-05-21 19:53:58 +02:00
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2014-05-22 16:44:47 +02:00
|
|
|
def quickmark_save(self):
|
|
|
|
"""Save the current page as a quickmark."""
|
2014-10-15 16:55:55 +02:00
|
|
|
quickmark_manager = objreg.get('quickmark-manager')
|
2016-10-27 13:40:34 +02:00
|
|
|
quickmark_manager.prompt_save(self._current_url())
|
2014-05-22 16:44:47 +02:00
|
|
|
|
2014-10-14 22:26:57 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
2016-05-10 23:04:52 +02:00
|
|
|
maxsplit=0)
|
2016-09-20 12:52:52 +02:00
|
|
|
@cmdutils.argument('name', completion=miscmodels.quickmark)
|
2014-09-28 22:41:43 +02:00
|
|
|
def quickmark_load(self, name, tab=False, bg=False, window=False):
|
2014-09-07 21:03:20 +02:00
|
|
|
"""Load a quickmark.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
name: The name of the quickmark to load.
|
2014-09-13 00:22:27 +02:00
|
|
|
tab: Load the quickmark in a new tab.
|
|
|
|
bg: Load the quickmark in a new background tab.
|
2014-09-28 22:41:43 +02:00
|
|
|
window: Load the quickmark in a new window.
|
2014-09-07 21:03:20 +02:00
|
|
|
"""
|
2015-07-29 12:34:53 +02:00
|
|
|
try:
|
|
|
|
url = objreg.get('quickmark-manager').get(name)
|
|
|
|
except urlmarks.Error as e:
|
|
|
|
raise cmdexc.CommandError(str(e))
|
2014-09-28 22:41:43 +02:00
|
|
|
self._open(url, tab, bg, window)
|
2014-05-22 16:44:47 +02:00
|
|
|
|
2016-07-22 03:15:15 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
maxsplit=0)
|
2016-09-20 12:52:52 +02:00
|
|
|
@cmdutils.argument('name', completion=miscmodels.quickmark)
|
2016-07-22 03:15:15 +02:00
|
|
|
def quickmark_del(self, name=None):
|
|
|
|
"""Delete a quickmark.
|
|
|
|
|
|
|
|
Args:
|
2016-07-26 08:36:16 +02:00
|
|
|
name: The name of the quickmark to delete. If not given, delete the
|
2016-07-22 03:15:15 +02:00
|
|
|
quickmark for the current page (choosing one arbitrarily
|
|
|
|
if there are more than one).
|
|
|
|
"""
|
|
|
|
quickmark_manager = objreg.get('quickmark-manager')
|
|
|
|
if name is None:
|
|
|
|
url = self._current_url()
|
2016-07-23 17:21:27 +02:00
|
|
|
try:
|
|
|
|
name = quickmark_manager.get_by_qurl(url)
|
|
|
|
except urlmarks.DoesNotExistError as e:
|
|
|
|
raise cmdexc.CommandError(str(e))
|
2016-07-23 16:55:57 +02:00
|
|
|
try:
|
|
|
|
quickmark_manager.delete(name)
|
|
|
|
except KeyError:
|
|
|
|
raise cmdexc.CommandError("Quickmark '{}' not found!".format(name))
|
2016-07-22 03:15:15 +02:00
|
|
|
|
2015-05-22 00:17:22 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2016-07-23 04:46:30 +02:00
|
|
|
def bookmark_add(self, url=None, title=None, toggle=False):
|
2016-07-08 16:38:58 +02:00
|
|
|
"""Save the current page as a bookmark, or a specific url.
|
|
|
|
|
|
|
|
If no url and title are provided, then save the current page as a
|
|
|
|
bookmark.
|
|
|
|
If a url and title have been provided, then save the given url as
|
|
|
|
a bookmark with the provided title.
|
|
|
|
|
2016-07-23 14:27:42 +02:00
|
|
|
You can view all saved bookmarks on the
|
|
|
|
link:qute://bookmarks[bookmarks page].
|
|
|
|
|
2016-07-08 16:38:58 +02:00
|
|
|
Args:
|
|
|
|
url: url to save as a bookmark. If None, use url of current page.
|
2016-07-08 16:58:47 +02:00
|
|
|
title: title of the new bookmark.
|
2016-07-23 04:46:30 +02:00
|
|
|
toggle: remove the bookmark instead of raising an error if it
|
|
|
|
already exists.
|
2016-07-08 16:58:47 +02:00
|
|
|
"""
|
2016-07-07 21:16:19 +02:00
|
|
|
if url and not title:
|
|
|
|
raise cmdexc.CommandError('Title must be provided if url has '
|
2016-07-08 16:33:13 +02:00
|
|
|
'been provided')
|
2015-05-22 00:17:22 +02:00
|
|
|
bookmark_manager = objreg.get('bookmark-manager')
|
2016-07-08 16:33:13 +02:00
|
|
|
if url is None:
|
|
|
|
url = self._current_url()
|
|
|
|
else:
|
2016-07-07 21:16:19 +02:00
|
|
|
try:
|
|
|
|
url = urlutils.fuzzy_url(url)
|
|
|
|
except urlutils.InvalidUrlError as e:
|
|
|
|
raise cmdexc.CommandError(e)
|
|
|
|
if not title:
|
|
|
|
title = self._current_title()
|
2015-07-29 12:34:53 +02:00
|
|
|
try:
|
2016-07-26 15:48:35 +02:00
|
|
|
was_added = bookmark_manager.add(url, title, toggle=toggle)
|
2015-07-29 12:34:53 +02:00
|
|
|
except urlmarks.Error as e:
|
|
|
|
raise cmdexc.CommandError(str(e))
|
2015-08-18 19:03:28 +02:00
|
|
|
else:
|
2017-04-16 13:08:15 +02:00
|
|
|
msg = "Bookmarked {}" if was_added else "Removed bookmark {}"
|
2016-09-14 20:52:32 +02:00
|
|
|
message.info(msg.format(url.toDisplayString()))
|
2015-05-22 00:17:22 +02:00
|
|
|
|
2015-05-25 01:07:57 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
2016-05-10 23:04:52 +02:00
|
|
|
maxsplit=0)
|
2016-09-20 12:52:52 +02:00
|
|
|
@cmdutils.argument('url', completion=miscmodels.bookmark)
|
2016-07-23 05:18:14 +02:00
|
|
|
def bookmark_load(self, url, tab=False, bg=False, window=False,
|
|
|
|
delete=False):
|
2015-05-25 01:07:57 +02:00
|
|
|
"""Load a bookmark.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
url: The url of the bookmark to load.
|
|
|
|
tab: Load the bookmark in a new tab.
|
|
|
|
bg: Load the bookmark in a new background tab.
|
|
|
|
window: Load the bookmark in a new window.
|
2016-07-23 05:18:14 +02:00
|
|
|
delete: Whether to delete the bookmark afterwards.
|
2015-05-25 01:07:57 +02:00
|
|
|
"""
|
2015-07-29 12:34:53 +02:00
|
|
|
try:
|
2016-07-23 05:18:14 +02:00
|
|
|
qurl = urlutils.fuzzy_url(url)
|
2015-08-09 18:47:36 +02:00
|
|
|
except urlutils.InvalidUrlError as e:
|
2015-07-29 12:34:53 +02:00
|
|
|
raise cmdexc.CommandError(e)
|
2016-07-23 05:18:14 +02:00
|
|
|
self._open(qurl, tab, bg, window)
|
|
|
|
if delete:
|
|
|
|
self.bookmark_del(url)
|
2015-05-25 01:07:57 +02:00
|
|
|
|
2016-07-22 02:46:44 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
maxsplit=0)
|
2016-09-20 12:52:52 +02:00
|
|
|
@cmdutils.argument('url', completion=miscmodels.bookmark)
|
2016-07-22 02:46:44 +02:00
|
|
|
def bookmark_del(self, url=None):
|
|
|
|
"""Delete a bookmark.
|
|
|
|
|
|
|
|
Args:
|
2016-07-26 08:36:16 +02:00
|
|
|
url: The url of the bookmark to delete. If not given, use the
|
2016-07-22 02:46:44 +02:00
|
|
|
current page's url.
|
|
|
|
"""
|
|
|
|
if url is None:
|
2016-08-09 17:28:14 +02:00
|
|
|
url = self._current_url().toString(QUrl.RemovePassword |
|
|
|
|
QUrl.FullyEncoded)
|
2016-07-23 16:55:57 +02:00
|
|
|
try:
|
|
|
|
objreg.get('bookmark-manager').delete(url)
|
|
|
|
except KeyError:
|
|
|
|
raise cmdexc.CommandError("Bookmark '{}' not found!".format(url))
|
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2016-07-05 20:11:42 +02:00
|
|
|
def follow_selected(self, *, tab=False):
|
2015-05-30 19:15:53 +02:00
|
|
|
"""Follow the selected text.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
tab: Load the selected link in a new tab.
|
|
|
|
"""
|
2016-07-04 13:35:38 +02:00
|
|
|
try:
|
|
|
|
self._current_widget().caret.follow_selected(tab=tab)
|
2016-07-10 17:23:08 +02:00
|
|
|
except browsertab.WebTabError as e:
|
2016-07-04 13:35:38 +02:00
|
|
|
raise cmdexc.CommandError(str(e))
|
2015-05-30 16:37:25 +02:00
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', name='inspector',
|
2016-08-03 15:23:32 +02:00
|
|
|
scope='window')
|
2014-05-26 15:35:05 +02:00
|
|
|
def toggle_inspector(self):
|
2015-07-16 20:54:10 +02:00
|
|
|
"""Toggle the web inspector.
|
|
|
|
|
|
|
|
Note: Due a bug in Qt, the inspector will show incorrect request
|
|
|
|
headers in the network tab.
|
|
|
|
"""
|
2016-07-04 17:49:25 +02:00
|
|
|
tab = self._current_widget()
|
2016-08-04 12:44:46 +02:00
|
|
|
# FIXME:qtwebengine have a proper API for this
|
|
|
|
page = tab._widget.page() # pylint: disable=protected-access
|
2016-08-03 15:23:32 +02:00
|
|
|
|
2016-08-04 12:44:46 +02:00
|
|
|
try:
|
|
|
|
if tab.data.inspector is None:
|
|
|
|
tab.data.inspector = inspector.create()
|
|
|
|
tab.data.inspector.inspect(page)
|
2014-05-26 15:35:05 +02:00
|
|
|
else:
|
2016-08-04 12:44:46 +02:00
|
|
|
tab.data.inspector.toggle(page)
|
|
|
|
except inspector.WebInspectorError as e:
|
|
|
|
raise cmdexc.CommandError(e)
|
2014-05-26 15:35:05 +02:00
|
|
|
|
2016-11-07 10:59:25 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2016-05-10 20:34:03 +02:00
|
|
|
@cmdutils.argument('dest_old', hide=True)
|
|
|
|
def download(self, url=None, dest_old=None, *, mhtml_=False, dest=None):
|
2015-02-09 12:06:49 +01:00
|
|
|
"""Download a given URL, or current page if no URL given.
|
2015-02-08 22:08:16 +01:00
|
|
|
|
2015-10-07 23:13:17 +02:00
|
|
|
The form `:download [url] [dest]` is deprecated, use `:download --dest
|
|
|
|
[dest] [url]` instead.
|
|
|
|
|
2015-02-08 22:08:16 +01:00
|
|
|
Args:
|
2015-02-09 17:38:50 +01:00
|
|
|
url: The URL to download. If not given, download the current page.
|
2015-10-07 23:13:17 +02:00
|
|
|
dest_old: (deprecated) Same as dest.
|
2015-02-08 22:08:16 +01:00
|
|
|
dest: The file path to write the download to, or None to ask.
|
2015-10-07 23:53:03 +02:00
|
|
|
mhtml_: Download the current page and all assets as mhtml file.
|
2015-02-08 22:08:16 +01:00
|
|
|
"""
|
2015-10-07 23:13:17 +02:00
|
|
|
if dest_old is not None:
|
2016-09-14 20:52:32 +02:00
|
|
|
message.warning(":download [url] [dest] is deprecated - use "
|
|
|
|
":download --dest [dest] [url]")
|
2015-10-07 23:13:17 +02:00
|
|
|
if dest is not None:
|
|
|
|
raise cmdexc.CommandError("Can't give two destinations for the"
|
|
|
|
" download.")
|
|
|
|
dest = dest_old
|
|
|
|
|
2016-11-01 17:50:29 +01:00
|
|
|
# FIXME:qtwebengine do this with the QtWebEngine download manager?
|
|
|
|
download_manager = objreg.get('qtnetwork-download-manager',
|
|
|
|
scope='window', window=self._win_id)
|
2017-01-04 15:18:56 +01:00
|
|
|
target = None
|
|
|
|
if dest is not None:
|
2017-03-24 12:30:29 +01:00
|
|
|
dest = downloads.transform_path(dest)
|
|
|
|
if dest is None:
|
|
|
|
raise cmdexc.CommandError("Invalid target filename")
|
2017-01-04 15:18:56 +01:00
|
|
|
target = downloads.FileDownloadTarget(dest)
|
|
|
|
|
2017-02-07 21:59:21 +01:00
|
|
|
tab = self._current_widget()
|
|
|
|
user_agent = tab.user_agent()
|
|
|
|
|
2015-02-09 12:06:49 +01:00
|
|
|
if url:
|
2015-10-07 23:53:03 +02:00
|
|
|
if mhtml_:
|
2015-10-07 23:22:21 +02:00
|
|
|
raise cmdexc.CommandError("Can only download the current page"
|
|
|
|
" as mhtml.")
|
2015-02-08 22:08:16 +01:00
|
|
|
url = urlutils.qurl_from_user_input(url)
|
|
|
|
urlutils.raise_cmdexc_if_invalid(url)
|
2017-02-07 21:59:21 +01:00
|
|
|
download_manager.get(url, user_agent=user_agent, target=target)
|
2016-07-07 11:47:02 +02:00
|
|
|
elif mhtml_:
|
2017-02-07 00:15:39 +01:00
|
|
|
tab = self._current_widget()
|
|
|
|
if tab.backend == usertypes.Backend.QtWebEngine:
|
|
|
|
webengine_download_manager = objreg.get(
|
|
|
|
'webengine-download-manager')
|
|
|
|
try:
|
|
|
|
webengine_download_manager.get_mhtml(tab, target)
|
|
|
|
except browsertab.UnsupportedOperationError as e:
|
|
|
|
raise cmdexc.CommandError(e)
|
|
|
|
else:
|
|
|
|
download_manager.get_mhtml(tab, target)
|
2015-02-08 22:08:16 +01:00
|
|
|
else:
|
2017-02-07 21:59:21 +01:00
|
|
|
qnam = tab.networkaccessmanager()
|
2017-07-06 11:59:02 +02:00
|
|
|
|
2017-07-06 17:41:54 +02:00
|
|
|
suggested_fn = downloads.suggested_fn_from_title(
|
|
|
|
self._current_url().path(), tab.title()
|
|
|
|
)
|
2017-07-06 11:59:02 +02:00
|
|
|
|
2017-06-26 23:21:32 +02:00
|
|
|
download_manager.get(
|
|
|
|
self._current_url(),
|
|
|
|
user_agent=user_agent,
|
|
|
|
qnam=qnam,
|
|
|
|
target=target,
|
2017-07-06 11:59:02 +02:00
|
|
|
suggested_fn=suggested_fn
|
2017-06-26 23:21:32 +02:00
|
|
|
)
|
2014-06-19 17:58:46 +02:00
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2014-09-15 17:59:54 +02:00
|
|
|
def view_source(self):
|
2017-02-04 22:29:45 +01:00
|
|
|
"""Show the source of the current page in a new tab."""
|
2016-07-04 16:34:07 +02:00
|
|
|
tab = self._current_widget()
|
|
|
|
if tab.data.viewing_source:
|
2014-09-15 17:59:54 +02:00
|
|
|
raise cmdexc.CommandError("Already viewing source!")
|
2016-07-04 16:34:07 +02:00
|
|
|
|
2017-06-12 18:13:17 +02:00
|
|
|
try:
|
|
|
|
current_url = self._current_url()
|
|
|
|
except cmdexc.CommandError as e:
|
|
|
|
message.error(str(e))
|
|
|
|
return
|
|
|
|
|
2016-07-04 16:34:07 +02:00
|
|
|
def show_source_cb(source):
|
2016-07-05 20:11:42 +02:00
|
|
|
"""Show source as soon as it's ready."""
|
2017-10-22 22:16:02 +02:00
|
|
|
# WORKAROUND for https://github.com/PyCQA/pylint/issues/491
|
|
|
|
# pylint: disable=no-member
|
2016-07-04 16:34:07 +02:00
|
|
|
lexer = pygments.lexers.HtmlLexer()
|
2017-05-15 09:16:24 +02:00
|
|
|
formatter = pygments.formatters.HtmlFormatter(
|
|
|
|
full=True, linenos='table',
|
|
|
|
title='Source for {}'.format(current_url.toDisplayString()))
|
2017-10-22 22:16:02 +02:00
|
|
|
# pylint: enable=no-member
|
2016-07-04 16:34:07 +02:00
|
|
|
highlighted = pygments.highlight(source, lexer, formatter)
|
2017-05-15 09:16:24 +02:00
|
|
|
|
2017-05-12 22:26:22 +02:00
|
|
|
new_tab = self._tabbed_browser.tabopen()
|
2017-05-15 08:59:38 +02:00
|
|
|
new_tab.set_html(highlighted)
|
2016-07-04 16:34:07 +02:00
|
|
|
new_tab.data.viewing_source = True
|
|
|
|
|
|
|
|
tab.dump_async(show_source_cb)
|
2014-09-15 17:59:54 +02:00
|
|
|
|
2015-11-23 14:35:46 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
debug=True)
|
|
|
|
def debug_dump_page(self, dest, plain=False):
|
|
|
|
"""Dump the current page's content to a file.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
dest: Where to write the file to.
|
|
|
|
plain: Write plain text instead of HTML.
|
|
|
|
"""
|
2016-06-14 13:26:30 +02:00
|
|
|
tab = self._current_widget()
|
2015-11-24 18:04:10 +01:00
|
|
|
dest = os.path.expanduser(dest)
|
|
|
|
|
2016-06-14 13:26:30 +02:00
|
|
|
def callback(data):
|
|
|
|
try:
|
|
|
|
with open(dest, 'w', encoding='utf-8') as f:
|
|
|
|
f.write(data)
|
|
|
|
except OSError as e:
|
2016-09-14 20:52:32 +02:00
|
|
|
message.error('Could not write page: {}'.format(e))
|
2016-06-14 13:26:30 +02:00
|
|
|
else:
|
2016-09-14 20:52:32 +02:00
|
|
|
message.info("Dumped page to {}.".format(dest))
|
2016-06-14 13:26:30 +02:00
|
|
|
|
|
|
|
tab.dump_async(callback, plain=plain)
|
|
|
|
|
2017-02-11 17:22:35 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
|
|
|
def history(self, tab=True, bg=False, window=False):
|
|
|
|
"""Show browsing history.
|
2017-02-10 13:47:20 +01:00
|
|
|
|
|
|
|
Args:
|
|
|
|
tab: Open in a new tab.
|
|
|
|
bg: Open in a background tab.
|
|
|
|
window: Open in a new window.
|
|
|
|
"""
|
|
|
|
url = QUrl('qute://history/')
|
|
|
|
self._open(url, tab, bg, window)
|
|
|
|
|
2014-09-23 21:35:08 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', name='help',
|
2014-09-28 22:13:14 +02:00
|
|
|
scope='window')
|
2016-09-20 12:52:52 +02:00
|
|
|
@cmdutils.argument('topic', completion=miscmodels.helptopic)
|
2014-10-06 20:40:00 +02:00
|
|
|
def show_help(self, tab=False, bg=False, window=False, topic=None):
|
2014-09-08 07:44:32 +02:00
|
|
|
r"""Show help about a command or setting.
|
2014-09-08 06:57:22 +02:00
|
|
|
|
|
|
|
Args:
|
2014-10-06 20:40:00 +02:00
|
|
|
tab: Open in a new tab.
|
|
|
|
bg: Open in a background tab.
|
|
|
|
window: Open in a new window.
|
2014-09-08 06:57:22 +02:00
|
|
|
topic: The topic to show help for.
|
|
|
|
|
|
|
|
- :__command__ for commands.
|
2017-06-16 18:10:04 +02:00
|
|
|
- __section__.__option__ for settings.
|
2014-09-08 06:57:22 +02:00
|
|
|
"""
|
2014-09-08 12:18:54 +02:00
|
|
|
if topic is None:
|
|
|
|
path = 'index.html'
|
|
|
|
elif topic.startswith(':'):
|
2014-09-08 06:57:22 +02:00
|
|
|
command = topic[1:]
|
|
|
|
if command not in cmdutils.cmd_dict:
|
|
|
|
raise cmdexc.CommandError("Invalid command {}!".format(
|
|
|
|
command))
|
|
|
|
path = 'commands.html#{}'.format(command)
|
2017-06-19 12:09:09 +02:00
|
|
|
elif topic in configdata.DATA:
|
|
|
|
path = 'settings.html#{}'.format(topic)
|
2014-09-08 06:57:22 +02:00
|
|
|
else:
|
|
|
|
raise cmdexc.CommandError("Invalid help topic {}!".format(topic))
|
2014-10-06 20:40:00 +02:00
|
|
|
url = QUrl('qute://help/{}'.format(path))
|
|
|
|
self._open(url, tab, bg, window)
|
2014-09-08 06:57:22 +02:00
|
|
|
|
2016-05-12 03:23:38 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2017-06-08 12:35:21 +02:00
|
|
|
def messages(self, level='info', plain=False, tab=False, bg=False,
|
2016-05-12 03:23:38 +02:00
|
|
|
window=False):
|
|
|
|
"""Show a log of past messages.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
level: Include messages with `level` or higher severity.
|
|
|
|
Valid values: vdebug, debug, info, warning, error, critical.
|
|
|
|
plain: Whether to show plaintext (as opposed to html).
|
2016-05-12 13:47:20 +02:00
|
|
|
tab: Open in a new tab.
|
|
|
|
bg: Open in a background tab.
|
|
|
|
window: Open in a new window.
|
2016-05-12 03:23:38 +02:00
|
|
|
"""
|
2016-05-12 13:47:20 +02:00
|
|
|
if level.upper() not in log.LOG_LEVELS:
|
|
|
|
raise cmdexc.CommandError("Invalid log level {}!".format(level))
|
2016-05-12 03:23:38 +02:00
|
|
|
if plain:
|
|
|
|
url = QUrl('qute://plainlog?level={}'.format(level))
|
|
|
|
else:
|
|
|
|
url = QUrl('qute://log?level={}'.format(level))
|
|
|
|
self._open(url, tab, bg, window)
|
|
|
|
|
2016-08-08 16:24:34 +02:00
|
|
|
def _open_editor_cb(self, elem):
|
|
|
|
"""Open editor after the focus elem was found in open_editor."""
|
|
|
|
if elem is None:
|
2016-09-14 20:52:32 +02:00
|
|
|
message.error("No element focused!")
|
2016-08-08 16:24:34 +02:00
|
|
|
return
|
|
|
|
if not elem.is_editable(strict=True):
|
2016-09-14 20:52:32 +02:00
|
|
|
message.error("Focused element is not editable!")
|
2016-08-08 16:24:34 +02:00
|
|
|
return
|
|
|
|
|
2016-11-16 12:15:43 +01:00
|
|
|
text = elem.value()
|
2016-11-29 11:31:03 +01:00
|
|
|
if text is None:
|
|
|
|
message.error("Could not get text from the focused element.")
|
|
|
|
return
|
2017-04-28 15:15:32 +02:00
|
|
|
assert isinstance(text, str), text
|
2016-11-29 11:31:03 +01:00
|
|
|
|
2017-10-12 22:43:31 +02:00
|
|
|
caret_position = elem.caret_position()
|
|
|
|
|
2016-09-14 20:52:32 +02:00
|
|
|
ed = editor.ExternalEditor(self._tabbed_browser)
|
2016-08-08 16:24:34 +02:00
|
|
|
ed.editing_finished.connect(functools.partial(
|
|
|
|
self.on_editing_finished, elem))
|
2017-10-12 22:43:31 +02:00
|
|
|
ed.edit(text, caret_position)
|
2016-08-08 16:24:34 +02:00
|
|
|
|
2017-11-02 19:30:17 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2014-05-17 23:22:10 +02:00
|
|
|
def open_editor(self):
|
2014-08-03 00:33:39 +02:00
|
|
|
"""Open an external editor with the currently selected form field.
|
|
|
|
|
|
|
|
The editor which should be launched can be configured via the
|
2017-06-16 18:10:04 +02:00
|
|
|
`editor.command` config option.
|
2014-05-03 14:27:44 +02:00
|
|
|
"""
|
2016-07-05 20:11:42 +02:00
|
|
|
tab = self._current_widget()
|
2016-08-18 14:01:27 +02:00
|
|
|
tab.elements.find_focused(self._open_editor_cb)
|
2014-05-21 15:37:18 +02:00
|
|
|
|
|
|
|
def on_editing_finished(self, elem, text):
|
2014-04-30 10:46:20 +02:00
|
|
|
"""Write the editor text into the form field and clean up tempfile.
|
2014-04-29 18:00:22 +02:00
|
|
|
|
2015-06-09 07:57:27 +02:00
|
|
|
Callback for GUIProcess when the editor was closed.
|
2014-05-21 15:37:18 +02:00
|
|
|
|
|
|
|
Args:
|
2014-09-04 08:00:05 +02:00
|
|
|
elem: The WebElementWrapper which was modified.
|
2014-05-21 15:37:18 +02:00
|
|
|
text: The new text to insert.
|
2014-04-29 18:00:22 +02:00
|
|
|
"""
|
2014-09-04 08:00:05 +02:00
|
|
|
try:
|
2016-11-16 12:15:43 +01:00
|
|
|
elem.set_value(text)
|
2016-09-05 18:20:48 +02:00
|
|
|
except webelem.Error as e:
|
|
|
|
raise cmdexc.CommandError(str(e))
|
2015-04-17 07:49:08 +02:00
|
|
|
|
2016-08-07 02:43:08 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', maxsplit=0,
|
2016-09-07 11:20:32 +02:00
|
|
|
scope='window')
|
2016-08-07 02:43:08 +02:00
|
|
|
def insert_text(self, text):
|
|
|
|
"""Insert text at cursor position.
|
|
|
|
|
|
|
|
Args:
|
2016-08-10 22:29:19 +02:00
|
|
|
text: The text to insert.
|
|
|
|
"""
|
2016-07-05 20:11:42 +02:00
|
|
|
tab = self._current_widget()
|
2016-09-07 11:20:32 +02:00
|
|
|
|
|
|
|
def _insert_text_cb(elem):
|
|
|
|
if elem is None:
|
2016-09-14 20:52:32 +02:00
|
|
|
message.error("No element focused!")
|
2016-09-07 11:20:32 +02:00
|
|
|
return
|
|
|
|
try:
|
|
|
|
elem.insert_text(text)
|
|
|
|
except webelem.Error as e:
|
2016-09-14 20:52:32 +02:00
|
|
|
message.error(str(e))
|
2016-09-07 11:20:32 +02:00
|
|
|
return
|
|
|
|
|
|
|
|
tab.elements.find_focused(_insert_text_cb)
|
2016-01-21 22:02:08 +01:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2016-08-18 15:30:04 +02:00
|
|
|
@cmdutils.argument('filter_', choices=['id'])
|
|
|
|
def click_element(self, filter_: str, value, *,
|
2017-10-09 21:20:47 +02:00
|
|
|
target: usertypes.ClickTarget =
|
2017-02-03 22:20:15 +01:00
|
|
|
usertypes.ClickTarget.normal,
|
|
|
|
force_event=False):
|
2016-08-18 15:30:04 +02:00
|
|
|
"""Click the element matching the given filter.
|
|
|
|
|
|
|
|
The given filter needs to result in exactly one element, otherwise, an
|
|
|
|
error is shown.
|
|
|
|
|
|
|
|
Args:
|
2016-08-18 16:53:28 +02:00
|
|
|
filter_: How to filter the elements.
|
|
|
|
id: Get an element based on its ID.
|
2016-08-18 15:30:04 +02:00
|
|
|
value: The value to filter for.
|
2016-08-18 16:53:28 +02:00
|
|
|
target: How to open the clicked element (normal/tab/tab-bg/window).
|
2017-02-03 22:20:15 +01:00
|
|
|
force_event: Force generating a fake click event.
|
2016-08-18 15:30:04 +02:00
|
|
|
"""
|
|
|
|
tab = self._current_widget()
|
|
|
|
|
|
|
|
def single_cb(elem):
|
|
|
|
"""Click a single element."""
|
|
|
|
if elem is None:
|
2016-11-15 19:43:03 +01:00
|
|
|
message.error("No element found with id {}!".format(value))
|
2016-08-18 15:30:04 +02:00
|
|
|
return
|
2016-09-04 21:09:15 +02:00
|
|
|
try:
|
2017-02-03 22:20:15 +01:00
|
|
|
elem.click(target, force_event=force_event)
|
2016-09-04 21:09:15 +02:00
|
|
|
except webelem.Error as e:
|
2016-09-14 20:52:32 +02:00
|
|
|
message.error(str(e))
|
2016-09-04 21:09:15 +02:00
|
|
|
return
|
2016-08-18 15:30:04 +02:00
|
|
|
|
|
|
|
# def multiple_cb(elems):
|
|
|
|
# """Click multiple elements (with only one expected)."""
|
|
|
|
# if not elems:
|
2016-09-14 20:52:32 +02:00
|
|
|
# message.error("No element found!")
|
2016-08-18 15:30:04 +02:00
|
|
|
# return
|
|
|
|
# elif len(elems) != 1:
|
2016-09-14 20:52:32 +02:00
|
|
|
# message.error("{} elements found!".format(len(elems)))
|
2016-08-18 15:30:04 +02:00
|
|
|
# return
|
|
|
|
# elems[0].click(target)
|
|
|
|
|
|
|
|
handlers = {
|
|
|
|
'id': (tab.elements.find_id, single_cb),
|
|
|
|
}
|
|
|
|
handler, callback = handlers[filter_]
|
|
|
|
handler(value, callback)
|
|
|
|
|
2016-07-12 16:10:35 +02:00
|
|
|
def _search_cb(self, found, *, tab, old_scroll_pos, options, text, prev):
|
|
|
|
"""Callback called from search/search_next/search_prev.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
found: Whether the text was found.
|
|
|
|
tab: The AbstractTab in which the search was made.
|
|
|
|
old_scroll_pos: The scroll position (QPoint) before the search.
|
|
|
|
options: The options (dict) the search was made with.
|
|
|
|
text: The text searched for.
|
|
|
|
prev: Whether we're searching backwards (i.e. :search-prev)
|
|
|
|
"""
|
|
|
|
# :search/:search-next without reverse -> down
|
|
|
|
# :search/:search-next with reverse -> up
|
|
|
|
# :search-prev without reverse -> up
|
|
|
|
# :search-prev with reverse -> down
|
|
|
|
going_up = options['reverse'] ^ prev
|
|
|
|
|
|
|
|
if found:
|
|
|
|
# Check if the scroll position got smaller and show info.
|
2016-07-26 13:19:07 +02:00
|
|
|
if not going_up and tab.scroller.pos_px().y() < old_scroll_pos.y():
|
2016-09-14 20:52:32 +02:00
|
|
|
message.info("Search hit BOTTOM, continuing at TOP")
|
2016-07-26 13:19:07 +02:00
|
|
|
elif going_up and tab.scroller.pos_px().y() > old_scroll_pos.y():
|
2016-09-14 20:52:32 +02:00
|
|
|
message.info("Search hit TOP, continuing at BOTTOM")
|
2016-07-12 16:10:35 +02:00
|
|
|
else:
|
2016-09-14 20:52:32 +02:00
|
|
|
message.warning("Text '{}' not found on page!".format(text))
|
2016-07-12 16:10:35 +02:00
|
|
|
|
2015-04-17 07:49:08 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
maxsplit=0)
|
|
|
|
def search(self, text="", reverse=False):
|
|
|
|
"""Search for a text on the current page. With no text, clear results.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
text: The text to search for.
|
|
|
|
reverse: Reverse search direction.
|
|
|
|
"""
|
2016-04-16 06:12:14 +02:00
|
|
|
self.set_mark("'")
|
2016-07-04 11:23:46 +02:00
|
|
|
tab = self._current_widget()
|
2017-07-17 14:58:02 +02:00
|
|
|
if tab.search.search_displayed:
|
|
|
|
tab.search.clear()
|
2016-07-04 11:23:46 +02:00
|
|
|
|
2017-03-20 21:34:33 +01:00
|
|
|
if not text:
|
|
|
|
return
|
|
|
|
|
2016-07-04 11:23:46 +02:00
|
|
|
options = {
|
2017-06-12 13:19:56 +02:00
|
|
|
'ignore_case': config.val.ignore_case,
|
2016-07-04 11:23:46 +02:00
|
|
|
'reverse': reverse,
|
|
|
|
}
|
2017-03-20 21:34:33 +01:00
|
|
|
|
2015-09-18 07:01:51 +02:00
|
|
|
self._tabbed_browser.search_text = text
|
2016-07-12 16:10:35 +02:00
|
|
|
self._tabbed_browser.search_options = dict(options)
|
|
|
|
|
2017-03-20 21:34:33 +01:00
|
|
|
cb = functools.partial(self._search_cb, tab=tab,
|
2017-03-20 22:06:05 +01:00
|
|
|
old_scroll_pos=tab.scroller.pos_px(),
|
|
|
|
options=options, text=text, prev=False)
|
2016-07-12 16:10:35 +02:00
|
|
|
options['result_cb'] = cb
|
2017-03-20 21:34:33 +01:00
|
|
|
|
2016-07-12 16:10:35 +02:00
|
|
|
tab.search.search(text, **options)
|
2015-04-17 07:49:08 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.argument('count', count=True)
|
2015-04-20 19:29:29 +02:00
|
|
|
def search_next(self, count=1):
|
2015-04-17 07:49:08 +02:00
|
|
|
"""Continue the search to the ([count]th) next term.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
count: How many elements to ignore.
|
|
|
|
"""
|
2016-07-04 11:23:46 +02:00
|
|
|
tab = self._current_widget()
|
|
|
|
window_text = self._tabbed_browser.search_text
|
|
|
|
window_options = self._tabbed_browser.search_options
|
|
|
|
|
2016-07-12 16:10:35 +02:00
|
|
|
if window_text is None:
|
|
|
|
raise cmdexc.CommandError("No search done yet.")
|
|
|
|
|
2016-04-16 06:12:14 +02:00
|
|
|
self.set_mark("'")
|
2015-09-18 02:18:54 +02:00
|
|
|
|
2016-07-04 11:23:46 +02:00
|
|
|
if window_text is not None and window_text != tab.search.text:
|
|
|
|
tab.search.clear()
|
|
|
|
tab.search.search(window_text, **window_options)
|
|
|
|
count -= 1
|
2015-09-18 02:18:54 +02:00
|
|
|
|
2016-07-12 16:10:35 +02:00
|
|
|
if count == 0:
|
|
|
|
return
|
|
|
|
|
|
|
|
cb = functools.partial(self._search_cb, tab=tab,
|
2016-07-26 13:19:07 +02:00
|
|
|
old_scroll_pos=tab.scroller.pos_px(),
|
2016-07-12 16:10:35 +02:00
|
|
|
options=window_options, text=window_text,
|
|
|
|
prev=False)
|
|
|
|
|
|
|
|
for _ in range(count - 1):
|
2016-07-04 11:23:46 +02:00
|
|
|
tab.search.next_result()
|
2016-07-12 16:10:35 +02:00
|
|
|
tab.search.next_result(result_cb=cb)
|
2015-04-17 07:49:08 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.argument('count', count=True)
|
2015-04-20 19:29:29 +02:00
|
|
|
def search_prev(self, count=1):
|
2015-04-17 07:49:08 +02:00
|
|
|
"""Continue the search to the ([count]th) previous term.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
count: How many elements to ignore.
|
|
|
|
"""
|
2016-07-04 11:23:46 +02:00
|
|
|
tab = self._current_widget()
|
|
|
|
window_text = self._tabbed_browser.search_text
|
|
|
|
window_options = self._tabbed_browser.search_options
|
|
|
|
|
2016-07-12 16:10:35 +02:00
|
|
|
if window_text is None:
|
|
|
|
raise cmdexc.CommandError("No search done yet.")
|
|
|
|
|
2016-04-16 06:12:14 +02:00
|
|
|
self.set_mark("'")
|
2016-07-04 11:23:46 +02:00
|
|
|
|
|
|
|
if window_text is not None and window_text != tab.search.text:
|
|
|
|
tab.search.clear()
|
|
|
|
tab.search.search(window_text, **window_options)
|
|
|
|
count -= 1
|
|
|
|
|
2016-07-12 16:10:35 +02:00
|
|
|
if count == 0:
|
|
|
|
return
|
|
|
|
|
|
|
|
cb = functools.partial(self._search_cb, tab=tab,
|
2016-07-26 13:19:07 +02:00
|
|
|
old_scroll_pos=tab.scroller.pos_px(),
|
2016-07-12 16:10:35 +02:00
|
|
|
options=window_options, text=window_text,
|
|
|
|
prev=True)
|
|
|
|
|
|
|
|
for _ in range(count - 1):
|
2016-07-04 11:23:46 +02:00
|
|
|
tab.search.prev_result()
|
2016-07-12 16:10:35 +02:00
|
|
|
tab.search.prev_result(result_cb=cb)
|
2015-04-22 07:13:56 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', modes=[KeyMode.caret],
|
|
|
|
scope='window')
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.argument('count', count=True)
|
2015-05-07 07:51:10 +02:00
|
|
|
def move_to_next_line(self, count=1):
|
2015-05-11 22:29:44 +02:00
|
|
|
"""Move the cursor or selection to the next line.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
count: How many lines to move.
|
|
|
|
"""
|
2016-06-14 18:08:46 +02:00
|
|
|
self._current_widget().caret.move_to_next_line(count)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', modes=[KeyMode.caret],
|
|
|
|
scope='window')
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.argument('count', count=True)
|
2015-05-07 07:51:10 +02:00
|
|
|
def move_to_prev_line(self, count=1):
|
2015-05-11 22:29:44 +02:00
|
|
|
"""Move the cursor or selection to the prev line.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
count: How many lines to move.
|
|
|
|
"""
|
2016-06-14 18:08:46 +02:00
|
|
|
self._current_widget().caret.move_to_prev_line(count)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', modes=[KeyMode.caret],
|
|
|
|
scope='window')
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.argument('count', count=True)
|
2015-05-07 07:51:10 +02:00
|
|
|
def move_to_next_char(self, count=1):
|
2015-05-11 22:29:44 +02:00
|
|
|
"""Move the cursor or selection to the next char.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
count: How many lines to move.
|
|
|
|
"""
|
2016-06-14 18:08:46 +02:00
|
|
|
self._current_widget().caret.move_to_next_char(count)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', modes=[KeyMode.caret],
|
|
|
|
scope='window')
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.argument('count', count=True)
|
2015-05-07 07:51:10 +02:00
|
|
|
def move_to_prev_char(self, count=1):
|
2015-05-11 22:29:44 +02:00
|
|
|
"""Move the cursor or selection to the previous char.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
count: How many chars to move.
|
|
|
|
"""
|
2016-06-14 18:08:46 +02:00
|
|
|
self._current_widget().caret.move_to_prev_char(count)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', modes=[KeyMode.caret],
|
|
|
|
scope='window')
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.argument('count', count=True)
|
2015-05-07 07:51:10 +02:00
|
|
|
def move_to_end_of_word(self, count=1):
|
2015-05-11 22:29:44 +02:00
|
|
|
"""Move the cursor or selection to the end of the word.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
count: How many words to move.
|
|
|
|
"""
|
2016-06-14 18:08:46 +02:00
|
|
|
self._current_widget().caret.move_to_end_of_word(count)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', modes=[KeyMode.caret],
|
|
|
|
scope='window')
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.argument('count', count=True)
|
2015-05-07 07:51:10 +02:00
|
|
|
def move_to_next_word(self, count=1):
|
2015-05-11 22:29:44 +02:00
|
|
|
"""Move the cursor or selection to the next word.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
count: How many words to move.
|
|
|
|
"""
|
2016-06-14 18:08:46 +02:00
|
|
|
self._current_widget().caret.move_to_next_word(count)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', modes=[KeyMode.caret],
|
|
|
|
scope='window')
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.argument('count', count=True)
|
2015-05-07 07:51:10 +02:00
|
|
|
def move_to_prev_word(self, count=1):
|
2015-05-11 22:29:44 +02:00
|
|
|
"""Move the cursor or selection to the previous word.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
count: How many words to move.
|
|
|
|
"""
|
2016-06-14 18:08:46 +02:00
|
|
|
self._current_widget().caret.move_to_prev_word(count)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', modes=[KeyMode.caret],
|
|
|
|
scope='window')
|
2015-05-18 22:28:11 +02:00
|
|
|
def move_to_start_of_line(self):
|
2015-05-18 22:27:44 +02:00
|
|
|
"""Move the cursor or selection to the start of the line."""
|
2016-06-14 18:08:46 +02:00
|
|
|
self._current_widget().caret.move_to_start_of_line()
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', modes=[KeyMode.caret],
|
|
|
|
scope='window')
|
2015-05-18 22:28:11 +02:00
|
|
|
def move_to_end_of_line(self):
|
2015-05-18 22:27:44 +02:00
|
|
|
"""Move the cursor or selection to the end of line."""
|
2016-06-14 18:08:46 +02:00
|
|
|
self._current_widget().caret.move_to_end_of_line()
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', modes=[KeyMode.caret],
|
|
|
|
scope='window')
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.argument('count', count=True)
|
2015-05-07 08:19:35 +02:00
|
|
|
def move_to_start_of_next_block(self, count=1):
|
2015-05-11 22:29:44 +02:00
|
|
|
"""Move the cursor or selection to the start of next block.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
count: How many blocks to move.
|
|
|
|
"""
|
2016-06-14 18:08:46 +02:00
|
|
|
self._current_widget().caret.move_to_start_of_next_block(count)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', modes=[KeyMode.caret],
|
|
|
|
scope='window')
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.argument('count', count=True)
|
2015-05-07 08:19:35 +02:00
|
|
|
def move_to_start_of_prev_block(self, count=1):
|
2015-05-11 22:29:44 +02:00
|
|
|
"""Move the cursor or selection to the start of previous block.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
count: How many blocks to move.
|
|
|
|
"""
|
2016-06-14 18:08:46 +02:00
|
|
|
self._current_widget().caret.move_to_start_of_prev_block(count)
|
2015-05-07 08:19:35 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', modes=[KeyMode.caret],
|
|
|
|
scope='window')
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.argument('count', count=True)
|
2015-05-07 08:19:35 +02:00
|
|
|
def move_to_end_of_next_block(self, count=1):
|
2015-05-11 22:29:44 +02:00
|
|
|
"""Move the cursor or selection to the end of next block.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
count: How many blocks to move.
|
|
|
|
"""
|
2016-06-14 18:08:46 +02:00
|
|
|
self._current_widget().caret.move_to_end_of_next_block(count)
|
2015-05-07 08:19:35 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', modes=[KeyMode.caret],
|
|
|
|
scope='window')
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.argument('count', count=True)
|
2015-05-07 08:19:35 +02:00
|
|
|
def move_to_end_of_prev_block(self, count=1):
|
2015-05-11 22:29:44 +02:00
|
|
|
"""Move the cursor or selection to the end of previous block.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
count: How many blocks to move.
|
|
|
|
"""
|
2016-06-14 18:08:46 +02:00
|
|
|
self._current_widget().caret.move_to_end_of_prev_block(count)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', modes=[KeyMode.caret],
|
|
|
|
scope='window')
|
2015-04-09 18:55:42 +02:00
|
|
|
def move_to_start_of_document(self):
|
2015-05-11 22:29:44 +02:00
|
|
|
"""Move the cursor or selection to the start of the document."""
|
2016-06-14 18:08:46 +02:00
|
|
|
self._current_widget().caret.move_to_start_of_document()
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', modes=[KeyMode.caret],
|
|
|
|
scope='window')
|
2015-04-09 18:55:42 +02:00
|
|
|
def move_to_end_of_document(self):
|
2015-05-11 22:29:44 +02:00
|
|
|
"""Move the cursor or selection to the end of the document."""
|
2016-06-14 18:08:46 +02:00
|
|
|
self._current_widget().caret.move_to_end_of_document()
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', modes=[KeyMode.caret],
|
|
|
|
scope='window')
|
2015-05-05 06:18:24 +02:00
|
|
|
def toggle_selection(self):
|
|
|
|
"""Toggle caret selection mode."""
|
2016-06-14 18:08:46 +02:00
|
|
|
self._current_widget().caret.toggle_selection()
|
2015-05-05 06:18:24 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', modes=[KeyMode.caret],
|
|
|
|
scope='window')
|
2015-04-09 18:55:42 +02:00
|
|
|
def drop_selection(self):
|
2015-05-05 06:18:24 +02:00
|
|
|
"""Drop selection and keep selection mode enabled."""
|
2016-06-14 18:08:46 +02:00
|
|
|
self._current_widget().caret.drop_selection()
|
2015-04-28 15:47:19 +02:00
|
|
|
|
2015-04-22 07:13:56 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
2016-05-10 19:51:11 +02:00
|
|
|
debug=True)
|
|
|
|
@cmdutils.argument('count', count=True)
|
2015-04-22 07:13:56 +02:00
|
|
|
def debug_webaction(self, action, count=1):
|
|
|
|
"""Execute a webaction.
|
|
|
|
|
2017-05-09 22:02:30 +02:00
|
|
|
Available actions:
|
|
|
|
http://doc.qt.io/archives/qt-5.5/qwebpage.html#WebAction-enum (WebKit)
|
|
|
|
http://doc.qt.io/qt-5/qwebenginepage.html#WebAction-enum (WebEngine)
|
2015-04-22 07:13:56 +02:00
|
|
|
|
|
|
|
Args:
|
|
|
|
action: The action to execute, e.g. MoveToNextChar.
|
|
|
|
count: How many times to repeat the action.
|
|
|
|
"""
|
2016-07-04 15:45:20 +02:00
|
|
|
tab = self._current_widget()
|
2015-04-22 07:13:56 +02:00
|
|
|
for _ in range(count):
|
2017-05-09 22:02:30 +02:00
|
|
|
try:
|
|
|
|
tab.action.run_string(action)
|
|
|
|
except browsertab.WebTabError as e:
|
|
|
|
raise cmdexc.CommandError(str(e))
|
2015-06-03 22:31:15 +02:00
|
|
|
|
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
maxsplit=0, no_cmd_split=True)
|
2017-02-24 19:07:30 +01:00
|
|
|
def jseval(self, js_code, file=False, quiet=False, *,
|
2017-10-09 21:20:47 +02:00
|
|
|
world: typing.Union[usertypes.JsWorld, int] = None):
|
2015-06-03 22:31:15 +02:00
|
|
|
"""Evaluate a JavaScript string.
|
|
|
|
|
|
|
|
Args:
|
2017-02-24 19:07:30 +01:00
|
|
|
js_code: The string/file to evaluate.
|
|
|
|
file: Interpret js-code as a path to a file.
|
2015-06-12 11:24:04 +02:00
|
|
|
quiet: Don't show resulting JS object.
|
2016-09-12 18:23:23 +02:00
|
|
|
world: Ignored on QtWebKit. On QtWebEngine, a world ID or name to
|
|
|
|
run the snippet in.
|
2015-06-03 22:31:15 +02:00
|
|
|
"""
|
2016-09-12 18:27:51 +02:00
|
|
|
if world is None:
|
|
|
|
world = usertypes.JsWorld.jseval
|
|
|
|
|
2015-06-12 11:24:04 +02:00
|
|
|
if quiet:
|
2016-06-14 18:47:26 +02:00
|
|
|
jseval_cb = None
|
2015-06-05 20:07:47 +02:00
|
|
|
else:
|
2016-06-14 18:47:26 +02:00
|
|
|
def jseval_cb(out):
|
|
|
|
if out is None:
|
|
|
|
# Getting the actual error (if any) seems to be difficult.
|
|
|
|
# The error does end up in
|
2016-07-05 20:11:42 +02:00
|
|
|
# BrowserPage.javaScriptConsoleMessage(), but
|
|
|
|
# distinguishing between :jseval errors and errors from the
|
|
|
|
# webpage is not trivial...
|
2016-09-14 20:52:32 +02:00
|
|
|
message.info('No output or error')
|
2016-06-14 18:47:26 +02:00
|
|
|
else:
|
|
|
|
# The output can be a string, number, dict, array, etc. But
|
|
|
|
# *don't* output too much data, as this will make
|
|
|
|
# qutebrowser hang
|
|
|
|
out = str(out)
|
|
|
|
if len(out) > 5000:
|
2016-07-05 20:11:42 +02:00
|
|
|
out = out[:5000] + ' [...trimmed...]'
|
2016-09-14 20:52:32 +02:00
|
|
|
message.info(out)
|
2016-06-14 18:47:26 +02:00
|
|
|
|
2017-02-24 19:07:30 +01:00
|
|
|
if file:
|
2017-08-13 02:34:50 +02:00
|
|
|
path = os.path.expanduser(js_code)
|
2017-02-24 19:07:30 +01:00
|
|
|
try:
|
2017-08-13 02:34:50 +02:00
|
|
|
with open(path, 'r', encoding='utf-8') as f:
|
2017-02-24 19:07:30 +01:00
|
|
|
js_code = f.read()
|
|
|
|
except OSError as e:
|
|
|
|
raise cmdexc.CommandError(str(e))
|
|
|
|
|
2016-09-12 15:59:17 +02:00
|
|
|
widget = self._current_widget()
|
2016-09-12 18:27:51 +02:00
|
|
|
widget.run_js_async(js_code, callback=jseval_cb, world=world)
|
2016-06-14 18:47:26 +02:00
|
|
|
|
2015-11-20 07:05:16 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
|
|
|
def fake_key(self, keystring, global_=False):
|
|
|
|
"""Send a fake keypress or key string to the website or qutebrowser.
|
|
|
|
|
|
|
|
:fake-key xy - sends the keychain 'xy'
|
|
|
|
:fake-key <Ctrl-x> - sends Ctrl-x
|
|
|
|
:fake-key <Escape> - sends the escape key
|
|
|
|
|
|
|
|
Args:
|
|
|
|
keystring: The keystring to send.
|
|
|
|
global_: If given, the keys are sent to the qutebrowser UI.
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
keyinfos = utils.parse_keystring(keystring)
|
|
|
|
except utils.KeyParseError as e:
|
|
|
|
raise cmdexc.CommandError(str(e))
|
|
|
|
|
|
|
|
for keyinfo in keyinfos:
|
|
|
|
press_event = QKeyEvent(QEvent.KeyPress, keyinfo.key,
|
|
|
|
keyinfo.modifiers, keyinfo.text)
|
|
|
|
release_event = QKeyEvent(QEvent.KeyRelease, keyinfo.key,
|
|
|
|
keyinfo.modifiers, keyinfo.text)
|
|
|
|
|
|
|
|
if global_:
|
2016-08-18 18:44:33 +02:00
|
|
|
window = QApplication.focusWindow()
|
|
|
|
if window is None:
|
2015-11-20 07:05:16 +01:00
|
|
|
raise cmdexc.CommandError("No focused window!")
|
2016-09-11 19:37:09 +02:00
|
|
|
QApplication.postEvent(window, press_event)
|
|
|
|
QApplication.postEvent(window, release_event)
|
2015-11-20 07:05:16 +01:00
|
|
|
else:
|
2016-08-18 18:44:33 +02:00
|
|
|
tab = self._current_widget()
|
2016-08-18 21:36:43 +02:00
|
|
|
tab.send_event(press_event)
|
|
|
|
tab.send_event(release_event)
|
2016-01-13 21:05:48 +01:00
|
|
|
|
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
2016-11-10 21:11:59 +01:00
|
|
|
debug=True, backend=usertypes.Backend.QtWebKit)
|
2016-01-13 21:05:48 +01:00
|
|
|
def debug_clear_ssl_errors(self):
|
|
|
|
"""Clear remembered SSL error answers."""
|
2016-07-04 14:01:28 +02:00
|
|
|
self._current_widget().clear_ssl_errors()
|
2016-04-05 02:33:08 +02:00
|
|
|
|
2016-05-10 19:51:11 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2017-10-25 16:24:38 +02:00
|
|
|
def edit_url(self, url=None, bg=False, tab=False, window=False,
|
2017-10-27 04:13:35 +02:00
|
|
|
private=False, related=False):
|
2016-04-05 02:33:08 +02:00
|
|
|
"""Navigate to a url formed in an external editor.
|
|
|
|
|
|
|
|
The editor which should be launched can be configured via the
|
2017-06-16 18:10:04 +02:00
|
|
|
`editor.command` config option.
|
2016-04-05 02:33:08 +02:00
|
|
|
|
|
|
|
Args:
|
|
|
|
url: URL to edit; defaults to the current page url.
|
|
|
|
bg: Open in a new background tab.
|
|
|
|
tab: Open in a new tab.
|
|
|
|
window: Open in a new window.
|
2017-10-25 16:24:38 +02:00
|
|
|
private: Open a new window in private browsing mode.
|
2017-10-27 04:13:35 +02:00
|
|
|
related: If opening a new tab, position the tab as related to the
|
|
|
|
current one (like clicking on a link).
|
2016-04-05 02:33:08 +02:00
|
|
|
"""
|
2016-04-06 06:44:50 +02:00
|
|
|
cmdutils.check_exclusive((tab, bg, window), 'tbw')
|
|
|
|
|
2016-06-30 17:48:06 +02:00
|
|
|
old_url = self._current_url().toString()
|
|
|
|
|
2016-09-14 20:52:32 +02:00
|
|
|
ed = editor.ExternalEditor(self._tabbed_browser)
|
2016-04-05 02:33:08 +02:00
|
|
|
|
|
|
|
# Passthrough for openurl args (e.g. -t, -b, -w)
|
2016-04-05 18:37:50 +02:00
|
|
|
ed.editing_finished.connect(functools.partial(
|
2016-06-30 17:48:06 +02:00
|
|
|
self._open_if_changed, old_url=old_url, bg=bg, tab=tab,
|
2017-10-27 04:13:35 +02:00
|
|
|
window=window, private=private, related=related))
|
2016-04-05 02:33:08 +02:00
|
|
|
|
2016-06-30 17:48:06 +02:00
|
|
|
ed.edit(url or old_url)
|
2016-04-12 04:33:26 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2016-04-12 04:33:26 +02:00
|
|
|
def set_mark(self, key):
|
2016-04-13 04:00:29 +02:00
|
|
|
"""Set a mark at the current scroll position in the current tab.
|
2016-04-12 04:33:26 +02:00
|
|
|
|
|
|
|
Args:
|
|
|
|
key: mark identifier; capital indicates a global mark
|
|
|
|
"""
|
2016-04-14 05:14:53 +02:00
|
|
|
self._tabbed_browser.set_mark(key)
|
2016-04-12 04:33:26 +02:00
|
|
|
|
2017-11-13 07:59:54 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2016-04-12 04:33:26 +02:00
|
|
|
def jump_mark(self, key):
|
2016-04-13 04:00:29 +02:00
|
|
|
"""Jump to the mark named by `key`.
|
2016-04-12 04:33:26 +02:00
|
|
|
|
|
|
|
Args:
|
|
|
|
key: mark identifier; capital indicates a global mark
|
|
|
|
"""
|
2016-04-14 14:39:30 +02:00
|
|
|
self._tabbed_browser.jump_mark(key)
|
2016-06-30 17:48:06 +02:00
|
|
|
|
|
|
|
def _open_if_changed(self, url=None, old_url=None, bg=False, tab=False,
|
2017-10-27 04:13:35 +02:00
|
|
|
window=False, private=False, related=False):
|
2016-06-30 17:48:06 +02:00
|
|
|
"""Open a URL unless it's already open in the tab.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
old_url: The original URL to compare against.
|
|
|
|
url: The URL to open.
|
|
|
|
bg: Open in a new background tab.
|
|
|
|
tab: Open in a new tab.
|
|
|
|
window: Open in a new window.
|
2017-10-25 16:24:38 +02:00
|
|
|
private: Open a new window in private browsing mode.
|
2017-10-27 04:13:35 +02:00
|
|
|
related: If opening a new tab, position the tab as related to the
|
|
|
|
current one (like clicking on a link).
|
2016-06-30 17:48:06 +02:00
|
|
|
"""
|
2017-10-27 04:13:35 +02:00
|
|
|
if bg or tab or window or private or related or url != old_url:
|
2017-10-25 16:24:38 +02:00
|
|
|
self.openurl(url=url, bg=bg, tab=tab, window=window,
|
2017-10-27 04:13:35 +02:00
|
|
|
private=private, related=related)
|
2017-01-02 20:02:02 +01:00
|
|
|
|
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
|
|
|
def fullscreen(self, leave=False):
|
|
|
|
"""Toggle fullscreen mode.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
leave: Only leave fullscreen if it was entered by the page.
|
|
|
|
"""
|
|
|
|
if leave:
|
|
|
|
tab = self._current_widget()
|
|
|
|
try:
|
|
|
|
tab.action.exit_fullscreen()
|
|
|
|
except browsertab.UnsupportedOperationError:
|
|
|
|
pass
|
|
|
|
return
|
|
|
|
|
|
|
|
window = self._tabbed_browser.window()
|
|
|
|
if window.isFullScreen():
|
2017-07-07 11:30:18 +02:00
|
|
|
window.setWindowState(
|
|
|
|
window.state_before_fullscreen & ~Qt.WindowFullScreen)
|
2017-01-02 20:02:02 +01:00
|
|
|
else:
|
2017-07-04 23:30:06 +02:00
|
|
|
window.state_before_fullscreen = window.windowState()
|
2017-01-02 20:02:02 +01:00
|
|
|
window.showFullScreen()
|
2017-07-07 11:30:18 +02:00
|
|
|
log.misc.debug('state before fullscreen: {}'.format(
|
|
|
|
debug.qflags_key(Qt, window.state_before_fullscreen)))
|