#!/usr/bin/python3
#
# directory.py
"""
Class to represent directory of icons.
"""
#
# Copyright (C) 2020 Dominic Davis-Foster <dominic@davis-foster.co.uk>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
# stdlib
import configparser
import pathlib
from typing import List, Optional, Type, TypeVar
# 3rd party
from domdf_python_tools.bases import Dictable
from domdf_python_tools.doctools import prettify_docstrings
from domdf_python_tools.typing import PathLike
from memoized_property import memoized_property # type: ignore
# this package
from .constants import IconTypes, mime
from .icon import Icon
__all__ = ["Directory"]
_D = TypeVar("_D", bound="Directory")
[docs]@prettify_docstrings
class Directory(Dictable):
"""
Represents a directory containing icons.
:param path: The absolute path to the directory
:param size: Nominal (unscaled) size of the icons in this directory.
:param scale: Target scale of the icons in this directory.
Any directory with a scale other than 1 should be listed in the ScaledDirectories list rather
than Directories for backwards compatibility.
:param context: The context the icon is normally used in. This is in detail discussed in the section called “Context”.
:param type: The type of icon sizes for the icons in this directory.
Valid types are ``'Fixed'``, ``'Scalable'`` and ``'Threshold'``.
The type decides what other keys in the section are used.
:param max_size: Specifies the maximum (unscaled) size that the icons in this directory can be scaled to.
Defaults to the value of Size if not present.
:no-default max_size:
:param min_size: Specifies the minimum (unscaled) size that the icons in this directory can be scaled to.
Defaults to the value of Size if not present.
:no-default min_size:
:param threshold: The icons in this directory can be used if the size differ at most this much from the desired (unscaled) size. Defaults to 2 if not present.
:param theme: The name of the theme this directory is a part of
"""
max_size: int
min_size: int
def __init__(
self,
path: PathLike,
size: int,
scale: int = 1,
context: str = '',
type: IconTypes = "Threshold", # noqa: A002 # pylint: disable=redefined-builtin
max_size: Optional[int] = None,
min_size: Optional[int] = None,
threshold: int = 2,
theme: str = '',
):
super().__init__()
self.scale: int = int(scale)
self.context: str = str(context)
self.threshold: str = str(threshold)
self.theme: str = str(theme)
if not isinstance(path, pathlib.Path):
path = pathlib.Path(path)
self.path: pathlib.Path = path.resolve()
if not isinstance(size, int):
raise TypeError("'size' must be a integer.")
self.size: int = int(size)
if type not in {"Fixed", "Scalable", "Threshold"}:
raise ValueError("'type' must be one of 'Fixed', 'Scalable' or 'Threshold'.")
self.type = type
if max_size:
if not isinstance(max_size, int):
raise TypeError("'max_size' must be a integer.")
self.max_size = max_size
else:
self.max_size = size
if min_size:
if not isinstance(min_size, int):
raise TypeError("'min_size' must be a integer.")
self.min_size = min_size
else:
self.min_size = size
@property
def __dict__(self):
return dict(
path=self.path,
size=self.size,
scale=self.scale,
context=self.context,
type=self.type,
max_size=self.max_size,
min_size=self.min_size,
threshold=self.threshold,
theme=self.theme,
)
[docs] @classmethod
def from_configparser(
cls: Type[_D],
config_section: configparser.SectionProxy,
theme_content_root: pathlib.Path,
) -> _D:
"""
Constructs a :class:`~.Directory` from a
`configparser <https://docs.python.org/3/library/configparser.html>`_ section.
:param config_section:
:param theme_content_root:
:rtype: :class:`~.Directory`
""" # noqa: D400
if not isinstance(config_section, configparser.SectionProxy):
raise TypeError("'config_section' must be a 'configparser.SectionProxy' object")
path = theme_content_root / config_section.name
size = int(config_section.get("Size"))
scale = int(config_section.get("Scale", fallback='1'))
context = config_section.get("Context", fallback='')
type_ = config_section.get("Type", fallback="Threshold")
max_size = config_section.get("MaxSize", fallback=None)
max_size_: Optional[int]
if max_size:
max_size_ = int(max_size)
else:
max_size_ = None
min_size = config_section.get("MinSize", fallback=None)
min_size_: Optional[int]
if min_size:
min_size_ = int(min_size)
else:
min_size_ = None
threshold = int(config_section.get("Threshold", fallback='2'))
return cls(
path,
size,
scale,
context,
type_, # type: ignore
max_size_,
min_size_,
threshold,
)
@memoized_property
def icons(self) -> List[Icon]:
"""
Returns a list of icons in this :class:`~.Directory`.
"""
absolute_dir_path = self.path.resolve()
# print(absolute_dir_path)
icons = []
for item in absolute_dir_path.iterdir():
if item.is_file():
if mime.from_file(str(item.resolve())) in {"image/svg+xml", "image/png"}:
icon = Icon(item.stem, item, self.size, self.type, self.max_size, self.min_size, self.theme)
icons.append(icon)
return icons
[docs] def __repr__(self) -> str:
return f"Directory({self.path})"