# ==================================================================== # Copyright (C) 1997,1998 Stephen Farrell # # All rights reserved. This program is free software; you can # redistribute it and/or modify it under the same terms as Perl # itself. # # ==================================================================== # File: Component.pm # Author: Stephen Farrell # Created: August, 1997 # Locations: http://www.palefire.org/~sfarrell/TableLayout/ # CVS $Id: Component.pm,v 1.17 1998/09/20 21:05:28 sfarrell Exp $ # ==================================================================== ## ## This class is abstract ## package HTML::TableLayout::Component; use HTML::TableLayout::Symbols; @HTML::TableLayout::Component::ISA=qw(HTML::TableLayout::TL_BASE); use Carp; use strict; ## ## Default init ## sub tl_init { my $this = shift; ## ## QUIZ--how do i avoid this temporary variable? ## my %params = @_; $this->{TL_PARAMS} = \ %params; $this->SUPER::tl_init(); } ## ## tl_setContext(): Sets the context in the heirarchy when packing and ## displaying. This is done "late" ## sub tl_setContext { my ($this, $container) = @_; my $window = $container->{TL_WINDOW}; my $form = $container->{TL_FORM}; ## ==================================================================== ## ## DEBUGGING ## confess "container is null" unless $container; confess "window is null" unless $window; ## ## it's ok for the form to be null, but if it is, we don't want to ## clobber an existing value for it. ## ## ==================================================================== defined $container and $this->{TL_CONTAINER} = $container; defined $window and $this->{TL_WINDOW} = $window; defined $form and $this->{TL_FORM} = $form; } ## ## tl_getContainer(),tl_getWindow(),tl_getForm(): Accessors for the ## above--notethat these might not be used much b/c we know the name ## of the data very well. ## sub tl_getContainer { return shift->{TL_CONTAINER} } sub tl_getWindow { return shift->{TL_WINDOW} } sub tl_getForm { return shift->{TL_FORM} } ## ## tl_setup(): is called just before printing, and is meant to provide ## "late" packing and searching for requirements in containers (like ## looking for a Form). Actually, it's called everywhere before ## anything prints, so if you want to play with values in your ## neighboring components, have fun. ## ## If you override this, you must call your super's version. (like ## $this->SUPER::tl_setup()). ok ok I'm lying right now b/c as you can ## see, there is nothing here so obviously you don't HAVE to call it. ## but I might add something later. Also, if your parent is a ## componentcontainer, then you MUST call it (or do equivalent and ## keep your fingers crossed for future versions). ## sub tl_setup { } ## ## tl_print(): uses i_print() and f_print() to display object. ## sub tl_print { } ## ## tl_breakAfter(): The component has a break "
" after it. This ## doesn't happen automatically--the component printing it needs to ## check if it is there and print it itself. ## sub tl_breakAfter { return shift->{TL_BREAK_AFTER} } sub tl_destroy { my ($this) = @_; undef $this->{TL_BREAK_AFTER}; undef $this->{TL_CONTAINER}; undef $this->{TL_WINDOW}; undef $this->{TL_FORM}; $this->SUPER::tl_destroy(); } # --------------------------------------------------------------------- package HTML::TableLayout::ComponentContainer; use HTML::TableLayout::Symbols; @HTML::TableLayout::ComponentContainer::ISA=qw(HTML::TableLayout::Component); sub tl_init { my $this = shift; $this->SUPER::tl_init(@_); $this->{TL_COMPONENTS} = []; $this->{TL_BREAKS} = []; } ## ## insert(): add a component. subclasses should always call this, ## like tl_setup() ## sub insert { my ($this, $obj, $br) = @_; if (! ref $obj) { $obj = HTML::TableLayout::Component::Text->new($obj); } if ($obj->isa("HTML::TableLayout::Form")) { $this->{TL_FORM} = $obj; $this->{form_is_mine} = $obj; } else { push @{ $this->{TL_COMPONENTS} }, $obj; push @{ $this->{TL_BREAKS} }, $br; } return $this; } ## ## insertLn(): add a component w/
afterwards. Generally I've ## handled this as a wrapper method that calls insert with a second ## argument of "1". ## sub insertLn { return shift->insert(shift,1) } ## ## tl_setup(): if you choose to override this method, then you must do ## what is done here, or call $this->SUPER::tl_setup(). Of course, if ## you replicate this method's functionality, you should be aware that ## in the future this function might change, and you might need to ## update your equivalent functionality in the future.... (yes, I'm ## scrounging for hints on OO design!) ## sub tl_setup { my ($this) = @_; $this->tl_setup_form(); foreach my $cmp (@{ $this->{TL_COMPONENTS} }) { die("null comp.") unless $cmp; ## ## Maybe it is a form input, in which case it needs to be inserted ## into the appropriate form. ## if ($cmp->isa("HTML::TableLayout::FormComponent")) { my $f = $this->{TL_FORM}; if ($f) { $f->insert($cmp); $cmp->tl_setContext($this); } else { die("No Form to insert this FormComponent [$cmp] into [$this]"); } } $cmp->tl_setContext($this); $cmp->tl_setup(); } $this->SUPER::tl_setup(); } sub tl_setup_form { my $this = shift; ## ## If we have a form, this is the time to set its context ## if ($this->{form_is_mine}) { if ($this->{form_is_mine} ne $this->{TL_FORM}) { die("Nested forms detected!"); } else { $this->{TL_FORM}->tl_setContext($this); $this->{TL_WINDOW}->_incrementNumForms(); } } } ## ## this makes a ComponentContainer an implementable object--and a very ## useful one at that. YOu can just stick stuff in it and it'll print ## the various things with no added overhead. unfortunately, ## subclasses will need to reproduce any behavior here... ## sub tl_print { my ($this) = @_; $this->{form_is_mine} and $this->{TL_FORM}->tl_print(); foreach (0..$#{ $this->{TL_COMPONENTS} }) { $this->{TL_COMPONENTS}->[$_]->tl_print(); $this->{TL_BREAKS}->[$_] and $this->{TL_WINDOW}->i_print(">{form_is_mine} and $this->{TL_FORM}->_print_end(); } sub tl_destroy { my ($this) = @_; foreach(@{ $this->{TL_COMPONENTS} }) { $_->tl_destroy(); } undef $this->{TL_BREAKS}; undef $this->{TL_COMPONENTS}; $this->SUPER::tl_destroy(); } sub getAllChildren { my ($this, $what) = @_; my @children; if (scalar(@{ $this->{TL_COMPONENTS} })) { foreach my $child (@{ $this->{TL_COMPONENTS} }) { push @children, $child if (! $what or $child->isa($what)); push @children, $child->getAllChildren($what) if $child->isa("HTML::TableLayout::ComponentContainer"); } } return @children; } # --------------------------------------------------------------------- ## clearly this is not what I meant... FIXME! package HTML::TableLayout::ComponentCell; @HTML::TableLayout::ComponentCell::ISA=qw(HTML::TableLayout::Cell); # --------------------------------------------------------------------- package HTML::TableLayout::ComponentTable; @HTML::TableLayout::ComponentTable::ISA=qw(HTML::TableLayout::Table); # --------------------------------------------------------------------- package HTML::TableLayout::Component::Text; use HTML::TableLayout::Symbols; use Carp; @HTML::TableLayout::Component::Text::ISA=qw(HTML::TableLayout::Component); my %MARKUP = (bold => "B", italic => "I", big => "BIG", small => "SMALL"); sub tl_init { my $this = shift; $this->{text} = shift; $this->SUPER::tl_init(@_); } sub tl_getParameters { my ($this) = @_; confess("WAS DESTROYED") if $this->{WAS_DESTROYED}; confess("TL_PARAMS undef [$this]") unless $this->{TL_PARAMS}; confess("TL_WINDOW undef [$this]") unless $this->{TL_WINDOW}; my %params = ($this->{TL_WINDOW}->{PARAMETERS}->get($this), %{ $this->{TL_PARAMS} }); foreach("italic","bold", "big", "small") { if (exists $params{$_}) { delete $params{$_}; push @{ $this->{markup} }, $MARKUP{$_}; } } return (%params); } sub tl_print { my ($this) = @_; my $w = $this->{TL_WINDOW}; my %p = $this->tl_getParameters(); $w->i_print(); my $m; foreach $m (@{ $this->{markup} }) { $w->f_print("><$m"); } $w->f_print(">"); if ($this->{tl_do_not_pad}) { $w->f_print($this->{"text"}); } else { $w->f_print(" " . $this->{"text"} . " "); } $w->f_print("{markup} }) { $w->f_print(">< ## style tagging... the problem is that if we don't pad, the text is ## glued together unexpectedly. if i do pad, then links look bad. ## This function is here so a link can tell it's text components not ## to pad. ## sub tl_do_not_pad { shift->{tl_do_not_pad} = 1 } # --------------------------------------------------------------------- package HTML::TableLayout::Component::Image; use HTML::TableLayout::Symbols; @HTML::TableLayout::Component::Image::ISA=qw(HTML::TableLayout::Component); sub tl_init { my ($this, $url, %params) = @_; $this->SUPER::tl_init(%params); $this->{url} = $url; } sub tl_print { my ($this, %ops) = @_; my $w = $this->{TL_WINDOW}; my $p = params($this->tl_getParameters()) || ""; $w->i_print(qq{>{href} = shift; $this->{anchor} = shift; $this->SUPER::tl_init(@_); if (ref $this->{anchor}) { $this->{TL_COMPONENTS}->[0] = $this->{anchor}; } else { $this->{TL_COMPONENTS}->[0] = HTML::TableLayout::Component::Text->new($this->{anchor}); } if ($this->{TL_COMPONENTS}->[0]->isa("HTML::TableLayout::Component::Text")) { ## ## see comment for tl_do_not_pad() method of Text ## $this->{TL_COMPONENTS}->[0]->tl_do_not_pad(); } } sub passCGI { my ($this, $cgi, @pass) = @_; if (! (ref $cgi eq "HASH")) { die("malformed passcgi") } $this->{href} .= "?"; my @p = scalar(@pass) ? @pass : keys %$cgi; my ($k, $v); foreach (@p) { if (/^([^=]+)=(.*)$/) { ($k, $v) = ($1, $2); } else { ($k, $v) = ($_, $cgi->{$_}); } $this->{href} .= $k . "=" . escape_url($v) . "&"; } return $this; } ## ## stolen from cgi.pm ## sub escape_url { my $s = shift; $s eq undef and return undef; $s=~s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg; return $s } sub tl_print { my ($this, %ops) = @_; my $w = $this->{TL_WINDOW}; my $p = params($this->tl_getParameters()) || ""; $w->i_print(qq{>{TL_COMPONENTS}->[0]->tl_print(); $w->f_print(">{pre} = shift; $this->SUPER::tl_init(@_); } sub tl_print { my ($this) = @_; my $w = $this->{TL_WINDOW}; $w->i_print(">
");
  $w->f_print($this->{"pre"}."");
  $w->i_print("{comment} = shift;
  $this->SUPER::tl_init(@_);
}

