<commit_before># -*- coding: utf-8 -*-
#
# Copyright (C) 2014-2018 khalim19
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
This module defines API that can be used to create plug-in settings and GUI
elements associated with the settings.
"""

from __future__ import absolute_import, division, print_function, unicode_literals
from future.builtins import *
import future.utils

import abc
import collections
import copy
import os
import types

import gimp
from gimp import pdb
import gimpcolor
import gimpenums

from . import pgconstants
from . import pgpath
from . import pgsettingpersistor
from . import pgsettingpresenter
from . import pgsettingutils
from . import pgutils
from .pgsettingpresenters_gtk import SettingGuiTypes


class SettingPdbTypes(object):
  int32 = gimpenums.PDB_INT32
  int16 = gimpenums.PDB_INT16
  int8 = gimpenums.PDB_INT8
  int = int32
  float = gimpenums.PDB_FLOAT
  string = gimpenums.PDB_STRING
  
  image = gimpenums.PDB_IMAGE
  item = gimpenums.PDB_ITEM
  drawable = gimpenums.PDB_DRAWABLE
  layer = gimpenums.PDB_LAYER
  channel = gimpenums.PDB_CHANNEL
  selection = gimpenums.PDB_SELECTION
  vectors = gimpenums.PDB_VECTORS
  path = vectors
  
  color = gimpenums.PDB_COLOR
  parasite = gimpenums.PDB_PARASITE
  display = gimpenums.PDB_DISPLAY
  pdb_status = gimpenums.PDB_STATUS
  
  array_int32 = gimpenums.PDB_INT32ARRAY
  array_int16 = gimpenums.PDB_INT16ARRAY
  array_int8 = gimpenums.PDB_INT8ARRAY
  array_int = array_int32
  array_float = gimpenums.PDB_FLOATARRAY
  array_string = gimpenums.PDB_STRINGARRAY
  array_color = gimpenums.PDB_COLORARRAY
  
  none = None
  automatic = type(b"AutomaticSettingPdbType", (), {})()


@future.utils.python_2_unicode_compatible
class Setting(pgsettingutils.SettingParentMixin, pgsettingutils.SettingEventsMixin):
  """
  This class holds data about a plug-in setting.
  
  Properties and methods in settings can be used in multiple scenarios, such as:
  * using setting values as variables in the main logic of plug-ins
  * registering GIMP Procedural Database (PDB) parameters to plug-ins
  * managing GUI element properties (values, labels, etc.)
  
  This class in particular can store any data. However, it is strongly
  recommended to use the appropriate `Setting` subclass for a particular data
  type, as the subclasses offer the following benefits:
  * setting can be registered to the GIMP procedural database (PDB),
  * automatic validation of input values,
  * readily available GUI element, keeping the GUI and the setting value in
    sync.
  
  Settings can contain event handlers that are triggered when a setting property
  changes, e.g. `value` (when `set_value()` method is called). This way, for
  example, other settings can be updated automatically according to the new
  value of the modified setting.

  The following specific event types are invoked for settings:
  
    * `"value-changed"` - invoked after `set_value()` or `reset()` is called
      and before events of type `"after-set-value"` or `"after-reset"`.
    
    * `"before-set-value"` - invoked before `set_value()` is called.
    
    * `"after-set-value"` - invoked after `set_value()` is called.
    
    * `"before-reset"` - invoked before `reset()` is called.
    
    * `"after-reset"` - invoked after `reset()` is called.
    
    * `"before-set-gui"` - invoked before `set_gui()` is called.
    
    * `"after-set-gui"` - invoked after `set_gui()` is called.
    
    * `"before-load"` - invoked before loading a setting via
      `pgsettingpersistor.SettingPersistor.load`.
    
    * `"after-load"` - invoked after loading a setting via
      `pgsettingpersistor.SettingPersistor.load`. Events will not be invoked
      if loading settings failed (i.e. `SettingPersistor` returns `READ_FAIL`
      status). Events will be invoked for all settings, even if some of them
      were not found in setting sources (i.e. `SettingPersistor` returns
      `NOT_ALL_SETTINGS_FOUND` status).
    
    * `"before-save"` - invoked before saving a setting via
    `pgsettingpersistor.SettingPersistor.save`.
    
    * `"after-save"` - invoked after saving a setting via
      `pgsettingpersistor.SettingPersistor.save`. Events will not be invoked
      if saving settings failed (i.e. `SettingPersistor` returns `SAVE_FAIL`
      status).
    
    * `"before-load-group"` - invoked before loading settings in a group via
      `SettingGroup.load`.
    
    * `"after-load-group"` - invoked after loading settings in a group via
      `SettingGroup.load`. This is useful if the group contains settings with
      different setting sources so that the event is invoked only once after
      all settings from different sources are loaded. This also applies to
      other related events (`"before-load-group"`, `"before-save-group"`,
      `"after-save-group"`).
    
    * `"before-save-group"` - invoked before saving settings in a group via
      `SettingGroup.load`.
    
    * `"after-save-group"` - invoked after saving settings in a group via
      `SettingGroup.load`.
  
  If a setting subclass supports "empty" values, such values will not be
  considered invalid when used as default values. However, empty values will be
  treated as invalid when assigning the setting one of such values after
  instantiation. Examples of empty values include `None` for an image object, or
  "Choose an item" for an enumerated setting. Empty values are useful when users
  must choose a different value, yet no valid value is a good candidate for a
  default value.
  
  Attributes:
  
  * `name` (read-only) - A name (string) that uniquely identifies the setting.
  
  * `value` (read-only) - The setting value. To set the value, call the
    `set_value()` method. `value` is initially set to `default_value`.
  
  * `default_value` (read-only) - Default value of the setting assigned upon its
    initialization or after the `reset()` method is called. If not specified or
    if `DEFAULT_VALUE` is passed explicitly, a default value is assigned
    automatically. The value depends on the particular setting subclass. This
    class uses `None` as the default value. Note that it is still a good
    practice to specify default values explicitly.
  
  * `gui` (read-only) - `SettingPresenter` instance acting as a wrapper of a GUI
    element. With `gui`, you may modify GUI-specific attributes, such as
    visibility or sensitivity.
  
  * `display_name` (read-only) - Setting name in human-readable format. Useful
    e.g. as GUI labels. The display name may contain underscores, which can be
    interpreted by the GUI as keyboard mnemonics.
  
  * `description` (read-only) - Usually `display_name` plus additional
    information in parentheses (such as boundaries for numeric values). Useful
    as a setting description when registering the setting as a plug-in parameter
    to the GIMP Procedural Database (PDB). If the class uses `display_name` to
    generate the description and `display_name` contains underscores, they are
    removed in the description.
  
  * `pdb_type` (read-only) - GIMP PDB parameter type, used when registering the
    setting as a plug-in parameter to the PDB. In Setting subclasses, only
    specific PDB types are allowed. Refer to the documentation of the subclasses
    for the list of allowed PDB types.
  
  * `pdb_name` (read-only) - Setting name as it appears in the GIMP PDB as
    a PDB parameter name.
  
  * `setting_sources` (read-only) - Default setting sources to use when loading
    or saving the setting. If `None`, no default sources are specified.
  
  * `error_messages` (read-only) - A dictionary of error messages containing
    (message name, message contents) pairs, which can be used e.g. if a value
    assigned to the setting is invalid. You can add your own error messages and
    assign them to one of the "default" error messages (such as "invalid_value"
    in several `Setting` subclasses) depending on the context in which the value
    assigned is invalid.
  
  * `tags` - A set of arbitrary tags attached to the setting. Tags can be used
    to e.g. iterate over a specific subset of settings.
  """
  
  DEFAULT_VALUE = type(b"DefaultValue", (), {})()
  
  _ALLOWED_PDB_TYPES = []
  _ALLOWED_GUI_TYPES = []
  _DEFAULT_DEFAULT_VALUE = None
  _EMPTY_VALUES = []
  
  def __init__(
        self,
        name,
        default_value=DEFAULT_VALUE,
        display_name=None,
        description=None,
        pdb_type=SettingPdbTypes.automatic,
        gui_type=SettingGuiTypes.automatic,
        allow_empty_values=False,
        auto_update_gui_to_setting=True,
        setting_sources=None,
        error_messages=None,
        tags=None):
    """
    Described are only those parameters that do not correspond to
    any attribute in this class, or parameters requiring additional information.
    
    Parameters:
    
    * `default_value` - During Setting initialization, the default value is
      validated. If one of the so called "empty values" (specific to each
      setting class) is passed as the default value, default value validation is
      not performed.
    
    * `pdb_type` - one of the `SettingPdbTypes` items. If set to
      `SettingPdbTypes.automatic` (the default), the first PDB type in the list
      of allowed PDB types for a particular Setting subclass is chosen. If no
      allowed PDB types are defined for that subclass, the setting cannot be
      registered (`None` is assigned).
    
    * `gui_type` - Type of GUI element to be created by the `set_gui` method.
      Use the `SettingGuiTypes` enum to specify the desired GUI type.
    
      If `gui_type` is `SettingGuiTypes.automatic`, choose the first GUI type
      from the list of allowed GUI type for the corresponding `Setting`
      subclass. If there are no allowed GUI types for that subclass, no GUI is
      created for this setting.
      
      If an explicit GUI type is specified, it must be one of the types from the
      list of allowed GUI types for the corresponding `Setting` subclass. If
      not, `ValueError` is raised.
      
      If the `gui_type` is `None`, no GUI is created for this setting.
    
    * `allow_empty_values` - If `False` and an empty value is passed to the
      `set_value` method, then the value is considered invalid. Otherwise, the
      value is considered valid.
    
    * `auto_update_gui_to_setting` - If `True`, automatically update the setting
      value if the GUI value is updated. If `False`, the setting must be updated
      manually by calling `Setting.gui.update_setting_value` when needed.
      
      This parameter does not have any effect if the GUI type used in
      this setting cannot provide automatic GUI-to-setting update.
    
    * `error_messages` - A dictionary containing (message name, message
      contents) pairs. Use this to pass custom error messages. This way, you may
      also override default error messages defined in classes.
    
    * `tags` - An iterable container (list, set, etc.) of arbitrary tags
      attached to the setting. Tags can be used to e.g. iterate over a specific
      subset of settings.
    """
    super().__init__()
    
    self._name = name
    pgsettingutils.check_setting_name(self._name)
    
    self._default_value = self._get_default_value(default_value)
    
    self._value = self._copy_value(self._default_value)
    
    self._allow_empty_values = allow_empty_values
    self._empty_values = list(self._EMPTY_VALUES)
    
    self._display_name = pgsettingutils.get_processed_display_name(
      display_name, self._name)
    self._description = pgsettingutils.get_processed_description(
      description, self._display_name)
    
    self._pdb_type = self._get_pdb_type(pdb_type)
    self._pdb_name = pgsettingutils.get_pdb_name(self._name)
    
    self._setting_sources = setting_sources
    
    self._setting_value_synchronizer = pgsettingpresenter.SettingValueSynchronizer()
    self._setting_value_synchronizer.apply_gui_value_to_setting = (
      self._apply_gui_value_to_setting)
    
    self._gui_type = self._get_gui_type(gui_type)
    self._gui = pgsettingpresenter.NullSettingPresenter(
      self, None, self._setting_value_synchronizer,
      auto_update_gui_to_setting=auto_update_gui_to_setting)
    
    self._error_messages = {}
    self._init_error_messages()
    if error_messages is not None:
      self._error_messages.update(error_messages)
    
    self._tags = set(tags) if tags is not None else set()
    
    if self._should_validate_default_value():
      self._validate_default_value()
  
  @property
  def name(self):
    return self._name
  
  @property
  def value(self):
    return self._value
  
  @property
  def default_value(self):
    return self._default_value
  
  @property
  def gui(self):
    return self._gui
  
  @property
  def display_name(self):
    return self._display_name
  
  @property
  def description(self):
    return self._description
  
  @property
  def pdb_type(self):
    return self._pdb_type
  
  @property
  def pdb_name(self):
    return self._pdb_name
  
  @property
  def setting_sources(self):
    return self._setting_sources
  
  @property
  def error_messages(self):
    return self._error_messages
  
  @property
  def tags(self):
    return self._tags
  
  def __str__(self):
    return pgutils.stringify_object(self, self.name)
  
  def get_path(self, relative_path_setting_group=None):
    """
    This is a wrapper method for `pgsettingutils.get_setting_path`. Consult the
    method for more information.
    """
    return pgsettingutils.get_setting_path(self, relative_path_setting_group)
  
  def set_value(self, value):
    """
    Set the setting value.
    
    Before the assignment, validate the value. If the value is invalid, raise
    `SettingValueError`.
    
    Update the value of the GUI element. Even if the setting has no GUI element
    assigned, the value is recorded. Once a GUI element is assigned to the
    setting, the recorded value is copied over to the GUI element.
    
    Invoke event handlers of types `"before-set-value"` before assigning the
    value and `"value-changed"` and `"after-set-value"` (in this order) after
    assigning the value.
    
    Note: This is a method and not a property because of the additional overhead
    introduced by validation, GUI updating and event handling. `value` still
    remains a property for the sake of brevity.
    """
    self.invoke_event("before-set-value")
    
    self._validate_and_assign_value(value)
    self._setting_value_synchronizer.apply_setting_value_to_gui(value)
    
    self.invoke_event("value-changed")
    self.invoke_event("after-set-value")
  
  def reset(self):
    """
    Reset setting value to its default value.
    
    This is different from
    
      setting.set_value(setting.default_value)
    
    in that `reset()` does not validate the default value.
    
    Invoke event handlers of types `"before-reset"` before resetting and
    `"value-changed"` and `"after-reset"` (in this order) after resetting.
    
    `reset()` also updates the GUI.
    
    If the default value is an empty container (list, dict, ...), resetting
    works properly. If the default value is a non-empty container, it is the
    responsibility of the caller to ensure that the default value does not get
    modified, for example by connecting a `"before-reset"` event that sets the
    value to the correct default value before resetting.
    """
    self.invoke_event("before-reset")
    
    self._value = self._copy_value(self._default_value)
    self._setting_value_synchronizer.apply_setting_value_to_gui(self._value)
    
    self.invoke_event("value-changed")
    self.invoke_event("after-reset")
  
  def set_gui(
        self,
        gui_type=SettingGuiTypes.automatic,
        gui_element=None,
        auto_update_gui_to_setting=True):
    """
    Create a new GUI object (`SettingPresenter` instance) for this setting or
    remove the GUI. The state of the previous GUI object is copied to the new
    GUI object (such as its value, visibility and sensitivity).
    
    Parameters:
    
    * `gui_type` - `SettingPresenter` type to wrap `gui_element` around.
      
      When calling this method, `gui_type` does not have to be one of the
      allowed GUI types specified in the setting.
      
      If `gui_type` is `SettingGuiTypes.automatic`, create a GUI object of the
      type specified in the `gui_type` parameter in `__init__`.
      
      To specify an existing GUI element, pass a specific `gui_type` and the
      GUI element in `gui_element`. This is useful if you wish to use the GUI
      element for multiple settings or for other purposes outside this setting.
      
      If `gui_type` is `None`, remove the GUI and disconnect any events the GUI
      had. The state of the old GUI is still preserved.
    
    * `gui_element` - A GUI element (wrapped in a `SettingPresenter` instance).
    
      If `gui_type` is `SettingGuiTypes.automatic`, `gui_element` is ignored.
      If `gui_type` is not `SettingGuiTypes.automatic` and `gui_element` is
      `None`, raise `ValueError`.
    
    * `auto_update_gui_to_setting` - See `auto_update_gui_to_setting` parameter
      in `__init__`.
    """
    if gui_type != SettingGuiTypes.automatic and gui_element is None:
      raise ValueError("gui_element cannot be None if gui_type is automatic")
    if gui_type == SettingGuiTypes.automatic and gui_element is not None:
      raise ValueError("gui_type cannot be automatic if gui_element is not None")
    
    self.invoke_event("before-set-gui")
    
    if gui_type == SettingGuiTypes.automatic:
      gui_type = self._gui_type
    elif gui_type is None:
      gui_type = pgsettingpresenter.NullSettingPresenter
      # We need to disconnect the "GUI changed" event before removing the GUI.
      self._gui.auto_update_gui_to_setting(False)
    
    self._gui = gui_type(
      self,
      gui_element,
      setting_value_synchronizer=self._setting_value_synchronizer,
      old_setting_presenter=self._gui,
      auto_update_gui_to_setting=auto_update_gui_to_setting)
    
    self.invoke_event("after-set-gui")
  
  def load(self, setting_sources=None):
    """
    Load setting value from the specified setting source(s). See
    `pgsettingpersistor.SettingPersistor.load` for more information about
    setting sources.
    
    If `setting_sources` is `None`, use the default setting sources. If
    specified, use a subset of sources matching the default sources. For
    example, if the default sources contain a persistent and a
    session-persistent source and `setting_sources` contains a
    session-persistent source, the setting value is loaded from the
    session-persistent source only.
    
    If there are no default setting sources or `setting_sources` does not match
    any of the default sources, this method has no effect.
    """
    self._load_save(setting_sources, pgsettingpersistor.SettingPersistor.load)
  
  def save(self, setting_sources=None):
    """
    Save setting value to the specified setting source(s). See
    `pgsettingpersistor.SettingPersistor.save` for more information about
    setting sources.
    
    If `setting_sources` is `None`, use the default setting sources. If
    specified, use a subset of sources matching the default sources. For
    example, if the default sources contain a persistent and a
    session-persistent source and `setting_sources` contains a
    session-persistent source, the setting value is loaded from the
    session-persistent source only.
    
    If there are no default setting sources or `setting_sources` does not match
    any of the default sources, this method has no effect.
    """
    self._load_save(setting_sources, pgsettingpersistor.SettingPersistor.save)
  
  def is_value_empty(self):
    """
    Return `True` if the setting value is one of the empty values defined for
    the setting class, otherwise return `False`.
    """
    return self._is_value_empty(self._value)
  
  def can_be_registered_to_pdb(self):
    """
    Return `True` if setting can be registered as a parameter to GIMP PDB,
    `False` otherwise.
    """
    return self._pdb_type != SettingPdbTypes.none
  
  def get_pdb_param(self):
    """
    Return a list of tuples, each tuple containing data describing the setting
    as a GIMP PDB parameter - PDB type, PDB name and description.
    
    If the setting does not support any PDB type, return `None`.
    
    Most setting classes return a list of only one tuple, meaning the setting is
    represented by one PDB parameter.
    """
    if self.can_be_registered_to_pdb():
      return [(
        self.pdb_type,
        self.pdb_name.encode(pgconstants.GIMP_CHARACTER_ENCODING),
        self.description.encode(pgconstants.GIMP_CHARACTER_ENCODING))]
    else:
      return None
  
  def _validate(self, value):
    """
    Check whether the specified value is valid. If the value is invalid, raise
    `SettingValueError`.
    """
    pass
  
  def _init_error_messages(self):
    """
    Initialize custom error messages in the `error_messages` dictionary.
    """
    pass
  
  def _copy_value(self, value):
    """
    Create a shallow copy of the specified value. By default, iterables (except
    strings) are copied, otherwise the original objects are returned.
    
    Override this method in subclasses in case copying must be handled
    differently.
    """
    if (isinstance(value, collections.Iterable)
        and not isinstance(value, types.StringTypes)):
      return copy.copy(value)
    else:
      return value
  
  def _assign_value(self, value):
    """
    Assign specified value to the `_value` attribute after validation.
    
    Override this method in subclasses if other modifications to the `_value`
    attribute must be made other than mere assignment.
    """
    self._value = value
  
  def _is_value_empty(self, value):
    return value in self._empty_values
  
  def _validate_and_assign_value(self, value):
    if not self._allow_empty_values:
      self._validate_setting(value)
    else:
      if not self._is_value_empty(value):
        self._validate_setting(value)
    
    self._assign_value(value)
  
  def _apply_gui_value_to_setting(self, value):
    self._validate_and_assign_value(value)
    self.invoke_event("value-changed")
  
  def _validate_setting(self, value):
    try:
      self._validate(value)
    except SettingValueError as e:
      raise SettingValueError(str(e), setting=self)
  
  def _should_validate_default_value(self):
    return not self._is_value_empty(self._default_value)
  
  def _validate_default_value(self):
    """
    Check whether the default value of the setting is valid. If the default
    value is invalid, raise `SettingDefaultValueError`.
    """
    try:
      self._validate(self._default_value)
    except SettingValueError as e:
      raise SettingDefaultValueError(str(e), setting=self)
  
  def _get_default_value(self, default_value):
    if isinstance(default_value, type(self.DEFAULT_VALUE)):
      if not callable(self._DEFAULT_DEFAULT_VALUE):
        return self._DEFAULT_DEFAULT_VALUE
      else:
        return self._DEFAULT_DEFAULT_VALUE()
    else:
      return default_value
  
  def _get_pdb_type(self, pdb_type):
    if pdb_type == SettingPdbTypes.automatic:
      return self._get_default_pdb_type()
    elif pdb_type is None or pdb_type == SettingPdbTypes.none:
      return SettingPdbTypes.none
    elif pdb_type in self._ALLOWED_PDB_TYPES:
      return pdb_type
    else:
      raise ValueError(
        "GIMP PDB type '{}' not allowed; for the list of allowed PDB types, refer to "
        "the documentation of the appropriate Setting class".format(pdb_type))
  
  def _get_default_pdb_type(self):
    if self._ALLOWED_PDB_TYPES:
      return self._ALLOWED_PDB_TYPES[0]
    else:
      return SettingPdbTypes.none
  
  def _get_gui_type(self, gui_type):
    gui_type_to_return = None
    
    if gui_type is None:
      gui_type_to_return = SettingGuiTypes.none
    elif gui_type == SettingGuiTypes.automatic:
      if self._ALLOWED_GUI_TYPES:
        gui_type_to_return = self._ALLOWED_GUI_TYPES[0]
      else:
        gui_type_to_return = SettingGuiTypes.none
    else:
      if gui_type in self._ALLOWED_GUI_TYPES:
        gui_type_to_return = gui_type
      elif gui_type in [SettingGuiTypes.none, pgsettingpresenter.NullSettingPresenter]:
        gui_type_to_return = gui_type
      else:
        raise ValueError(
          "invalid GUI type; must be one of {}".format(
            [type_.__name__ for type_ in self._ALLOWED_GUI_TYPES]))
    
    return gui_type_to_return
  
  def _load_save(self, setting_sources, load_save_func):
    if setting_sources is None:
      setting_sources = self._setting_sources
    else:
      if self._setting_sources is not None:
        setting_sources = [
          source for source in setting_sources if source in self._setting_sources]<commit_msg>pgsetting: Clarify error message about invalid GUI type<commit_after># -*- coding: utf-8 -*-
#
# Copyright (C) 2014-2018 khalim19
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
This module defines API that can be used to create plug-in settings and GUI
elements associated with the settings.
"""

from __future__ import absolute_import, division, print_function, unicode_literals
from future.builtins import *
import future.utils

import abc
import collections
import copy
import os
import types

import gimp
from gimp import pdb
import gimpcolor
import gimpenums

from . import pgconstants
from . import pgpath
from . import pgsettingpersistor
from . import pgsettingpresenter
from . import pgsettingutils
from . import pgutils
from .pgsettingpresenters_gtk import SettingGuiTypes


class SettingPdbTypes(object):
  int32 = gimpenums.PDB_INT32
  int16 = gimpenums.PDB_INT16
  int8 = gimpenums.PDB_INT8
  int = int32
  float = gimpenums.PDB_FLOAT
  string = gimpenums.PDB_STRING
  
  image = gimpenums.PDB_IMAGE
  item = gimpenums.PDB_ITEM
  drawable = gimpenums.PDB_DRAWABLE
  layer = gimpenums.PDB_LAYER
  channel = gimpenums.PDB_CHANNEL
  selection = gimpenums.PDB_SELECTION
  vectors = gimpenums.PDB_VECTORS
  path = vectors
  
  color = gimpenums.PDB_COLOR
  parasite = gimpenums.PDB_PARASITE
  display = gimpenums.PDB_DISPLAY
  pdb_status = gimpenums.PDB_STATUS
  
  array_int32 = gimpenums.PDB_INT32ARRAY
  array_int16 = gimpenums.PDB_INT16ARRAY
  array_int8 = gimpenums.PDB_INT8ARRAY
  array_int = array_int32
  array_float = gimpenums.PDB_FLOATARRAY
  array_string = gimpenums.PDB_STRINGARRAY
  array_color = gimpenums.PDB_COLORARRAY
  
  none = None
  automatic = type(b"AutomaticSettingPdbType", (), {})()


