package foldertree /** * The child items are dynamically created and cleared when `expandedProperty` changes. * i.e. We clear the children when this item is contracted and recreated when expanded. * * We also watch the folder, so that when files/folders are created/deleted, the tree is automatically updated. * This is tricker than you might imagine, because we cannot just clear the items and rebuild then, * because that would lose the "expanded" property of subfolders (and the tree would collapse). * So when we refresh due to a watched folder changing, we need to reuse existing child FolderItems, * remove any whose folders no longer exist and insert new ones for newly created folders. */ class FolderTreeItem( folder : File, val form : FolderTreeForm ) : FileTreeItem( folder.nameOrSlash(), folder ), FolderWatcher { var watching = false init { leaf = false expandedProperty.listen( this:>onExpanded ) } meth refreshRecursively() { if (expanded) { refresh() for (child in children()) { if (child is FolderTreeItem) { (child as FolderTreeItem).refreshRecursively() } } } } meth refresh() { if (expanded) { if (isEmpty()) { addFolders() addFiles() } else { // We cannot just clear child FolderItems and rebuild them (See class documentation). // But we will just remove and rebuild the FileTreeItems (for simplisity, not efficiency). val subFolders = file.folders(form.showHiddenFolders.value) // Lets remove all FileTreeItems and any FolderItem which no longer exist. val oldChildren = children() for (child in oldChildren) { if (child is FolderTreeItem) { if ( ! subFolders.contains( (child as FolderTreeItem).file) ) { remove(child) } } else { remove(child) } } // Now lets INSERT any new subFolders into the correct place. // NOTE. This is inefficient. It is O(n²) where n is the number of subfolders. // I can't see that being an issue though??? for (sub in subFolders) { var found = false for (old in oldChildren) { if ((old as FileTreeItem).file == sub) { found = true break } } if (!found) { // `sub` is a new folder, which needs to be inserted into the correct place. // Folders are sorted alphabetically, ignoring case. val subNameUpperCase = sub.name.toUpperCase() // Look for an existing child, which is AFTER `sub`. // And if none are found, then insert at the end. var insertIndex = children().size() var index = 0 for ( child in children() ) { if ( (child as FileTreeItem).file.name.toUpperCase() > subNameUpperCase ) { insertIndex = index break } index ++ } add( insertIndex, FolderTreeItem( sub, form ) ) } } // The folders are done, so we just need to add the FileTreeItems addFiles() } if (!watching) { watchFolder( this, file ) watching = true } } else { unwatchFolder( this, file ) watching = false clear() } } meth addFiles() { if (form.noFiles.value) return val startIndex = if (form.filesFirst.value) 0 else children().size() var i = 0 for (file in file.files(form.showHiddenFiles.value)) { val item = FileTreeItem( file ) add(startIndex + i, item) i ++ } } meth addFolders() { for (subfolder in file.folders(form.showHiddenFolders.value)) { val item = FolderTreeItem( subfolder, form ) add(item) } } meth onExpanded() { refresh() } override meth deleted( file : File ) { refresh() } override meth created( file : File ) { refresh() } }