From f570b956d33f56e839ac6b1916f762a8a8845f29 Mon Sep 17 00:00:00 2001 From: Maarten Vanraes Date: Tue, 15 Mar 2016 23:56:12 +0100 Subject: ExtWidget: define a base class for ExtTab --- lib/ManaTools/Shared/GUI/ExtTab.pm | 386 ++--------------------- lib/ManaTools/Shared/GUI/ExtWidget.pm | 555 ++++++++++++++++++++++++++++++++++ 2 files changed, 586 insertions(+), 355 deletions(-) create mode 100644 lib/ManaTools/Shared/GUI/ExtWidget.pm (limited to 'lib') diff --git a/lib/ManaTools/Shared/GUI/ExtTab.pm b/lib/ManaTools/Shared/GUI/ExtTab.pm index 66f242bb..a6b31ae3 100644 --- a/lib/ManaTools/Shared/GUI/ExtTab.pm +++ b/lib/ManaTools/Shared/GUI/ExtTab.pm @@ -12,7 +12,7 @@ use ManaTools::Shared::GUI::ExtTab; my $exttab = ManaTools::Shared::GUI::ExtTab->new(name => "Tab1", eventHandler => $dialog, parentWidget => $widget, callback => { my $self = shift; my $yevent = shift; my $backenditem = $_; ... }); -$exttab->addTabItem("Label 1", $backenditem1, sub { +$exttab->addSelectorItem("Label 1", $backenditem1, sub { my ($self, $parent, $backendItem) = @_; my $dialog = $self->parentDialog(); my $factory = $dialog->factory(); @@ -27,10 +27,10 @@ $exttab->addTabItem("Label 1", $backenditem1, sub { my $button2 = $self->addWidget($backendItem->label() .'_button2', $factory->createPushButton('Button 2', $vbox), sub {...}, $backendItem); ... }); -$exttab->addTabItem("Label 2", $backenditem2, sub { my ($self, $parent, $backendItem) = @_; my $factory = $self->parentDialog()->factory(); my $vbox = $factory->createVBox($parent); ... } ); -$exttab->addTabItem("Label 3", $backenditem3, sub { my ($self, $parent, $backendItem) = @_; my $factory = $self->parentDialog()->factory(); my $vbox = $factory->createVBox($parent); ... } ); -$exttab->addTabItem("Label 4", $backenditem4, sub { my ($self, $parent, $backendItem) = @_; my $factory = $self->parentDialog()->factory(); my $vbox = $factory->createVBox($parent); ... } ); -$exttab->finishedTabItems(); +$exttab->addSelectorItem("Label 2", $backenditem2, sub { my ($self, $parent, $backendItem) = @_; my $factory = $self->parentDialog()->factory(); my $vbox = $factory->createVBox($parent); ... } ); +$exttab->addSelectorItem("Label 3", $backenditem3, sub { my ($self, $parent, $backendItem) = @_; my $factory = $self->parentDialog()->factory(); my $vbox = $factory->createVBox($parent); ... } ); +$exttab->addSelectorItem("Label 4", $backenditem4, sub { my ($self, $parent, $backendItem) = @_; my $factory = $self->parentDialog()->factory(); my $vbox = $factory->createVBox($parent); ... } ); +$exttab->finishedSelectorItems(); =head1 DESCRIPTION @@ -78,16 +78,18 @@ use Moose; use diagnostics; use utf8; -has 'basename' => ( - is => 'ro', - isa => 'Str', +extends 'ManaTools::Shared::GUI::ExtWidget'; + +has '+basename' => ( default => 'ExtTab', ); -with 'ManaTools::Shared::GUI::EventRole'; +has '+eventType' => ( + required => 0, + default => $yui::YEvent::WidgetEvent, +); use yui; -use ManaTools::Shared::GUI::ReplacePoint; #============================================================= @@ -104,324 +106,65 @@ use ManaTools::Shared::GUI::ReplacePoint; =head3 DESCRIPTION - new is inherited from Moose, to create a ExtTab object + new is inherited from ExtWidget, to create a ExtTab object =cut #============================================================= -has '+eventType' => ( - required => 0, - default => $yui::YEvent::WidgetEvent, -); - -has 'parentWidget' => ( - is => 'ro', - isa => 'yui::YWidget', - required => 1, -); - -has 'callback' => ( - is => 'ro', - isa => 'Maybe[CodeRef]', - lazy => 1, - default => sub { - return undef; - } -); - -has 'items' => ( - is => 'ro', - isa => 'ArrayRef[ManaTools::Shared::GUI::ExtTab::Item]', - lazy => 1, - init_arg => undef, - default => sub { - return []; - } -); - -has 'replacepoint' => ( - is => 'rw', - isa => 'Maybe[ManaTools::Shared::GUI::ReplacePoint]', - init_arg => undef, - handles => ['addEvent', 'delEvent', 'getEvent', 'addWidget', 'delWidget', 'widget', 'addItem', 'delItem', 'item'], - default => sub { - return undef; - } -); - -has 'tab' => ( - is => 'ro', - isa => 'yui::YDumbTab', - init_arg => undef, - lazy => 1, - builder => 'buildTab', -); - -has 'lastItem' => ( - is => 'rw', - isa => 'Maybe[ManaTools::Shared::GUI::ExtTab::Item]', - init_arg => undef, - default => sub { - return undef; - } -); - -has 'itemcollection' => ( - is => 'rw', - isa => 'yui::YItemCollection', - init_arg => undef, - default => sub { - return new yui::YItemCollection(); - } -); - -#============================================================= - -=head2 buildTab - -=head3 INPUT - - $self: this object - -=head3 DESCRIPTION - - builds the YDumbTab widget - -=cut - -#============================================================= -sub buildTab { - my $self = shift; - my $dialog = $self->parentDialog(); - my $optFactory = $dialog->optFactory(); - my $parentWidget = $self->parentWidget(); - - # create the tab - my $tab = $optFactory->createDumbTab($parentWidget); - - # create a replacepoint on the tab - $self->{replacepoint} = ManaTools::Shared::GUI::ReplacePoint->new(parentWidget => $tab); - - # parentEventHandler must be set directly, because we don't add the - # eventHandler to a parentEventHandler, instead the events are processed - # through the Tab's EventRole processEvent function - $self->{replacepoint}->parentEventHandler($self->{eventHandler}); - - # don't add any children right away - $self->{replacepoint}->finished(); - - return $tab; -} - -#============================================================= - -=head2 processEvent +=head2 _selectorItem =head3 INPUT $self: this object $yevent: yui::YEvent -=head3 DESCRIPTION - - handles the YDumbTab events and executes callback if necessary - -=cut - -#============================================================= -sub processEvent { - my $self = shift; - my $yevent = shift; - my $replacepoint = $self->replacepoint(); - my $items = $self->items(); - - # call subevents - my $processed = $replacepoint->processEvents($yevent); - return $processed if $processed >= 0; - - # only MenuEvents here... - return -1 if ($yevent->eventType() != $yui::YEvent::MenuEvent); - - # only items from *this* tab - my $yitem = $yevent->item(); - my $item = $self->findTabItem($yitem); - return -1 if !defined($item); - - # build the children - $self->buildTabItem($item); - - # execute callback if needed - my $callback = $self->callback(); - my $result = -1; - $result = $callback->($self, $yevent, $item->backend()) if defined($callback); - - # mark last item as this one - $self->lastItem($item); - - # return result of callback - return $result; -} - -#============================================================= - -=head2 addTabItem - -=head3 INPUT - - $self: this object - $label: a label for the YItem - $backendItem: a backendItem needed to identify and/or handle the event - $buildWidget: a CodeRef to rebuild the widget when required - =head3 OUTPUT - the created ManaTools::Shared::GUI::ExtTab::Item + YItem: the selected item =head3 DESCRIPTION - Creates an item and adds it to the ExtTab. Internally, it creates a - yui::YItem and adds it to the YItemCollection. If it's the first item, - mark it as the lastitem. + returns the items that is selected when an event fires =cut #============================================================= -sub addTabItem { +sub _selectorItem { my $self = shift; - my $label = shift; - my $backendItem = shift; - my $buildWidget = shift; - my $items = $self->items(); - my $item = ManaTools::Shared::GUI::ExtTab::Item->new(backend => $backendItem, builder => $buildWidget); - push @{$items}, $item; - $item->setLabel($label); - $item->addToCollection($self->itemcollection()); - if (scalar(@{$items}) == 1) { - $self->lastItem($item); - } - return $item; -} - -#============================================================= - -=head2 findTabItem - -=head3 INPUT - - $self: this object - $yitem: the YItem to be found - -=head3 DESCRIPTION - - returns a ManaTools::Shared::GUI::ExtTab::Item that has the YItem - -=cut - -#============================================================= -sub findTabItem { - my $self = shift; - my $yitem = shift; - # loop all the items - for my $i (@{$self->items()}) { - return $i if ($i->equals($yitem)); - } - return undef; -} - -#============================================================= - -=head2 buildTabItem - -=head3 INPUT - - $self: this object - $item: the item to be built (widgets from this tab will be recreated in the tab) - -=head3 DESCRIPTION - - builds an item on the internal replace point - -=cut - -#============================================================= -sub buildTabItem { - my $self = shift; - my $item = shift; - my $replacepoint = $self->replacepoint(); - my $container = $replacepoint->container(); - - # clear out any previous children/events - $replacepoint->clear(); - - # build item's widgetbuilder - my $builder = $item->builder(); - $builder->($self, $container, $item->backend()) if (defined $builder); - - # finished with replacepoint children - $replacepoint->finished(); + my $yevent = shift; + return $yevent->item(); } #============================================================= -=head2 clearTabItems +=head2 _buildSelectorWidget =head3 INPUT $self: this object -=head3 DESCRIPTION - - clears the tab to prepare for re-adding new items, call finishedTabItems() afterwards - -=cut - -#============================================================= -sub clearTabItems { - my $self = shift; - my $items = $self->items(); - - # remove all events before deleting all items - $self->clearEvents(); - - for (my $i = 0; $i < scalar(@{$items}); $i = $i + 1) { - delete $items->[$i]; - } -} - -#============================================================= - -=head2 finishedTabItems +=head3 OUTPUT -=head3 INPUT - - $self: this object + ($selector, $parent): $selector is the YSelectionWidget; $parent is the replacepoint's parent =head3 DESCRIPTION - finalizes the items on the ExtTab + builds the YDumbTab widget =cut #============================================================= -sub finishedTabItems { +override('_buildSelectorWidget', sub { my $self = shift; + my $parentWidget = shift; + my $dialog = $self->parentDialog(); + my $optFactory = $dialog->optFactory(); - # remove all Items before adding - $self->tab->deleteAllItems(); - - # add items from collection - $self->tab->addItems($self->itemcollection); - - # set last item to know the active item - my $item = $self->lastItem(); - - # show the current one if there is one - $self->buildTabItem($item) if defined($item); - - # create a new itemcollection for adding new items - $self->itemcollection(new yui::YItemCollection()); -} + # create the tab + my $tab = $optFactory->createDumbTab($parentWidget); + return ($tab, $tab); +}); #============================================================= @@ -429,71 +172,4 @@ no Moose; __PACKAGE__->meta->make_immutable; -1; - -#============================================================= - -package ManaTools::Shared::GUI::ExtTab::Item; - -use Moose; -use diagnostics; -use utf8; - -use yui; - -has 'builder' => ( - is => 'ro', - isa => 'Maybe[CodeRef]', - lazy => 1, - default => sub { - return undef; - } -); - -has 'item' => ( - is => 'ro', - isa => 'yui::YItem', - init_arg => undef, - default => sub { - return new yui::YItem('', 0); - } -); - -has 'backend' => ( - is => 'rw', - isa => 'Maybe[Ref]', - lazy => 1, - default => sub { - return undef; - } -); - -#============================================================= - -sub setLabel { - my $self = shift; - my $label = shift; - my $yitem = $self->item(); - $yitem->setLabel($label); -} - -sub equals { - my $self = shift; - my $item = shift; - return ($self->item() == $item); -} - -sub addToCollection { - my $self = shift; - my $collection = shift; - my $yitem = $self->item(); - $yitem->DISOWN(); - $collection->push($yitem); -} - -#============================================================= - -no Moose; -__PACKAGE__->meta->make_immutable; - 1; diff --git a/lib/ManaTools/Shared/GUI/ExtWidget.pm b/lib/ManaTools/Shared/GUI/ExtWidget.pm new file mode 100644 index 00000000..2c886427 --- /dev/null +++ b/lib/ManaTools/Shared/GUI/ExtWidget.pm @@ -0,0 +1,555 @@ +# vim: set et ts=4 sw=4: +package ManaTools::Shared::GUI::ExtWidget; +#============================================================= -*-perl-*- + +=head1 NAME + +ManaTools::Shared::GUI::ExtWidget - Class to manage a selection widget which has different controls + +=head1 SYNOPSIS + +use ManaTools::Shared::GUI::ExtWidget; + +my $extwidget = ManaTools::Shared::GUI::ExtWidget->new(name => "Selection1", eventHandler => $dialog, parentWidget => $widget, callback => { my $self = shift; my $yevent = shift; my $backenditem = $_; ... }); + +$extwidget->addSelectorItem("Label 1", $backenditem1, sub { + my ($self, $parent, $backendItem) = @_; + my $dialog = $self->parentDialog(); + my $factory = $dialog->factory(); + my $vbox = $factory->createVBox($parent); + my $button1 = $self->addWidget($backendItem->label() .'_button1', $factory->createPushButton('Button 1', $vbox), sub { + my $self = shift; + my $yevent = shift; + my $backendItem = shift; + my $selectorWidget = $self->eventHandler(); + ... + }, $backendItem); + my $button2 = $self->addWidget($backendItem->label() .'_button2', $factory->createPushButton('Button 2', $vbox), sub {...}, $backendItem); + ... +}); +$extwidget->addSelectorItem("Label 2", $backenditem2, sub { my ($self, $parent, $backendItem) = @_; my $factory = $self->parentDialog()->factory(); my $vbox = $factory->createVBox($parent); ... } ); +$extwidget->addSelectorItem("Label 3", $backenditem3, sub { my ($self, $parent, $backendItem) = @_; my $factory = $self->parentDialog()->factory(); my $vbox = $factory->createVBox($parent); ... } ); +$extwidget->addSelectorItem("Label 4", $backenditem4, sub { my ($self, $parent, $backendItem) = @_; my $factory = $self->parentDialog()->factory(); my $vbox = $factory->createVBox($parent); ... } ); +$extwidget->finishedSelectorItems(); + + +=head1 DESCRIPTION + +This class wraps a selector Widget with backend items to handle + + +=head1 SUPPORT + +You can find documentation for this module with the perldoc command: + +perldoc ManaTools::Shared::GUI::ExtWidget + +=head1 SEE ALSO + +yui::YSelectionWidget + +=head1 AUTHOR + +Maarten Vanraes + +=head1 COPYRIGHT and LICENSE + +Copyright (C) 2015-2016, Maarten Vanraes. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2, as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +=head1 FUNCTIONS + +=cut + + +use Moose; +use diagnostics; +use utf8; + +has 'basename' => ( + is => 'ro', + isa => 'Str', + default => 'ExtWidget', +); + +with 'ManaTools::Shared::GUI::EventRole'; + +use yui; +use ManaTools::Shared::GUI::ReplacePoint; + +#============================================================= + +=head2 new + +=head3 INPUT + + hash ref containing + name: a name for the widget to add event to the eventHandler + eventHandler: the parent that does eventHandlerRole + parentWidget: the parent widget + callback: optional parameter to execute a callback when an item has changed + + +=head3 DESCRIPTION + + new is inherited from Moose, to create a ExtWidget object + +=cut + +#============================================================= + +has '+eventType' => ( + required => 0, + default => $yui::YEvent::WidgetEvent, +); + +has 'parentWidget' => ( + is => 'ro', + isa => 'yui::YWidget', + required => 1, +); + +has 'callback' => ( + is => 'ro', + isa => 'Maybe[CodeRef]', + lazy => 1, + default => sub { + return undef; + } +); + +has 'items' => ( + is => 'ro', + isa => 'ArrayRef[ManaTools::Shared::GUI::ExtWidget::Item]', + lazy => 1, + init_arg => undef, + default => sub { + return []; + } +); + +has 'itemEventType' => ( + is => 'ro', + isa => 'Int', + init_arg => 0, + default => $yui::YEvent::MenuEvent, +); + +has 'replacepoint' => ( + is => 'rw', + isa => 'Maybe[ManaTools::Shared::GUI::ReplacePoint]', + init_arg => undef, + handles => ['addEvent', 'delEvent', 'getEvent', 'addWidget', 'delWidget', 'widget', 'addItem', 'delItem', 'item'], + default => sub { + return undef; + } +); + +has 'selector' => ( + is => 'ro', + does => 'yui::YWidget', + init_arg => undef, + lazy => 1, + builder => 'buildSelectionWidget', +); + +has 'lastItem' => ( + is => 'rw', + isa => 'Maybe[ManaTools::Shared::GUI::ExtWidget::Item]', + init_arg => undef, + default => sub { + return undef; + } +); + +has 'itemcollection' => ( + is => 'rw', + isa => 'yui::YItemCollection', + init_arg => undef, + default => sub { + return new yui::YItemCollection(); + } +); + +#============================================================= + +=head2 _buildSelectorWidget + +=head3 INPUT + + $self: this object + +=head3 OUTPUT + + ($selector, $parent): $selector is the YSelectionWidget; $parent is the replacepoint's parent + +=head3 DESCRIPTION + + builds the selection widget, needs to be overridden in subclasses + +=cut + +#============================================================= +sub _buildSelectorWidget { + my $self = shift; + my $parentWidget = shift; + return (undef, $parentWidget); +} + +#============================================================= + +=head2 buildSelectionWidget + +=head3 INPUT + + $self: this object + +=head3 DESCRIPTION + + builds the selection widget + +=cut + +#============================================================= +sub buildSelectionWidget { + my $self = shift; + + # this builds the actual widget in subclasses + my ($selectorWidget, $parentWidget) = $self->_buildSelectorWidget($self->parentWidget()); + + # create a replacepoint on the selectionWidget + $self->{replacepoint} = ManaTools::Shared::GUI::ReplacePoint->new(parentWidget => $parentWidget); + + # parentEventHandler must be set directly, because we don't add the + # eventHandler to a parentEventHandler, instead the events are processed + # through the selectorWidget's EventRole processEvent function + $self->{replacepoint}->parentEventHandler($self->{eventHandler}); + + # don't add any children right away + $self->{replacepoint}->finished(); + + return $selectorWidget; +} + +#============================================================= + +=head2 _selectorItem + +=head3 INPUT + + $self: this object + $yevent: yui::YEvent + +=head3 OUTPUT + + YItem: the selected item + +=head3 DESCRIPTION + + returns the items that is selected when an event fires + +=cut + +#============================================================= +sub _selectorItem { + my $self = shift; + my $yevent = shift; + return undef; +} + +#============================================================= + +=head2 processEvent + +=head3 INPUT + + $self: this object + $yevent: yui::YEvent + +=head3 DESCRIPTION + + handles the SelectorWidget events and executes callback if necessary + +=cut + +#============================================================= +sub processEvent { + my $self = shift; + my $yevent = shift; + my $replacepoint = $self->replacepoint(); + my $items = $self->items(); + + # call subevents + my $processed = $replacepoint->processEvents($yevent); + return $processed if $processed >= 0; + + # filter out the item event type... + return -1 if ($yevent->eventType() != $self->itemEventType()); + + # only items from *this* selected Item + my $yitem = $self->_selectorItem($yevent); + my $item = $self->findSelectorItem($yitem); + return -1 if !defined($item); + + # build the children + $self->buildSelectorItem($item); + + # execute callback if needed + my $callback = $self->callback(); + my $result = -1; + $result = $callback->($self, $yevent, $item->backend()) if defined($callback); + + # mark last item as this one + $self->lastItem($item); + + # return result of callback + return $result; +} + +#============================================================= + +=head2 addSelectorItem + +=head3 INPUT + + $self: this object + $label: a label for the YItem + $backendItem: a backendItem needed to identify and/or handle the event + $buildWidget: a CodeRef to rebuild the widget when required + +=head3 OUTPUT + + the created ManaTools::Shared::GUI::ExtWidget::Item + +=head3 DESCRIPTION + + Creates an item and adds it to the ExtWidget. Internally, it creates a + yui::YItem and adds it to the YItemCollection. If it's the first item, + mark it as the lastitem. + +=cut + +#============================================================= +sub addSelectorItem { + my $self = shift; + my $label = shift; + my $backendItem = shift; + my $buildWidget = shift; + my $items = $self->items(); + my $item = ManaTools::Shared::GUI::ExtWidget::Item->new(backend => $backendItem, builder => $buildWidget); + push @{$items}, $item; + $item->setLabel($label); + $item->addToCollection($self->itemcollection()); + if (scalar(@{$items}) == 1) { + $self->lastItem($item); + } + return $item; +} + +#============================================================= + +=head2 findSelectorItem + +=head3 INPUT + + $self: this object + $yitem: the YItem to be found + +=head3 DESCRIPTION + + returns a ManaTools::Shared::GUI::ExtWidget::Item that has the YItem + +=cut + +#============================================================= +sub findSelectorItem { + my $self = shift; + my $yitem = shift; + # loop all the items + for my $i (@{$self->items()}) { + return $i if ($i->equals($yitem)); + } + return undef; +} + +#============================================================= + +=head2 buildSelectorItem + +=head3 INPUT + + $self: this object + $item: the item to be built (child widgets from this SelectorWidget will be recreated inside the associated replacepoint) + +=head3 DESCRIPTION + + builds an item on the internal replace point + +=cut + +#============================================================= +sub buildSelectorItem { + my $self = shift; + my $item = shift; + my $replacepoint = $self->replacepoint(); + my $container = $replacepoint->container(); + + # clear out any previous children/events + $replacepoint->clear(); + + # build item's widgetbuilder + my $builder = $item->builder(); + $builder->($self, $container, $item->backend()) if (defined $builder); + + # finished with replacepoint children + $replacepoint->finished(); +} + +#============================================================= + +=head2 clearSelectorItems + +=head3 INPUT + + $self: this object + +=head3 DESCRIPTION + + clears the selectorWidget of items to prepare for re-adding new items, call finishedSelectorItems() afterwards + +=cut + +#============================================================= +sub clearSelectorItems { + my $self = shift; + my $items = $self->items(); + + # remove all events before deleting all items + $self->clearEvents(); + + for (my $i = 0; $i < scalar(@{$items}); $i = $i + 1) { + delete $items->[$i]; + } +} + +#============================================================= + +=head2 finishedSelectorItems + +=head3 INPUT + + $self: this object + +=head3 DESCRIPTION + + finalizes the items on the ExtWidget + +=cut + +#============================================================= +sub finishedSelectorItems { + my $self = shift; + my $selector = $self->selector(); + + # remove all Items before adding + $selector->deleteAllItems(); + + # add items from collection + $selector->addItems($self->itemcollection); + + # set last item to know the active item + my $item = $self->lastItem(); + + # show the current one if there is one + $self->buildSelectorItem($item) if defined($item); + + # create a new itemcollection for adding new items + $self->itemcollection(new yui::YItemCollection()); +} + +#============================================================= + +no Moose; +__PACKAGE__->meta->make_immutable; + + +1; + +#============================================================= + +package ManaTools::Shared::GUI::ExtWidget::Item; + +use Moose; +use diagnostics; +use utf8; + +use yui; + +has 'builder' => ( + is => 'ro', + isa => 'Maybe[CodeRef]', + lazy => 1, + default => sub { + return undef; + } +); + +has 'item' => ( + is => 'ro', + isa => 'yui::YItem', + init_arg => undef, + default => sub { + return new yui::YItem('', 0); + } +); + +has 'backend' => ( + is => 'rw', + isa => 'Maybe[Ref]', + lazy => 1, + default => sub { + return undef; + } +); + +#============================================================= + +sub setLabel { + my $self = shift; + my $label = shift; + my $yitem = $self->item(); + $yitem->setLabel($label); +} + +sub equals { + my $self = shift; + my $item = shift; + return ($self->item() == $item); +} + +sub addToCollection { + my $self = shift; + my $collection = shift; + my $yitem = $self->item(); + $yitem->DISOWN(); + $collection->push($yitem); +} + +#============================================================= + +no Moose; +__PACKAGE__->meta->make_immutable; + +1; -- cgit v1.2.1