Aaaargh, CRLFs

This commit is contained in:
Florian Bruhin 2014-01-27 21:42:00 +01:00
parent f2223064b3
commit 7a549b38a4
5 changed files with 448 additions and 448 deletions

View File

@ -1,171 +1,171 @@
import logging
from collections import OrderedDict
from PyQt5.QtCore import (QAbstractItemModel, Qt, QModelIndex, QVariant,
QSortFilterProxyModel, pyqtSignal)
class CompletionModel(QAbstractItemModel):
def __init__(self, parent=None):
super().__init__(parent)
self._data = OrderedDict()
self.parents = []
self.root = CompletionItem([""] * 2)
def removeRows(self, position=0, count=1, parent=QModelIndex()):
node = self.node(parent)
self.beginRemoveRows(parent, position, position + count - 1)
node.childItems.pop(position)
self.endRemoveRows()
def node(self, index):
if index.isValid():
return index.internalPointer()
else:
return self.root
def columnCount(self, parent=QModelIndex()):
return self.root.column_count()
def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return QVariant()
item = index.internalPointer()
try:
return QVariant(item.data(index.column(), role))
except (IndexError, ValueError):
return QVariant()
def flags(self, index):
# 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(index.internalPointer().children) > 0:
return flags
else:
return flags | Qt.ItemIsSelectable
def headerData(self, section, orientation, role=Qt.DisplayRole):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return QVariant(self.root.data(section))
return None
def setData(self, index, value, role=Qt.EditRole):
if not index.isValid():
return False
item = index.internalPointer()
try:
item.setdata(index.column(), value, role)
except (IndexError, ValueError):
return False
self.dataChanged.emit(index, index)
return True
def index(self, row, column, parent=QModelIndex()):
if (0 <= row < self.rowCount(parent) and
0 <= column < self.columnCount(parent)):
pass
else:
return QModelIndex()
if not parent.isValid():
parent_item = self.root
else:
parent_item = parent.internalPointer()
child_item = parent_item.children[row]
if child_item:
return self.createIndex(row, column, child_item)
else:
return QModelIndex()
def parent(self, index):
if not index.isValid():
return QModelIndex()
item = index.internalPointer().parent
if item == self.root or item is None:
return QModelIndex()
return self.createIndex(item.row(), 0, item)
def rowCount(self, parent=QModelIndex()):
if parent.column() > 0:
return 0
if not parent.isValid():
pitem = self.root
else:
pitem = parent.internalPointer()
return len(pitem.children)
def init_data(self):
for (cat, items) in self._data.items():
newcat = CompletionItem([cat], self.root)
self.root.children.append(newcat)
for item in items:
newitem = CompletionItem(item, newcat)
newcat.children.append(newitem)
class CompletionItem():
parent = None
_data = None
children = None
_marks = None
def __init__(self, data, parent=None):
self.parent = parent
self._data = data
self.children = []
self._marks = []
def data(self, column, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
return self._data[column]
elif role == Qt.UserRole:
return self._marks
else:
raise ValueError
def setdata(self, column, value, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
self._data[column] = value
elif role == Qt.UserRole:
self._marks = value
else:
raise ValueError
def column_count(self):
return len(self._data)
def row(self):
if self.parent:
return self.parent.children.index(self)
return 0
class CompletionFilterModel(QSortFilterProxyModel):
_pattern = None
# FIXME sort in a way strings starting with pattern are first
@property
def pattern(self):
return self._pattern
@pattern.setter
def pattern(self, val):
self._pattern = val
self.invalidateFilter()
def __init__(self, parent=None):
super().__init__(parent)
self.pattern = ''
def filterAcceptsRow(self, row, parent):
if parent == QModelIndex():
return True
idx = self.sourceModel().index(row, 0, parent);
data = self.sourceModel().data(idx).value()
# TODO more sophisticated filtering
if not self.pattern:
return True
return self.pattern in data
import logging
from collections import OrderedDict
from PyQt5.QtCore import (QAbstractItemModel, Qt, QModelIndex, QVariant,
QSortFilterProxyModel, pyqtSignal)
class CompletionModel(QAbstractItemModel):
def __init__(self, parent=None):
super().__init__(parent)
self._data = OrderedDict()
self.parents = []
self.root = CompletionItem([""] * 2)
def removeRows(self, position=0, count=1, parent=QModelIndex()):
node = self.node(parent)
self.beginRemoveRows(parent, position, position + count - 1)
node.childItems.pop(position)
self.endRemoveRows()
def node(self, index):
if index.isValid():
return index.internalPointer()
else:
return self.root
def columnCount(self, parent=QModelIndex()):
return self.root.column_count()
def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return QVariant()
item = index.internalPointer()
try:
return QVariant(item.data(index.column(), role))
except (IndexError, ValueError):
return QVariant()
def flags(self, index):
# 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(index.internalPointer().children) > 0:
return flags
else:
return flags | Qt.ItemIsSelectable
def headerData(self, section, orientation, role=Qt.DisplayRole):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return QVariant(self.root.data(section))
return None
def setData(self, index, value, role=Qt.EditRole):
if not index.isValid():
return False
item = index.internalPointer()
try:
item.setdata(index.column(), value, role)
except (IndexError, ValueError):
return False
self.dataChanged.emit(index, index)
return True
def index(self, row, column, parent=QModelIndex()):
if (0 <= row < self.rowCount(parent) and
0 <= column < self.columnCount(parent)):
pass
else:
return QModelIndex()
if not parent.isValid():
parent_item = self.root
else:
parent_item = parent.internalPointer()
child_item = parent_item.children[row]
if child_item:
return self.createIndex(row, column, child_item)
else:
return QModelIndex()
def parent(self, index):
if not index.isValid():
return QModelIndex()
item = index.internalPointer().parent
if item == self.root or item is None:
return QModelIndex()
return self.createIndex(item.row(), 0, item)
def rowCount(self, parent=QModelIndex()):
if parent.column() > 0:
return 0
if not parent.isValid():
pitem = self.root
else:
pitem = parent.internalPointer()
return len(pitem.children)
def init_data(self):
for (cat, items) in self._data.items():
newcat = CompletionItem([cat], self.root)
self.root.children.append(newcat)
for item in items:
newitem = CompletionItem(item, newcat)
newcat.children.append(newitem)
class CompletionItem():
parent = None
_data = None
children = None
_marks = None
def __init__(self, data, parent=None):
self.parent = parent
self._data = data
self.children = []
self._marks = []
def data(self, column, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
return self._data[column]
elif role == Qt.UserRole:
return self._marks
else:
raise ValueError
def setdata(self, column, value, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
self._data[column] = value
elif role == Qt.UserRole:
self._marks = value
else:
raise ValueError
def column_count(self):
return len(self._data)
def row(self):
if self.parent:
return self.parent.children.index(self)
return 0
class CompletionFilterModel(QSortFilterProxyModel):
_pattern = None
# FIXME sort in a way strings starting with pattern are first
@property
def pattern(self):
return self._pattern
@pattern.setter
def pattern(self, val):
self._pattern = val
self.invalidateFilter()
def __init__(self, parent=None):
super().__init__(parent)
self.pattern = ''
def filterAcceptsRow(self, row, parent):
if parent == QModelIndex():
return True
idx = self.sourceModel().index(row, 0, parent);
data = self.sourceModel().data(idx).value()
# TODO more sophisticated filtering
if not self.pattern:
return True
return self.pattern in data

View File

@ -1,66 +1,66 @@
import os.path
import os
import logging
from configparser import ConfigParser
default_config = {
'keybind': {
'o': 'open',
'O': 'tabopen',
'd': 'tabclose',
'J': 'tabnext',
'K': 'tabprev',
'r': 'reload',
'H': 'back',
'L': 'forward',
'h': 'scroll -50 0',
'j': 'scroll 0 50',
'k': 'scroll 0 -50',
'l': 'scroll 50 0',
'u': 'undo',
'gg': 'scrollpercenty 0',
'G': 'scrollpercenty',
}
}
class Config(ConfigParser):
"""Our own ConfigParser"""
configdir = None
FNAME = 'config'
def __init__(self, configdir):
"""configdir: directory to store the config in"""
super().__init__()
self.optionxform = lambda opt: opt # be case-insensitive
self.configdir = configdir
if self.configdir is None:
self.init_config()
return
self.configfile = os.path.join(self.configdir, self.FNAME)
if not os.path.isfile(self.configfile):
self.init_config()
logging.debug("Reading config from {}".format(self.configfile))
self.read(self.configfile)
def init_config(self):
logging.info("Initializing default config.")
if self.configdir is None:
self.read_dict(default_config)
return
cp = ConfigParser()
cp.optionxform = lambda opt: opt # be case-insensitive
cp.read_dict(default_config)
if not os.path.exists(self.configdir):
os.makedirs(self.configdir, 0o755)
with open(self.configfile, 'w') as f:
cp.write(f)
def save(self):
if self.configdir is None:
return
if not os.path.exists(self.configdir):
os.makedirs(self.configdir, 0o755)
logging.debug("Saving config to {}".format(self.configfile))
with open(self.configfile, 'w') as f:
self.write(f)
import os.path
import os
import logging
from configparser import ConfigParser
default_config = {
'keybind': {
'o': 'open',
'O': 'tabopen',
'd': 'tabclose',
'J': 'tabnext',
'K': 'tabprev',
'r': 'reload',
'H': 'back',
'L': 'forward',
'h': 'scroll -50 0',
'j': 'scroll 0 50',
'k': 'scroll 0 -50',
'l': 'scroll 50 0',
'u': 'undo',
'gg': 'scrollpercenty 0',
'G': 'scrollpercenty',
}
}
class Config(ConfigParser):
"""Our own ConfigParser"""
configdir = None
FNAME = 'config'
def __init__(self, configdir):
"""configdir: directory to store the config in"""
super().__init__()
self.optionxform = lambda opt: opt # be case-insensitive
self.configdir = configdir
if self.configdir is None:
self.init_config()
return
self.configfile = os.path.join(self.configdir, self.FNAME)
if not os.path.isfile(self.configfile):
self.init_config()
logging.debug("Reading config from {}".format(self.configfile))
self.read(self.configfile)
def init_config(self):
logging.info("Initializing default config.")
if self.configdir is None:
self.read_dict(default_config)
return
cp = ConfigParser()
cp.optionxform = lambda opt: opt # be case-insensitive
cp.read_dict(default_config)
if not os.path.exists(self.configdir):
os.makedirs(self.configdir, 0o755)
with open(self.configfile, 'w') as f:
cp.write(f)
def save(self):
if self.configdir is None:
return
if not os.path.exists(self.configdir):
os.makedirs(self.configdir, 0o755)
logging.debug("Saving config to {}".format(self.configfile))
with open(self.configfile, 'w') as f:
self.write(f)

View File

@ -1,129 +1,129 @@
import logging
from PyQt5.QtWidgets import QLineEdit, QShortcut
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtGui import QValidator, QKeySequence
from qutebrowser.commands.utils import CommandCompletionModel
class Command(QLineEdit):
"""The commandline part of the statusbar"""
got_cmd = pyqtSignal(str) # Emitted when a command is triggered by the user
bar = None # The status bar object
esc_pressed = pyqtSignal() # Emitted when escape is pressed
tab_pressed = pyqtSignal(bool) # Emitted when tab is pressed (arg: shift)
hide_completion = pyqtSignal() # Hide completion window
history = [] # The command history, with newer commands at the bottom
_tmphist = []
_histpos = None
# FIXME won't the tab key switch to the next widget?
# See http://www.saltycrane.com/blog/2008/01/how-to-capture-tab-key-press-event-with/
# for a possible fix.
def __init__(self, bar):
super().__init__(bar)
# FIXME
self.bar = bar
self.setStyleSheet("border: 0px; padding-left: 1px")
self.setValidator(Validator())
self.returnPressed.connect(self.process_cmd)
self.textEdited.connect(self._histbrowse_stop)
for (key, handler) in [(Qt.Key_Escape, self.esc_pressed),
(Qt.Key_Up, self.key_up_handler),
(Qt.Key_Down, self.key_down_handler),
(Qt.Key_Tab | Qt.SHIFT, self.key_stab_handler),
(Qt.Key_Tab, self.key_tab_handler)]:
sc = QShortcut(self)
sc.setKey(QKeySequence(key))
sc.setContext(Qt.WidgetWithChildrenShortcut)
sc.activated.connect(handler)
def process_cmd(self):
"""Handle the command in the status bar"""
self._histbrowse_stop()
text = self.text().lstrip(':')
if not self.history or text != self.history[-1]:
self.history.append(text)
self.setText('')
self.got_cmd.emit(text)
def set_cmd(self, text):
"""Preset the statusbar to some text"""
self.setText(':' + text)
self.setFocus()
def append_cmd(self, text):
"""Append text to the commandline"""
# FIXME do the right thing here
self.setText(':' + text)
self.setFocus()
def focusOutEvent(self, e):
"""Clear the statusbar text if it's explicitely unfocused"""
if e.reason() in [Qt.MouseFocusReason, Qt.TabFocusReason,
Qt.BacktabFocusReason, Qt.OtherFocusReason]:
self.setText('')
self._histbrowse_stop()
self.hide_completion.emit()
super().focusOutEvent(e)
def focusInEvent(self, e):
"""Clear error message when the statusbar is focused"""
self.bar.clear_error()
super().focusInEvent(e)
def _histbrowse_start(self):
pre = self.text().strip().lstrip(':')
logging.debug('Preset text: "{}"'.format(pre))
if pre:
self._tmphist = [e for e in self.history if e.startswith(pre)]
else:
self._tmphist = self.history
self._histpos = len(self._tmphist) - 1
def _histbrowse_stop(self):
self._histpos = None
def key_up_handler(self):
logging.debug("history up [pre]: pos {}".format(self._histpos))
if self._histpos is None:
self._histbrowse_start()
elif self._histpos <= 0:
return
else:
self._histpos -= 1
if not self._tmphist:
return
logging.debug("history up: {} / len {} / pos {}".format(
self._tmphist, len(self._tmphist), self._histpos))
self.set_cmd(self._tmphist[self._histpos])
def key_down_handler(self):
logging.debug("history up [pre]: pos {}".format(self._histpos,
self._tmphist, len(self._tmphist), self._histpos))
if (self._histpos is None or
self._histpos >= len(self._tmphist) - 1 or
not self._tmphist):
return
self._histpos += 1
logging.debug("history up: {} / len {} / pos {}".format(
self._tmphist, len(self._tmphist), self._histpos))
self.set_cmd(self._tmphist[self._histpos])
def key_tab_handler(self):
self.tab_pressed.emit(False)
def key_stab_handler(self):
self.tab_pressed.emit(True)
class Validator(QValidator):
"""Validator to prevent the : from getting deleted"""
def validate(self, string, pos):
if string.startswith(':'):
return (QValidator.Acceptable, string, pos)
else:
return (QValidator.Invalid, string, pos)
import logging
from PyQt5.QtWidgets import QLineEdit, QShortcut
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtGui import QValidator, QKeySequence
from qutebrowser.commands.utils import CommandCompletionModel
class Command(QLineEdit):
"""The commandline part of the statusbar"""
got_cmd = pyqtSignal(str) # Emitted when a command is triggered by the user
bar = None # The status bar object
esc_pressed = pyqtSignal() # Emitted when escape is pressed
tab_pressed = pyqtSignal(bool) # Emitted when tab is pressed (arg: shift)
hide_completion = pyqtSignal() # Hide completion window
history = [] # The command history, with newer commands at the bottom
_tmphist = []
_histpos = None
# FIXME won't the tab key switch to the next widget?
# See http://www.saltycrane.com/blog/2008/01/how-to-capture-tab-key-press-event-with/
# for a possible fix.
def __init__(self, bar):
super().__init__(bar)
# FIXME
self.bar = bar
self.setStyleSheet("border: 0px; padding-left: 1px")
self.setValidator(Validator())
self.returnPressed.connect(self.process_cmd)
self.textEdited.connect(self._histbrowse_stop)
for (key, handler) in [(Qt.Key_Escape, self.esc_pressed),
(Qt.Key_Up, self.key_up_handler),
(Qt.Key_Down, self.key_down_handler),
(Qt.Key_Tab | Qt.SHIFT, self.key_stab_handler),
(Qt.Key_Tab, self.key_tab_handler)]:
sc = QShortcut(self)
sc.setKey(QKeySequence(key))
sc.setContext(Qt.WidgetWithChildrenShortcut)
sc.activated.connect(handler)
def process_cmd(self):
"""Handle the command in the status bar"""
self._histbrowse_stop()
text = self.text().lstrip(':')
if not self.history or text != self.history[-1]:
self.history.append(text)
self.setText('')
self.got_cmd.emit(text)
def set_cmd(self, text):
"""Preset the statusbar to some text"""
self.setText(':' + text)
self.setFocus()
def append_cmd(self, text):
"""Append text to the commandline"""
# FIXME do the right thing here
self.setText(':' + text)
self.setFocus()
def focusOutEvent(self, e):
"""Clear the statusbar text if it's explicitely unfocused"""
if e.reason() in [Qt.MouseFocusReason, Qt.TabFocusReason,
Qt.BacktabFocusReason, Qt.OtherFocusReason]:
self.setText('')
self._histbrowse_stop()
self.hide_completion.emit()
super().focusOutEvent(e)
def focusInEvent(self, e):
"""Clear error message when the statusbar is focused"""
self.bar.clear_error()
super().focusInEvent(e)
def _histbrowse_start(self):
pre = self.text().strip().lstrip(':')
logging.debug('Preset text: "{}"'.format(pre))
if pre:
self._tmphist = [e for e in self.history if e.startswith(pre)]
else:
self._tmphist = self.history
self._histpos = len(self._tmphist) - 1
def _histbrowse_stop(self):
self._histpos = None
def key_up_handler(self):
logging.debug("history up [pre]: pos {}".format(self._histpos))
if self._histpos is None:
self._histbrowse_start()
elif self._histpos <= 0:
return
else:
self._histpos -= 1
if not self._tmphist:
return
logging.debug("history up: {} / len {} / pos {}".format(
self._tmphist, len(self._tmphist), self._histpos))
self.set_cmd(self._tmphist[self._histpos])
def key_down_handler(self):
logging.debug("history up [pre]: pos {}".format(self._histpos,
self._tmphist, len(self._tmphist), self._histpos))
if (self._histpos is None or
self._histpos >= len(self._tmphist) - 1 or
not self._tmphist):
return
self._histpos += 1
logging.debug("history up: {} / len {} / pos {}".format(
self._tmphist, len(self._tmphist), self._histpos))
self.set_cmd(self._tmphist[self._histpos])
def key_tab_handler(self):
self.tab_pressed.emit(False)
def key_stab_handler(self):
self.tab_pressed.emit(True)
class Validator(QValidator):
"""Validator to prevent the : from getting deleted"""
def validate(self, string, pos):
if string.startswith(':'):
return (QValidator.Acceptable, string, pos)
else:
return (QValidator.Invalid, string, pos)

View File

@ -1,47 +1,47 @@
from PyQt5.QtWidgets import QProgressBar, QSizePolicy
from PyQt5.QtCore import QSize
class Progress(QProgressBar):
""" The progress bar part of the status bar"""
bar = None
_stylesheet = """
QProgressBar {
border-radius: 0px;
border: 2px solid transparent;
margin-left: 1px;
}
QProgressBar::chunk {
background-color: white;
}
"""
def __init__(self, bar):
self.bar = bar
super().__init__(bar)
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self.setTextVisible(False)
self.setStyleSheet(self._stylesheet.strip())
self.hide()
def minimumSizeHint(self):
status_size = self.bar.size()
return QSize(100, status_size.height())
def sizeHint(self):
return self.minimumSizeHint()
def set_progress(self, prog):
"""Sets the progress of the bar and shows/hides it if necessary"""
# TODO display failed loading in some meaningful way?
if prog == 100:
self.setValue(prog)
self.hide()
else:
self.setValue(prog)
self.show()
def load_finished(self, ok):
self.hide()
from PyQt5.QtWidgets import QProgressBar, QSizePolicy
from PyQt5.QtCore import QSize
class Progress(QProgressBar):
""" The progress bar part of the status bar"""
bar = None
_stylesheet = """
QProgressBar {
border-radius: 0px;
border: 2px solid transparent;
margin-left: 1px;
}
QProgressBar::chunk {
background-color: white;
}
"""
def __init__(self, bar):
self.bar = bar
super().__init__(bar)
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self.setTextVisible(False)
self.setStyleSheet(self._stylesheet.strip())
self.hide()
def minimumSizeHint(self):
status_size = self.bar.size()
return QSize(100, status_size.height())
def sizeHint(self):
return self.minimumSizeHint()
def set_progress(self, prog):
"""Sets the progress of the bar and shows/hides it if necessary"""
# TODO display failed loading in some meaningful way?
if prog == 100:
self.setValue(prog)
self.hide()
else:
self.setValue(prog)
self.show()
def load_finished(self, ok):
self.hide()

View File

@ -1,35 +1,35 @@
from PyQt5.QtWidgets import QLabel
class Text(QLabel):
"""The text part of the status bar, composed of several 'widgets'"""
keystring = ''
error = ''
text = ''
scrollperc = ''
def __init__(self, bar):
super().__init__(bar)
self.setStyleSheet("padding-right: 1px")
def __setattr__(self, name, value):
super().__setattr__(name, value)
self.update()
def set_keystring(self, s):
"""Setter to be used as a Qt slot"""
self.keystring = s
def set_perc(self, x, y):
"""Setter to be used as a Qt slot"""
if y == 0:
self.scrollperc = '[top]'
elif y == 100:
self.scrollperc = '[bot]'
else:
self.scrollperc = '[{}%]'.format(y)
def update(self):
"""Update the text displayed"""
self.setText(' '.join([self.keystring, self.error, self.text,
self.scrollperc]))
from PyQt5.QtWidgets import QLabel
class Text(QLabel):
"""The text part of the status bar, composed of several 'widgets'"""
keystring = ''
error = ''
text = ''
scrollperc = ''
def __init__(self, bar):
super().__init__(bar)
self.setStyleSheet("padding-right: 1px")
def __setattr__(self, name, value):
super().__setattr__(name, value)
self.update()
def set_keystring(self, s):
"""Setter to be used as a Qt slot"""
self.keystring = s
def set_perc(self, x, y):
"""Setter to be used as a Qt slot"""
if y == 0:
self.scrollperc = '[top]'
elif y == 100:
self.scrollperc = '[bot]'
else:
self.scrollperc = '[{}%]'.format(y)
def update(self):
"""Update the text displayed"""
self.setText(' '.join([self.keystring, self.error, self.text,
self.scrollperc]))