sub tl_print {
  my ($this) = @_;
  ##
  ## This is a pretty ugly hack--note fake tag ""
  ##
  $this->{TL_WINDOW}->i_print(">{TL_WINDOW}->i_print(">tl_getParameters())."");
} 

# ---------------------------------------------------------------------
package HTML::TableLayout::Component::Font;
use HTML::TableLayout::Symbols;
@HTML::TableLayout::Component::Font::ISA=
  qw(HTML::TableLayout::ComponentContainer);


sub tl_print {
  my $this = shift;

  my %p = $this->tl_getParameters();
  $this->{TL_WINDOW}->i_print(">{TL_COMPONENTS} }) {
    $this->{TL_WINDOW}->_indentIncrement();
    $_->tl_print();
    $this->{TL_WINDOW}->_indentDecrement();
  }
  $this->{TL_WINDOW}->i_print(">");
}
    

# ---------------------------------------------------------------------
package HTML::TableLayout::Component::List;
use HTML::TableLayout::Symbols;
@HTML::TableLayout::Component::List::ISA=
  qw(HTML::TableLayout::ComponentContainer);

sub tl_init {
  my $this = shift;
  $this->{numbered} = shift;
  $this->{delimited} = shift;
  $this->SUPER::tl_init(@_);
}

sub insert {
  my ($this, $component, $br) = @_;
  if (! ref $component) {
    $component = HTML::TableLayout::Component::Text->new($component);
  }

  push @{ $this->{TL_BREAKS} }, $br;

  $this->SUPER::insert($component);
}



sub tl_print {
  my ($this) = @_;
  
  my $w = $this->{TL_WINDOW};
  my $list_denoter;
  if ($this->{numbered}) {
    $list_denoter = "OL";
  }
  else {
    $list_denoter = "UL";
  }
  $w->i_print("><$list_denoter");
  my $i;
  foreach $i (0..$#{ $this->{TL_COMPONENTS} }) {
    my $c = $this->{TL_COMPONENTS}->[$i];

    if ($this->{delimited} and
	! $c->isa("HTML::TableLayout::Component::List")) {
      $w->f_print(">_indentIncrement();
    $c->tl_print();
    $w->_indentDecrement();

    ## do this if the component is a list??
    $this->{TL_BREAKS}->[$i] and $w->f_print(">i_print(">