FileTree
package FileTree;
use strict;
use warnings;
use QtCore4;
use QtGui4;
use QtXmlPatterns4;
use QtCore4::isa qw( Qt::SimpleXmlNodeModel );
use List::MoreUtils qw ( first_index );
use constant {
File => 1,
Directory => 2,
AttributeFileName => 3,
AttributeFilePath => 4,
AttributeSize => 5,
AttributeMIMEType => 6,
AttributeSuffix => 7,
};
#! [2]
#mutable Qt::Vector<Qt::FileInfo> m_fileInfos;
#Qt::Dir::Filters m_filterAllowAll;
#Qt::Dir::SortFlags m_sortFlags;
#Qt::Vector<Qt::XmlName> m_names;
sub m_fileInfos() {
return this->{m_fileInfos};
}
sub m_filterAllowAll() {
return this->{m_filterAllowAll};
}
sub m_sortFlags() {
return this->{m_sortFlags};
}
sub m_names() {
return this->{m_names};
}
# [2]
sub NEW {
my ($class, $pool) = @_;
$class->SUPER::NEW($pool);
this->{m_fileInfos} = [];
this->{m_filterAllowAll} = (Qt::Dir::AllEntries() |
Qt::Dir::AllDirs() |
Qt::Dir::NoDotAndDotDot() |
Qt::Dir::Hidden()),
this->{m_sortFlags} = Qt::Dir::Name();
my $np = namePool();
this->{m_names} = {
File , Qt::XmlName($np, 'file'),
Directory , Qt::XmlName($np, 'directory'),
AttributeFileName , Qt::XmlName($np, 'fileName'),
AttributeFilePath , Qt::XmlName($np, 'filePath'),
AttributeSize , Qt::XmlName($np, 'size'),
AttributeMIMEType , Qt::XmlName($np, 'mimeType'),
AttributeSuffix , Qt::XmlName($np, 'suffix'),
};
}
# [2]
sub nodeFor
{
my ($dirName) = @_;
my $dirInfo = Qt::FileInfo(Qt::Dir::cleanPath($dirName));
die "Directory $dirName does not exist." unless $dirInfo->exists();
return toNodeIndex($dirInfo);
}
# [6]
sub toFileInfo
{
my ($nodeIndex) = @_;
return m_fileInfos->[$nodeIndex->data()];
}
# [6]
# [1]
sub toNodeIndex
{
my ($fileInfo, $attributeName) = @_;
if ( !defined $attributeName ) {
return toNodeIndex($fileInfo, $fileInfo->isDir() ? Directory : File);
}
my $indexOf = first_index { $_ == $fileInfo } @{m_fileInfos()};
if ($indexOf == -1) {
push @{m_fileInfos()}, $fileInfo;
return createIndex( scalar @{m_fileInfos()}-1, $attributeName);
}
else {
return createIndex($indexOf, $attributeName);
}
}
# [1]
# [5]
sub nextSibling
{
my ($nodeIndex, $fileInfo, $offset) = @_;
die "Offset must be -1 or 1" unless ($offset == -1 || $offset == 1);
# Get the context node's parent.
my $parent = Qt::XmlNodeModelIndex(nextFromSimpleAxis(Qt::AbstractXmlNodeModel::Parent(), $nodeIndex));
if ($parent->isNull()) {
return Qt::XmlNodeModelIndex();
}
# Get the parent's child list.
my $parentFI = Qt::FileInfo(toFileInfo($parent));
die "Parent's type must be 'Directory" unless $parent->additionalData() == Directory;
my $siblings = Qt::Dir($parentFI->absoluteFilePath())->entryInfoList([],
m_filterAllowAll,
m_sortFlags);
print STDERR 'Can\'t happen! We started at a child.' if (!defined $siblings || scalar @{$siblings} < 0);
# Find the index of the child where we started.
my $indexOfMe = first_index { $_ == $fileInfo } @{$siblings};
# Apply the offset.
my $siblingIndex = $indexOfMe + $offset;
if ($siblingIndex < 0 || $siblingIndex > scalar @{$siblings} - 1) {
return Qt::XmlNodeModelIndex();
}
else {
return toNodeIndex($siblings->[$siblingIndex]);
}
}
# [5]
# [4]
sub nextFromSimpleAxis
{
my ($axis, $nodeIndex) = @_;
my $fi = Qt::FileInfo(toFileInfo($nodeIndex));
my $type = $nodeIndex->additionalData();
if ($type != File && $type != Directory) {
print STDERR 'An attribute only has a parent!' if $axis == Qt::AbstractXmlNodeModel::Parent();
return toNodeIndex($fi, Directory);
}
if ( $axis == Qt::AbstractXmlNodeModel::Parent() ) {
return toNodeIndex(Qt::FileInfo($fi->path()), Directory);
}
elsif ( $axis == Qt::AbstractXmlNodeModel::FirstChild() ) {
if ($type == File) { # A file has no children.
return Qt::XmlNodeModelIndex();
}
else {
die "Type must be Directory" unless ($type == Directory);
print STDERR 'It isn\'t really a directory!' unless $fi->isDir();
my $dir = Qt::Dir($fi->absoluteFilePath());
die "Directory doesn't exist." unless $dir->exists();
my $children = $dir->entryInfoList([],
m_filterAllowAll,
m_sortFlags);
if (defined $children && scalar @{$children} <= 0) {
return Qt::XmlNodeModelIndex();
}
my $firstChild = Qt::FileInfo($children->[0]);
return toNodeIndex($firstChild);
}
}
elsif ( $axis == Qt::AbstractXmlNodeModel::PreviousSibling() ) {
return nextSibling($nodeIndex, $fi, -1);
}
elsif ( $axis == Qt::AbstractXmlNodeModel::NextSibling() ) {
return nextSibling($nodeIndex, $fi, 1);
}
print STDERR 'Don\'t ever get here!';
return Qt::XmlNodeModelIndex();
}
# [4]
sub documentUri
{
return Qt::Url('file:#/');
}
sub kind
{
my ($node) = @_;
if ( $node->additionalData() == Directory || $node->additionalData() == File ) {
return Qt::XmlNodeModelIndex::Element();
}
else {
return Qt::XmlNodeModelIndex::Attribute();
}
}
sub compareOrder
{
return Qt::XmlNodeModelIndex::Precedes();
}
# [3]
sub name
{
my ($node) = @_;
return Qt::XmlName(m_names->{$node->additionalData()});
}
# [3]
sub root
{
return toNodeIndex(Qt::FileInfo('/'));
}
sub typedValue
{
my ($node) = @_;
my $fi = toFileInfo($node);
if ( $node->additionalData() == Directory ) {
# deliberate fall through.
}
elsif ( $node->additionalData() == File ) {
Qt::Variant(Qt::String());
}
elsif ( $node->additionalData() == AttributeFileName ) {
return Qt::Variant(Qt::String($fi->fileName()));
}
elsif ( $node->additionalData() == AttributeFilePath ) {
return Qt::Variant(Qt::String($fi->filePath()));
}
elsif ( $node->additionalData() == AttributeSize ) {
return Qt::Variant(Qt::String($fi->size()));
}
elsif ( $node->additionalData() == AttributeMIMEType ) {
# We don't have any MIME detection code currently, so return
# the most generic one. */
return Qt::Variant(Qt::String('application/octet-stream'));
}
elsif ( $node->additionalData() == AttributeSuffix ) {
return Qt::Variant(Qt::String($fi->suffix()));
}
print STDERR 'This line should never be reached.';
return Qt::Variant(Qt::String());
}
sub attributes
{
my ($element) = @_;
my @result;
# Both elements has this attribute.
my $forElement = toFileInfo($element);
push @result, toNodeIndex($forElement, AttributeFilePath);
push @result, toNodeIndex($forElement, AttributeFileName);
if ($element->additionalData() == File) {
push @result, toNodeIndex($forElement, AttributeSize);
push @result, toNodeIndex($forElement, AttributeSuffix);
#push @result, toNodeIndex(forElement, AttributeMIMEType));
}
else {
die "Type must be a directory"
unless $element->additionalData() == Directory;
}
return \@result;
}
1;