terom@64: """ terom@64: Per-directory gallery state terom@64: """ terom@12: terom@64: import filesystem, image, html terom@12: terom@76: from utils import lazy_load, lazy_load_iter terom@64: terom@76: import math terom@76: terom@76: class Folder (filesystem.Directory) : terom@12: """ terom@133: A Folder is a filesystem Directory that: terom@133: * has a title/description (XXX: Page) terom@133: * contains any number of other sub-Folders and Images. terom@133: * can load custom configuration for itself and sub-nodes to override the base config terom@133: * holds sub-dirs for previews/thumbs terom@133: * has a HTML view terom@12: """ terom@12: terom@118: def iter_config_files (self) : terom@118: """ terom@118: Iterate over the possible config files for this dir terom@138: terom@138: XXX: abstract this terom@118: """ terom@118: terom@118: yield self.subfile('degal.cfg') terom@118: terom@76: def __init__ (self, *args, **kwargs) : terom@76: super(Folder, self).__init__(*args, **kwargs) terom@12: terom@118: # find config terom@118: for file in self.iter_config_files() : terom@118: if file.exists() : terom@118: # yay! More configuration! terom@118: self.config = self.config.load(file.path) terom@118: terom@118: break terom@118: terom@133: # load some info for title? terom@118: if self.config and self.config.title : terom@133: # override our default title terom@118: self.title = self.config.title terom@118: terom@118: # disable it so it won't be used by children terom@118: # XXX: figure out a better way of doing this terom@118: self.config.title = None terom@118: terom@118: @lazy_load terom@118: def title (self) : terom@118: """ terom@133: Find the default title for this dir. terom@118: """ terom@118: terom@118: # default terom@118: return self.name.title() terom@118: terom@118: @lazy_load terom@118: def description (self) : terom@118: """ terom@118: Find the descriptive text for this dir terom@118: """ terom@118: terom@118: # default terom@118: return None terom@118: terom@64: @lazy_load terom@64: def preview_dir (self) : terom@64: """ terom@133: Load and return the Directory used for for preview Thumbnails terom@64: """ terom@64: terom@64: return self.subdir(self.config.preview_dir, create=True) terom@12: terom@64: @lazy_load terom@64: def thumb_dir (self) : terom@64: """ terom@133: Load and return the Directory used for thumb Thumbnails terom@64: """ terom@64: terom@64: return self.subdir(self.config.thumb_dir, create=True) terom@64: terom@64: @lazy_load_iter terom@64: def subnodes (self) : terom@64: """ terom@64: Load and return an ordered list of child-Nodes terom@64: """ terom@64: terom@76: return super(Folder, self).subnodes(skip_dotfiles=True, sort=True) terom@64: terom@64: @lazy_load_iter terom@64: def subfolders (self) : terom@64: """ terom@64: Load and return an ordered list of sub-Folders terom@64: """ terom@90: terom@90: # filter out valid subfolders terom@90: for node in self.subnodes : terom@90: # skip non-dirs terom@90: if not node.is_dir() : terom@90: continue terom@64: terom@90: # skip the previews/thumbs dirs terom@90: if node.name in (self.config.preview_dir, self.config.thumb_dir) : terom@90: continue terom@90: terom@90: # ok terom@90: yield Folder(node) terom@64: terom@64: @lazy_load_iter terom@64: def images (self) : terom@64: """ terom@64: Load and return an ordered/linked list of sub-Images terom@64: """ terom@64: terom@64: prev = None terom@64: terom@64: for node in self.subnodes : terom@64: # skip non-relevant ones terom@76: if not node.is_file() or not self.config.is_image(filesystem.File(node)) : terom@64: continue terom@64: terom@64: # create new terom@77: img = image.Image(node) terom@64: terom@64: # link up terom@64: if prev : terom@76: img.prev = prev terom@64: prev.next = img terom@64: terom@64: # yield the linked-up prev terom@64: yield prev terom@64: terom@64: # continue terom@64: prev = img terom@64: terom@64: # and the last one terom@64: if prev : terom@64: yield prev terom@132: terom@132: @lazy_load terom@132: def empty (self) : terom@132: """ terom@132: A Folder is empty if it does not contain any Images, and all of its sub-Folders (if any) are also empty. terom@132: terom@132: This is implemented recursively. terom@132: """ terom@132: terom@132: # simple logic, my dear friend Watson terom@132: return not self.images and all(subfolder.empty for subfolder in self.subfolders) terom@64: terom@64: @property terom@64: def page_count (self) : terom@64: """ terom@131: Returns the number of pages needed to show this folder's images or subfolders. May be zero, one or more. terom@64: """ terom@131: terom@131: if self.images : terom@131: return int(math.ceil(len(self.images) / float(self.config.images_per_page))) terom@131: terom@131: elif self.subfolders : terom@131: # paginate these? terom@131: return 1 terom@64: terom@131: else : terom@131: # nothing to render, really terom@131: return 0 terom@64: terom@64: def images_for_page (self, page) : terom@64: """ terom@64: Returns the list of Images to be displayed for the given page, if any terom@64: """ terom@64: terom@64: # offset to first image terom@64: offset = page * self.config.images_per_page terom@64: terom@64: # slice terom@64: return self.images[offset : offset + self.config.images_per_page] terom@119: terom@119: @property terom@119: def html (self) : terom@119: """ terom@119: Path to default page terom@119: """ terom@64: terom@119: return self.html_page() terom@119: terom@119: def html_page (self, page=0) : terom@64: """ terom@64: Returns the File representing the .html for the given page terom@64: """ terom@64: terom@64: if page : terom@64: return self.subfile("index_%d.html" % page) terom@64: terom@64: else : terom@64: return self.subfile("index.html") terom@64: terom@85: def index_images (self, for_update=True) : terom@64: """ terom@85: Return a series of Images inside of this folder. terom@70: terom@85: If `for_update` is given, only images that are stale will be returned. terom@64: """ terom@64: terom@85: for image in self.images : terom@85: if for_update and not image.stale() : terom@85: # skip terom@85: continue terom@64: terom@85: yield image terom@85: