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
|
|
|
|
2015-01-03 15:51:31 +01:00
|
|
|
# Copyright 2014-2015 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-09-22 21:51:09 +02:00
|
|
|
import re
|
2014-04-29 18:00:22 +02:00
|
|
|
import os
|
2015-04-20 18:44:58 +02:00
|
|
|
import shlex
|
2014-05-03 14:25:22 +02:00
|
|
|
import subprocess
|
2014-09-22 21:13:42 +02:00
|
|
|
import posixpath
|
2014-09-28 22:13:14 +02:00
|
|
|
import functools
|
2015-05-31 17:56:27 +02:00
|
|
|
import xml.etree.ElementTree
|
2014-04-29 18:00:22 +02:00
|
|
|
|
2015-05-30 19:56:36 +02:00
|
|
|
from PyQt5.QtWebKit import QWebSettings
|
2014-10-07 20:30:31 +02:00
|
|
|
from PyQt5.QtWidgets import QApplication, QTabBar
|
2015-05-15 18:59:46 +02:00
|
|
|
from PyQt5.QtCore import Qt, QUrl, QEvent
|
|
|
|
from PyQt5.QtGui import QClipboard, QKeyEvent
|
2014-05-06 18:31:08 +02:00
|
|
|
from PyQt5.QtPrintSupport import QPrintDialog, QPrintPreviewDialog
|
2015-04-20 20:40:03 +02:00
|
|
|
from PyQt5.QtWebKitWidgets import QWebPage
|
2014-09-15 17:59:54 +02:00
|
|
|
import pygments
|
|
|
|
import pygments.lexers
|
|
|
|
import pygments.formatters
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2014-08-26 20:48:39 +02:00
|
|
|
from qutebrowser.commands import userscripts, cmdexc, cmdutils
|
2014-12-17 11:17:18 +01:00
|
|
|
from qutebrowser.config import config, configexc
|
2015-04-20 20:40:03 +02:00
|
|
|
from qutebrowser.browser import webelem, inspector
|
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,
|
|
|
|
objreg, utils)
|
2015-04-28 16:50:42 +02:00
|
|
|
from qutebrowser.utils.usertypes import KeyMode
|
2014-12-13 17:28:50 +01:00
|
|
|
from qutebrowser.misc import editor
|
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-05-21 15:37:18 +02:00
|
|
|
self._editor = None
|
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
|
|
|
|
2015-05-18 21:31:54 +02:00
|
|
|
def _new_tabbed_browser(self):
|
|
|
|
"""Get a tabbed-browser from a new window."""
|
2015-02-16 22:56:12 +01:00
|
|
|
from qutebrowser.mainwindow import mainwindow
|
2015-05-18 21:31:54 +02:00
|
|
|
new_window = mainwindow.MainWindow()
|
|
|
|
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."""
|
2015-05-18 21:31:54 +02:00
|
|
|
return 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
|
|
|
|
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
|
|
|
|
|
2015-03-30 12:14:15 +02:00
|
|
|
def _open(self, url, tab=False, background=False, window=False):
|
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
|
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
|
2014-10-07 20:39:02 +02:00
|
|
|
cmdutils.check_exclusive((tab, background, window), 'tbw')
|
|
|
|
if window:
|
2015-05-18 21:31:54 +02:00
|
|
|
tabbed_browser = self._new_tabbed_browser()
|
2014-09-28 22:41:43 +02:00
|
|
|
tabbed_browser.tabopen(url)
|
2014-09-22 21:59:21 +02:00
|
|
|
elif tab:
|
2014-09-24 20:21:43 +02:00
|
|
|
tabbed_browser.tabopen(url, background=False, explicit=True)
|
2014-09-22 21:59:21 +02:00
|
|
|
elif background:
|
2014-09-24 20:21:43 +02:00
|
|
|
tabbed_browser.tabopen(url, background=True, explicit=True)
|
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
|
|
|
|
|
2015-04-20 19:29:29 +02:00
|
|
|
def _scroll_percent(self, perc=None, count=None, orientation=None):
|
2014-04-17 09:44:26 +02:00
|
|
|
"""Inner logic for scroll_percent_(x|y).
|
|
|
|
|
|
|
|
Args:
|
|
|
|
perc: How many percent to scroll, or None
|
|
|
|
count: How many percent to scroll, or None
|
|
|
|
orientation: Qt.Horizontal or Qt.Vertical
|
|
|
|
"""
|
|
|
|
if perc is None and count is None:
|
|
|
|
perc = 100
|
|
|
|
elif perc is None:
|
2014-09-02 21:54:07 +02:00
|
|
|
perc = count
|
2015-05-16 15:51:41 +02:00
|
|
|
if perc == 0:
|
|
|
|
self.scroll('top')
|
|
|
|
elif perc == 100:
|
|
|
|
self.scroll('bottom')
|
|
|
|
else:
|
|
|
|
perc = qtutils.check_overflow(perc, 'int', fatal=False)
|
|
|
|
frame = self._current_widget().page().currentFrame()
|
|
|
|
m = frame.scrollBarMaximum(orientation)
|
|
|
|
if m == 0:
|
|
|
|
return
|
|
|
|
frame.setScrollBarValue(orientation, int(m * perc / 100))
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2014-05-17 22:38:07 +02:00
|
|
|
def _tab_move_absolute(self, idx):
|
|
|
|
"""Get an index for moving a tab absolutely.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
idx: The index to get, as passed as count.
|
|
|
|
"""
|
|
|
|
if idx is None:
|
|
|
|
return 0
|
|
|
|
elif idx == 0:
|
2014-09-24 20:21:43 +02:00
|
|
|
return self._count() - 1
|
2014-05-17 22:38:07 +02:00
|
|
|
else:
|
|
|
|
return idx - 1
|
|
|
|
|
|
|
|
def _tab_move_relative(self, direction, delta):
|
|
|
|
"""Get an index for moving a tab relatively.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
direction: + or - for relative moving, None for absolute.
|
|
|
|
delta: Delta to the current tab.
|
|
|
|
"""
|
|
|
|
if delta is None:
|
2014-08-25 15:40:48 +02:00
|
|
|
# We don't set delta to 1 in the function arguments because this
|
|
|
|
# gets called from tab_move which has delta set to None by default.
|
|
|
|
delta = 1
|
2014-05-17 22:38:07 +02:00
|
|
|
if direction == '-':
|
2014-09-24 20:21:43 +02:00
|
|
|
return self._current_index() - delta
|
2014-05-17 22:38:07 +02:00
|
|
|
elif direction == '+':
|
2014-09-24 20:21:43 +02:00
|
|
|
return self._current_index() + delta
|
2014-05-17 22:38:07 +02:00
|
|
|
|
2014-08-03 00:39:39 +02:00
|
|
|
def _tab_focus_last(self):
|
|
|
|
"""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:
|
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
|
|
|
|
2014-05-17 22:38:07 +02:00
|
|
|
def _editor_cleanup(self, oshandle, filename):
|
|
|
|
"""Clean up temporary file when the editor was closed."""
|
|
|
|
try:
|
2014-12-10 18:00:49 +01:00
|
|
|
os.close(oshandle)
|
2014-05-17 22:38:07 +02:00
|
|
|
os.remove(filename)
|
2014-12-10 18:00:49 +01:00
|
|
|
except OSError:
|
2014-08-26 19:10:14 +02:00
|
|
|
raise cmdexc.CommandError("Failed to delete tempfile...")
|
2014-05-17 22:38:07 +02:00
|
|
|
|
2014-10-07 20:30:31 +02:00
|
|
|
def _get_selection_override(self, left, right, opposite):
|
|
|
|
"""Helper function for tab_close to get the tab to select.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
left: Force selecting the tab to the left of the current tab.
|
|
|
|
right: Force selecting the tab to the right of the current tab.
|
2015-03-31 20:49:29 +02:00
|
|
|
opposite: Force selecting the tab in the opposite direction of
|
2014-10-07 20:30:31 +02:00
|
|
|
what's configured in 'tabs->select-on-remove'.
|
|
|
|
|
|
|
|
Return:
|
|
|
|
QTabBar.SelectLeftTab, QTabBar.SelectRightTab, or None if no change
|
|
|
|
should be made.
|
|
|
|
"""
|
2014-10-07 20:39:02 +02:00
|
|
|
cmdutils.check_exclusive((left, right, opposite), 'lro')
|
2014-10-07 20:30:31 +02:00
|
|
|
if left:
|
|
|
|
return QTabBar.SelectLeftTab
|
|
|
|
elif right:
|
|
|
|
return QTabBar.SelectRightTab
|
|
|
|
elif opposite:
|
|
|
|
conf_selection = config.get('tabs', 'select-on-remove')
|
|
|
|
if conf_selection == QTabBar.SelectLeftTab:
|
|
|
|
return QTabBar.SelectRightTab
|
|
|
|
elif conf_selection == QTabBar.SelectRightTab:
|
|
|
|
return QTabBar.SelectLeftTab
|
|
|
|
elif conf_selection == QTabBar.SelectPreviousTab:
|
|
|
|
raise cmdexc.CommandError(
|
|
|
|
"-o is not supported with 'tabs->select-on-remove' set to "
|
|
|
|
"'previous'!")
|
|
|
|
return None
|
|
|
|
|
2015-04-20 19:29:29 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
count='count')
|
|
|
|
def tab_close(self, left=False, right=False, opposite=False, count=None):
|
2014-05-17 22:38:07 +02:00
|
|
|
"""Close the current/[count]th tab.
|
|
|
|
|
|
|
|
Args:
|
2014-10-07 20:30:31 +02:00
|
|
|
left: Force selecting the tab to the left of the current tab.
|
|
|
|
right: Force selecting the tab to the right of the current tab.
|
2015-03-31 20:49:29 +02:00
|
|
|
opposite: Force selecting the tab in the opposite direction of
|
2014-10-07 20:30:31 +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
|
|
|
|
"""
|
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
|
2015-05-18 21:31:54 +02:00
|
|
|
tabbar = self._tabbed_browser.tabBar()
|
2014-10-07 20:30:31 +02:00
|
|
|
selection_override = self._get_selection_override(left, right,
|
|
|
|
opposite)
|
|
|
|
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
|
|
|
|
2014-09-23 21:35:08 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', name='open',
|
2015-04-20 19:29:29 +02:00
|
|
|
maxsplit=0, scope='window', count='count',
|
2015-03-11 21:53:22 +01:00
|
|
|
completion=[usertypes.Completion.url])
|
2015-04-20 19:29:29 +02:00
|
|
|
def openurl(self, url=None, bg=False, tab=False, window=False, count=None):
|
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
|
|
|
|
|
|
|
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.
|
2014-04-17 09:44:26 +02:00
|
|
|
count: The tab index to open the URL in, or None.
|
|
|
|
"""
|
2015-02-27 08:10:00 +01:00
|
|
|
if url is None:
|
|
|
|
if tab or bg or window:
|
|
|
|
url = config.get('general', 'default-page')
|
|
|
|
else:
|
|
|
|
raise cmdexc.CommandError("No URL given, but -t/-b/-w is not "
|
|
|
|
"set!")
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
url = urlutils.fuzzy_url(url)
|
|
|
|
except urlutils.FuzzyUrlError as e:
|
|
|
|
raise cmdexc.CommandError(e)
|
2014-09-28 22:41:43 +02:00
|
|
|
if tab or bg or window:
|
|
|
|
self._open(url, tab, bg, window)
|
2014-04-17 09:44:26 +02:00
|
|
|
else:
|
2014-09-24 19:53:31 +02:00
|
|
|
curtab = self._cntwidget(count)
|
2014-09-02 21:54:07 +02:00
|
|
|
if curtab is None:
|
|
|
|
if count is None:
|
|
|
|
# We want to open a URL in the current tab, but none exists
|
|
|
|
# yet.
|
2015-05-18 21:31:54 +02:00
|
|
|
self._tabbed_browser.tabopen(url)
|
2014-09-02 21:54:07 +02:00
|
|
|
else:
|
|
|
|
# Explicit count with a tab that doesn't exist.
|
|
|
|
return
|
|
|
|
else:
|
|
|
|
curtab.openurl(url)
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', name='reload',
|
2015-04-20 19:29:29 +02:00
|
|
|
scope='window', count='count')
|
|
|
|
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:
|
2014-12-19 15:27:22 +01:00
|
|
|
if force:
|
2014-12-29 22:12:09 +01:00
|
|
|
tab.page().triggerAction(QWebPage.ReloadAndBypassCache)
|
2014-12-19 15:27:22 +01:00
|
|
|
else:
|
|
|
|
tab.reload()
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2015-04-20 19:29:29 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
count='count')
|
|
|
|
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()
|
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', name='print',
|
2015-04-20 19:29:29 +02:00
|
|
|
scope='window', count='count')
|
|
|
|
def printpage(self, preview=False, count=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.
|
|
|
|
"""
|
2014-08-26 19:10:14 +02:00
|
|
|
if not qtutils.check_print_compat():
|
2014-09-02 20:44:58 +02:00
|
|
|
# WORKAROUND (remove this when we bump the requirements to 5.3.0)
|
2014-08-26 19:10:14 +02:00
|
|
|
raise cmdexc.CommandError(
|
|
|
|
"Printing on Qt < 5.3.0 on Windows is broken, please upgrade!")
|
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:
|
2014-09-02 21:54:07 +02:00
|
|
|
if preview:
|
|
|
|
diag = QPrintPreviewDialog()
|
|
|
|
diag.setAttribute(Qt.WA_DeleteOnClose)
|
2014-12-16 15:08:28 +01:00
|
|
|
diag.setWindowFlags(diag.windowFlags() |
|
|
|
|
Qt.WindowMaximizeButtonHint |
|
|
|
|
Qt.WindowMinimizeButtonHint)
|
2014-09-02 21:54:07 +02:00
|
|
|
diag.paintRequested.connect(tab.print)
|
|
|
|
diag.exec_()
|
|
|
|
else:
|
|
|
|
diag = QPrintDialog()
|
|
|
|
diag.setAttribute(Qt.WA_DeleteOnClose)
|
2014-09-03 10:47:27 +02:00
|
|
|
diag.open(lambda: tab.print(diag.printer()))
|
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.
|
|
|
|
"""
|
2014-10-06 17:58:40 +02:00
|
|
|
if bg and window:
|
|
|
|
raise cmdexc.CommandError("Only one of -b/-w can be given!")
|
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())
|
2015-03-08 14:54:42 +01:00
|
|
|
# The new tab could be in a new tabbed_browser (e.g. because of
|
|
|
|
# tabs-are-windows being set)
|
2015-05-18 21:31:54 +02:00
|
|
|
if window:
|
|
|
|
new_tabbed_browser = self._new_tabbed_browser()
|
|
|
|
else:
|
|
|
|
new_tabbed_browser = self._tabbed_browser
|
2015-03-08 15:01:38 +01:00
|
|
|
newtab = new_tabbed_browser.tabopen(background=bg, explicit=True)
|
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)
|
|
|
|
new_tabbed_browser.set_page_title(idx, cur_title)
|
|
|
|
new_tabbed_browser.setTabIcon(idx, curtab.icon())
|
2015-01-26 07:18:07 +01:00
|
|
|
newtab.keep_icon = True
|
2015-01-26 07:20:03 +01:00
|
|
|
newtab.setZoomFactor(curtab.zoomFactor())
|
2014-09-27 23:13:11 +02:00
|
|
|
history = qtutils.serialize(curtab.history())
|
|
|
|
qtutils.deserialize(history, newtab.history())
|
|
|
|
return newtab
|
|
|
|
|
2015-03-30 12:14:15 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
|
|
|
def tab_detach(self):
|
|
|
|
"""Detach the current tab to its own window."""
|
|
|
|
url = self._current_url()
|
|
|
|
self._open(url, window=True)
|
|
|
|
cur_widget = self._current_widget()
|
2015-05-18 21:31:54 +02:00
|
|
|
self._tabbed_browser.close_tab(cur_widget)
|
2015-03-30 12:14:15 +02:00
|
|
|
|
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."""
|
2014-10-06 19:47:35 +02:00
|
|
|
if (not forward and not
|
|
|
|
self._current_widget().page().history().canGoBack()):
|
|
|
|
raise cmdexc.CommandError("At beginning of history.")
|
|
|
|
if (forward and not
|
|
|
|
self._current_widget().page().history().canGoForward()):
|
|
|
|
raise cmdexc.CommandError("At end of history.")
|
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()
|
|
|
|
for _ in range(count):
|
|
|
|
if forward:
|
2014-10-06 19:47:35 +02:00
|
|
|
widget.forward()
|
2014-09-27 23:13:27 +02:00
|
|
|
else:
|
2014-10-06 19:47:35 +02:00
|
|
|
widget.back()
|
2014-09-27 23:13:27 +02:00
|
|
|
|
2015-04-20 19:29:29 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
count='count')
|
|
|
|
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
|
|
|
|
2015-04-20 19:29:29 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
count='count')
|
|
|
|
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
|
|
|
|
2014-10-06 17:58:40 +02:00
|
|
|
def _navigate_incdec(self, url, incdec, tab, background, window):
|
2014-09-22 22:36:31 +02:00
|
|
|
"""Helper method for :navigate when `where' is increment/decrement.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
url: The current url.
|
|
|
|
incdec: Either 'increment' or 'decrement'.
|
2014-10-06 17:58:40 +02:00
|
|
|
tab: Whether to open the link in a new tab.
|
|
|
|
background: Open the link in a new background tab.
|
|
|
|
window: Open the link in a new window.
|
2014-09-22 22:36:31 +02:00
|
|
|
"""
|
|
|
|
encoded = bytes(url.toEncoded()).decode('ascii')
|
|
|
|
# Get the last number in a string
|
|
|
|
match = re.match(r'(.*\D|^)(\d+)(.*)', encoded)
|
|
|
|
if not match:
|
|
|
|
raise cmdexc.CommandError("No number found in URL!")
|
|
|
|
pre, number, post = match.groups()
|
|
|
|
if not number:
|
|
|
|
raise cmdexc.CommandError("No number found in URL!")
|
|
|
|
try:
|
|
|
|
val = int(number)
|
|
|
|
except ValueError:
|
|
|
|
raise cmdexc.CommandError("Could not parse number '{}'.".format(
|
|
|
|
number))
|
|
|
|
if incdec == 'decrement':
|
|
|
|
if val <= 0:
|
|
|
|
raise cmdexc.CommandError("Can't decrement {}!".format(val))
|
|
|
|
val -= 1
|
|
|
|
elif incdec == 'increment':
|
|
|
|
val += 1
|
|
|
|
else:
|
|
|
|
raise ValueError("Invalid value {} for indec!".format(incdec))
|
|
|
|
urlstr = ''.join([pre, str(val), post]).encode('ascii')
|
|
|
|
new_url = QUrl.fromEncoded(urlstr)
|
2014-10-06 17:58:40 +02:00
|
|
|
self._open(new_url, tab, background, window)
|
2014-09-22 22:36:31 +02:00
|
|
|
|
2014-10-06 17:58:40 +02:00
|
|
|
def _navigate_up(self, url, tab, background, window):
|
2014-09-22 22:36:31 +02:00
|
|
|
"""Helper method for :navigate when `where' is up.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
url: The current url.
|
|
|
|
tab: Whether to open the link in a new tab.
|
2014-10-06 17:58:40 +02:00
|
|
|
background: Open the link in a new background tab.
|
|
|
|
window: Open the link in a new window.
|
2014-09-22 22:36:31 +02:00
|
|
|
"""
|
|
|
|
path = url.path()
|
|
|
|
if not path or path == '/':
|
|
|
|
raise cmdexc.CommandError("Can't go up!")
|
|
|
|
new_path = posixpath.join(path, posixpath.pardir)
|
|
|
|
url.setPath(new_path)
|
2014-10-06 17:58:40 +02:00
|
|
|
self._open(url, tab, background, window)
|
2014-09-22 22:36:31 +02:00
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2014-10-20 17:20:39 +02:00
|
|
|
def navigate(self, where: {'type': ('prev', 'next', 'up', 'increment',
|
|
|
|
'decrement')},
|
2014-10-06 17:58:40 +02:00
|
|
|
tab=False, bg=False, window=False):
|
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.
|
2014-08-03 00:33:39 +02:00
|
|
|
"""
|
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()
|
|
|
|
frame = widget.page().currentFrame()
|
2014-09-24 20:21:43 +02:00
|
|
|
url = self._current_url()
|
2014-09-22 21:08:11 +02:00
|
|
|
if frame is None:
|
|
|
|
raise cmdexc.CommandError("No frame focused!")
|
2015-03-19 22:28:24 +01:00
|
|
|
hintmanager = objreg.get('hintmanager', scope='tab', tab='current')
|
2014-09-22 20:57:50 +02:00
|
|
|
if where == 'prev':
|
2014-10-06 17:58:40 +02:00
|
|
|
hintmanager.follow_prevnext(frame, url, prev=True, tab=tab,
|
2014-10-06 19:53:50 +02:00
|
|
|
background=bg, window=window)
|
2014-09-22 20:57:50 +02:00
|
|
|
elif where == 'next':
|
2014-10-06 17:58:40 +02:00
|
|
|
hintmanager.follow_prevnext(frame, url, prev=False, tab=tab,
|
2014-10-06 19:53:50 +02:00
|
|
|
background=bg, window=window)
|
2014-09-22 21:13:42 +02:00
|
|
|
elif where == 'up':
|
2014-10-06 19:53:50 +02:00
|
|
|
self._navigate_up(url, tab, bg, window)
|
2014-09-22 21:51:09 +02:00
|
|
|
elif where in ('decrement', 'increment'):
|
2014-10-06 19:53:50 +02:00
|
|
|
self._navigate_incdec(url, where, tab, bg, window)
|
2014-09-22 21:08:11 +02:00
|
|
|
else:
|
|
|
|
raise ValueError("Got called with invalid value {} for "
|
|
|
|
"`where'.".format(where))
|
2014-05-01 15:27:32 +02:00
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
2015-04-20 19:29:29 +02:00
|
|
|
scope='window', count='count')
|
2015-05-15 18:59:46 +02:00
|
|
|
def scroll_px(self, dx: {'type': float}, dy: {'type': float}, count=1):
|
|
|
|
"""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.
|
|
|
|
dy: How much to scroll in x-direction.
|
|
|
|
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')
|
2014-09-03 11:32:56 +02:00
|
|
|
self._current_widget().page().currentFrame().scroll(dx, dy)
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2015-05-15 18:59:46 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
|
|
|
scope='window', count='count')
|
|
|
|
def scroll(self,
|
|
|
|
direction: {'type': (str, float)},
|
|
|
|
dy: {'type': float, 'hide': True}=None,
|
|
|
|
count=1):
|
|
|
|
"""Scroll the current tab in the given direction.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
direction: In which direction to scroll
|
|
|
|
(up/down/left/right/top/bottom).
|
|
|
|
dy: Deprecated argument to support the old dx/dy form.
|
|
|
|
count: multiplier
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
# Check for deprecated dx/dy form (like with scroll-px).
|
|
|
|
dx = float(direction)
|
|
|
|
dy = float(dy)
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
# Invalid values will get handled later.
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
message.warning(self._win_id, ":scroll with dx/dy arguments is "
|
|
|
|
"deprecated - use :scroll-px instead!")
|
|
|
|
self.scroll_px(dx, dy, count=count)
|
|
|
|
return
|
|
|
|
|
|
|
|
fake_keys = {
|
|
|
|
'up': Qt.Key_Up,
|
|
|
|
'down': Qt.Key_Down,
|
|
|
|
'left': Qt.Key_Left,
|
|
|
|
'right': Qt.Key_Right,
|
|
|
|
'top': Qt.Key_Home,
|
|
|
|
'bottom': Qt.Key_End,
|
|
|
|
'page-up': Qt.Key_PageUp,
|
|
|
|
'page-down': Qt.Key_PageDown,
|
|
|
|
}
|
|
|
|
try:
|
|
|
|
key = fake_keys[direction]
|
|
|
|
except KeyError:
|
|
|
|
raise cmdexc.CommandError("Invalid value {!r} for direction - "
|
|
|
|
"expected one of: {}".format(
|
|
|
|
direction, ', '.join(fake_keys)))
|
|
|
|
widget = self._current_widget()
|
|
|
|
press_evt = QKeyEvent(QEvent.KeyPress, key, Qt.NoModifier, 0, 0, 0)
|
|
|
|
release_evt = QKeyEvent(QEvent.KeyRelease, key, Qt.NoModifier, 0, 0, 0)
|
|
|
|
|
2015-05-16 15:51:41 +02:00
|
|
|
# Count doesn't make sense with top/bottom
|
|
|
|
if direction in ('top', 'bottom'):
|
|
|
|
count = 1
|
|
|
|
|
2015-05-15 18:59:46 +02:00
|
|
|
for _ in range(count):
|
|
|
|
widget.keyPressEvent(press_evt)
|
|
|
|
widget.keyReleaseEvent(release_evt)
|
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
2015-04-20 19:29:29 +02:00
|
|
|
scope='window', count='count')
|
2014-10-20 17:20:39 +02:00
|
|
|
def scroll_perc(self, perc: {'type': float}=None,
|
2015-04-20 19:29:29 +02:00
|
|
|
horizontal: {'flag': 'x'}=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.
|
|
|
|
"""
|
2014-09-02 21:54:07 +02:00
|
|
|
self._scroll_percent(perc, count,
|
|
|
|
Qt.Horizontal if horizontal else Qt.Vertical)
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
2015-04-20 19:29:29 +02:00
|
|
|
scope='window', count='count')
|
|
|
|
def scroll_page(self, x: {'type': float}, y: {'type': float}, 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.
|
2014-04-17 09:44:26 +02:00
|
|
|
count: multiplier
|
|
|
|
"""
|
2015-05-15 18:59:46 +02:00
|
|
|
mult_x = count * x
|
|
|
|
mult_y = count * y
|
|
|
|
if mult_y.is_integer():
|
|
|
|
if mult_y == 0:
|
|
|
|
pass
|
|
|
|
elif mult_y < 0:
|
2015-05-15 21:32:42 +02:00
|
|
|
self.scroll('page-up', count=-int(mult_y))
|
2015-05-15 18:59:46 +02:00
|
|
|
elif mult_y > 0:
|
2015-05-15 21:32:42 +02:00
|
|
|
self.scroll('page-down', count=int(mult_y))
|
2015-05-15 18:59:46 +02:00
|
|
|
mult_y = 0
|
|
|
|
if mult_x == 0 and mult_y == 0:
|
|
|
|
return
|
2014-09-03 11:32:56 +02:00
|
|
|
frame = self._current_widget().page().currentFrame()
|
2014-04-22 14:17:17 +02:00
|
|
|
size = frame.geometry()
|
2015-05-15 18:59:46 +02:00
|
|
|
dx = mult_x * size.width()
|
|
|
|
dy = mult_y * size.height()
|
2014-05-14 23:29:18 +02:00
|
|
|
cmdutils.check_overflow(dx, 'int')
|
|
|
|
cmdutils.check_overflow(dy, 'int')
|
|
|
|
frame.scroll(dx, dy)
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2014-09-02 21:54:07 +02:00
|
|
|
def yank(self, title=False, sel=False):
|
|
|
|
"""Yank the current URL/title to the clipboard or primary selection.
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2014-05-18 08:14:11 +02:00
|
|
|
Args:
|
2014-09-13 00:22:27 +02:00
|
|
|
sel: Use the primary selection instead of the clipboard.
|
|
|
|
title: Yank the title instead of the URL.
|
2014-05-18 08:14:11 +02:00
|
|
|
"""
|
2014-06-24 12:04:36 +02:00
|
|
|
clipboard = QApplication.clipboard()
|
2014-09-02 21:54:07 +02:00
|
|
|
if title:
|
2015-05-18 21:31:54 +02:00
|
|
|
s = self._tabbed_browser.page_title(self._current_index())
|
2014-05-19 11:56:51 +02:00
|
|
|
else:
|
2014-09-24 20:21:43 +02:00
|
|
|
s = self._current_url().toString(
|
2014-09-02 21:54:07 +02:00
|
|
|
QUrl.FullyEncoded | QUrl.RemovePassword)
|
2014-06-24 12:04:36 +02:00
|
|
|
if sel and clipboard.supportsSelection():
|
2014-05-19 11:56:51 +02:00
|
|
|
mode = QClipboard.Selection
|
|
|
|
target = "primary selection"
|
|
|
|
else:
|
|
|
|
mode = QClipboard.Clipboard
|
|
|
|
target = "clipboard"
|
2014-09-02 21:54:07 +02:00
|
|
|
log.misc.debug("Yanking to {}: '{}'".format(target, s))
|
|
|
|
clipboard.setText(s, mode)
|
2014-09-03 10:47:27 +02:00
|
|
|
what = 'Title' if title else 'URL'
|
2014-09-28 22:13:14 +02:00
|
|
|
message.info(self._win_id, "{} yanked to {}".format(what, target))
|
2014-04-17 09:44:26 +02:00
|
|
|
|
2015-04-20 19:29:29 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
count='count')
|
|
|
|
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()
|
2014-04-17 09:44:26 +02:00
|
|
|
tab.zoom(count)
|
|
|
|
|
2015-04-20 19:29:29 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
count='count')
|
|
|
|
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()
|
2014-04-17 09:44:26 +02:00
|
|
|
tab.zoom(-count)
|
2014-04-29 18:00:22 +02:00
|
|
|
|
2015-04-20 19:29:29 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
count='count')
|
|
|
|
def zoom(self, zoom: {'type': int}=None, count=None):
|
2014-08-03 00:33:39 +02:00
|
|
|
"""Set the zoom level for the current tab.
|
|
|
|
|
|
|
|
The zoom can be given as argument or as [count]. If neither of both is
|
2014-12-22 18:04:28 +01:00
|
|
|
given, the zoom is set to the default zoom.
|
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
|
|
|
"""
|
2014-05-09 19:12:08 +02:00
|
|
|
try:
|
2014-12-22 18:04:28 +01:00
|
|
|
default = config.get('ui', 'default-zoom')
|
|
|
|
level = cmdutils.arg_or_count(zoom, count, default=default)
|
2014-05-09 19:12:08 +02:00
|
|
|
except ValueError as e:
|
2014-08-26 19:10:14 +02:00
|
|
|
raise cmdexc.CommandError(e)
|
2014-09-03 11:32:56 +02:00
|
|
|
tab = self._current_widget()
|
2014-05-09 14:20:26 +02:00
|
|
|
tab.zoom_perc(level)
|
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2014-10-07 20:56:47 +02:00
|
|
|
def tab_only(self, left=False, right=False):
|
|
|
|
"""Close all tabs except for the current one.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
left: Keep tabs to the left of the current.
|
|
|
|
right: Keep tabs to the right of the current.
|
|
|
|
"""
|
|
|
|
cmdutils.check_exclusive((left, right), 'lr')
|
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
|
|
|
|
|
2015-05-18 21:31:54 +02:00
|
|
|
for i, tab in enumerate(self._tabbed_browser.widgets()):
|
2014-10-07 20:56:47 +02:00
|
|
|
if (i == cur_idx or (left and i < cur_idx) or
|
|
|
|
(right and i > cur_idx)):
|
2014-05-17 22:38:07 +02:00
|
|
|
continue
|
2014-10-07 20:56:47 +02:00
|
|
|
else:
|
2015-05-18 21:31:54 +02:00
|
|
|
self._tabbed_browser.close_tab(tab)
|
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):
|
2014-08-03 00:33:39 +02:00
|
|
|
"""Re-open a closed tab (optionally skipping [count] closed 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
|
|
|
|
2015-04-20 19:29:29 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
count='count')
|
|
|
|
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.
|
|
|
|
"""
|
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)
|
2014-08-06 08:10:32 +02:00
|
|
|
elif config.get('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
|
|
|
|
2015-04-20 19:29:29 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
count='count')
|
|
|
|
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.
|
|
|
|
"""
|
2014-09-24 20:21:43 +02:00
|
|
|
newidx = self._current_index() + count
|
|
|
|
if newidx < self._count():
|
|
|
|
self._set_current_index(newidx)
|
2014-08-06 08:10:32 +02:00
|
|
|
elif config.get('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
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2014-09-28 22:41:43 +02:00
|
|
|
def paste(self, sel=False, tab=False, bg=False, window=False):
|
2014-05-17 22:38:07 +02:00
|
|
|
"""Open a page from the clipboard.
|
|
|
|
|
|
|
|
Args:
|
2014-09-13 00:22:27 +02:00
|
|
|
sel: Use the primary selection instead of the clipboard.
|
|
|
|
tab: Open in a new tab.
|
|
|
|
bg: Open in a background tab.
|
2014-09-28 22:41:43 +02:00
|
|
|
window: Open in new window.
|
2014-05-17 22:38:07 +02:00
|
|
|
"""
|
2014-08-25 15:28:07 +02:00
|
|
|
clipboard = QApplication.clipboard()
|
|
|
|
if sel and clipboard.supportsSelection():
|
|
|
|
mode = QClipboard.Selection
|
|
|
|
target = "Primary selection"
|
|
|
|
else:
|
|
|
|
mode = QClipboard.Clipboard
|
|
|
|
target = "Clipboard"
|
|
|
|
text = clipboard.text(mode)
|
2014-06-20 16:33:01 +02:00
|
|
|
if not text:
|
2014-08-26 19:10:14 +02:00
|
|
|
raise cmdexc.CommandError("{} is empty.".format(target))
|
2014-08-25 15:28:07 +02:00
|
|
|
log.misc.debug("{} contained: '{}'".format(target, text))
|
2014-06-20 16:33:01 +02:00
|
|
|
try:
|
|
|
|
url = urlutils.fuzzy_url(text)
|
2014-06-20 23:57:52 +02:00
|
|
|
except urlutils.FuzzyUrlError as e:
|
2014-08-26 19:10:14 +02:00
|
|
|
raise cmdexc.CommandError(e)
|
2014-09-28 22:41:43 +02:00
|
|
|
self._open(url, tab, bg, window)
|
2014-05-17 22:38:07 +02:00
|
|
|
|
2015-04-20 19:29:29 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
count='count')
|
|
|
|
def tab_focus(self, index: {'type': (int, 'last')}=None, count=None):
|
2014-05-17 22:38:07 +02:00
|
|
|
"""Select the tab given as argument/[count].
|
|
|
|
|
|
|
|
Args:
|
2014-08-03 00:39:39 +02:00
|
|
|
index: The tab index to focus, starting with 1. The special value
|
|
|
|
`last` focuses the last focused 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
|
|
|
"""
|
2014-08-03 00:39:39 +02:00
|
|
|
if index == 'last':
|
|
|
|
self._tab_focus_last()
|
|
|
|
return
|
2014-05-17 22:38:07 +02:00
|
|
|
try:
|
|
|
|
idx = cmdutils.arg_or_count(index, count, default=1,
|
2014-09-24 20:21:43 +02:00
|
|
|
countzero=self._count())
|
2014-05-17 22:38:07 +02:00
|
|
|
except ValueError as e:
|
2014-08-26 19:10:14 +02:00
|
|
|
raise cmdexc.CommandError(e)
|
2014-05-17 22:38:07 +02:00
|
|
|
cmdutils.check_overflow(idx + 1, 'int')
|
2014-09-24 20:21:43 +02:00
|
|
|
if 1 <= idx <= self._count():
|
|
|
|
self._set_current_index(idx - 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(
|
|
|
|
idx))
|
2014-05-17 22:38:07 +02:00
|
|
|
|
2015-04-20 19:29:29 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
count='count')
|
|
|
|
def tab_move(self, direction: {'type': ('+', '-')}=None, count=None):
|
2014-05-17 22:38:07 +02:00
|
|
|
"""Move the current tab.
|
|
|
|
|
|
|
|
Args:
|
2014-09-13 00:22:27 +02:00
|
|
|
direction: `+` or `-` for relative moving, not given for absolute
|
|
|
|
moving.
|
2014-08-03 00:33:39 +02:00
|
|
|
count: If moving absolutely: New position (default: 0)
|
2014-05-17 22:38:07 +02:00
|
|
|
If moving relatively: Offset.
|
|
|
|
"""
|
|
|
|
if direction is None:
|
|
|
|
new_idx = self._tab_move_absolute(count)
|
|
|
|
elif direction in '+-':
|
|
|
|
try:
|
|
|
|
new_idx = self._tab_move_relative(direction, count)
|
|
|
|
except ValueError:
|
2014-08-26 19:10:14 +02:00
|
|
|
raise cmdexc.CommandError("Count must be given for relative "
|
|
|
|
"moving!")
|
2014-05-17 22:38:07 +02:00
|
|
|
else:
|
2014-08-26 19:10:14 +02:00
|
|
|
raise cmdexc.CommandError("Invalid direction '{}'!".format(
|
|
|
|
direction))
|
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(
|
2014-05-17 22:38:07 +02:00
|
|
|
new_idx))
|
2014-09-03 11:32:56 +02:00
|
|
|
tab = self._current_widget()
|
2014-09-24 20:21:43 +02:00
|
|
|
cur_idx = self._current_index()
|
2015-05-18 21:31:54 +02:00
|
|
|
icon = self._tabbed_browser.tabIcon(cur_idx)
|
|
|
|
label = self._tabbed_browser.page_title(cur_idx)
|
2014-05-17 22:38:07 +02:00
|
|
|
cmdutils.check_overflow(cur_idx, 'int')
|
|
|
|
cmdutils.check_overflow(new_idx, 'int')
|
2015-05-18 21:31:54 +02:00
|
|
|
self._tabbed_browser.setUpdatesEnabled(False)
|
2014-08-25 15:41:19 +02:00
|
|
|
try:
|
2015-05-18 21:31:54 +02:00
|
|
|
self._tabbed_browser.removeTab(cur_idx)
|
|
|
|
self._tabbed_browser.insertTab(new_idx, tab, icon, label)
|
2014-09-24 20:21:43 +02:00
|
|
|
self._set_current_index(new_idx)
|
2014-08-25 15:41:19 +02:00
|
|
|
finally:
|
2015-05-18 21:31:54 +02:00
|
|
|
self._tabbed_browser.setUpdatesEnabled(True)
|
2014-05-17 22:38:07 +02:00
|
|
|
|
2015-04-20 19:29:29 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
win_id='win_id')
|
|
|
|
def spawn(self, win_id, userscript=False, quiet=False, *args):
|
2014-07-28 01:40:25 +02:00
|
|
|
"""Spawn a command in a shell.
|
2014-05-03 14:25:22 +02:00
|
|
|
|
2014-07-28 01:40:25 +02:00
|
|
|
Note the {url} variable which gets replaced by the current URL might be
|
|
|
|
useful here.
|
2014-05-03 14:25:22 +02:00
|
|
|
|
2014-07-16 20:09:41 +02:00
|
|
|
//
|
|
|
|
|
2014-07-28 01:40:25 +02:00
|
|
|
We use subprocess rather than Qt's QProcess here because we really
|
|
|
|
don't care about the process anymore as soon as it's spawned.
|
2014-05-03 14:27:44 +02:00
|
|
|
|
2014-05-03 14:25:22 +02:00
|
|
|
Args:
|
2015-01-15 22:28:04 +01:00
|
|
|
userscript: Run the command as an userscript.
|
2015-04-20 18:44:58 +02:00
|
|
|
quiet: Don't print the commandline being executed.
|
2014-08-03 00:33:39 +02:00
|
|
|
*args: The commandline to execute.
|
2014-05-03 14:25:22 +02:00
|
|
|
"""
|
2015-01-15 22:28:04 +01:00
|
|
|
log.procs.debug("Executing: {}, userscript={}".format(
|
|
|
|
args, userscript))
|
2015-04-20 18:44:58 +02:00
|
|
|
if not quiet:
|
|
|
|
fake_cmdline = ' '.join(shlex.quote(arg) for arg in args)
|
|
|
|
message.info(win_id, 'Executing: ' + fake_cmdline)
|
2015-01-15 22:28:04 +01:00
|
|
|
if userscript:
|
2015-01-22 07:10:32 +01:00
|
|
|
cmd = args[0]
|
|
|
|
args = [] if not args else args[1:]
|
|
|
|
self.run_userscript(cmd, *args)
|
2015-01-15 22:28:04 +01:00
|
|
|
else:
|
|
|
|
try:
|
|
|
|
subprocess.Popen(args)
|
|
|
|
except OSError as e:
|
|
|
|
raise cmdexc.CommandError("Error while spawning command: "
|
|
|
|
"{}".format(e))
|
2014-05-03 14:25:22 +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."""
|
|
|
|
self.openurl(config.get('general', 'startpage')[0])
|
|
|
|
|
2015-01-15 22:28:04 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
deprecated='Use :spawn --userscript instead!')
|
2014-09-14 23:56:19 +02:00
|
|
|
def run_userscript(self, cmd, *args: {'nargs': '*'}):
|
2014-07-29 01:45:42 +02:00
|
|
|
"""Run an userscript given as argument.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
cmd: The userscript to run.
|
|
|
|
args: Arguments to pass to the userscript.
|
|
|
|
"""
|
2014-12-29 22:17:58 +01:00
|
|
|
cmd = os.path.expanduser(cmd)
|
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
|
|
|
|
2015-05-18 21:31:54 +02:00
|
|
|
webview = self._tabbed_browser.currentWidget()
|
2015-04-20 07:50:47 +02:00
|
|
|
if webview is None:
|
|
|
|
mainframe = None
|
|
|
|
else:
|
2015-04-09 17:45:16 +02:00
|
|
|
if webview.hasSelection():
|
|
|
|
env['QUTE_SELECTED_TEXT'] = webview.selectedText()
|
|
|
|
env['QUTE_SELECTED_HTML'] = webview.selectedHtml()
|
|
|
|
mainframe = webview.page().mainFrame()
|
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)
|
|
|
|
|
2015-04-20 07:50:47 +02:00
|
|
|
env.update(userscripts.store_source(mainframe))
|
2015-01-04 20:16:15 +01:00
|
|
|
userscripts.run(cmd, *args, win_id=self._win_id, env=env)
|
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')
|
|
|
|
quickmark_manager.prompt_save(self._win_id, 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',
|
2014-12-11 20:25:54 +01:00
|
|
|
maxsplit=0,
|
2014-10-20 00:29:19 +02:00
|
|
|
completion=[usertypes.Completion.quickmark_by_name])
|
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
|
|
|
"""
|
2014-10-15 16:55:55 +02:00
|
|
|
url = objreg.get('quickmark-manager').get(name)
|
2014-09-28 22:41:43 +02:00
|
|
|
self._open(url, tab, bg, window)
|
2014-05-22 16:44:47 +02:00
|
|
|
|
2015-05-30 19:56:36 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', name='select-follow',
|
|
|
|
scope='window')
|
2015-05-30 19:15:53 +02:00
|
|
|
def select_follow(self, tab=False):
|
|
|
|
"""Follow the selected text.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
tab: Load the selected link in a new tab.
|
|
|
|
"""
|
2015-05-30 16:37:25 +02:00
|
|
|
widget = self._current_widget()
|
2015-05-31 18:18:27 +02:00
|
|
|
page = widget.page()
|
2015-05-30 19:56:36 +02:00
|
|
|
if QWebSettings.globalSettings().testAttribute(
|
|
|
|
QWebSettings.JavascriptEnabled):
|
|
|
|
if tab:
|
2015-05-31 18:18:27 +02:00
|
|
|
page.open_target = usertypes.ClickTarget.tab
|
|
|
|
page.currentFrame().evaluateJavaScript(
|
2015-05-30 19:56:36 +02:00
|
|
|
'window.getSelection().anchorNode.parentNode.click()')
|
|
|
|
else:
|
2015-05-31 00:03:39 +02:00
|
|
|
try:
|
2015-05-31 17:56:27 +02:00
|
|
|
selected_element = xml.etree.ElementTree.fromstring(
|
2015-05-31 00:03:39 +02:00
|
|
|
'<html>' + widget.selectedHtml() + '</html>').find('a')
|
2015-05-31 17:56:27 +02:00
|
|
|
except xml.etree.ElementTree.ParseError:
|
2015-05-31 18:07:08 +02:00
|
|
|
raise cmdexc.CommandError('Parse error!')
|
2015-05-31 18:02:15 +02:00
|
|
|
|
|
|
|
if selected_element is not None:
|
2015-05-31 18:07:08 +02:00
|
|
|
try:
|
|
|
|
url = selected_element.attrib['href']
|
|
|
|
except KeyError:
|
|
|
|
raise cmdexc.CommandError('Anchor elment without href!')
|
2015-05-31 18:13:37 +02:00
|
|
|
url = self._current_url().resolved(QUrl(url))
|
|
|
|
self._open(url, tab)
|
2015-05-30 16:37:25 +02:00
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', name='inspector',
|
|
|
|
scope='window')
|
2014-05-26 15:35:05 +02:00
|
|
|
def toggle_inspector(self):
|
|
|
|
"""Toggle the web inspector."""
|
2014-09-03 11:32:56 +02:00
|
|
|
cur = self._current_widget()
|
2014-05-26 15:35:05 +02:00
|
|
|
if cur.inspector is None:
|
2014-06-03 20:28:51 +02:00
|
|
|
if not config.get('general', 'developer-extras'):
|
2014-08-26 19:10:14 +02:00
|
|
|
raise cmdexc.CommandError(
|
|
|
|
"Please enable developer-extras before using the "
|
|
|
|
"webinspector!")
|
2015-04-20 20:40:03 +02:00
|
|
|
cur.inspector = inspector.WebInspector()
|
2014-05-27 16:04:45 +02:00
|
|
|
cur.inspector.setPage(cur.page())
|
2014-05-26 15:35:05 +02:00
|
|
|
cur.inspector.show()
|
|
|
|
elif cur.inspector.isVisible():
|
|
|
|
cur.inspector.hide()
|
|
|
|
else:
|
2014-06-03 20:28:51 +02:00
|
|
|
if not config.get('general', 'developer-extras'):
|
2014-08-26 19:10:14 +02:00
|
|
|
raise cmdexc.CommandError(
|
|
|
|
"Please enable developer-extras before using the "
|
|
|
|
"webinspector!")
|
2014-05-26 15:35:05 +02:00
|
|
|
else:
|
|
|
|
cur.inspector.show()
|
|
|
|
|
2014-09-28 22:13:14 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
2015-02-08 22:08:16 +01:00
|
|
|
def download(self, url=None, 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
|
|
|
|
|
|
|
Args:
|
2015-02-09 17:38:50 +01:00
|
|
|
url: The URL to download. If not given, download the current page.
|
2015-02-08 22:08:16 +01:00
|
|
|
dest: The file path to write the download to, or None to ask.
|
|
|
|
"""
|
2014-11-11 21:36:47 +01:00
|
|
|
download_manager = objreg.get('download-manager', scope='window',
|
|
|
|
window=self._win_id)
|
2015-02-09 12:06:49 +01:00
|
|
|
if url:
|
2015-02-08 22:08:16 +01:00
|
|
|
url = urlutils.qurl_from_user_input(url)
|
|
|
|
urlutils.raise_cmdexc_if_invalid(url)
|
|
|
|
download_manager.get(url, filename=dest)
|
|
|
|
else:
|
|
|
|
page = self._current_widget().page()
|
|
|
|
download_manager.get(self._current_url(), page)
|
2014-06-19 17:58:46 +02:00
|
|
|
|
2015-02-09 12:06:49 +01:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
deprecated="Use :download instead.")
|
|
|
|
def download_page(self):
|
|
|
|
"""Download the current page."""
|
|
|
|
self.download()
|
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):
|
|
|
|
"""Show the source of the current page."""
|
2014-09-15 22:01:13 +02:00
|
|
|
# pylint: disable=no-member
|
2015-03-11 20:14:39 +01:00
|
|
|
# https://bitbucket.org/logilab/pylint/issue/491/
|
2014-09-15 17:59:54 +02:00
|
|
|
widget = self._current_widget()
|
|
|
|
if widget.viewing_source:
|
|
|
|
raise cmdexc.CommandError("Already viewing source!")
|
|
|
|
frame = widget.page().currentFrame()
|
|
|
|
html = frame.toHtml()
|
|
|
|
lexer = pygments.lexers.HtmlLexer()
|
2014-09-15 18:19:56 +02:00
|
|
|
formatter = pygments.formatters.HtmlFormatter(
|
|
|
|
full=True, linenos='table')
|
2014-09-15 17:59:54 +02:00
|
|
|
highlighted = pygments.highlight(html, lexer, formatter)
|
2014-10-02 06:06:08 +02:00
|
|
|
current_url = self._current_url()
|
2015-05-18 21:31:54 +02:00
|
|
|
tab = self._tabbed_browser.tabopen(explicit=True)
|
2014-10-02 06:06:08 +02:00
|
|
|
tab.setHtml(highlighted, current_url)
|
2014-09-15 17:59:54 +02:00
|
|
|
tab.viewing_source = True
|
|
|
|
|
2014-09-23 21:35:08 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', name='help',
|
2014-09-28 22:13:14 +02:00
|
|
|
completion=[usertypes.Completion.helptopic],
|
|
|
|
scope='window')
|
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.
|
|
|
|
- __section__\->__option__ for settings.
|
|
|
|
"""
|
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)
|
|
|
|
elif '->' in topic:
|
|
|
|
parts = topic.split('->')
|
|
|
|
if len(parts) != 2:
|
|
|
|
raise cmdexc.CommandError("Invalid help topic {}!".format(
|
|
|
|
topic))
|
|
|
|
try:
|
|
|
|
config.get(*parts)
|
2014-12-17 11:17:18 +01:00
|
|
|
except configexc.NoSectionError:
|
2014-09-08 06:57:22 +02:00
|
|
|
raise cmdexc.CommandError("Invalid section {}!".format(
|
|
|
|
parts[0]))
|
2014-12-17 11:17:18 +01:00
|
|
|
except configexc.NoOptionError:
|
2014-09-08 06:57:22 +02:00
|
|
|
raise cmdexc.CommandError("Invalid option {}!".format(
|
|
|
|
parts[1]))
|
|
|
|
path = 'settings.html#{}'.format(topic.replace('->', '-'))
|
|
|
|
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
|
|
|
|
2014-09-23 21:35:08 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher',
|
2015-04-28 16:50:42 +02:00
|
|
|
modes=[KeyMode.insert], hide=True, 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
|
|
|
|
`general -> editor` config option.
|
2014-05-03 14:27:44 +02:00
|
|
|
|
2014-07-16 20:09:41 +02:00
|
|
|
//
|
|
|
|
|
2014-05-03 14:27:44 +02:00
|
|
|
We use QProcess rather than subprocess here because it makes it a lot
|
|
|
|
easier to execute some code as soon as the process has been finished
|
|
|
|
and do everything async.
|
|
|
|
"""
|
2014-09-03 11:32:56 +02:00
|
|
|
frame = self._current_widget().page().currentFrame()
|
2014-09-04 08:00:05 +02:00
|
|
|
try:
|
|
|
|
elem = webelem.focus_elem(frame)
|
|
|
|
except webelem.IsNullError:
|
2014-08-26 19:10:14 +02:00
|
|
|
raise cmdexc.CommandError("No element focused!")
|
2014-09-04 08:00:05 +02:00
|
|
|
if not elem.is_editable(strict=True):
|
2014-08-26 19:10:14 +02:00
|
|
|
raise cmdexc.CommandError("Focused element is not editable!")
|
2014-09-04 08:00:05 +02:00
|
|
|
if elem.is_content_editable():
|
|
|
|
text = str(elem)
|
2014-07-16 07:42:02 +02:00
|
|
|
else:
|
|
|
|
text = elem.evaluateJavaScript('this.value')
|
2014-09-28 22:13:14 +02:00
|
|
|
self._editor = editor.ExternalEditor(
|
2015-05-18 21:31:54 +02:00
|
|
|
self._win_id, self._tabbed_browser)
|
2014-05-21 17:29:09 +02:00
|
|
|
self._editor.editing_finished.connect(
|
2014-09-28 22:13:14 +02:00
|
|
|
functools.partial(self.on_editing_finished, elem))
|
2014-05-21 15:37:18 +02:00
|
|
|
self._editor.edit(text)
|
|
|
|
|
|
|
|
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
|
|
|
|
2014-04-30 10:46:20 +02:00
|
|
|
Callback for QProcess 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:
|
|
|
|
if elem.is_content_editable():
|
|
|
|
log.misc.debug("Filling element {} via setPlainText.".format(
|
|
|
|
elem.debug_text()))
|
|
|
|
elem.setPlainText(text)
|
|
|
|
else:
|
|
|
|
log.misc.debug("Filling element {} via javascript.".format(
|
|
|
|
elem.debug_text()))
|
|
|
|
text = webelem.javascript_escape(text)
|
|
|
|
elem.evaluateJavaScript("this.value='{}'".format(text))
|
|
|
|
except webelem.IsNullError:
|
2014-08-26 19:10:14 +02:00
|
|
|
raise cmdexc.CommandError("Element vanished while editing!")
|
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.
|
|
|
|
"""
|
|
|
|
view = self._current_widget()
|
|
|
|
if view.search_text is not None and view.search_text != text:
|
|
|
|
# We first clear the marked text, then the highlights
|
|
|
|
view.search('', 0)
|
|
|
|
view.search('', QWebPage.HighlightAllOccurrences)
|
|
|
|
|
|
|
|
flags = 0
|
|
|
|
ignore_case = config.get('general', 'ignore-case')
|
|
|
|
if ignore_case == 'smart':
|
|
|
|
if not text.islower():
|
|
|
|
flags |= QWebPage.FindCaseSensitively
|
|
|
|
elif not ignore_case:
|
|
|
|
flags |= QWebPage.FindCaseSensitively
|
|
|
|
if config.get('general', 'wrap-search'):
|
|
|
|
flags |= QWebPage.FindWrapsAroundDocument
|
|
|
|
if reverse:
|
|
|
|
flags |= QWebPage.FindBackward
|
|
|
|
# We actually search *twice* - once to highlight everything, then again
|
|
|
|
# to get a mark so we can navigate.
|
|
|
|
view.search(text, flags)
|
|
|
|
view.search(text, flags | QWebPage.HighlightAllOccurrences)
|
|
|
|
view.search_text = text
|
|
|
|
view.search_flags = flags
|
|
|
|
|
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
2015-04-20 19:29:29 +02:00
|
|
|
scope='window', count='count')
|
|
|
|
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.
|
|
|
|
"""
|
|
|
|
view = self._current_widget()
|
|
|
|
if view.search_text is not None:
|
|
|
|
for _ in range(count):
|
|
|
|
view.search(view.search_text, view.search_flags)
|
|
|
|
|
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
2015-04-20 19:29:29 +02:00
|
|
|
scope='window', count='count')
|
|
|
|
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.
|
|
|
|
"""
|
|
|
|
view = self._current_widget()
|
|
|
|
if view.search_text is None:
|
|
|
|
return
|
|
|
|
# The int() here serves as a QFlags constructor to create a copy of the
|
|
|
|
# QFlags instance rather as a reference. I don't know why it works this
|
|
|
|
# way, but it does.
|
|
|
|
flags = int(view.search_flags)
|
|
|
|
if flags & QWebPage.FindBackward:
|
|
|
|
flags &= ~QWebPage.FindBackward
|
|
|
|
else:
|
|
|
|
flags |= QWebPage.FindBackward
|
|
|
|
for _ in range(count):
|
|
|
|
view.search(view.search_text, flags)
|
2015-04-22 07:13:56 +02:00
|
|
|
|
2015-04-28 16:50:42 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
2015-05-07 07:51:10 +02:00
|
|
|
modes=[KeyMode.caret], scope='window', count='count')
|
|
|
|
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.
|
|
|
|
"""
|
2015-05-05 06:18:24 +02:00
|
|
|
webview = self._current_widget()
|
|
|
|
if not webview.selection_enabled:
|
2015-04-09 18:55:42 +02:00
|
|
|
act = QWebPage.MoveToNextLine
|
|
|
|
else:
|
|
|
|
act = QWebPage.SelectNextLine
|
2015-05-07 07:51:10 +02:00
|
|
|
for _ in range(count):
|
|
|
|
webview.triggerPageAction(act)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2015-04-28 16:50:42 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
2015-05-07 07:51:10 +02:00
|
|
|
modes=[KeyMode.caret], scope='window', count='count')
|
|
|
|
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.
|
|
|
|
"""
|
2015-05-05 06:18:24 +02:00
|
|
|
webview = self._current_widget()
|
|
|
|
if not webview.selection_enabled:
|
2015-04-09 18:55:42 +02:00
|
|
|
act = QWebPage.MoveToPreviousLine
|
|
|
|
else:
|
|
|
|
act = QWebPage.SelectPreviousLine
|
2015-05-07 07:51:10 +02:00
|
|
|
for _ in range(count):
|
|
|
|
webview.triggerPageAction(act)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2015-04-28 16:50:42 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
2015-05-07 07:51:10 +02:00
|
|
|
modes=[KeyMode.caret], scope='window', count='count')
|
|
|
|
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.
|
|
|
|
"""
|
2015-05-05 06:18:24 +02:00
|
|
|
webview = self._current_widget()
|
|
|
|
if not webview.selection_enabled:
|
2015-04-09 18:55:42 +02:00
|
|
|
act = QWebPage.MoveToNextChar
|
|
|
|
else:
|
|
|
|
act = QWebPage.SelectNextChar
|
2015-05-07 07:51:10 +02:00
|
|
|
for _ in range(count):
|
|
|
|
webview.triggerPageAction(act)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2015-04-28 16:50:42 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
2015-05-07 07:51:10 +02:00
|
|
|
modes=[KeyMode.caret], scope='window', count='count')
|
|
|
|
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.
|
|
|
|
"""
|
2015-05-05 06:18:24 +02:00
|
|
|
webview = self._current_widget()
|
|
|
|
if not webview.selection_enabled:
|
2015-04-09 18:55:42 +02:00
|
|
|
act = QWebPage.MoveToPreviousChar
|
|
|
|
else:
|
|
|
|
act = QWebPage.SelectPreviousChar
|
2015-05-07 07:51:10 +02:00
|
|
|
for _ in range(count):
|
|
|
|
webview.triggerPageAction(act)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2015-04-28 16:50:42 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
2015-05-07 07:51:10 +02:00
|
|
|
modes=[KeyMode.caret], scope='window', count='count')
|
|
|
|
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.
|
|
|
|
"""
|
2015-05-05 06:18:24 +02:00
|
|
|
webview = self._current_widget()
|
|
|
|
if not webview.selection_enabled:
|
2015-04-09 18:55:42 +02:00
|
|
|
act = QWebPage.MoveToNextWord
|
|
|
|
else:
|
|
|
|
act = QWebPage.SelectNextWord
|
2015-05-07 07:51:10 +02:00
|
|
|
for _ in range(count):
|
|
|
|
webview.triggerPageAction(act)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2015-04-28 16:50:42 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
2015-05-07 07:51:10 +02:00
|
|
|
modes=[KeyMode.caret], scope='window', count='count')
|
|
|
|
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.
|
|
|
|
"""
|
2015-05-05 06:18:24 +02:00
|
|
|
webview = self._current_widget()
|
|
|
|
if not webview.selection_enabled:
|
2015-04-09 18:55:42 +02:00
|
|
|
act = [QWebPage.MoveToNextWord, QWebPage.MoveToNextChar]
|
|
|
|
else:
|
|
|
|
act = [QWebPage.SelectNextWord, QWebPage.SelectNextChar]
|
2015-05-07 07:51:10 +02:00
|
|
|
for _ in range(count):
|
|
|
|
for a in act:
|
|
|
|
webview.triggerPageAction(a)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2015-04-28 16:50:42 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
2015-05-07 07:51:10 +02:00
|
|
|
modes=[KeyMode.caret], scope='window', count='count')
|
|
|
|
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.
|
|
|
|
"""
|
2015-05-05 06:18:24 +02:00
|
|
|
webview = self._current_widget()
|
|
|
|
if not webview.selection_enabled:
|
2015-04-09 18:55:42 +02:00
|
|
|
act = QWebPage.MoveToPreviousWord
|
|
|
|
else:
|
|
|
|
act = QWebPage.SelectPreviousWord
|
2015-05-07 07:51:10 +02:00
|
|
|
for _ in range(count):
|
|
|
|
webview.triggerPageAction(act)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2015-04-28 16:50:42 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
2015-05-18 22:28:11 +02:00
|
|
|
modes=[KeyMode.caret], scope='window')
|
|
|
|
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."""
|
2015-05-05 06:18:24 +02:00
|
|
|
webview = self._current_widget()
|
|
|
|
if not webview.selection_enabled:
|
2015-04-09 18:55:42 +02:00
|
|
|
act = QWebPage.MoveToStartOfLine
|
|
|
|
else:
|
|
|
|
act = QWebPage.SelectStartOfLine
|
2015-05-18 22:28:11 +02:00
|
|
|
webview.triggerPageAction(act)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2015-04-28 16:50:42 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
2015-05-18 22:28:11 +02:00
|
|
|
modes=[KeyMode.caret], scope='window')
|
|
|
|
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."""
|
2015-05-05 06:18:24 +02:00
|
|
|
webview = self._current_widget()
|
|
|
|
if not webview.selection_enabled:
|
2015-04-09 18:55:42 +02:00
|
|
|
act = QWebPage.MoveToEndOfLine
|
|
|
|
else:
|
|
|
|
act = QWebPage.SelectEndOfLine
|
2015-05-18 22:28:11 +02:00
|
|
|
webview.triggerPageAction(act)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2015-04-28 16:50:42 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
2015-05-07 07:51:10 +02:00
|
|
|
modes=[KeyMode.caret], scope='window', count='count')
|
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.
|
|
|
|
"""
|
2015-05-05 06:18:24 +02:00
|
|
|
webview = self._current_widget()
|
|
|
|
if not webview.selection_enabled:
|
2015-05-11 20:32:27 +02:00
|
|
|
act = [QWebPage.MoveToEndOfBlock, QWebPage.MoveToNextLine,
|
|
|
|
QWebPage.MoveToStartOfBlock]
|
2015-04-09 18:55:42 +02:00
|
|
|
else:
|
2015-05-11 20:32:27 +02:00
|
|
|
act = [QWebPage.SelectEndOfBlock, QWebPage.SelectNextLine,
|
|
|
|
QWebPage.SelectStartOfBlock]
|
2015-05-07 07:51:10 +02:00
|
|
|
for _ in range(count):
|
2015-05-07 08:19:35 +02:00
|
|
|
for a in act:
|
|
|
|
webview.triggerPageAction(a)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2015-04-28 16:50:42 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
2015-05-07 07:51:10 +02:00
|
|
|
modes=[KeyMode.caret], scope='window', count='count')
|
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.
|
|
|
|
"""
|
2015-05-05 06:18:24 +02:00
|
|
|
webview = self._current_widget()
|
|
|
|
if not webview.selection_enabled:
|
2015-05-11 20:32:27 +02:00
|
|
|
act = [QWebPage.MoveToStartOfBlock, QWebPage.MoveToPreviousLine,
|
|
|
|
QWebPage.MoveToStartOfBlock]
|
2015-04-09 18:55:42 +02:00
|
|
|
else:
|
2015-05-11 20:32:27 +02:00
|
|
|
act = [QWebPage.SelectStartOfBlock, QWebPage.SelectPreviousLine,
|
|
|
|
QWebPage.SelectStartOfBlock]
|
2015-05-07 07:51:10 +02:00
|
|
|
for _ in range(count):
|
2015-05-07 08:19:35 +02:00
|
|
|
for a in act:
|
|
|
|
webview.triggerPageAction(a)
|
|
|
|
|
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
|
|
|
modes=[KeyMode.caret], scope='window', count='count')
|
|
|
|
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.
|
|
|
|
"""
|
2015-05-07 08:19:35 +02:00
|
|
|
webview = self._current_widget()
|
|
|
|
if not webview.selection_enabled:
|
2015-05-11 20:32:27 +02:00
|
|
|
act = [QWebPage.MoveToEndOfBlock, QWebPage.MoveToNextLine,
|
|
|
|
QWebPage.MoveToEndOfBlock]
|
2015-05-07 08:19:35 +02:00
|
|
|
else:
|
2015-05-11 20:32:27 +02:00
|
|
|
act = [QWebPage.SelectEndOfBlock, QWebPage.SelectNextLine,
|
|
|
|
QWebPage.SelectEndOfBlock]
|
2015-05-07 08:19:35 +02:00
|
|
|
for _ in range(count):
|
|
|
|
for a in act:
|
|
|
|
webview.triggerPageAction(a)
|
|
|
|
|
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
|
|
|
modes=[KeyMode.caret], scope='window', count='count')
|
|
|
|
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.
|
|
|
|
"""
|
2015-05-07 08:19:35 +02:00
|
|
|
webview = self._current_widget()
|
|
|
|
if not webview.selection_enabled:
|
2015-05-11 20:32:27 +02:00
|
|
|
act = [QWebPage.MoveToStartOfBlock, QWebPage.MoveToPreviousLine,
|
|
|
|
QWebPage.MoveToEndOfBlock]
|
2015-05-07 08:19:35 +02:00
|
|
|
else:
|
2015-05-11 20:32:27 +02:00
|
|
|
act = [QWebPage.SelectStartOfBlock, QWebPage.SelectPreviousLine,
|
|
|
|
QWebPage.SelectEndOfBlock]
|
2015-05-07 08:19:35 +02:00
|
|
|
for _ in range(count):
|
|
|
|
for a in act:
|
|
|
|
webview.triggerPageAction(a)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2015-04-28 16:50:42 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
2015-05-05 06:18:24 +02:00
|
|
|
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."""
|
2015-05-05 06:18:24 +02:00
|
|
|
webview = self._current_widget()
|
|
|
|
if not webview.selection_enabled:
|
2015-04-09 18:55:42 +02:00
|
|
|
act = QWebPage.MoveToStartOfDocument
|
|
|
|
else:
|
|
|
|
act = QWebPage.SelectStartOfDocument
|
2015-05-05 06:18:24 +02:00
|
|
|
webview.triggerPageAction(act)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2015-04-28 16:50:42 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
2015-05-05 06:18:24 +02:00
|
|
|
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."""
|
2015-05-05 06:18:24 +02:00
|
|
|
webview = self._current_widget()
|
|
|
|
if not webview.selection_enabled:
|
2015-04-09 18:55:42 +02:00
|
|
|
act = QWebPage.MoveToEndOfDocument
|
|
|
|
else:
|
|
|
|
act = QWebPage.SelectEndOfDocument
|
2015-05-05 06:18:24 +02:00
|
|
|
webview.triggerPageAction(act)
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2015-04-28 16:50:42 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
2015-05-05 06:18:24 +02:00
|
|
|
modes=[KeyMode.caret], scope='window')
|
2015-05-13 07:58:33 +02:00
|
|
|
def yank_selected(self, sel=False, keep=False):
|
2015-05-11 22:29:44 +02:00
|
|
|
"""Yank the selected text to the clipboard or primary selection.
|
2015-04-09 18:55:42 +02:00
|
|
|
|
|
|
|
Args:
|
|
|
|
sel: Use the primary selection instead of the clipboard.
|
2015-05-13 07:58:33 +02:00
|
|
|
keep: If given, stay in visual mode after yanking.
|
2015-04-09 18:55:42 +02:00
|
|
|
"""
|
|
|
|
s = self._current_widget().selectedText()
|
|
|
|
if not self._current_widget().hasSelection() or len(s) == 0:
|
|
|
|
message.info(self._win_id, "Nothing to yank")
|
|
|
|
return
|
|
|
|
|
|
|
|
clipboard = QApplication.clipboard()
|
|
|
|
if sel and clipboard.supportsSelection():
|
|
|
|
mode = QClipboard.Selection
|
|
|
|
target = "primary selection"
|
|
|
|
else:
|
|
|
|
mode = QClipboard.Clipboard
|
|
|
|
target = "clipboard"
|
|
|
|
log.misc.debug("Yanking to {}: '{}'".format(target, s))
|
|
|
|
clipboard.setText(s, mode)
|
2015-04-28 16:50:42 +02:00
|
|
|
message.info(self._win_id, "{} {} yanked to {}".format(
|
|
|
|
len(s), "char" if len(s) == 1 else "chars", target))
|
2015-05-13 07:58:33 +02:00
|
|
|
if not keep:
|
2015-05-13 07:29:17 +02:00
|
|
|
modeman.leave(self._win_id, KeyMode.caret, "yank selected")
|
2015-04-09 18:55:42 +02:00
|
|
|
|
2015-04-28 16:50:42 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
2015-05-05 06:18:24 +02:00
|
|
|
modes=[KeyMode.caret], scope='window')
|
|
|
|
def toggle_selection(self):
|
|
|
|
"""Toggle caret selection mode."""
|
2015-05-11 20:32:27 +02:00
|
|
|
widget = self._current_widget()
|
|
|
|
widget.selection_enabled = not widget.selection_enabled
|
|
|
|
mainwindow = objreg.get('main-window', scope='window',
|
|
|
|
window=self._win_id)
|
2015-05-13 21:52:42 +02:00
|
|
|
mainwindow.status.set_mode_active(usertypes.KeyMode.caret, True)
|
2015-05-05 06:18:24 +02:00
|
|
|
|
|
|
|
@cmdutils.register(instance='command-dispatcher', hide=True,
|
|
|
|
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."""
|
2015-04-09 18:55:42 +02:00
|
|
|
self._current_widget().triggerPageAction(QWebPage.MoveToNextChar)
|
2015-04-28 15:47:19 +02:00
|
|
|
|
2015-04-22 07:13:56 +02:00
|
|
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
|
|
|
count='count', debug=True)
|
|
|
|
def debug_webaction(self, action, count=1):
|
|
|
|
"""Execute a webaction.
|
|
|
|
|
|
|
|
See http://doc.qt.io/qt-5/qwebpage.html#WebAction-enum for the
|
|
|
|
available actions.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
action: The action to execute, e.g. MoveToNextChar.
|
|
|
|
count: How many times to repeat the action.
|
|
|
|
"""
|
|
|
|
member = getattr(QWebPage, action, None)
|
|
|
|
if not isinstance(member, QWebPage.WebAction):
|
|
|
|
raise cmdexc.CommandError("{} is not a valid web action!".format(
|
2015-04-22 07:46:01 +02:00
|
|
|
action))
|
2015-04-22 07:13:56 +02:00
|
|
|
view = self._current_widget()
|
|
|
|
for _ in range(count):
|
|
|
|
view.triggerPageAction(member)
|