diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index f9412b0f1..1c7227ded 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -21,10 +21,12 @@ import os import io import os.path import logging +from collections import OrderedDict from configparser import (ConfigParser, ExtendedInterpolation, NoSectionError, NoOptionError) from qutebrowser.utils.misc import read_file +from qutebrowser.config.options import * config = None state = None @@ -46,6 +48,47 @@ def init(confdir): state = Config(confdir, 'state', always_save=True) +class ConfigStructure: + + def __init__(self): + self.config = OrderedDict([ + ('general', KeyValueSection( + ('show_completion', ShowCompletion()), + ('completion_height', CompletionHeight()), + ('ignorecase', IgnoreCase()), + ('wrapsearch', WrapSearch()), + ('startpage', StartPage()), + ('auto_search', AutoSearch()), + ('zoomlevels', ZoomLevels()), + ('defaultzoom', DefaultZoom()), + )), + ('tabbar', KeyValueSection( + ('movable', Movable()), + ('closebuttons', CloseButtons()), + ('scrollbuttons', ScrollButtons()), + ('position', Position()), + ('select_on_remove', SelectOnRemove()), + ('last_close', LastClose()), + )), + ('searchengines', ValueListSection( + SearchEngineKeyValue() + )), + ('keybind', ValueListSection( + KeybindKeyValue() + )), + ('aliases', ValueListSection( + AliasKeyValue() + )), + ('colors', KeyValueSection( + ('completion.fg', CompletionFgColor()), + ('completion.item.bg', CompletionItemBgColor()), + # FIXME ... + )), + ('fonts', KeyValueSection( + )), + ]) + + class Config(ConfigParser): """Our own ConfigParser subclass. diff --git a/qutebrowser/config/options.py b/qutebrowser/config/options.py new file mode 100644 index 000000000..bc33feb38 --- /dev/null +++ b/qutebrowser/config/options.py @@ -0,0 +1,370 @@ +# Copyright 2014 Florian Bruhin (The Compiler) +# +# 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 . + +"""Setting options used for qutebrowser.""" + +from qutebrowser.config.templates import * + +class ShowCompletion(BoolSettingValue): + + """Whether to show the autocompletion window or not.""" + + default = "true" + +class CompletionHeight(SettingValue): + + """The height of the completion, in px or as percentage of the window.""" + + default = "50%" + + def validate(self, value): + if value.endswith('%'): + try: + intval = int(value.rstrip('%')) + except ValueError: + return False + else: + return 0 <= intval <= 100 + else: + try: + intval = int(value) + except ValueError: + return False + else: + return intval > 0 + + +class IgnoreCase(BoolSettingValue): + + """Whether to do case-insensitive searching.""" + + default = "true" + + +class WrapSearch(BoolSettingValue): + + """Whether to wrap search to the top when arriving at the end.""" + + default = "true" + + +class StartPage(ListSettingValue): + + """The default page(s) to open at the start, separated with commas.""" + + default = "http://www.duckduckgo.com/" + + +class AutoSearch(BoolSettingValue): + + """Whether to start a search when something else than an URL is entered.""" + + values = [("naive", "Use simple/naive check."), + ("dns", "Use DNS requests (might be slow!)."), + ("false": "Never search automatically.")] + default = "naive" + + def validate(self, value): + if value.lower() in ["naive", "dns"]: + return True + else: + return super().validate(value) + return True + else: + return super().validate(value) + + def transform(self, value): + if value.lower() in ["naive", "dns"]: + return value.lower() + elif super().transform(value): + # boolean true is an alias for naive matching + return "naive" + else: + return "false" + + +class ZoomLevels(IntListSettingValue): + + """The available zoom levels, separated by commas.""" + + default = "25,33,50,67,75,90,100,110,125,150,175,200,250,300,400,500" + + +class DefaultZoom(IntSettingValue): + + """The default zoom level.""" + + # FIXME we might want to validate if defaultzoom is in zoomlevels... + + default = "100" + + +class Movable(BoolSettingValue): + + """Whether tabs should be movable.""" + + default = "true" + + +class CloseButtons(BoolSettingValue): + + """Whether tabs should have close-buttons.""" + + default = "false" + + +class ScrollButtons(BoolSettingValue): + + """Whether there should be scroll buttons if there are too many tabs.""" + + default = "true" + + +class Position(SettingValue): + + """The position of the tab bar.""" + + values = ["north", "south", "east", "west"] + default = "north" + + +class SelectOnRemove(SettingValue): + + """Which tab to select when the focused tab is removed.""" + + values = [("left", "Select the tab on the left."), + ("right", "Select the tab on the right."), + ("previous", "Select the previously selected tab.")] + default = "previous" + + +class LastClose(SettingValue): + + """Behaviour when the last tab is closed.""" + + values = [("ignore", "Don't do anything."), + ("blank", "Load about:blank."), + ("quit", "Quit qutebrowser.")] + default = "ignore" + +### FIXME what to do with list-style sections? + +class SearchEngine(SettingValue): + + """A search engine setting.""" + + def validate(self, value): + return "{}" in value + + +class CompletionFgColor(ColorSettingValue): + + """Text color of the completion widget.""" + + default = "#333333" + + +class CompletionItemBgColor(ColorSettingValue): + + """Background color of completion widget items.""" + + default = "white" + + +class CompletionCategoryBgColor(ColorSettingValue): + + """Background color of the completion widget category headers.""" + + default = ("completion.category.bg = qlineargradient(" + "x1:0, y1:0, x2:0, y2:1, stop:0 #e4e4e4, stop:1 #dbdbdb") + + +class CompletionCategoryTopBorderColor(ColorSettingValue): + + """Top border color of the completion widget category headers.""" + + default = "#808080" + + +class CompletionCategoryBottomBorderColor(ColorSettingValue): + + """Bottom border color of the completion widget category headers.""" + + default = "#bbbbbb" + + +class CompletionItemSelectedFgColor(ColorSettingValue): + + """Foreground color of the selected completion item.""" + + default = "#333333" + + +class CompletionItemSelectedBgColor(ColorSettingValue): + + """Background color of the selected completion item.""" + + default = "#ffec8b" + + +class CompletionItemSelectedTopBorderColor(ColorSettingValue): + + """Top border color of the selected completion item.""" + + default = "#f2f2c0" + + +class CompletionItemSelectedBottomBorderColor(ColorSettingValue): + + """Bottom border color of the selected completion item.""" + + default = "#ffec8b" + + +class CompletionMatchFgColor(ColorSettingValue): + + """Foreground color of the matched text in the completion.""" + + default = "red" + + +class StatusbarBgColor(ColorSettingValue): + + """Background color of the statusbar.""" + + default = "black" + + +class StatusbarFgColor(ColorSettingValue): + + """Foreground color of the statusbar.""" + + default = "white" + + +class StatusbarFgErrorColor(ColorSettingValue): + + """Foreground color of the statusbar if there was an error.""" + + default = "${statusbar.fg}" + + +class StatusbarBgErrorColor(ColorSettingValue): + + """Background color of the statusbar if there was an error.""" + + default = "red" + + +class StatusbarProgressBgColor(ColorSettingValue): + + """Background color of the progress bar.""" + + default = "white" + + +class StatusbarUrlFgColor(ColorSettingValue): + + """Default foreground color of the URL in the statusbar.""" + + default = "${statusbar.fg}" + + +class StatusbarUrlSuccessFgColor(ColorSettingValue): + + """Foreground color of the URL in the statusbar on successful load.""" + + default = "lime" + + +class StatusbarUrlErrorFgColor(ColorSettingValue): + + """Foreground color of the URL in the statusbar on error.""" + + default = "orange" + + +class StatusbarUrlWarnFgColor(ColorSettingValue): + + """Foreground color of the URL in the statusbar when there's a warning.""" + + default = "yellow" + + +class StatusbarUrlHoverFgColor(ColorSettingValue): + + """Foreground color of the URL in the statusbar for hovered links.""" + + default = "aqua" + + +class TabFgColor(ColorSettingValue): + + """Foreground color of the tabbar.""" + + default = "white" + + +class TabBgColor(ColorSettingValue): + + """Background color of the tabbar.""" + + default = "grey" + + +class TabSelectedBgColor(ColorSettingValue): + + """Background color of the tabbar for the selected tab.""" + + default = "black" + + +class TabSeperatorColor(ColorSettingValue): + + """Color for the tab seperator.""" + + default = "white" + + +class MonospaceFonts(FontSettingValue): + + """Default monospace fonts.""" + + default = ('Monospace, "DejaVu Sans Mono", Consolas, Monaco, ' + '"Bitstream Vera Sans Mono", "Andale Mono", "Liberation Mono", ' + '"Courier New", Courier, monospace, Fixed, Terminal') + + +class CompletionFont(FontSettingValue): + + """Font used in the completion widget.""" + + default = "8pt ${_monospace}" + + +class TabbarFont(FontSettingValue): + + """Font used in the tabbar.""" + + default = "8pt ${_monospace}" + + +class StatusbarFont(FontSettingValue): + + """Font used in the statusbar.""" + + default = "8pt ${_monospace}" diff --git a/qutebrowser/config/templates.py b/qutebrowser/config/templates.py new file mode 100644 index 000000000..4dd57d1f1 --- /dev/null +++ b/qutebrowser/config/templates.py @@ -0,0 +1,162 @@ +# Copyright 2014 Florian Bruhin (The Compiler) +# +# 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 . + +"""Templates for setting options.""" + +import qutebrowser.commands.utils as cmdutils + +class SettingValue: + + """Base class for settings. The docstring is used as a description.""" + + # Possible values, if they are fixed. + # Either a list of strings, or a list of (value, desc) tuples. + values = None + + # Default value if user has not overriden it, as a string. + default = None + + def transform(self, value): + """Transform the setting value. + + This method can assume the value is indeed a valid value. + + The default implementation returns the original value. + + Args: + value: The value to transform. + + Return: + The transformed value. + + """ + return value + + def validate(self, value): + """Validate value against possible values. + + The default implementation checks the value against self.values if it + was defined. + + Args: + value: The value to validate. + + Return: + Ture if validation succeeded, False otherwise. + + Raise: + NotImplementedError if self.values is not defined and this method + should be overridden. + + """ + if self.values is not None: + return value in self.values + else: + raise NotImplementedError + +class BoolSettingValue(SettingValue): + + """Base class for a boolean setting.""" + values = ['true', 'false'] + + # Taken from configparser + _BOOLEAN_STATES = {'1': True, 'yes': True, 'true': True, 'on': True, + '0': False, 'no': False, 'false': False, 'off': False} + + def transform(self, value): + return self._BOOLEAN_STATES[value.lower()] + + def validate(self, value): + return value.lower() in self._BOOLEAN_STATES + + +class IntSettingValue(SettingValue): + + """Base class for an integer setting.""" + + def transform(self, value): + return int(value) + + def validate(self, value): + try: + int(value) + except ValueError: + return False + else: + return True + + +class ListSettingValue(SettingValue): + + """Base class for a (string-)list setting.""" + + def transform(self, value): + return value.split(',') + + def validate(self, value): + return True + + +class IntListSettingValue(ListSettingValue): + + """Base class for an int-list setting.""" + + def transform(self, value): + vals = super().transform(value) + return map(int, vals) + + def validate(self, value) + try: + self.transform(value) + except ValueError: + return False + else: + return True + + +class CommandSettingValue(SettingValue): + + """Base class for a command value with arguments.""" + + values = cmdutils.cmd_dict.values() + + def validate(self, value): + cp = cmdutils.CommandParser() + try: + cp.parse(value) + except cmdutils.NoSuchCommandError: + return False + else: + return True + + +class ColorSettingValue(SettingValue): + + """Base class for a color value.""" + + def validate(self, value): + # FIXME validate colors + return True + + +class FontSettingValue(SettingValue): + + """Base class for a font value.""" + + def validate(self, value): + # FIXME validate fonts + return True