@future.utils.python_2_unicode_compatible
class Setting(pgsettingutils.SettingParentMixin, pgsettingutils.SettingEventsMixin):
  """
  This class holds data about a plug-in setting.
  
  Properties and methods in settings can be used in multiple scenarios, such as:
  * using setting values as variables in the main logic of plug-ins
  * registering GIMP Procedural Database (PDB) parameters to plug-ins
  * managing GUI element properties (values, labels, etc.)
  
  This class in particular can store any data. However, it is strongly
  recommended to use the appropriate `Setting` subclass for a particular data
  type, as the subclasses offer the following benefits:
  * setting can be registered to the GIMP procedural database (PDB),
  * automatic validation of input values,
  * readily available GUI element, keeping the GUI and the setting value in
    sync.
  
  Settings can contain event handlers that are triggered when a setting property
  changes, e.g. `value` (when `set_value()` method is called). This way, for
  example, other settings can be updated automatically according to the new
  value of the modified setting.

  The following specific event types are invoked for settings:
  
    * `"value-changed"` - invoked after `set_value()` or `reset()` is called
      and before events of type `"after-set-value"` or `"after-reset"`.
    
    * `"before-set-value"` - invoked before `set_value()` is called.
    
    * `"after-set-value"` - invoked after `set_value()` is called.
    
    * `"before-reset"` - invoked before `reset()` is called.
    
    * `"after-reset"` - invoked after `reset()` is called.
    
    * `"before-set-gui"` - invoked before `set_gui()` is called.
    
    * `"after-set-gui"` - invoked after `set_gui()` is called.
    
    * `"before-load"` - invoked before loading a setting via
      `pgsettingpersistor.SettingPersistor.load`.
    
    * `"after-load"` - invoked after loading a setting via
      `pgsettingpersistor.SettingPersistor.load`. Events will not be invoked
      if loading settings failed (i.e. `SettingPersistor` returns `READ_FAIL`
      status). Events will be invoked for all settings, even if some of them
      were not found in setting sources (i.e. `SettingPersistor` returns
      `NOT_ALL_SETTINGS_FOUND` status).
    
    * `"before-save"` - invoked before saving a setting via
    `pgsettingpersistor.SettingPersistor.save`.
    
    * `"after-save"` - invoked after saving a setting via
      `pgsettingpersistor.SettingPersistor.save`. Events will not be invoked
      if saving settings failed (i.e. `SettingPersistor` returns `SAVE_FAIL`
      status).
    
    * `"before-load-group"` - invoked before loading settings in a group via
      `SettingGroup.load`.
    
    * `"after-load-group"` - invoked after loading settings in a group via
      `SettingGroup.load`. This is useful if the group contains settings with
      different setting sources so that the event is invoked only once after
      all settings from different sources are loaded. This also applies to
      other related events (`"before-load-group"`, `"before-save-group"`,
      `"after-save-group"`).
    
    * `"before-save-group"` - invoked before saving settings in a group via
      `SettingGroup.load`.
    
    * `"after-save-group"` - invoked after saving settings in a group via
      `SettingGroup.load`.
  
  If a setting subclass supports "empty" values, such values will not be
  considered invalid when used as default values. However, empty values will be
  treated as invalid when assigning the setting one of such values after
  instantiation. Examples of empty values include `None` for an image object, or
  "Choose an item" for an enumerated setting. Empty values are useful when users
  must choose a different value, yet no valid value is a good candidate for a
  default value.
  
  Attributes:
  
  * `name` (read-only) - A name (string) that uniquely identifies the setting.
  
  * `value` (read-only) - The setting value. To set the value, call the
    `set_value()` method. `value` is initially set to `default_value`.
  
  * `default_value` (read-only) - Default value of the setting assigned upon its
    initialization or after the `reset()` method is called. If not specified or
    if `DEFAULT_VALUE` is passed explicitly, a default value is assigned
    automatically. The value depends on the particular setting subclass. This
    class uses `None` as the default value. Note that it is still a good
    practice to specify default values explicitly.
  
  * `gui` (read-only) - `SettingPresenter` instance acting as a wrapper of a GUI
    element. With `gui`, you may modify GUI-specific attributes, such as
    visibility or sensitivity.
  
  * `display_name` (read-only) - Setting name in human-readable format. Useful
    e.g. as GUI labels. The display name may contain underscores, which can be
    interpreted by the GUI as keyboard mnemonics.
  
  * `description` (read-only) - Usually `display_name` plus additional
    information in parentheses (such as boundaries for numeric values). Useful
    as a setting description when registering the setting as a plug-in parameter
    to the GIMP Procedural Database (PDB). If the class uses `display_name` to
    generate the description and `display_name` contains underscores, they are
    removed in the description.
  
  * `pdb_type` (read-only) - GIMP PDB parameter type, used when registering the
    setting as a plug-in parameter to the PDB. In Setting subclasses, only
    specific PDB types are allowed. Refer to the documentation of the subclasses
    for the list of allowed PDB types.
  
  * `pdb_name` (read-only) - Setting name as it appears in the GIMP PDB as
    a PDB parameter name.
  
  * `setting_sources` (read-only) - Default setting sources to use when loading
    or saving the setting. If `None`, no default sources are specified.
  
  * `error_messages` (read-only) - A dictionary of error messages containing
    (message name, message contents) pairs, which can be used e.g. if a value
    assigned to the setting is invalid. You can add your own error messages and
    assign them to one of the "default" error messages (such as "invalid_value"
    in several `Setting` subclasses) depending on the context in which the value
    assigned is invalid.
  
  * `tags` - A set of arbitrary tags attached to the setting. Tags can be used
    to e.g. iterate over a specific subset of settings.
  """
  
  DEFAULT_VALUE = type(b"DefaultValue", (), {})()
  
  _ALLOWED_PDB_TYPES = []
  _ALLOWED_GUI_TYPES = []
  _DEFAULT_DEFAULT_VALUE = None
  _EMPTY_VALUES = []
  
  def __init__(
        self,
        name,
        default_value=DEFAULT_VALUE,
        display_name=None,
        description=None,
        pdb_type=SettingPdbTypes.automatic,
        gui_type=SettingGuiTypes.automatic,
        allow_empty_values=False,
        auto_update_gui_to_setting=True,
        setting_sources=None,
        error_messages=None,
        tags=None):
    """
    Described are only those parameters that do not correspond to
    any attribute in this class, or parameters requiring additional information.
    
    Parameters:
    
    * `default_value` - During Setting initialization, the default value is
      validated. If one of the so called "empty values" (specific to each
      setting class) is passed as the default value, default value validation is
      not performed.
    
    * `pdb_type` - one of the `SettingPdbTypes` items. If set to
      `SettingPdbTypes.automatic` (the default), the first PDB type in the list
      of allowed PDB types for a particular Setting subclass is chosen. If no
      allowed PDB types are defined for that subclass, the setting cannot be
      registered (`None` is assigned).
    
    * `gui_type` - Type of GUI element to be created by the `set_gui` method.
      Use the `SettingGuiTypes` enum to specify the desired GUI type.
    
      If `gui_type` is `SettingGuiTypes.automatic`, choose the first GUI type
      from the list of allowed GUI type for the corresponding `Setting`
      subclass. If there are no allowed GUI types for that subclass, no GUI is
      created for this setting.
      
      If an explicit GUI type is specified, it must be one of the types from the
      list of allowed GUI types for the corresponding `Setting` subclass. If
      not, `ValueError` is raised.
      
      If the `gui_type` is `None`, no GUI is created for this setting.
    
    * `allow_empty_values` - If `False` and an empty value is passed to the
      `set_value` method, then the value is considered invalid. Otherwise, the
      value is considered valid.
    
    * `auto_update_gui_to_setting` - If `True`, automatically update the setting
      value if the GUI value is updated. If `False`, the setting must be updated
      manually by calling `Setting.gui.update_setting_value` when needed.
      
      This parameter does not have any effect if the GUI type used in
      this setting cannot provide automatic GUI-to-setting update.
    
    * `error_messages` - A dictionary containing (message name, message
      contents) pairs. Use this to pass custom error messages. This way, you may
      also override default error messages defined in classes.
    
    * `tags` - An iterable container (list, set, etc.) of arbitrary tags
      attached to the setting. Tags can be used to e.g. iterate over a specific
      subset of settings.
    """
    super().__init__()
    
    self._name = name
    pgsettingutils.check_setting_name(self._name)
    
    self._default_value = self._get_default_value(default_value)
    
    self._value = self._copy_value(self._default_value)
    
    self._allow_empty_values = allow_empty_values
    self._empty_values = list(self._EMPTY_VALUES)
    
    self._display_name = pgsettingutils.get_processed_display_name(
      display_name, self._name)
    self._description = pgsettingutils.get_processed_description(
      description, self._display_name)
    
    self._pdb_type = self._get_pdb_type(pdb_type)
    self._pdb_name = pgsettingutils.get_pdb_name(self._name)
    
    self._setting_sources = setting_sources
    
    self._setting_value_synchronizer = pgsettingpresenter.SettingValueSynchronizer()
    self._setting_value_synchronizer.apply_gui_value_to_setting = (
      self._apply_gui_value_to_setting)
    
    self._gui_type = self._get_gui_type(gui_type)
    self._gui = pgsettingpresenter.NullSettingPresenter(
      self, None, self._setting_value_synchronizer,
      auto_update_gui_to_setting=auto_update_gui_to_setting)
    
    self._error_messages = {}
    self._init_error_messages()
    if error_messages is not None:
      self._error_messages.update(error_messages)
    
    self._tags = set(tags) if tags is not None else set()
    
    if self._should_validate_default_value():
      self._validate_default_value()
  
  @property
  def name(self):
    return self._name
  
  @property
  def value(self):
    return self._value
  
  @property
  def default_value(self):
    return self._default_value
  
  @property
  def gui(self):
    return self._gui
  
  @property
  def display_name(self):
    return self._display_name
  
  @property
  def description(self):
    return self._description
  
  @property
  def pdb_type(self):
    return self._pdb_type
  
  @property
  def pdb_name(self):
    return self._pdb_name
  
  @property
  def setting_sources(self):
    return self._setting_sources
  
  @property
  def error_messages(self):
    return self._error_messages
  
  @property
  def tags(self):
    return self._tags
  
  def __str__(self):
    return pgutils.stringify_object(self, self.name)
  
  def get_path(self, relative_path_setting_group=None):
    """
    This is a wrapper method for `pgsettingutils.get_setting_path`. Consult the
    method for more information.
    """
    return pgsettingutils.get_setting_path(self, relative_path_setting_group)
  
  def set_value(self, value):
    """
    Set the setting value.
    
    Before the assignment, validate the value. If the value is invalid, raise
    `SettingValueError`.
    
    Update the value of the GUI element. Even if the setting has no GUI element
    assigned, the value is recorded. Once a GUI element is assigned to the
    setting, the recorded value is copied over to the GUI element.
    
    Invoke event handlers of types `"before-set-value"` before assigning the
    value and `"value-changed"` and `"after-set-value"` (in this order) after
    assigning the value.
    
    Note: This is a method and not a property because of the additional overhead
    introduced by validation, GUI updating and event handling. `value` still
    remains a property for the sake of brevity.
    """
    self.invoke_event("before-set-value")
    
    self._validate_and_assign_value(value)
    self._setting_value_synchronizer.apply_setting_value_to_gui(value)
    
    self.invoke_event("value-changed")
    self.invoke_event("after-set-value")
  
  def reset(self):
    """
    Reset setting value to its default value.
    
    This is different from
    
      setting.set_value(setting.default_value)
    
    in that `reset()` does not validate the default value.
    
    Invoke event handlers of types `"before-reset"` before resetting and
    `"value-changed"` and `"after-reset"` (in this order) after resetting.
    
    `reset()` also updates the GUI.
    
    If the default value is an empty container (list, dict, ...), resetting
    works properly. If the default value is a non-empty container, it is the
    responsibility of the caller to ensure that the default value does not get
    modified, for example by connecting a `"before-reset"` event that sets the
    value to the correct default value before resetting.
    """
    self.invoke_event("before-reset")
    
    self._value = self._copy_value(self._default_value)
    self._setting_value_synchronizer.apply_setting_value_to_gui(self._value)
    
    self.invoke_event("value-changed")
    self.invoke_event("after-reset")
  
  def set_gui(
        self,
        gui_type=SettingGuiTypes.automatic,
        gui_element=None,
        auto_update_gui_to_setting=True):
    """
    Create a new GUI object (`SettingPresenter` instance) for this setting or
    remove the GUI. The state of the previous GUI object is copied to the new
    GUI object (such as its value, visibility and sensitivity).
    
    Parameters:
    
    * `gui_type` - `SettingPresenter` type to wrap `gui_element` around.
      
      When calling this method, `gui_type` does not have to be one of the
      allowed GUI types specified in the setting.
      
      If `gui_type` is `SettingGuiTypes.automatic`, create a GUI object of the
      type specified in the `gui_type` parameter in `__init__`.
      
      To specify an existing GUI element, pass a specific `gui_type` and the
      GUI element in `gui_element`. This is useful if you wish to use the GUI
      element for multiple settings or for other purposes outside this setting.
      
      If `gui_type` is `None`, remove the GUI and disconnect any events the GUI
      had. The state of the old GUI is still preserved.
    
    * `gui_element` - A GUI element (wrapped in a `SettingPresenter` instance).
    
      If `gui_type` is `SettingGuiTypes.automatic`, `gui_element` is ignored.
      If `gui_type` is not `SettingGuiTypes.automatic` and `gui_element` is
      `None`, raise `ValueError`.
    
    * `auto_update_gui_to_setting` - See `auto_update_gui_to_setting` parameter
      in `__init__`.
    """
    if gui_type != SettingGuiTypes.automatic and gui_element is None:
      raise ValueError("gui_element cannot be None if gui_type is automatic")
    if gui_type == SettingGuiTypes.automatic and gui_element is not None:
      raise ValueError("gui_type cannot be automatic if gui_element is not None")
    
    self.invoke_event("before-set-gui")
    
    if gui_type == SettingGuiTypes.automatic:
      gui_type = self._gui_type
    elif gui_type is None:
      gui_type = pgsettingpresenter.NullSettingPresenter
      # We need to disconnect the "GUI changed" event before removing the GUI.
      self._gui.auto_update_gui_to_setting(False)
    
    self._gui = gui_type(
      self,
      gui_element,
      setting_value_synchronizer=self._setting_value_synchronizer,
      old_setting_presenter=self._gui,
      auto_update_gui_to_setting=auto_update_gui_to_setting)
    
    self.invoke_event("after-set-gui")
  
  def load(self, setting_sources=None):
    """
    Load setting value from the specified setting source(s). See
    `pgsettingpersistor.SettingPersistor.load` for more information about
    setting sources.
    
    If `setting_sources` is `None`, use the default setting sources. If
    specified, use a subset of sources matching the default sources. For
    example, if the default sources contain a persistent and a
    session-persistent source and `setting_sources` contains a
    session-persistent source, the setting value is loaded from the
    session-persistent source only.
    
    If there are no default setting sources or `setting_sources` does not match
    any of the default sources, this method has no effect.
    """
    self._load_save(setting_sources, pgsettingpersistor.SettingPersistor.load)
  
  def save(self, setting_sources=None):
    """
    Save setting value to the specified setting source(s). See
    `pgsettingpersistor.SettingPersistor.save` for more information about
    setting sources.
    
    If `setting_sources` is `None`, use the default setting sources. If
    specified, use a subset of sources matching the default sources. For
    example, if the default sources contain a persistent and a
    session-persistent source and `setting_sources` contains a
    session-persistent source, the setting value is loaded from the
    session-persistent source only.
    
    If there are no default setting sources or `setting_sources` does not match
    any of the default sources, this method has no effect.
    """
    self._load_save(setting_sources, pgsettingpersistor.SettingPersistor.save)
  
  def is_value_empty(self):
    """
    Return `True` if the setting value is one of the empty values defined for
    the setting class, otherwise return `False`.
    """
    return self._is_value_empty(self._value)
  
  def can_be_registered_to_pdb(self):
    """
    Return `True` if setting can be registered as a parameter to GIMP PDB,
    `False` otherwise.
    """
    return self._pdb_type != SettingPdbTypes.none
  
  def get_pdb_param(self):
    """
    Return a list of tuples, each tuple containing data describing the setting
    as a GIMP PDB parameter - PDB type, PDB name and description.
    
    If the setting does not support any PDB type, return `None`.
    
    Most setting classes return a list of only one tuple, meaning the setting is
    represented by one PDB parameter.
    """
    if self.can_be_registered_to_pdb():
      return [(
        self.pdb_type,
        self.pdb_name.encode(pgconstants.GIMP_CHARACTER_ENCODING),
        self.description.encode(pgconstants.GIMP_CHARACTER_ENCODING))]
    else:
      return None
  
  def _validate(self, value):
    """
    Check whether the specified value is valid. If the value is invalid, raise
    `SettingValueError`.
    """
    pass
  
  def _init_error_messages(self):
    """
    Initialize custom error messages in the `error_messages` dictionary.
    """
    pass
  
  def _copy_value(self, value):
    """
    Create a shallow copy of the specified value. By default, iterables (except
    strings) are copied, otherwise the original objects are returned.
    
    Override this method in subclasses in case copying must be handled
    differently.
    """
    if (isinstance(value, collections.Iterable)
        and not isinstance(value, types.StringTypes)):
      return copy.copy(value)
    else:
      return value
  
  def _assign_value(self, value):
    """
    Assign specified value to the `_value` attribute after validation.
    
    Override this method in subclasses if other modifications to the `_value`
    attribute must be made other than mere assignment.
    """
    self._value = value
  
  def _is_value_empty(self, value):
    return value in self._empty_values
  
  def _validate_and_assign_value(self, value):
    if not self._allow_empty_values:
      self._validate_setting(value)
    else:
      if not self._is_value_empty(value):
        self._validate_setting(value)
    
    self._assign_value(value)
  
  def _apply_gui_value_to_setting(self, value):
    self._validate_and_assign_value(value)
    self.invoke_event("value-changed")
  
  def _validate_setting(self, value):
    try:
      self._validate(value)
    except SettingValueError as e:
      raise SettingValueError(str(e), setting=self)
  
  def _should_validate_default_value(self):
    return not self._is_value_empty(self._default_value)
  
  def _validate_default_value(self):
    """
    Check whether the default value of the setting is valid. If the default
    value is invalid, raise `SettingDefaultValueError`.
    """
    try:
      self._validate(self._default_value)
    except SettingValueError as e:
      raise SettingDefaultValueError(str(e), setting=self)
  
  def _get_default_value(self, default_value):
    if isinstance(default_value, type(self.DEFAULT_VALUE)):
      if not callable(self._DEFAULT_DEFAULT_VALUE):
        return self._DEFAULT_DEFAULT_VALUE
      else:
        return self._DEFAULT_DEFAULT_VALUE()
    else:
      return default_value
  
  def _get_pdb_type(self, pdb_type):
    if pdb_type == SettingPdbTypes.automatic:
      return self._get_default_pdb_type()
    elif pdb_type is None or pdb_type == SettingPdbTypes.none:
      return SettingPdbTypes.none
    elif pdb_type in self._ALLOWED_PDB_TYPES:
      return pdb_type
    else:
      raise ValueError(
        "GIMP PDB type '{}' not allowed; for the list of allowed PDB types, refer to "
        "the documentation of the appropriate Setting class".format(pdb_type))
  
  def _get_default_pdb_type(self):
    if self._ALLOWED_PDB_TYPES:
      return self._ALLOWED_PDB_TYPES[0]
    else:
      return SettingPdbTypes.none
  
  def _get_gui_type(self, gui_type):
    gui_type_to_return = None
    
    if gui_type is None:
      gui_type_to_return = SettingGuiTypes.none
    elif gui_type == SettingGuiTypes.automatic:
      if self._ALLOWED_GUI_TYPES:
        gui_type_to_return = self._ALLOWED_GUI_TYPES[0]
      else:
        gui_type_to_return = SettingGuiTypes.none
    else:
      if gui_type in self._ALLOWED_GUI_TYPES:
        gui_type_to_return = gui_type
      elif gui_type in [SettingGuiTypes.none, pgsettingpresenter.NullSettingPresenter]:
        gui_type_to_return = gui_type
      else:
        raise ValueError(
          "{}: invalid GUI type '{}'; must be one of {}".format(
            self.name,
            gui_type,
            [type_.__name__ for type_ in self._ALLOWED_GUI_TYPES]))
    
    return gui_type_to_return
  
  def _load_save(self, setting_sources, load_save_func):
    if setting_sources is None:
      setting_sources = self._setting_sources
    else:
      if self._setting_sources is not None:
        setting_sources = [
          source for source in setting_sources if source in self._setting_sources]
{'fraction_non_alphanumeric': 0.07020186213911274, 'fraction_numerical': 0.001643063923010719, 'mean_word_length': 3.2234613795952085, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1670626', 'n_tokens_mistral': 14886, 'n_tokens_neox': 14601, 'n_words': 5234}