wiki:Technical/Preferences

Preferences


Current Issues

  • Unable to provide dynamic content.
  • Poor grouping of options.
  • Many desirable user-configurable options not available.
  • Difficult to maintain.

New Features

  • Easy to maintain (separate source file per visible pane)
  • Dynamic configuration options. See Dynamic Options.

Design Considerations

  • Where possible, when an option is related to a function that has an icon, that icon should be displayed.

Dynamic Options

Having the options pane change shape or displayed options is generally a bad idea. When an option requires dynamic content it should pop up it's own window for option-specific settings.

For example
the portaudio player will always have different output devices, displaying a list of those devices, even if there is only 1 is fine. Beside that list there should be a button called Config which will pop up a new dialogue to set options specific to the selected output device. This would apply to any option that requires option-specific settings.

Layout

This section contains a new proposted layout for the new preference panes.

  • General
  • Subtitles
  • Video
    • Input
    • Output
  • Audio
    • Input
    • Output
  • Interface
    • Colours
      • Subtitle (edit box + grid on same pane)
      • Audio Display
    • Hotkeys
  • Paths
  • File Associations
  • Backup
  • Advanced
    • General
    • Interface
    • Audio
    • Video

File Associations

This should be a simple list with enable/disable checkboxes. Also if we currently don't handle a file type, list the program that is currently handling it if any so the user can make a more informed choice when 'stealing' the association.

See also #1037 Integrate better with Windows' file type associations. The implementation made for that issue does almost everything needed, except attempting to get a view of any current owner of associations.


Options Storage

Code-wise it will be an almost entire re-write, usage in source will only be slightly different, and the on-disk written version will be the same with a few extra bits of data.

Types

  • bool
  • char
  • colour
    • wxColour saved as (R,G,B)
  • float
  • int

There will have to be some way to have random-length arrays of values.

Storage

The config format will be as follows:

[section]
<name> (?|*)= <value>
  • [sections] can by convention have sub-sections by forming a "path" with slashes for section name.
  • Option names must be unique in each section.
  • All values are always <name> = <value>
  • ? denotes a value that has not been changed from its default value
  • * has been changed from it's default by the user.

Manipulation

Each option is declared in a single place. An option can not be used before it is declared. An option declaration consists of at least a section name, an option name, an option data type and a default value. A discussion point is whether options should be able to declare a range of valid values, e.g. an integer range or a set of strings. At the time the config file is read from disk, any option in the file that is not declared is ignored. This implies that all options must be declared before the config file is read, i.e. during module initialisation. (This will rule out declaring wanted/unwanted dynamic modules in the config file, meaning modules will either have to be detected or be listed in a separate configuration.) Declaring an option multiple times is a fatal error. Accessing an option that is not declared (yet) is a fatal error.

When an option is declared, an option object is allocated, this option object stores the value of the option at runtime. All accessing of the option passes through the option object. Accessing an option involves obtaining a reference to the option object, then getting or setting the value in the option object. The option object stores the option's type, if code attempts to access an option by the wrong type, it is a fatal error. Proposed interface:

class OptionValue {
public:
    // All operations are synchronised for thread safety
    enum OptionValueType {
        Type_Bool, Type_String, Type_Colour, Type_Double, Type_Int
    };
    const wxString & GetSection() const;
    const wxString & GetKey() const;
    OptionValueType GetType() const;
    bool GetBool() const;
    wxString GetString() const;
    wxColour GetColour() const;
    double GetDouble() const;
    int GetInt() const;
    void SetBool(bool);
    void SetString(const wxString &);
    void SetColour(const wxColour &);
    void SetDouble(double);
    void SetInt(int);
    bool IsDefault() const; /// Whether the current value is the default value or modified
    void ResetDefault();    /// Reset the option to unchanged from default
};

We have discussed a change notification system for option values, this should be handled per OptionValue object, which should keep a list of listeners and notify them on change.

The OptionsManager object keeps a master list of all OptionValue objects and handles the registration of new options. It could have an interface similar to this:

class OptionManager {
public:
    // OptionValue objects will probably be kept in a double dictionary of some sort
    OptionValue & GetOption(const wxString &section, const wxString &key) const;
    void DeclareOptionBool(const wxString &section, const wxString &key, bool default_value);
    void DeclareOptionString(const wxString &section, const wxString &key, const wxString &default_value);
    void DeclareOptionColour(const wxString &section, const wxString &key, const wxColour &default_value);
    void DeclareOptionDouble(const wxString &section, const wxString &key, double default_value);
    void DeclareOptionInt(const wxString &section, const wxString &key, int default_value);
    void SetFile(const wxString &filename); /// Set the disk file backing this options set and load any existing values from it
    void FlushToDisk() const;               /// Force writing and flushing all options to disk right now
};

We will probably want to implement a class for handling MRU lists on top of this, since we have a couple of those.

It shouldn't be needed to have a shorthand API for getting and setting option values one-shot, since it can be accomplished similar to this:

    int foo = Options.GetOption(_T("section/name"), _T("foo")).GetInt();
    Options.GetOption(_T("section/name"), _T("foo")).SetInt(foo);

Any code that needs to access an option with any frequency should obtain a reference to the OptionValue object and store it for direct access. Actual section and key names should be repeated as little as possible in code, and referencing option values by OptionValue objects should be preferred.