Reorder some methods, widgets not done yet
This commit is contained in:
parent
0425ce7b48
commit
5f83228c75
@ -131,6 +131,54 @@ class QuteBrowser(QApplication):
|
||||
timer = QTimer.singleShot(0, self._process_init_args)
|
||||
self._timers.append(timer)
|
||||
|
||||
def _parseopts(self):
|
||||
"""Parse command line options."""
|
||||
parser = ArgumentParser("usage: %(prog)s [options]")
|
||||
parser.add_argument('-l', '--log', dest='loglevel',
|
||||
help='Set loglevel', default='info')
|
||||
parser.add_argument('-c', '--confdir', help='Set config directory '
|
||||
'(empty for no config storage)')
|
||||
parser.add_argument('-d', '--debug', help='Turn on debugging options.',
|
||||
action='store_true')
|
||||
parser.add_argument('command', nargs='*', help='Commands to execute '
|
||||
'on startup.', metavar=':command')
|
||||
# URLs will actually be in command
|
||||
parser.add_argument('url', nargs='*', help='URLs to open on startup.')
|
||||
return parser.parse_args()
|
||||
|
||||
def _initlog(self):
|
||||
"""Initialisation of the logging output."""
|
||||
loglevel = 'debug' if self._args.debug else self._args.loglevel
|
||||
numeric_level = getattr(logging, loglevel.upper(), None)
|
||||
if not isinstance(numeric_level, int):
|
||||
raise ValueError('Invalid log level: {}'.format(loglevel))
|
||||
logging.basicConfig(
|
||||
level=numeric_level,
|
||||
format='%(asctime)s [%(levelname)s] '
|
||||
'[%(module)s:%(funcName)s:%(lineno)s] %(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S')
|
||||
|
||||
def _initmisc(self):
|
||||
"""Initialize misc things."""
|
||||
if self._args.debug:
|
||||
os.environ['QT_FATAL_WARNINGS'] = '1'
|
||||
self.setApplicationName("qutebrowser")
|
||||
self.setApplicationVersion(qutebrowser.__version__)
|
||||
|
||||
def _init_cmds(self):
|
||||
"""Initialisation of the qutebrowser commands.
|
||||
|
||||
Registers all commands, connects its signals, and sets up keyparser.
|
||||
|
||||
"""
|
||||
cmdutils.register_all()
|
||||
for cmd in cmdutils.cmd_dict.values():
|
||||
cmd.signal.connect(self.cmd_handler)
|
||||
try:
|
||||
self.keyparser.from_config_sect(config.config['keybind'])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def _process_init_args(self):
|
||||
"""Process initial positional args.
|
||||
|
||||
@ -158,6 +206,20 @@ class QuteBrowser(QApplication):
|
||||
for url in config.config.get('general', 'startpage').split(','):
|
||||
self.mainwindow.tabs.tabopen(url)
|
||||
|
||||
def _python_hacks(self):
|
||||
"""Get around some PyQt-oddities by evil hacks.
|
||||
|
||||
This sets up the uncaught exception hook, quits with an appropriate
|
||||
exit status, and handles Ctrl+C properly by passing control to the
|
||||
Python interpreter once all 500ms.
|
||||
|
||||
"""
|
||||
signal(SIGINT, lambda *args: self.exit(128 + SIGINT))
|
||||
timer = QTimer()
|
||||
timer.start(500)
|
||||
timer.timeout.connect(lambda: None)
|
||||
self._timers.append(timer)
|
||||
|
||||
def _recover_pages(self):
|
||||
"""Try to recover all open pages.
|
||||
|
||||
@ -179,6 +241,15 @@ class QuteBrowser(QApplication):
|
||||
pass
|
||||
return pages
|
||||
|
||||
def _save_geometry(self):
|
||||
"""Save the window geometry to the state config."""
|
||||
geom = b64encode(bytes(self.mainwindow.saveGeometry())).decode('ASCII')
|
||||
try:
|
||||
config.state.add_section('geometry')
|
||||
except configparser.DuplicateSectionError:
|
||||
pass
|
||||
config.state['geometry']['mainwindow'] = geom
|
||||
|
||||
def _exception_hook(self, exctype, excvalue, tb):
|
||||
"""Handle uncaught python exceptions.
|
||||
|
||||
@ -246,123 +317,6 @@ class QuteBrowser(QApplication):
|
||||
logging.debug("maybe_quit quitting.")
|
||||
self.quit()
|
||||
|
||||
def _python_hacks(self):
|
||||
"""Get around some PyQt-oddities by evil hacks.
|
||||
|
||||
This sets up the uncaught exception hook, quits with an appropriate
|
||||
exit status, and handles Ctrl+C properly by passing control to the
|
||||
Python interpreter once all 500ms.
|
||||
|
||||
"""
|
||||
signal(SIGINT, lambda *args: self.exit(128 + SIGINT))
|
||||
timer = QTimer()
|
||||
timer.start(500)
|
||||
timer.timeout.connect(lambda: None)
|
||||
self._timers.append(timer)
|
||||
|
||||
def _parseopts(self):
|
||||
"""Parse command line options."""
|
||||
parser = ArgumentParser("usage: %(prog)s [options]")
|
||||
parser.add_argument('-l', '--log', dest='loglevel',
|
||||
help='Set loglevel', default='info')
|
||||
parser.add_argument('-c', '--confdir', help='Set config directory '
|
||||
'(empty for no config storage)')
|
||||
parser.add_argument('-d', '--debug', help='Turn on debugging options.',
|
||||
action='store_true')
|
||||
parser.add_argument('command', nargs='*', help='Commands to execute '
|
||||
'on startup.', metavar=':command')
|
||||
# URLs will actually be in command
|
||||
parser.add_argument('url', nargs='*', help='URLs to open on startup.')
|
||||
return parser.parse_args()
|
||||
|
||||
def _initlog(self):
|
||||
"""Initialisation of the logging output."""
|
||||
loglevel = 'debug' if self._args.debug else self._args.loglevel
|
||||
numeric_level = getattr(logging, loglevel.upper(), None)
|
||||
if not isinstance(numeric_level, int):
|
||||
raise ValueError('Invalid log level: {}'.format(loglevel))
|
||||
logging.basicConfig(
|
||||
level=numeric_level,
|
||||
format='%(asctime)s [%(levelname)s] '
|
||||
'[%(module)s:%(funcName)s:%(lineno)s] %(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S')
|
||||
|
||||
def _initmisc(self):
|
||||
"""Initialize misc things."""
|
||||
if self._args.debug:
|
||||
os.environ['QT_FATAL_WARNINGS'] = '1'
|
||||
self.setApplicationName("qutebrowser")
|
||||
self.setApplicationVersion(qutebrowser.__version__)
|
||||
|
||||
def _init_cmds(self):
|
||||
"""Initialisation of the qutebrowser commands.
|
||||
|
||||
Registers all commands, connects its signals, and sets up keyparser.
|
||||
|
||||
"""
|
||||
cmdutils.register_all()
|
||||
for cmd in cmdutils.cmd_dict.values():
|
||||
cmd.signal.connect(self.cmd_handler)
|
||||
try:
|
||||
self.keyparser.from_config_sect(config.config['keybind'])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
@pyqtSlot()
|
||||
def shutdown(self, do_quit=True):
|
||||
"""Try to shutdown everything cleanly.
|
||||
|
||||
For some reason lastWindowClosing sometimes seem to get emitted twice,
|
||||
so we make sure we only run once here.
|
||||
|
||||
quit -- Whether to quit after shutting down.
|
||||
|
||||
"""
|
||||
if self._shutting_down:
|
||||
return
|
||||
self._shutting_down = True
|
||||
logging.debug("Shutting down... (do_quit={})".format(do_quit))
|
||||
try:
|
||||
config.config.save()
|
||||
except AttributeError:
|
||||
logging.exception("Could not save config.")
|
||||
try:
|
||||
self._save_geometry()
|
||||
config.state.save()
|
||||
except AttributeError:
|
||||
logging.exception("Could not save window geometry.")
|
||||
try:
|
||||
if do_quit:
|
||||
self.mainwindow.tabs.shutdown_complete.connect(
|
||||
self.on_tab_shutdown_complete)
|
||||
else:
|
||||
self.mainwindow.tabs.shutdown_complete.connect(
|
||||
functools.partial(self._maybe_quit, 'shutdown'))
|
||||
self.mainwindow.tabs.shutdown()
|
||||
except AttributeError: # mainwindow or tabs could still be None
|
||||
logging.exception("No mainwindow/tabs to shut down.")
|
||||
if do_quit:
|
||||
self.quit()
|
||||
|
||||
def _save_geometry(self):
|
||||
"""Save the window geometry to the state config."""
|
||||
geom = b64encode(bytes(self.mainwindow.saveGeometry())).decode('ASCII')
|
||||
try:
|
||||
config.state.add_section('geometry')
|
||||
except configparser.DuplicateSectionError:
|
||||
pass
|
||||
config.state['geometry']['mainwindow'] = geom
|
||||
|
||||
@pyqtSlot()
|
||||
def on_tab_shutdown_complete(self):
|
||||
"""Quit application after a shutdown.
|
||||
|
||||
Gets called when all tabs finished shutting down after shutdown().
|
||||
|
||||
"""
|
||||
logging.debug("Shutdown complete, quitting.")
|
||||
self.quit()
|
||||
|
||||
@pyqtSlot(tuple)
|
||||
def cmd_handler(self, tpl):
|
||||
"""Handle commands and delegate the specific actions.
|
||||
@ -438,3 +392,49 @@ class QuteBrowser(QApplication):
|
||||
|
||||
"""
|
||||
raise Exception
|
||||
|
||||
@pyqtSlot()
|
||||
def shutdown(self, do_quit=True):
|
||||
"""Try to shutdown everything cleanly.
|
||||
|
||||
For some reason lastWindowClosing sometimes seem to get emitted twice,
|
||||
so we make sure we only run once here.
|
||||
|
||||
quit -- Whether to quit after shutting down.
|
||||
|
||||
"""
|
||||
if self._shutting_down:
|
||||
return
|
||||
self._shutting_down = True
|
||||
logging.debug("Shutting down... (do_quit={})".format(do_quit))
|
||||
try:
|
||||
config.config.save()
|
||||
except AttributeError:
|
||||
logging.exception("Could not save config.")
|
||||
try:
|
||||
self._save_geometry()
|
||||
config.state.save()
|
||||
except AttributeError:
|
||||
logging.exception("Could not save window geometry.")
|
||||
try:
|
||||
if do_quit:
|
||||
self.mainwindow.tabs.shutdown_complete.connect(
|
||||
self.on_tab_shutdown_complete)
|
||||
else:
|
||||
self.mainwindow.tabs.shutdown_complete.connect(
|
||||
functools.partial(self._maybe_quit, 'shutdown'))
|
||||
self.mainwindow.tabs.shutdown()
|
||||
except AttributeError: # mainwindow or tabs could still be None
|
||||
logging.exception("No mainwindow/tabs to shut down.")
|
||||
if do_quit:
|
||||
self.quit()
|
||||
|
||||
@pyqtSlot()
|
||||
def on_tab_shutdown_complete(self):
|
||||
"""Quit application after a shutdown.
|
||||
|
||||
Gets called when all tabs finished shutting down after shutdown().
|
||||
|
||||
"""
|
||||
logging.debug("Shutdown complete, quitting.")
|
||||
self.quit()
|
||||
|
@ -62,35 +62,6 @@ class KeyParser(QObject):
|
||||
self._bindings = {}
|
||||
self._modifier_bindings = {}
|
||||
|
||||
def from_config_sect(self, sect):
|
||||
"""Load keybindings from a ConfigParser section.
|
||||
|
||||
Config format: key = command, e.g.:
|
||||
gg = scrollstart
|
||||
|
||||
"""
|
||||
for (key, cmd) in sect.items():
|
||||
if key.startswith('@') and key.endswith('@'):
|
||||
# normalize keystring
|
||||
keystr = self._normalize_keystr(key.strip('@'))
|
||||
logging.debug('registered mod key: {} -> {}'.format(keystr,
|
||||
cmd))
|
||||
self._modifier_bindings[keystr] = cmd
|
||||
else:
|
||||
logging.debug('registered key: {} -> {}'.format(key, cmd))
|
||||
self._bindings[key] = cmd
|
||||
|
||||
def handle(self, e):
|
||||
"""Handle a new keypress and call the respective handlers.
|
||||
|
||||
e -- the KeyPressEvent from Qt
|
||||
|
||||
"""
|
||||
handled = self._handle_modifier_key(e)
|
||||
if not handled:
|
||||
self._handle_single_key(e)
|
||||
self.keystring_updated.emit(self._keystring)
|
||||
|
||||
def _handle_modifier_key(self, e):
|
||||
"""Handle a new keypress with modifiers.
|
||||
|
||||
@ -239,3 +210,32 @@ class KeyParser(QObject):
|
||||
cmdstr))
|
||||
self.set_cmd_text.emit(':{} '.format(cmdstr))
|
||||
return
|
||||
|
||||
def from_config_sect(self, sect):
|
||||
"""Load keybindings from a ConfigParser section.
|
||||
|
||||
Config format: key = command, e.g.:
|
||||
gg = scrollstart
|
||||
|
||||
"""
|
||||
for (key, cmd) in sect.items():
|
||||
if key.startswith('@') and key.endswith('@'):
|
||||
# normalize keystring
|
||||
keystr = self._normalize_keystr(key.strip('@'))
|
||||
logging.debug('registered mod key: {} -> {}'.format(keystr,
|
||||
cmd))
|
||||
self._modifier_bindings[keystr] = cmd
|
||||
else:
|
||||
logging.debug('registered key: {} -> {}'.format(key, cmd))
|
||||
self._bindings[key] = cmd
|
||||
|
||||
def handle(self, e):
|
||||
"""Handle a new keypress and call the respective handlers.
|
||||
|
||||
e -- the KeyPressEvent from Qt
|
||||
|
||||
"""
|
||||
handled = self._handle_modifier_key(e)
|
||||
if not handled:
|
||||
self._handle_single_key(e)
|
||||
self.keystring_updated.emit(self._keystring)
|
||||
|
@ -69,24 +69,6 @@ class SearchParser(QObject):
|
||||
self._flags = 0
|
||||
super().__init__(parent)
|
||||
|
||||
@pyqtSlot(str)
|
||||
def search(self, text):
|
||||
"""Search for a text on a website.
|
||||
|
||||
text -- The text to search for.
|
||||
|
||||
"""
|
||||
self._search(text)
|
||||
|
||||
@pyqtSlot(str)
|
||||
def search_rev(self, text):
|
||||
"""Search for a text on a website in reverse direction.
|
||||
|
||||
text -- The text to search for.
|
||||
|
||||
"""
|
||||
self._search(text, rev=True)
|
||||
|
||||
def _search(self, text, rev=False):
|
||||
"""Search for a text on the current page.
|
||||
|
||||
@ -106,6 +88,24 @@ class SearchParser(QObject):
|
||||
self._flags |= QWebPage.FindBackward
|
||||
self.do_search.emit(self._text, self._flags)
|
||||
|
||||
@pyqtSlot(str)
|
||||
def search(self, text):
|
||||
"""Search for a text on a website.
|
||||
|
||||
text -- The text to search for.
|
||||
|
||||
"""
|
||||
self._search(text)
|
||||
|
||||
@pyqtSlot(str)
|
||||
def search_rev(self, text):
|
||||
"""Search for a text on a website in reverse direction.
|
||||
|
||||
text -- The text to search for.
|
||||
|
||||
"""
|
||||
self._search(text, rev=True)
|
||||
|
||||
def nextsearch(self, count=1):
|
||||
"""Continue the search to the ([count]th) next term."""
|
||||
if self._text is not None:
|
||||
|
@ -39,17 +39,6 @@ class CompletionModel(QAbstractItemModel):
|
||||
self._root = CompletionItem([""] * 2)
|
||||
self._id_map[id(self._root)] = self._root
|
||||
|
||||
def removeRows(self, position=0, count=1, parent=QModelIndex()):
|
||||
"""Remove rows from the model.
|
||||
|
||||
Override QAbstractItemModel::removeRows.
|
||||
|
||||
"""
|
||||
node = self._node(parent)
|
||||
self.beginRemoveRows(parent, position, position + count - 1)
|
||||
node.children.pop(position)
|
||||
self.endRemoveRows()
|
||||
|
||||
def _node(self, index):
|
||||
"""Return the interal data representation for index.
|
||||
|
||||
@ -62,6 +51,60 @@ class CompletionModel(QAbstractItemModel):
|
||||
else:
|
||||
return self._root
|
||||
|
||||
def _get_marks(self, needle, haystack):
|
||||
"""Return the marks for needle in haystack."""
|
||||
pos1 = pos2 = 0
|
||||
marks = []
|
||||
if not needle:
|
||||
return marks
|
||||
while True:
|
||||
pos1 = haystack.find(needle, pos2)
|
||||
if pos1 == -1:
|
||||
break
|
||||
pos2 = pos1 + len(needle)
|
||||
marks.append((pos1, pos2))
|
||||
return marks
|
||||
|
||||
def mark_all_items(self, needle):
|
||||
"""Mark a string in all items (children of root-children).
|
||||
|
||||
needle -- The string to mark.
|
||||
|
||||
"""
|
||||
for i in range(self.rowCount()):
|
||||
cat = self.index(i, 0)
|
||||
for k in range(self.rowCount(cat)):
|
||||
idx = self.index(k, 0, cat)
|
||||
old = self.data(idx).value()
|
||||
marks = self._get_marks(needle, old)
|
||||
self.setData(idx, marks, Qt.UserRole)
|
||||
|
||||
def init_data(self, data):
|
||||
"""Initialize the Qt model based on the data given.
|
||||
|
||||
data -- dict of data to process.
|
||||
|
||||
"""
|
||||
for (cat, items) in data.items():
|
||||
newcat = CompletionItem([cat], self._root)
|
||||
self._id_map[id(newcat)] = newcat
|
||||
self._root.children.append(newcat)
|
||||
for item in items:
|
||||
newitem = CompletionItem(item, newcat)
|
||||
self._id_map[id(newitem)] = newitem
|
||||
newcat.children.append(newitem)
|
||||
|
||||
def removeRows(self, position=0, count=1, parent=QModelIndex()):
|
||||
"""Remove rows from the model.
|
||||
|
||||
Override QAbstractItemModel::removeRows.
|
||||
|
||||
"""
|
||||
node = self._node(parent)
|
||||
self.beginRemoveRows(parent, position, position + count - 1)
|
||||
node.children.pop(position)
|
||||
self.endRemoveRows()
|
||||
|
||||
def columnCount(self, parent=QModelIndex()):
|
||||
"""Return the column count in the model.
|
||||
|
||||
@ -71,6 +114,23 @@ class CompletionModel(QAbstractItemModel):
|
||||
# pylint: disable=unused-argument
|
||||
return self._root.column_count()
|
||||
|
||||
def rowCount(self, parent=QModelIndex()):
|
||||
"""Return the children count of an item.
|
||||
|
||||
Use the root frame if parent is invalid.
|
||||
Override QAbstractItemModel::rowCount.
|
||||
|
||||
"""
|
||||
if parent.column() > 0:
|
||||
return 0
|
||||
|
||||
if not parent.isValid():
|
||||
pitem = self._root
|
||||
else:
|
||||
pitem = self._id_map[parent.internalId()]
|
||||
|
||||
return len(pitem.children)
|
||||
|
||||
def data(self, index, role=Qt.DisplayRole):
|
||||
"""Return the data for role/index as QVariant.
|
||||
|
||||
@ -89,23 +149,6 @@ class CompletionModel(QAbstractItemModel):
|
||||
except (IndexError, ValueError):
|
||||
return QVariant()
|
||||
|
||||
def flags(self, index):
|
||||
"""Return the item flags for index.
|
||||
|
||||
Return Qt.NoItemFlags on error.
|
||||
Override QAbstractItemModel::flags.
|
||||
|
||||
"""
|
||||
# FIXME categories are not selectable, but moving via arrow keys still
|
||||
# tries to select them
|
||||
if not index.isValid():
|
||||
return Qt.NoItemFlags
|
||||
flags = Qt.ItemIsEnabled
|
||||
if len(self._id_map[index.internalId()].children) > 0:
|
||||
return flags
|
||||
else:
|
||||
return flags | Qt.ItemIsSelectable
|
||||
|
||||
def headerData(self, section, orientation, role=Qt.DisplayRole):
|
||||
"""Return the header data for role/index as QVariant.
|
||||
|
||||
@ -134,6 +177,23 @@ class CompletionModel(QAbstractItemModel):
|
||||
self.dataChanged.emit(index, index)
|
||||
return True
|
||||
|
||||
def flags(self, index):
|
||||
"""Return the item flags for index.
|
||||
|
||||
Return Qt.NoItemFlags on error.
|
||||
Override QAbstractItemModel::flags.
|
||||
|
||||
"""
|
||||
# FIXME categories are not selectable, but moving via arrow keys still
|
||||
# tries to select them
|
||||
if not index.isValid():
|
||||
return Qt.NoItemFlags
|
||||
flags = Qt.ItemIsEnabled
|
||||
if len(self._id_map[index.internalId()].children) > 0:
|
||||
return flags
|
||||
else:
|
||||
return flags | Qt.ItemIsSelectable
|
||||
|
||||
def index(self, row, column, parent=QModelIndex()):
|
||||
"""Return the QModelIndex for row/column/parent.
|
||||
|
||||
@ -174,23 +234,6 @@ class CompletionModel(QAbstractItemModel):
|
||||
return QModelIndex()
|
||||
return self.createIndex(item.row(), 0, id(item))
|
||||
|
||||
def rowCount(self, parent=QModelIndex()):
|
||||
"""Return the children count of an item.
|
||||
|
||||
Use the root frame if parent is invalid.
|
||||
Override QAbstractItemModel::rowCount.
|
||||
|
||||
"""
|
||||
if parent.column() > 0:
|
||||
return 0
|
||||
|
||||
if not parent.isValid():
|
||||
pitem = self._root
|
||||
else:
|
||||
pitem = self._id_map[parent.internalId()]
|
||||
|
||||
return len(pitem.children)
|
||||
|
||||
def sort(self, column, order=Qt.AscendingOrder):
|
||||
"""Sort the data in column according to order.
|
||||
|
||||
@ -200,49 +243,6 @@ class CompletionModel(QAbstractItemModel):
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def init_data(self, data):
|
||||
"""Initialize the Qt model based on the data given.
|
||||
|
||||
data -- dict of data to process.
|
||||
|
||||
"""
|
||||
for (cat, items) in data.items():
|
||||
newcat = CompletionItem([cat], self._root)
|
||||
self._id_map[id(newcat)] = newcat
|
||||
self._root.children.append(newcat)
|
||||
for item in items:
|
||||
newitem = CompletionItem(item, newcat)
|
||||
self._id_map[id(newitem)] = newitem
|
||||
newcat.children.append(newitem)
|
||||
|
||||
def mark_all_items(self, needle):
|
||||
"""Mark a string in all items (children of root-children).
|
||||
|
||||
needle -- The string to mark.
|
||||
|
||||
"""
|
||||
for i in range(self.rowCount()):
|
||||
cat = self.index(i, 0)
|
||||
for k in range(self.rowCount(cat)):
|
||||
idx = self.index(k, 0, cat)
|
||||
old = self.data(idx).value()
|
||||
marks = self._get_marks(needle, old)
|
||||
self.setData(idx, marks, Qt.UserRole)
|
||||
|
||||
def _get_marks(self, needle, haystack):
|
||||
"""Return the marks for needle in haystack."""
|
||||
pos1 = pos2 = 0
|
||||
marks = []
|
||||
if not needle:
|
||||
return marks
|
||||
while True:
|
||||
pos1 = haystack.find(needle, pos2)
|
||||
if pos1 == -1:
|
||||
break
|
||||
pos2 = pos1 + len(needle)
|
||||
marks.append((pos1, pos2))
|
||||
return marks
|
||||
|
||||
|
||||
class CompletionItem():
|
||||
|
||||
|
@ -71,10 +71,21 @@ class CompletionFilterModel(QSortFilterProxyModel):
|
||||
model -- The new source model.
|
||||
|
||||
"""
|
||||
# FIXME change this to a property
|
||||
self.setSourceModel(model)
|
||||
self.srcmodel = model
|
||||
self.pattern = ''
|
||||
|
||||
def first_item(self):
|
||||
"""Return the first item in the model."""
|
||||
cat = self.index(0, 0)
|
||||
return self.index(0, 0, cat)
|
||||
|
||||
def last_item(self):
|
||||
"""Return the last item in the model."""
|
||||
cat = self.index(self.rowCount() - 1, 0)
|
||||
return self.index(self.rowCount(cat) - 1, 0, cat)
|
||||
|
||||
def filterAcceptsRow(self, row, parent):
|
||||
"""Custom filter implementation.
|
||||
|
||||
@ -120,13 +131,3 @@ class CompletionFilterModel(QSortFilterProxyModel):
|
||||
return False
|
||||
else:
|
||||
return left < right
|
||||
|
||||
def first_item(self):
|
||||
"""Return the first item in the model."""
|
||||
cat = self.index(0, 0)
|
||||
return self.index(0, 0, cat)
|
||||
|
||||
def last_item(self):
|
||||
"""Return the last item in the model."""
|
||||
cat = self.index(self.rowCount() - 1, 0)
|
||||
return self.index(self.rowCount(cat) - 1, 0, cat)
|
||||
|
@ -64,6 +64,10 @@ class SignalCache(QObject):
|
||||
self._uncached = uncached
|
||||
self._signal_dict = OrderedDict()
|
||||
|
||||
def _signal_needs_caching(self, signal):
|
||||
"""Return True if a signal should be cached, false otherwise."""
|
||||
return not signal_name(signal) in self._uncached
|
||||
|
||||
def add(self, sig, args):
|
||||
"""Add a new signal to the signal cache.
|
||||
|
||||
@ -88,7 +92,3 @@ class SignalCache(QObject):
|
||||
for (signal, args) in self._signal_dict.values():
|
||||
logging.debug('emitting {}'.format(dbg_signal(signal, args)))
|
||||
signal.emit(*args)
|
||||
|
||||
def _signal_needs_caching(self, signal):
|
||||
"""Return True if a signal should be cached, false otherwise."""
|
||||
return not signal_name(signal) in self._uncached
|
||||
|
@ -27,6 +27,52 @@ from PyQt5.QtCore import QUrl
|
||||
import qutebrowser.utils.config as config
|
||||
|
||||
|
||||
def _get_search_url(txt):
|
||||
"""Return a search engine URL (QUrl) for a text."""
|
||||
logging.debug('Finding search engine for "{}"'.format(txt))
|
||||
r = re.compile(r'(^|\s+)!(\w+)($|\s+)')
|
||||
m = r.search(txt)
|
||||
if m:
|
||||
engine = m.group(2)
|
||||
# FIXME why doesn't fallback work?!
|
||||
template = config.config.get('searchengines', engine, fallback=None)
|
||||
term = r.sub('', txt)
|
||||
logging.debug('engine {}, term "{}"'.format(engine, term))
|
||||
else:
|
||||
template = config.config.get('searchengines', 'DEFAULT',
|
||||
fallback=None)
|
||||
term = txt
|
||||
logging.debug('engine: default, term "{}"'.format(txt))
|
||||
if template is None or not term:
|
||||
raise ValueError
|
||||
# pylint: disable=maybe-no-member
|
||||
return QUrl.fromUserInput(template.format(urllib.parse.quote(term)))
|
||||
|
||||
|
||||
def _is_url_naive(url):
|
||||
"""Naive check if given url (QUrl) is really an url."""
|
||||
PROTOCOLS = ['http://', 'https://']
|
||||
u = urlstring(url)
|
||||
return (any(u.startswith(proto) for proto in PROTOCOLS) or '.' in u or
|
||||
u == 'localhost')
|
||||
|
||||
|
||||
def _is_url_dns(url):
|
||||
"""Check if an url (QUrl) is really an url via DNS."""
|
||||
# FIXME we could probably solve this in a nicer way by attempting to open
|
||||
# the page in the webview, and then open the search if that fails.
|
||||
host = url.host()
|
||||
logging.debug("DNS request for {}".format(host))
|
||||
if not host:
|
||||
return False
|
||||
try:
|
||||
socket.gethostbyname(host)
|
||||
except socket.gaierror:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def qurl(url):
|
||||
"""Get a QUrl from an url string."""
|
||||
return url if isinstance(url, QUrl) else QUrl(url)
|
||||
@ -64,28 +110,6 @@ def fuzzy_url(url):
|
||||
return newurl
|
||||
|
||||
|
||||
def _get_search_url(txt):
|
||||
"""Return a search engine URL (QUrl) for a text."""
|
||||
logging.debug('Finding search engine for "{}"'.format(txt))
|
||||
r = re.compile(r'(^|\s+)!(\w+)($|\s+)')
|
||||
m = r.search(txt)
|
||||
if m:
|
||||
engine = m.group(2)
|
||||
# FIXME why doesn't fallback work?!
|
||||
template = config.config.get('searchengines', engine, fallback=None)
|
||||
term = r.sub('', txt)
|
||||
logging.debug('engine {}, term "{}"'.format(engine, term))
|
||||
else:
|
||||
template = config.config.get('searchengines', 'DEFAULT',
|
||||
fallback=None)
|
||||
term = txt
|
||||
logging.debug('engine: default, term "{}"'.format(txt))
|
||||
if template is None or not term:
|
||||
raise ValueError
|
||||
# pylint: disable=maybe-no-member
|
||||
return QUrl.fromUserInput(template.format(urllib.parse.quote(term)))
|
||||
|
||||
|
||||
def is_about_url(url):
|
||||
"""Return True if url (QUrl) is an about:... or other special URL."""
|
||||
return urlstring(url).replace('http://', '').startswith('about:')
|
||||
@ -128,27 +152,3 @@ def is_url(url):
|
||||
return _is_url_naive(url)
|
||||
else:
|
||||
raise ValueError("Invalid autosearch value")
|
||||
|
||||
|
||||
def _is_url_naive(url):
|
||||
"""Naive check if given url (QUrl) is really an url."""
|
||||
PROTOCOLS = ['http://', 'https://']
|
||||
u = urlstring(url)
|
||||
return (any(u.startswith(proto) for proto in PROTOCOLS) or '.' in u or
|
||||
u == 'localhost')
|
||||
|
||||
|
||||
def _is_url_dns(url):
|
||||
"""Check if an url (QUrl) is really an url via DNS."""
|
||||
# FIXME we could probably solve this in a nicer way by attempting to open
|
||||
# the page in the webview, and then open the search if that fails.
|
||||
host = url.host()
|
||||
logging.debug("DNS request for {}".format(host))
|
||||
if not host:
|
||||
return False
|
||||
try:
|
||||
socket.gethostbyname(host)
|
||||
except socket.gaierror:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
@ -28,6 +28,29 @@ from PyQt5.QtWebKit import qWebKitVersion
|
||||
import qutebrowser
|
||||
|
||||
|
||||
def _git_str():
|
||||
"""Try to find out git version and return a string if possible.
|
||||
|
||||
Return None if there was an error or we're not in a git repo.
|
||||
|
||||
"""
|
||||
if hasattr(sys, "frozen"):
|
||||
return None
|
||||
try:
|
||||
gitpath = os.path.join(os.path.dirname(os.path.realpath(__file__)),
|
||||
os.path.pardir, os.path.pardir)
|
||||
except NameError:
|
||||
return None
|
||||
if not os.path.isdir(os.path.join(gitpath, ".git")):
|
||||
return None
|
||||
try:
|
||||
return subprocess.check_output(
|
||||
['git', 'describe', '--tags', '--dirty', '--always'],
|
||||
cwd=gitpath).decode('UTF-8').strip()
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
return None
|
||||
|
||||
|
||||
def version():
|
||||
"""Return a string with various version informations."""
|
||||
if sys.platform == 'linux':
|
||||
@ -56,26 +79,3 @@ def version():
|
||||
lines.append('\nGit commit: {}'.format(gitver))
|
||||
|
||||
return ''.join(lines)
|
||||
|
||||
|
||||
def _git_str():
|
||||
"""Try to find out git version and return a string if possible.
|
||||
|
||||
Return None if there was an error or we're not in a git repo.
|
||||
|
||||
"""
|
||||
if hasattr(sys, "frozen"):
|
||||
return None
|
||||
try:
|
||||
gitpath = os.path.join(os.path.dirname(os.path.realpath(__file__)),
|
||||
os.path.pardir, os.path.pardir)
|
||||
except NameError:
|
||||
return None
|
||||
if not os.path.isdir(os.path.join(gitpath, ".git")):
|
||||
return None
|
||||
try:
|
||||
return subprocess.check_output(
|
||||
['git', 'describe', '--tags', '--dirty', '--always'],
|
||||
cwd=gitpath).decode('UTF-8').strip()
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
return None
|
||||
|
Loading…
Reference in New Issue
Block a user