ó
dòôdc           @` s   d  Z  d d l m Z m Z m Z m Z d d l Td d l Z d d l m	 Z	 d g Z
 d e f d	 „  ƒ  YZ e j d
 d d d d g ƒ Z d S(   ua   Wrapper of `setting.sources` module to allow easy loading/saving using
multiple setting sources.
i    (   t   absolute_importt   divisiont   print_functiont   unicode_literals(   t   *Ni   (   t   _sources_errorsu	   Persistort	   Persistorc           B` s  e  Z d  Z d Z \ Z Z Z Z Z e	 j
 ƒ  Z e d „  ƒ Z e d „  ƒ Z e d e d „ ƒ Z e d	 „  ƒ Z e d e d
 „ ƒ Z e d „  ƒ Z e d d „ ƒ Z e d „  ƒ Z e d „  ƒ Z e d „  ƒ Z e d d d d „ ƒ Z e d „  ƒ Z e d „  ƒ Z e d „  ƒ Z RS(   uY  Loading and saving settings for multiple settings sources at once.
  
  Settings sources are `setting.sources.Source` instances.
  
  Apart from merely loading or saving settings, this class also triggers events
  before/after loading/saving for each setting and exits gracefully even if
  reading/writing settings to a setting source failed.
  i    i   i   i   i   c         C` s   t  j |  j j ƒ  ƒ S(   uŠ   Returns a copy of a dictionary containing default setting sources.
    
    See `set_default_setting_sources()` for more information.
    (   t   collectionst   OrderedDictt   _DEFAULT_SETTING_SOURCESt   items(   t   cls(    (    sS   /home/josie/.config/GIMP/2.10/plug-ins/export_layers/pygimplib/setting/persistor.pyt   get_default_setting_sources!   s    c         C` sF   | d k r t j ƒ  } n  t | t ƒ s9 t d ƒ ‚ n  | |  _ d S(   uc  Sets the dictionary of setting sources to use in methods of this class if
    no other setting sources in these methods are specified.
    
    The dictionary must contain pairs of (key, `setting.sources.Source` instance
    or list of `setting.sources.Source` instances).
    
    The key is a string that identifies a group of sources. The key can be
    specified in `setting.settings.Setting` instances within `setting_sources`
    to indicate which groups of sources the setting can be read from or written
    to. For example, if the `setting_sources` attribute of a setting contains
    [`'persistent'`], then only setting sources under the key `'persistent'`
    will be considered and other sources will be ignored. This is useful if you
    need to e.g. save settings to a different file while still ignoring settings
    not containing `'persistent'`.
    u   "sources" must be a dictionaryN(   t   NoneR   R   t
   isinstancet   dictt	   TypeErrorR	   (   R   t   sources(    (    sS   /home/josie/.config/GIMP/2.10/plug-ins/export_layers/pygimplib/setting/persistor.pyt   set_default_setting_sources)   s
    c         C` s½   | s |  j  |  j ƒ S| d k r. |  j } n  | sD |  j  |  j ƒ S|  j | ƒ } | si |  j  |  j ƒ S|  j | d | ƒ |  j | | ƒ \ } } } |  j | d | ƒ |  j | | | ƒ S(   uÉ
  Loads values from the specified settings or groups, or creates new
    settings within the specified groups if they do not exist.
    
    The order of setting sources in `settings_or_groups` indicates the
    preference of the sources, beginning with the first source. If not all
    settings could be found in the first source, the second source is read to
    assign values to the remaining settings. This continues until all sources
    are read or all settings are found.
    
    If the setting source(s) contain an invalid value for a setting, the default
    value for the setting will be assigned.
    Settings not found in any of the sources will also have their default values
    assigned.
    
    The following events are triggered for each `Setting` and `Group` instance
    within `settings_or_groups`, including child settings and groups:
    
    * `'before-load'` - invoked before loading settings. The event will not be
      triggered for settings present in the source but not in memory as they are
      not loaded yet.
    
    * `'after-load'` - invoked after loading settings. The event will also be
      triggered for settings originally not present in memory as they are now
      loaded. This event is triggered even if loading fails for any source.
    
    * events triggered in `Setting.set_value()` when a setting is being loaded.
    
    * events triggered in `Setting.reset()` when loading a setting was not
      successful (occurring when the loaded value was not valid).
    
    Parameters:
    
    * `settings_or_groups` - List of `settings.Setting` or `group.Group`
      instances whose values are loaded from `setting_sources`. For settings and
      groups that exist in `settings_or_groups`, only values are loaded.
      Child settings and groups not present in a group in memory but present in
      the source(s) are created within the group.
    
    * `setting_sources` - Dictionary or list of setting sources or `None`. If a
      dictionary, it must contain (key, setting source) pairs or
      (key, list of setting sources) pairs.
      See `set_default_setting_sources()` for more information on the key.
      If a list, it must contain keys and all keys must have a
      mapping to one of the default sources as returned by
      `get_default_setting_sources()`.
      If `None`, default setting sources are used as returned by
      `get_default_setting_sources()`.
    
    * `trigger_events` - If `True`, trigger `'before-load'` and `'after-load'`
      events for each setting. If `False`, these events are not triggered.
    
    Returns:
    
      A `PersistorResult` instance describing the result, particularly in the
      case of a failure. See `PersistorResult` for more information.
    u   before-loadu
   after-loadN(	   t   _resultt   NO_SETTINGSR   R	   t   FAILt   _process_setting_sourcest   _trigger_eventt   _loadt   _get_return_result(   R   t   settings_or_groupst   setting_sourcest   trigger_eventst   processed_setting_sourcest   settings_not_loadedt   statuses_per_sourcet   messages_per_source(    (    sS   /home/josie/.config/GIMP/2.10/plug-ins/export_layers/pygimplib/setting/persistor.pyt   loadB   s    :c   
      C` sÿ   | } i  } i  } xÝ | j  ƒ  D]Ï \ } } xÀ | D]¸ } y | j | ƒ Wne t j k
 r~ }	 |  j | | <t |	 ƒ | | <q2 t j k
 r° }	 |  j | | <t |	 ƒ | | <q2 X|  j | | <d | | <| j	 rÝ | j	 } q2 g  | | f Sq2 Wq W| | | f S(   Nu    (
   R
   t   readR   t   SourceNotFoundErrort   SOURCE_NOT_FOUNDt   strt   SourceErrorR   t   SUCCESSR   (
   R   R   R   R   R   R    t   unused_R   t   sourcet   e(    (    sS   /home/josie/.config/GIMP/2.10/plug-ins/export_layers/pygimplib/setting/persistor.pyR   “   s&    
	c         C` sº   | s |  j  |  j ƒ S| d k r. |  j } n  | sD |  j  |  j ƒ S|  j | ƒ } | si |  j  |  j ƒ S|  j | d | ƒ |  j | | ƒ \ } } |  j | d | ƒ |  j g  | | ƒ S(   uë  Saves settings to the specified setting sources.
    
    The following events for each `Setting` instance (including child settings
    in `Group` instances) within `settings_or_groups` are triggered:
    
    * `'before-save'` - invoked before saving settings.
    
    * `'after-save'` - invoked after saving settings. This event is triggered
      even if saving fails for any source.
    
    Parameters:
    
    * `settings_or_groups` - List of `settings.Setting` or `group.Group`
      instances whose values are saved to `setting_sources`.
    
    * `setting_sources` - Dictionary or list of setting sources or `None`. See
      `load()` for more information.
    
    * `trigger_events` - If `True`, trigger `'before-save'` and `'after-save'`
      events for each setting. If `False`, these events are not triggered.
    
    Returns:
    
      A `PersistorResult` instance describing the result, particularly in the
      case of a failure. See `PersistorResult` for more information.
    u   before-saveu
   after-saveN(	   R   R   R   R	   R   R   R   t   _saveR   (   R   R   R   R   R   R   R    (    (    sS   /home/josie/.config/GIMP/2.10/plug-ins/export_layers/pygimplib/setting/persistor.pyt   save¯   s    c   	      C` s¢   i  } i  } x‰ | j  ƒ  D]{ \ } } xl | D]d } y | j | ƒ Wn3 t j k
 rx } |  j | | <t | ƒ | | <q, X|  j | | <d | | <q, Wq W| | f S(   Nu    (   R
   t   writeR   R&   R   R%   R'   (	   R   R   R   R   R    R(   R   R)   R*   (    (    sS   /home/josie/.config/GIMP/2.10/plug-ins/export_layers/pygimplib/setting/persistor.pyR+   â   s    c         C` sl   | d k r |  j } n  |  j | ƒ } | d k	 rh x2 | j ƒ  D]! } x | D] } | j ƒ  qM Wq@ Wn  d S(   u  Removes all settings from all specified setting sources.
    
    Parameters:
    
    * `setting_sources` - Dictionary or list of setting sources or `None`. See
      `load()` for more information. If there are no sources to clear, this
      method has no effect.
    N(   R   R	   R   t   valuest   clear(   R   R   R   R   R)   (    (    sS   /home/josie/.config/GIMP/2.10/plug-ins/export_layers/pygimplib/setting/persistor.pyR/   ô   s    
c         C` s:   | r6 x- |  j  | d t ƒD] } | j | ƒ q Wn  d  S(   Nt   include_groups(   t   _list_settingst   Truet   invoke_event(   R   R   t
   event_nameR   t   setting(    (    sS   /home/josie/.config/GIMP/2.10/plug-ins/export_layers/pygimplib/setting/persistor.pyR     s    c         C` sY   g  } xL | D]D } | j  d  k	 r | | j  k r | j j | ƒ | j | ƒ q q W| S(   N(   R   R   t   tagst   addt   append(   R   t
   ignore_tagt   source_namet   filtered_settingst   settings_to_ignoreR5   (    (    sS   /home/josie/.config/GIMP/2.10/plug-ins/export_layers/pygimplib/setting/persistor.pyt   _set_up_settings_to_ignore  s    c         C` s%   x | D] } | j  j | ƒ q Wd  S(   N(   R6   t   discard(   R   R<   R9   R5   (    (    sS   /home/josie/.config/GIMP/2.10/plug-ins/export_layers/pygimplib/setting/persistor.pyt   _clean_up_settings_to_ignore  s    c         C` sR   | d  k r g  } n  | d  k r* i  } n  | d  k r? i  } n  t |  | | | ƒ S(   N(   R   t   PersistorResult(   t   statusR   R   R    (    (    sS   /home/josie/.config/GIMP/2.10/plug-ins/export_layers/pygimplib/setting/persistor.pyR     s    			c         ` s‘   t  ‡  f d †  | j ƒ  Dƒ ƒ r9 | r9 ˆ  j ˆ  j ƒ St  ‡  f d †  | j ƒ  Dƒ ƒ rt ˆ  j ˆ  j | | | ƒ Sˆ  j ˆ  j | | | ƒ Sd  S(   Nc         3` s   |  ] } | ˆ  j  k Vq d  S(   N(   R'   (   t   .0RA   (   R   (    sS   /home/josie/.config/GIMP/2.10/plug-ins/export_layers/pygimplib/setting/persistor.pys	   <genexpr>-  s    c         3` s   |  ] } | ˆ  j  k Vq d  S(   N(   R   (   RB   RA   (   R   (    sS   /home/josie/.config/GIMP/2.10/plug-ins/export_layers/pygimplib/setting/persistor.pys	   <genexpr>0  s    (   t   allR.   R   R'   R   t   PARTIAL_SUCCESS(   R   R   R   R    (    (   R   sS   /home/josie/.config/GIMP/2.10/plug-ins/export_layers/pygimplib/setting/persistor.pyR   +  s    ""c         C` s8  t  j ƒ  } t | t ƒ s¶ x| D] } | | k rA g  | | <n  y |  j | } Wn t k
 rf g  SXt | t  j ƒ rž x3 | D] } | | j | ƒ q€ Wq" | | j | ƒ q" Wn~ x{ | j ƒ  D]m \ } } | | k rè g  | | <n  t | t  j ƒ rx3 | D] } | | j | ƒ qWqÃ | | j | ƒ qÃ W| S(   N(	   R   R   R   R   R	   t   KeyErrort   IterableR8   R
   (   R   R   R   t   keyR)   t   item(    (    sS   /home/josie/.config/GIMP/2.10/plug-ins/export_layers/pygimplib/setting/persistor.pyR   6  s*    c         K` sr   g  } xe |  D]] } t  | t j ƒ rW | } | j | ƒ | j t | j |   ƒ ƒ q | } | j | ƒ q W| S(   N(   R   R   RF   R8   t   extendt   listt   walk(   R   t   walk_kwargst   settingst   setting_or_groupt   groupR5   (    (    sS   /home/josie/.config/GIMP/2.10/plug-ins/export_layers/pygimplib/setting/persistor.pyR1   W  s    (   i    i   i   i   i   N(   t   __name__t
   __module__t   __doc__t	   _STATUSESR'   RD   R$   R   R   R   R   R	   t   classmethodR   R   R   R2   R!   R   R,   R+   R/   R   R=   R?   t   staticmethodR   R   R   R1   (    (    (    sS   /home/josie/.config/GIMP/2.10/plug-ins/export_layers/pygimplib/setting/persistor.pyR      s*   P2
!u   PersistorResultu   statusu   settings_not_loadedu   statuses_per_sourceu   messages_per_source(   RR   t
   __future__R    R   R   R   t   future.builtinsR   t    R   t   __all__t   objectR   t
   namedtupleR@   (    (    (    sS   /home/josie/.config/GIMP/2.10/plug-ins/export_layers/pygimplib/setting/persistor.pyt   <module>   s   "
	ÿ S=                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   # -*- coding: utf-8 -*-

"""Classes to keep settings and their associated GUI elements in sync."""

from __future__ import absolute_import, division, print_function, unicode_literals
from future.builtins import *
import future.utils

import abc

from .. import utils as pgutils

from . import meta as meta_

__all__ = [
  'Presenter',
  'NullPresenter',
]


class SettingValueSynchronizer(object):
  """Helper class allowing `Setting` and `Presenter` instance to keep their
  values in sync.
  """
  
  def __init__(self):
    self.apply_setting_value_to_gui = pgutils.empty_func
    self.apply_gui_value_to_setting = pgutils.empty_func


class Presenter(future.utils.with_metaclass(meta_.PresenterMeta, object)):
  """Wrapper of a GUI element (widget, dialog, etc.) for settings.
  
  Various GUI elements have different attributes or methods to access their
  properties. This class wraps some of these attributes/methods so that they can
  be accessed with the same name.
  
  Subclasses can wrap any attribute of a GUI element into their `_get_value()`
  and `_set_value()` methods. The value does not have to be a 'direct' value,
  e.g. the checked state of a check button, but also e.g. the label of the
  check button.
  
  Instances of this class should not be created directly. Instead, use
  `Setting.gui` to access a setting's `Presenter` instance.
  
  Attributes:
  
  * `setting (read-only)` - Setting object.
  
  * `element (read-only)` - GUI element object.
  
  * `_VALUE_CHANGED_SIGNAL` - Object that indicates the type of event to
    connect to the GUI element. Once the event is triggered, it assigns the GUI
    element value to the setting value. If this attribute is `None`, no event
    can be connected.
  """
  
  _ABSTRACT = True
  
  _VALUE_CHANGED_SIGNAL = None
  
  def __init__(
        self,
        setting,
        element=None,
        setting_value_synchronizer=None,
        old_presenter=None,
        auto_update_gui_to_setting=True):
    """
    Parameters:
    
    * `element` - A GUI element.
      
      If `element` is `None`, create a new GUI element automatically. If the
      specific `Presenter` class does not support creating a GUI element, pass
      an existing GUI element.
    
    * `setting_value_synchronizer` - `SettingValueSynchronizer` instance to
      synchronize values between `setting` and this object.
    
    * `old_presenter` - `Presenter` object that was previously assigned to
      `setting` (as the `setting.gui` attribute). The state from that
      `Presenter` object will be copied to this object. If `old_presenter` is
      `None`, only `setting.value` will be copied to this object.
    
    * `auto_update_gui_to_setting` - If `True`, automatically update the setting
      value if the GUI value is updated. This parameter does not have any effect
      if:
        
        * the `Presenter` class cannot provide automatic GUI-to-setting update,
        
        * `old_presenter` is not `None` and the automatic GUI-to-setting update
          was disabled in that presenter.
    """
    self._setting = setting
    self._element = element
    self._setting_value_synchronizer = setting_value_synchronizer
    
    if auto_update_gui_to_setting:
      self._value_changed_signal = self._VALUE_CHANGED_SIGNAL
    else:
      self._value_changed_signal = None
    
    self._setting_value_synchronizer.apply_setting_value_to_gui = (
      self._apply_setting_value_to_gui)
    
    if self._element is None:
      self._element = self._create_gui_element(setting)
      
      if self._element is None:
        raise ValueError(
          'cannot instantiate class "{}": attribute "element" is None'
          ' and this class does not support the creation of a GUI element'.format(
            type(self).__name__))
    
    if old_presenter is not None:
      self._copy_state(old_presenter)
    else:
      self._setting_value_synchronizer.apply_setting_value_to_gui(self._setting.value)
    
    if self._value_changed_signal is not None:
      self._connect_value_changed_event()
  
  @property
  def setting(self):
    return self._setting
  
  @property
  def element(self):
    return self._element
  
  @property
  def gui_update_enabled(self):
    return self._value_changed_signal is not None
  
  @abc.abstractmethod
  def get_sensitive(self):
    """Returns the sensitive state of the GUI element."""
    pass
  
  @abc.abstractmethod
  def set_sensitive(self, sensitive):
    """Sets the sensitive state of the GUI element."""
    pass
  
  @abc.abstractmethod
  def get_visible(self):
    """Returns the visible state of the GUI element."""
    pass
  
  @abc.abstractmethod
  def set_visible(self, visible):
    """Sets the visible state of the GUI element."""
    pass
  
  def update_setting_value(self):
    """Manually assigns the GUI element value, entered by the user, to the
    setting value.
    
    This method will not have any effect if this object updates its setting
    value automatically.
    """
    # The `is_value_empty` check makes sure that settings with empty values
    # which are not allowed will be properly invalidated.
    if self._value_changed_signal is None or self._setting.is_value_empty():
      self._update_setting_value()
  
  def auto_update_gui_to_setting(self, enabled):
    """Enables or disables automatic GUI update.
    
    If `value` is `True` and the `Presenter` class does not support automatic
    GUI update, `ValueError` is raised.
    """
    if enabled and self._VALUE_CHANGED_SIGNAL is None:
      raise ValueError(
        'class "{}" does not support automatic GUI update'.format(type(self).__name__))
    
    if enabled:
      self._value_changed_signal = self._VALUE_CHANGED_SIGNAL
      self._connect_value_changed_event()
    else:
      self._value_changed_signal = None
      self._disconnect_value_changed_event()
  
  def _create_gui_element(self, setting):
    """Instantiates and returns a new GUI element using the attributes in the
    specified `Setting` instance (e.g. display name as GUI label).
    
    `None` is returned if the `Presenter` subclass does not support GUI element
    creation.
    """
    return None
  
  @abc.abstractmethod
  def _get_value(self):
    """Returns the value of the GUI element."""
    pass
  
  @abc.abstractmethod
  def _set_value(self, value):
    """Sets the value of the GUI element.
    
    If the value passed is one of the empty values allowed for the corresponding
    setting and the GUI element cannot handle the value, this method must wrap
    the empty value into a safe value (that the GUI element can handle).
    """
    pass
  
  def _copy_state(self, old_presenter):
    self._set_value(old_presenter._get_value())
    self.set_sensitive(old_presenter.get_sensitive())
    self.set_visible(old_presenter.get_visible())
    
    if not old_presenter.gui_update_enabled:
      self._value_changed_signal = None
  
  def _update_setting_value(self):
    """Assigns the GUI element value, entered by the user, to the setting value.
    """
    self._setting_value_synchronizer.apply_gui_value_to_setting(self._get_value())
  
  @abc.abstractmethod
  def _connect_value_changed_event(self):
    """Connects the `_on_value_changed` event handler to the GUI element using
    the `_value_changed_signal` attribute.
    
    Because the way event handlers are connected varies in each GUI framework,
    subclass this class and override this method for the GUI framework you use.
    """
    pass
  
  @abc.abstractmethod
  def _disconnect_value_changed_event(self):
    """Disconnects the `_on_value_changed` event handler from the GUI element.
    
    Because the way event handlers are disconnected varies in each GUI framework,
    subclass this class and override this method for the GUI framework you use.
    """
    pass
  
  def _on_value_changed(self, *args):
    """Event handler that automatically updates the value of the setting.
    
    The event is triggered when the user changes the value of the GUI element.
    """
    self._update_setting_value()
  
  def _apply_setting_value_to_gui(self, value):
    """Assigns the setting value to the GUI element. Used by the setting when
    its `set_value()` method is called.
    """
    self._set_value(value)


class NullPresenter(Presenter):
  """Empty `Presenter` class whose methods do nothing.
  
  This class is attached to `Setting` objects with no `Presenter` object
  specified upon its instantiation.
  
  This class also records the GUI state. In case a proper `Presenter` instance
  is assigned to the setting, the GUI state is copied over to the new instance.
  """
  
  # Make `NullPresenter` pretend to update GUI automatically.
  _VALUE_CHANGED_SIGNAL = 'null_signal'
  _NULL_GUI_ELEMENT = type(b'NullGuiElement', (), {})()
  
  def __init__(self, setting, element, *args, **kwargs):
    """
    `element` is ignored - its attributes are not read or set.
    """
    self._value = None
    self._sensitive = True
    self._visible = True
    
    Presenter.__init__(self, setting, self._NULL_GUI_ELEMENT, *args, **kwargs)
  
  def get_sensitive(self):
    return self._sensitive
  
  def set_sensitive(self, sensitive):
    self._sensitive = sensitive
  
  def get_visible(self):
    return self._visible
  
  def set_visible(self, visible):
    self._visible = visible
  
  def update_setting_value(self):
    pass
  
  def _get_value(self):
    return self._value
  
  def _set_value(self, value):
    self._value = value
  
  def _connect_value_changed_event(self):
    pass
  
  def _disconnect_value_changed_event(self):
    pass
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           