home *** CD-ROM | disk | FTP | other *** search
/ CD Actual Thematic 7: Programming / CDAT7.iso / Share / Editores / Perl5 / perl / lib / site / Tk / Derived.pm < prev    next >
Encoding:
Perl POD Document  |  1997-08-10  |  11.7 KB  |  518 lines

  1. # Copyright (c) 1995-1997 Nick Ing-Simmons. All rights reserved.
  2. # This program is free software; you can redistribute it and/or
  3. # modify it under the same terms as Perl itself.
  4. package Tk::Derived;
  5. require Tk::Widget;
  6. require Tk::Configure;
  7. use strict;
  8. use Carp;
  9.  
  10. =head1 NAME
  11.  
  12. Tk::Derived - Base class for widgets derived from others
  13.  
  14. =head1 SYNOPSIS
  15.  
  16.   package Tk::Whatever;
  17.   require Tk::Something; 
  18.   require Tk::Derived;
  19.   @ISA = qw(Tk::Derived Tk::Something);
  20.  
  21.   sub Populate
  22.   {
  23.    my ($cw,$args) = @_;
  24.    ...
  25.    $cw->SUPER::Populate($args);
  26.    $cw->ConfigSpecs(...);
  27.    ...
  28.   }
  29.  
  30. =head1 DESCRIPTION 
  31.  
  32. Tk::Derived is used with perl5's multiple inheritance to override some
  33. methods normally inherited from Tk::Widget.
  34.  
  35. Tk::Derived should precede any Tk widgets in the class's @ISA.
  36.  
  37. Tk::Derived's main purpose is to apply wrappers to C<configure> and C<cget>
  38. methods of widgets to allow the derived widget to add to or modify behaviour
  39. of the configure options supported by the base widget.
  40.  
  41. The derived class should normally override the C<Populate> method provided
  42. by Tk::Derived and call C<ConfigSpecs> to declare configure options.
  43.  
  44. The public methods provided by Tk::Derived are as follows:
  45.  
  46. =over 4
  47.  
  48. =cut
  49.  
  50. $Tk::Derived::Debug = 0;
  51.  
  52. use Tk qw(NORMAL_BG BLACK);
  53.  
  54. sub Subwidget 
  55. {
  56.  my $cw = shift;
  57.  my @result = ();
  58.  if (exists $cw->{SubWidget})
  59.   {
  60.    if (@_)
  61.     {
  62.      my $name;
  63.      foreach $name (@_)
  64.       {
  65.        push(@result,$cw->{SubWidget}{$name}) if (exists $cw->{SubWidget}{$name});
  66.       }
  67.     }
  68.    else
  69.     {
  70.      @result = values %{$cw->{SubWidget}};
  71.     }
  72.   }
  73.  return (wantarray) ? @result : $result[0];
  74.  
  75. sub _makelist
  76. {
  77.  my $widget = shift;
  78.  my (@specs) = (ref $widget && ref $widget eq "ARRAY") ? (@$widget) : ($widget);
  79.  return @specs;
  80. }
  81.  
  82. sub Subconfigure
  83. {
  84.  # This finds the widget or widgets to to which to apply a particular 
  85.  # configure option
  86.  my ($cw,$opt) = @_;
  87.  my $config = $cw->ConfigSpecs;
  88.  my $widget;
  89.  my @subwidget = ();
  90.  my @arg = ();
  91.  if (defined $opt)
  92.   {
  93.    $widget = $config->{$opt};
  94.    unless (defined $widget)
  95.     {
  96.      $widget = ($opt =~ /^-(.*)$/) ? $config->{$1} : $config->{-$opt};
  97.     }
  98.    # Handle alias entries
  99.    if (defined($widget) && !ref($widget))
  100.     {
  101.      $opt    = $widget;
  102.      $widget = $config->{$widget};
  103.     }
  104.    push(@arg,$opt) unless ($opt eq 'DEFAULT');
  105.   }
  106.  $widget = $config->{DEFAULT} unless (defined $widget);
  107.  if (defined $widget)
  108.   {
  109.    $cw->BackTrace("Invalid ConfigSpecs $widget") unless (ref($widget) && (ref $widget eq "ARRAY"));
  110.    $widget = $widget->[0];
  111.   }
  112.  else
  113.   {
  114.    $widget = 'SELF';
  115.   }
  116.  foreach $widget (_makelist($widget))
  117.   {
  118.    if (ref $widget)
  119.     {
  120.      my $ref = ref $widget;
  121.      if ($ref eq 'ARRAY')
  122.       {
  123.        $widget = Tk::Configure->new(@$widget); 
  124.        push(@subwidget,$widget)
  125.       }
  126.      elsif ($ref eq 'HASH')
  127.       {
  128.        my $key;
  129.        foreach $key (%$widget)
  130.         {
  131.          my $sw;  
  132.          foreach $sw (_makelist($widget->{$key}))
  133.           {
  134.            push(@subwidget,Tk::Configure->new($sw,$key));
  135.           }
  136.         }
  137.       }
  138.      else
  139.       {
  140.        push(@subwidget,$widget)
  141.       }
  142.     }
  143.    elsif ($widget eq 'ADVERTISED')
  144.     {
  145.      push(@subwidget,$cw->Subwidget)
  146.     }
  147.    elsif ($widget eq 'DESCENDANTS')
  148.     {
  149.      push(@subwidget,$cw->Descendants) 
  150.     }
  151.    elsif ($widget eq 'CHILDREN')
  152.     {
  153.      push(@subwidget,$cw->children) 
  154.     }
  155.    elsif ($widget eq 'METHOD')
  156.     {
  157.      my ($method) = ($opt =~ /^-?(.*)$/);
  158.      push(@subwidget,Tk::Configure->new($method,$method,$cw))
  159.     }
  160.    elsif ($widget eq 'SETMETHOD')
  161.     {
  162.      my ($method) = ($opt =~ /^-?(.*)$/);
  163.      push(@subwidget,Tk::Configure->new($method,'_cget',$cw,@arg))
  164.     }
  165.    elsif ($widget eq 'SELF')
  166.     {
  167.      push(@subwidget,Tk::Configure->new('configure_self', 'cget_self', $cw,@arg))
  168.     }
  169.    elsif ($widget eq 'PASSIVE') 
  170.     {
  171.      push(@subwidget,Tk::Configure->new('_configure','_cget',$cw,@arg))
  172.     }
  173.    elsif ($widget eq 'CALLBACK') 
  174.     {
  175.      push(@subwidget,Tk::Configure->new('_callback','_cget',$cw,@arg))
  176.     }
  177.    else
  178.     {
  179.      push(@subwidget,$cw->Subwidget($widget));
  180.     }
  181.   }
  182.  $cw->BackTrace("No delegate subwidget '$widget' for $opt") unless (@subwidget);
  183.  return (wantarray) ? @subwidget : $subwidget[0];
  184. }
  185.  
  186. sub _cget
  187. {
  188.  my ($cw,$opt) = @_;
  189.  $cw->BackTrace("Wrong number of args to cget") unless (@_ == 2);
  190.  return $cw->{Configure}{$opt}
  191. }
  192.  
  193. sub _configure
  194. {
  195.  my ($cw,$opt,$val) = @_;
  196.  $cw->BackTrace("Wrong number of args to configure") unless (@_ == 3);
  197.  $cw->{Configure}{$opt} = $val;
  198. }
  199.  
  200. sub _callback
  201. {
  202.  my ($cw,$opt,$val) = @_;
  203.  $cw->BackTrace("Wrong number of args to configure") unless (@_ == 3);
  204.  $val = Tk::Callback->new($val) if defined $val;
  205.  $cw->{Configure}{$opt} = $val;
  206. }
  207.  
  208. sub cget
  209. {my ($cw,$opt) = @_;
  210.  my (@subwidget) = $cw->Subconfigure($opt);
  211.  my @result = $cw->{Configure}{$opt};
  212.  if (@subwidget == 1)
  213.   {
  214.    @result = $subwidget[0]->cget($opt);
  215.   }
  216.  return (wantarray) ? @result : $result[0];
  217. }
  218.  
  219. sub Configured
  220. {
  221.  # Called whenever a derived widget is re-configured
  222.  my ($cw,$args,$changed) = @_;
  223.  if (@_ > 1)
  224.   {
  225.    $cw->DoWhenIdle(['ConfigChanged',$cw,$changed]) if (%$changed);
  226.   }
  227.  return exists $cw->{'Configure'};
  228. }
  229.  
  230. sub configure
  231. {
  232.  # The default composite widget configuration method uses hash stored
  233.  # in the widget's hash to map configuration options
  234.  # onto subwidgets. 
  235.  #
  236.  my @results = ();
  237.  my $cw = shift;
  238.  if (@_ <= 1)
  239.   {
  240.    # Enquiry cases 
  241.    my $spec = $cw->ConfigSpecs;
  242.    if (@_)
  243.     {
  244.      # Return info on the nominated option
  245.      my $opt  = $_[0];
  246.      my $info = $spec->{$opt};
  247.      unless (defined $info)
  248.       {
  249.        $info = ($opt =~ /^-(.*)$/) ? $spec->{$1} : $spec->{-$opt};
  250.       }
  251.      if (defined $info)
  252.       {
  253.        if (ref $info)
  254.         {
  255.          push(@results,$opt,$info->[1],$info->[2],$info->[3],$cw->cget($opt));
  256.         }
  257.        else
  258.         {
  259.          # Real (core) Tk widgets return db name rather than option name 
  260.          # for aliases so recurse to get that ...
  261.          my @real = $cw->configure($info);
  262.          push(@results,$opt,$real[1]);
  263.         }
  264.       }
  265.      else
  266.       {
  267.        push(@results,$cw->Subconfigure($opt)->configure($opt));
  268.       }
  269.     }
  270.    else
  271.     {
  272.      my $opt;
  273.      my %results;
  274.      if (exists $spec->{'DEFAULT'})
  275.       {
  276.        foreach $opt ($cw->Subconfigure('DEFAULT')->configure)
  277.         {
  278.          $results{$opt->[0]} = $opt;
  279.         }
  280.       }
  281.      foreach $opt (keys %$spec)
  282.       {
  283.        $results{$opt} = [$cw->configure($opt)] if ($opt ne 'DEFAULT');
  284.       }
  285.      foreach $opt (sort keys %results)
  286.       {
  287.        push(@results,$results{$opt});
  288.       }
  289.     }
  290.   }
  291.  else
  292.   {
  293.    my (%args) = @_;
  294.    my %changed = ();
  295.    my ($opt,$val);
  296.    $cw->{Configure} = {} unless exists $cw->{Configure};
  297.    while (($opt,$val) = each %args)
  298.     {
  299.      my $var = \$cw->{Configure}{$opt};
  300.      my $old = $$var;
  301.      my $subwidget;
  302.      $$var = $val;
  303.      my $accepted = 0;
  304.      my $error = "No widget handles $opt";
  305.      foreach $subwidget ($cw->Subconfigure($opt))
  306.       {
  307.        next unless (defined $subwidget);
  308.        eval {local $SIG{'__DIE__'};  $subwidget->configure($opt => $val) };
  309.        if ($@)
  310.         {
  311.          $error = $@; 
  312.          undef $@;
  313.         }
  314.        else
  315.         {
  316.          $accepted = 1;
  317.         }
  318.       }
  319.      $cw->BackTrace($error) unless ($accepted);
  320.      $val = $$var;
  321.      $changed{$opt} = $val if (!defined $old || !defined $val || $old ne $val);
  322.     }
  323.    $cw->Configured(\%args,\%changed);
  324.   }
  325.  return (wantarray) ? @results : $results[0];
  326. }
  327.  
  328. sub ConfigDefault
  329. {
  330.  my ($cw,$args) = @_;
  331.  
  332.  $cw->BackTrace("Bad args") unless (defined $args && ref $args eq 'HASH');
  333.  
  334.  my $specs = $cw->ConfigSpecs;
  335.  # Should we enforce a Delagates(DEFAULT => )  as well ?
  336.  $specs->{'DEFAULT'} = ['SELF'] unless (exists $specs->{'DEFAULT'});
  337.  
  338.  # 
  339.  # This is a pain with Text or Entry as core widget, they don't
  340.  # inherit SELF's cursor. So comment it out for Tk402.001
  341.  # 
  342.  # $specs->{'-cursor'} = ['SELF',undef,undef,undef] unless (exists $specs->{'-cursor'});
  343.  
  344.  # Now some hacks that cause colours to propogate down a composite widget 
  345.  # tree - really needs more thought, other options adding such as active 
  346.  # colours too and maybe fonts
  347.  
  348.  my $children = scalar($cw->children);
  349.  unless (exists($specs->{'-background'}))
  350.   {
  351.    my (@bg) = ('SELF');
  352.    push(@bg,'CHILDREN') if $children;
  353.    $specs->{'-background'} = [\@bg,'background','Background',NORMAL_BG]; 
  354.   }
  355.  unless (exists($specs->{'-foreground'}))
  356.   {
  357.    my (@fg) = ('PASSIVE');
  358.    unshift(@fg,'CHILDREN') if $children;
  359.    $specs->{'-foreground'} = [\@fg,'foreground','Foreground',BLACK];
  360.   }
  361.  $cw->ConfigAlias(-fg => '-foreground', -bg => '-background');
  362.  
  363.  # Pre-scan args for aliases - this avoids defaulting
  364.  # options specified via alias
  365.  my $opt; 
  366.  foreach $opt (keys %$args)
  367.   {
  368.    my $info = $specs->{$opt};
  369.    if (defined($info) && !ref($info))
  370.     {
  371.      $args->{$info} = delete $args->{$opt};
  372.     }
  373.   }
  374.  
  375.  # Now walk %$specs supplying defaults for all the options 
  376.  # which have a defined default value, potentially looking up .Xdefaults database
  377.  # options for the name/class of the 'frame' 
  378.  
  379.  foreach $opt (keys %$specs)
  380.   {
  381.    if ($opt ne 'DEFAULT')
  382.     {
  383.      unless (exists $args->{$opt})
  384.       {
  385.        my $info = $specs->{$opt};
  386.        if (ref $info)
  387.         {
  388.          # Not an alias
  389.          if (defined $info->[3])
  390.           {
  391.            # Only propagate if a default is supplied
  392.            $args->{$opt} = $info->[3];
  393.            # maybe should convert -fred into 'fred','Fred' here 
  394.            if (defined $info->[1] && defined $info->[2])
  395.             {
  396.              # Should we do this on the Subconfigure widget instead?
  397.              # to match *Entry.Background 
  398.              my $db = $cw->optionGet($info->[1],$info->[2]);
  399.              $args->{$opt} = $db if (defined $db);
  400.             }
  401.           }
  402.         }
  403.       }
  404.     }
  405.   }
  406. }
  407.  
  408. =item -E<gt>ConfigSpecs(-I<key> =E<gt> [I<kind>, I<name>, I<Class>, I<default], ...)
  409.  
  410. =cut 
  411.  
  412. sub ConfigSpecs
  413. {
  414.  my $cw = shift;
  415.  if (exists $cw->{'ConfigSpecs'})
  416.   {
  417.    my $specs = $cw->{'ConfigSpecs'};
  418.    while (@_)
  419.     {
  420.      my $key = shift;
  421.      my $val = shift;
  422.      $specs->{$key} = $val;
  423.     }
  424.   }
  425.  else
  426.   {
  427.    $cw->{'ConfigSpecs'} = { @_ };
  428.   }
  429.  return $cw->{'ConfigSpecs'};
  430. }
  431.  
  432. sub ConfigAlias
  433. {
  434.  my $cw = shift;
  435.  my $specs = $cw->ConfigSpecs;
  436.  while (@_ >= 2)
  437.   {
  438.    my $opt  = shift;
  439.    my $main = shift;
  440.    if (exists($specs->{$opt}) && ref($specs->{$opt}))
  441.     {
  442.      $specs->{$main} = $opt unless (exists $specs->{$main});
  443.     }
  444.    elsif (exists($specs->{$main}) && ref($specs->{$main}))
  445.     {
  446.      $specs->{$opt}  = $main unless (exists $specs->{$opt});
  447.     }
  448.    else 
  449.     {
  450.      $cw->BackTrace("Neither $opt nor $main exist");
  451.     }
  452.   }
  453.  $cw->BackTrace("Odd number of args to ConfigAlias") if (@_);
  454. }
  455.  
  456.  
  457. sub Delegate
  458. {
  459.  my ($cw,$method,@args) = @_;
  460.  my $widget = $cw->DelegateFor($method);
  461.  $method = "Tk::$method" if ($widget == $cw);
  462.  my @result;
  463.  if (wantarray)
  464.   {
  465.    @result   = $widget->$method(@args);
  466.   }
  467.  else
  468.   {
  469.    $result[0] = $widget->$method(@args);
  470.   }
  471.  return (wantarray) ? @result : $result[0];
  472. }
  473.  
  474. sub InitObject
  475. {
  476.  my ($cw,$args) = @_;
  477.  $cw->Populate($args);    
  478.  $cw->ConfigDefault($args);
  479. }
  480.  
  481. sub ConfigChanged
  482. {
  483.  my ($cw,$args) = @_;
  484. }       
  485.  
  486. sub Advertise
  487. {
  488.  my ($cw,$name,$widget)  = @_;
  489.  confess "No name" unless (defined $name);
  490.  croak "No widget" unless (defined $widget);
  491.  $cw->{SubWidget} = {} unless (exists $cw->{SubWidget});
  492.  $cw->{SubWidget}{$name} = $widget;              # advertise it
  493.  return $widget;
  494. }
  495.  
  496. sub Component
  497. {
  498.  my ($cw,$kind,$name,%args) = @_;
  499.  $args{'Name'} = "\l$name" if (defined $name && !exists $args{'Name'});
  500.  # my $pack = delete $args{'-pack'};
  501.  my $delegate = delete $args{'-delegate'};
  502.  my $w = $cw->$kind(%args);            # Create it
  503.  # $w->pack(@$pack) if (defined $pack);
  504.  $cw->Advertise($name,$w) if (defined $name);
  505.  $cw->Delegates(map(($_ => $w),@$delegate)) if (defined $delegate); 
  506.  return $w;                            # and return it
  507. }
  508.  
  509. 1;
  510. __END__
  511.  
  512. =back
  513.  
  514. =cut
  515.  
  516.  
  517.