Haiku API Bindings
Nodes
Not logged in

Documentation | Manual: Nodes

Chapter 11: Nodes

Although Nodes can do other things beside manipulate attributes, that is probably what you will mostly use them for. This chpter shows you how to do that.

This program for this chapter is called Nodes.ext.

App

We create a simple Application subclass for this. It takes an optional parameter: a file name, to be loaded as the initial content.

Perl

package AttributeViewerApp;
use parent 'Haiku::Application';

sub new {
    my ($class, $file) = @_;
    my $self = $class->SUPER::new("application/x.vnd-hab.perl.Nodes");

    $self->{window} = AttributeViewerWindow->new();
    $self->{window}->Show();

    if ($file) {
        my $message = Haiku::Message->new(B_REFS_RECEIVED);
        $message->AddRef("refs", $file);
        $self->{window}->PostMessage($message);
    }

    return $self;
}

Python

class AttributeViewerApp(Application):

    def __new__(cls, file):
        self = super(AttributeViewerApp, cls).__new__(cls, "application/x.vnd-hab.python.Nodes")

        self.window = AttributeViewerWindow()
        self.window.Show()

        if file:
            message = Message(B_REFS_RECEIVED)
            message.AddRef("refs", file)
            self.window.PostMessage(message)

        return self

Window

The Window subclass defines some standard attributes; this allows us to skip certain attributes, which can be via NodeInfo objects instead of directly through Node. (The NodeInfo method names are easier to remember than the attribute names.)

We also set up some storage for later use.

Perl

package AttributeViewerWindow;
use parent 'Haiku::Window';
use strict;

my %standard = (
    'BEOS:PPATH'      => 1,
    'BEOS:M:STD_ICON' => 1,
    'BEOS:L:STD_ICON' => 1,
    'BEOS:ICON'       => 1,
    'BEOS:PREF_APP'   => 1,
    'BEOS:TYPE'       => 1,
);

my %type_handlers = (
    # see program for detail
);

our $CHOOSE_FILE = code_to_int('chfl');

my @window_frame = (50,50,350,350);

my $button_width = 75;
my $button_height = 20;
my $spacer = 5;

sub new {
    my ($class) = @_;
    my $self = $class->SUPER::new(
        frame => \@window_frame,
        title => "Attribute Viewer",
        type  => B_TITLED_WINDOW,
        flags => B_QUIT_ON_WINDOW_CLOSE,
    );

    # View definitions go here; see below

    $self->{target} = Haiku::Messenger->new($self);
    $self->{items} = [];

    return $self;
}

Python

class AttributeViewerWindow(Window):
    standard = (
        'BEOS:PPATH',
        'BEOS:M:STD_ICON',
        'BEOS:L:STD_ICON',
        'BEOS:ICON',
        'BEOS:PREF_APP',
        'BEOS:TYPE',
    )

    type_handlers = {
        # see program for detail
    }

    CHOOSE_FILE = code_to_int('chfl')

    window_frame = [50,50,350,350]

    button_width = 75
    button_height = 20
    spacer = 5

    def __new__(cls):
        self = super(AttributeViewerWindow, cls).__new__(cls,
            frame = cls.window_frame,
            title = "Attribute Viewer",
            type  = B_TITLED_WINDOW,
            flags = B_QUIT_ON_WINDOW_CLOSE,
        )

        # View definitions go here; see below

        self.target = Messenger(self)
        self.items = []

        return self

Views

Our window will have two Views: a Button that can launch a FilePanel, and an OutlineListView to display the attributes.

Perl

    my @button_frame = (
        $spacer,
        $spacer,
    );
    $button_frame[2] = $button_frame[0]+$button_width;
    $button_frame[3] = $button_frame[1]+$button_height;
    $self->{button} = Haiku::Button->new(
        frame   => \@button_frame,
        name    => 'FileChooser',
        label   => 'Select File',
        message => Haiku::Message->new($CHOOSE_FILE),
    );
    $self->AddChild($self->{button});

    # button may resize its height
    $button_height = $self->{button}->Bounds()->Height();

    my @viewer_frame = (
        $spacer,
        $button_frame[3] + 3*$spacer,
        $window_frame[2] - $window_frame[0] - $spacer,
        $window_frame[3] - $window_frame[1] - $spacer,
    );
    $self->{viewer} = Haiku::OutlineListView->new(
        frame => \@viewer_frame,
        name  => 'AttributeViewer',
        resizingMode => B_FOLLOW_ALL,
    );

    my $scrollview = Haiku::ScrollView->new(
        name         => "AttributeView",
        target       => $self->{viewer},
        resizingMode => B_FOLLOW_ALL,
    );
    $self->AddChild($scrollview);

