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.