Python

        button_frame = [
            self.spacer,
            self.spacer,
            0,
            0,
        ]
        button_frame[2] = button_frame[0] + self.button_width
        button_frame[3] = button_frame[1] + self.button_height
        self.button = Button(
            frame   = button_frame,
            name    = 'FileChooser',
            label   = 'Select File',
            message = Message(self.CHOOSE_FILE),
        )
        self.AddChild(self.button)

        # button may resize its height
        button_height = self.button.Bounds().Height()

        viewer_frame = [
            self.spacer,
            button_frame[3] + 3*self.spacer,
            self.window_frame[2] - self.window_frame[0] - self.spacer,
            self.window_frame[3] - self.window_frame[1] - self.spacer,
        ]
        self.viewer = OutlineListView(
            frame = viewer_frame,
            name  = 'AttributeViewer',
            resizingMode = B_FOLLOW_ALL,
        )

        scrollview = ScrollView(
            name         = "AttributeView",
            target       = self.viewer,
            resizingMode = B_FOLLOW_ALL,
        )
        self.AddChild(scrollview)

Hooks

We define responses to both our internally defined message and to the system message sent by the FilePanel.

Perl

sub MessageReceived {
    my ($self, $message) = @_;

    my $what = $message->what;
    if ($what == $CHOOSE_FILE) {
        $self->{panel} = Haiku::FilePanel->new(
            mode      => B_OPEN_PANEL,
            target    => $self->{target},
            flavors   => B_FILE_NODE,
            modal     => 0,
            hideWhenDone => 1,
        );
        $self->{panel}->Show();
        return;
    }

    if ($message->what == B_REFS_RECEIVED) {
        undef $self->{panel};
        my $file = $message->FindRef("refs");
        $self->view_attributes($file);
        return;
    }

    $self->SUPER::MessageReceived($message);
}

Python

def MessageReceived(self, message):

    what = message.what
    if what == self.CHOOSE_FILE:
        self.panel = FilePanel(
            mode      = B_OPEN_PANEL,
            target    = self.target,
            flavors   = B_FILE_NODE,
            modal     = 0,
            hideWhenDone = 1,
        )
        self.panel.Show()
        return

    if message.what == B_REFS_RECEIVED:
        self.panel = None
        file = message.FindRef("refs")
        self.view_attributes(file)
        return

    super(AttributeViewerWindow, self).MessageReceived(message)

Attributes

Finally, we get to the meat of our program: the method that fetches the attributes for a given file, and then displays them for the user.

Perl

sub view_attributes {
    my ($self, $file) = @_;

    $self->{viewer}->MakeEmpty();
    my $name = Haiku::StringItem->new($file);
    my $parent1 = Haiku::StringItem->new('Standard Attributes');
    my $parent2 = Haiku::StringItem->new('Other Attributes');
    @{ $self->{items} } = ($name, $parent1, $parent2);
    $self->{viewer}->AddList($self->{items});

    my $node = Haiku::Node->new($file);
    my $ninfo = Haiku::NodeInfo->new($node);

    my ($item, $value);

    # AddUnder puts the new item at the top of the children
    # so we add the items in reverse of how we wish them to appear

    eval {
        $value = 'not present';
        my $app_hint = $ninfo->GetAppHint();
        $value = $type_handlers{ B_MIME_STRING_TYPE() }->($app_hint);
    };
    $item = Haiku::StringItem->new("App Hint = $value");
    push @{ $self->{items} }, $item;
    $self->{viewer}->AddUnder($item, $parent1);

    eval {
        $value = 'not present';
        my $pref_app = $ninfo->GetPreferredApp();
        $value = $type_handlers{ B_MIME_STRING_TYPE() }->($pref_app);
    };
    $item = Haiku::StringItem->new("Preferred App = $value");
    push @{ $self->{items} }, $item;
    $self->{viewer}->AddUnder($item, $parent1);

    eval {
        $value = 'not present';
        my $type = $ninfo->GetType();
        $value = $type_handlers{ B_MIME_STRING_TYPE() }->($type);
    };
    $item = Haiku::StringItem->new("Type = $value");
    push @{ $self->{items} }, $item;
    $self->{viewer}->AddUnder($item, $parent1);

    eval {
        $value = 'not present';
        my $vicon = $ninfo->GetIcon();
        $value = $type_handlers{ B_VECTOR_ICON_TYPE() }->($vicon);
    };
    $item = Haiku::StringItem->new("Vector Icon = $value");
    push @{ $self->{items} }, $item;
    $self->{viewer}->AddUnder($item, $parent1);

    eval {
        $value = 'not present';
        my $micon = $ninfo->GetIcon(B_MINI_ICON);
        $value = $type_handlers{ B_MINI_ICON_TYPE() }->($micon);
    };
    $item = Haiku::StringItem->new("Mini Icon = $value");
    push @{ $self->{items} }, $item;
    $self->{viewer}->AddUnder($item, $parent1);

    eval {
        $value = 'not present';
        my $licon = $ninfo->GetIcon(B_LARGE_ICON);
        $value = $type_handlers{ B_LARGE_ICON_TYPE() }->($licon);
    };
    $item = Haiku::StringItem->new("Large Icon = $value");
    push @{ $self->{items} }, $item;
    $self->{viewer}->AddUnder($item, $parent1);

    my $attrs = $node->Attrs();
    for my $attr (@$attrs) {
        next if $standard{$attr};
        my $ainfo = $node->GetAttrInfo($attr);
        my $handler = $type_handlers{ $ainfo->{type} };
        if (ref($handler)) {
            $value = $handler->( $node->ReadAttr($attr) );
        }
        elsif ($handler) {
            $value = "Type '$handler' not supported";
        }
        else {
            my $code = int_to_code($ainfo->{type});
            $value = "Type '$code' not recognized";
        }
        $item = Haiku::StringItem->new("$attr = $value");
        push @{ $self->{items} }, $item;
        $self->{viewer}->AddUnder($item, $parent2);
    }
}

Python

def view_attributes(self, file):

    self.viewer.MakeEmpty()
    name = StringItem(file)
    parent1 = StringItem('Standard Attributes')
    parent2 = StringItem('Other Attributes')
    self.items = [name, parent1, parent2]
    self.viewer.AddList(self.items)

    node = Node(file)
    ninfo = NodeInfo(node)

    item = None
    value = None

    # AddUnder puts the new item at the top of the children
    # so we add the items in reverse of how we wish them to appear

    try:
        value = 'not present'
        app_hint = ninfo.GetAppHint()
        value = self.type_handlers[B_MIME_STRING_TYPE](app_hint)
    except Exception as e:
        pass
    item = StringItem("App Hint = " + value)
    self.items.append(item)
    self.viewer.AddUnder(item, parent1)

    try:
        value = 'not present'
        pref_app = ninfo.GetPreferredApp()
        value = self.type_handlers[B_MIME_STRING_TYPE](pref_app)
    except Exception as e:
        pass
    item = StringItem("Preferred App = " + value)
    self.items.append(item)
    self.viewer.AddUnder(item, parent1)

    try:
        value = 'not present'
        type = ninfo.GetType()
        value = self.type_handlers[B_MIME_STRING_TYPE](type)
    except Exception as e:
        pass
    item = StringItem("Type = " + value)
    self.items.append(item)
    self.viewer.AddUnder(item, parent1)

    try:
        value = 'not present'
        vicon = ninfo.GetIcon()
        value = self.type_handlers[B_VECTOR_ICON_TYPE](vicon)
    except Exception as e:
        pass
    item = StringItem("Vector Icon = " + value)
    self.items.append(item)
    self.viewer.AddUnder(item, parent1)

    try:
        value = 'not present'
        micon = ninfo.GetIcon(B_MINI_ICON)
        value = self.type_handlers[B_MINI_ICON_TYPE](micon)
    except Exception as e:
        pass
    item = StringItem("Mini Icon = " + value)
    self.items.append(item)
    self.viewer.AddUnder(item, parent1)

    try:
        value = 'not present'
        licon = ninfo.GetIcon(B_LARGE_ICON)
        value = self.type_handlers[B_LARGE_ICON_TYPE](licon)
    except Exception as e:
        pass
    item = StringItem("Large Icon = " + value)
    self.items.append(item)
    self.viewer.AddUnder(item, parent1)

    attrs = node.Attrs()
    for attr in attrs:
        if attr in self.standard:
            continue
        ainfo = node.GetAttrInfo(attr)
        handler = self.type_handlers.get(ainfo['type'])
        if callable(handler):
            value = handler( node.ReadAttr(attr) )
        elif handler:
            value = "Type '" + handler + "' not supported"
        else:
            code = int_to_code(ainfo['type'])
            value = "Type '" + code + "' not recognized"
        item = StringItem(attr + " = " + value)
        self.items.append(item)
        self.viewer.AddUnder(item, parent2)

Running the app

Instantiate the app and run it.