Games-Axmud

 view release on metacpan or  search on metacpan

lib/Games/Axmud/OtherWin.pm  view on Meta::CPAN


            # Standard IVs for 'free' windows

            # The window's default size, in pixels
            widthPixels                 => $widthPixels,
            heightPixels                => $heightPixels,
            # Default border/item spacing sizes used in the window, in pixels
            borderPixels                => $axmud::CLIENT->constFreeBorderPixels,
            spacingPixels               => $axmud::CLIENT->constFreeSpacingPixels,

            # A string to use as the window title. If 'undef', a generic title is used
            title                       => $axmud::SCRIPT . ' information',
            # Hash containing any number of key-value pairs needed for this particular 'config'
            #   window; for example, for example, GA::PrefWin::TaskStart uses it to specify a task
            #   name and type. Set to an empty hash if not required
            configHash                  => {%configHash},

            # IVs for this type of window

            # Widgets
            notebook                    => undef,       # Gtk3::Notebook
            button                      => undef,       # Gtk3::Button
        };

        # Bless the object into existence
        bless $self, $class;

        return $self;
    }

    ##################
    # Methods

    # Standard window object functions

#   sub winSetup {}         # Inherited from GA::Generic::FreeWin

    sub winEnable {

        # Called by GA::Generic::Win->createFreeWin, after the call to $self->winSetup
        # After the Gtk3::Window has been setup and moved into position, makes it visible
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 on success

        my ($self, $check) = @_;

        # Local variables
        my $firstTab;

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->winEnable', @_);
        }

        # Make the window appear on the desktop
        $self->winShowAll($self->_objClass . '->winEnable');
        $self->ivPoke('enabledFlag', TRUE);

        # This type of window is unique (only one can be open at any time); inform the GA::Client
        #   it has opened
        $axmud::CLIENT->set_aboutWin($self);

        # If a tab to show on startup was specified, open it
        $firstTab = $self->ivShow('configHash', 'first_tab');
        if (defined $firstTab) {

            # (Window is open at the 'about' tab by default, so we don't have to check that
            #   $firstTab is set to 'about')
            if ($firstTab eq 'credits') {
                $self->notebook->set_current_page(1);
            } elsif ($firstTab eq 'help') {
                $self->notebook->set_current_page(2);
            } elsif ($firstTab eq 'peek') {
                $self->notebook->set_current_page(3);
            } elsif ($firstTab eq 'changes') {
                $self->notebook->set_current_page(4);
            } elsif ($firstTab eq 'install') {
                $self->notebook->set_current_page(5);
            } elsif ($firstTab eq 'license') {
                $self->notebook->set_current_page(6);
            } elsif ($firstTab eq 'license_2') {
                $self->notebook->set_current_page(7);
            }
        }

        return 1;
    }

#   sub winDesengage {}     # Inherited from GA::Generic::FreeWin

    sub winDestroy {

        # Can be called by anything
        # Updates IVs
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments, if the window can't be destroyed or if it has already
        #       been destroyed
        #   1 on success

        my ($self, $check) = @_;

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->winDestroy', @_);
        }

        if (! $self->winBox) {

            # Window already destroyed in a previous call to this function
            return undef;
        }

        # Close any 'free' windows for which this window is a parent
        foreach my $winObj ($self->ivValues('childFreeWinHash')) {

            $winObj->winDestroy();
        }

        # Destroy the Gtk3::Window
        eval { $self->winBox->destroy(); };
        if ($@) {

            # Window can't be destroyed
            return undef;

        } else {

            $self->ivUndef('winWidget');
            $self->ivUndef('winBox');
        }

        # Inform the owner and the desktop object of this 'free' window's demise
        $axmud::CLIENT->desktopObj->del_freeWin($self);
        if ($self->owner) {

            $self->owner->del_childFreeWin($self);
        }

        # This type of window is unique (only one can be open at any time); inform the GA::Client
        #   it has closed
        $axmud::CLIENT->set_aboutWin();

        return 1;
    }

#   sub winShowAll {}       # Inherited from GA::Generic::Win

    sub drawWidgets {

        # Called by $self->winSetup
        # Sets up the About window with its standard widgets
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $check) = @_;

        # Local variables
        my (
            $file, $fileHandle,
            @aboutList, @helpList, @peekList, @changesList, @installList, @licenseList,
            @license2List,
        );

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->drawWidgets', @_);
        }

        # Create a packing box
        my $packingBox = Gtk3::VBox->new(FALSE, 0);
        $self->winBox->add($packingBox);
        $packingBox->set_border_width(0);

        # Create an image on the left
        my $hBox = Gtk3::HBox->new(FALSE, 0);
        $packingBox->pack_start($hBox, TRUE, TRUE, 0);

        my $vBox = Gtk3::VBox->new(FALSE, 0);
        $hBox->pack_start($vBox, FALSE, FALSE, 0);

        my $frame = Gtk3::Frame->new(undef);
        $vBox->pack_start($frame, FALSE, FALSE, 0);
        $frame->set_size_request(64, 64);
        $frame->set_shadow_type($axmud::CLIENT->constShadowType);

        my $image = Gtk3::Image->new_from_file($axmud::CLIENT->getDialogueIcon());
        $frame->add($image);

lib/Games/Axmud/OtherWin.pm  view on Meta::CPAN

        }

        # Add the 'license' tab to the notebook
        $self->addTab($notebook, '_LGPL License', FALSE, @license2List);

        # Update IVs
        $self->ivPoke('packingBox', $packingBox);
        $self->ivPoke('notebook', $notebook);
        $self->ivPoke('button', $button);

        return 1;
    }

#   sub redrawWidgets {}    # Inherited from GA::Generic::Win

    # ->signal_connects

    # Other functions

    sub addTab {

        # Called by $self->drawWidgets
        # Adds a tab to the About window's notebook
        #
        # Expected arguments
        #   $notebook       - The Gtk3::Notebook to which the tab must be added
        #   $label          - The tab's label text
        #
        # Optional arguments
        #   $newlineFlag    - TRUE if a newline character should be added to every line in @list,
        #                       FALSE if not (because the contents of @list were loaded from a file
        #                       and already contain newline characters)
        #   @list           - A list of lines to add to the Gtk3::TextView (can be an empty list)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $notebook, $label, $newlineFlag, @list) = @_;

        # Check for improper arguments
        if (! defined $notebook || ! defined $label) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->addTab', @_);
        }

        # Add the tab
        my $tab = Gtk3::Label->new_with_mnemonic($label);

        my $scroller = Gtk3::ScrolledWindow->new();
        $notebook->append_page($scroller, $tab);
        $scroller->set_policy('automatic', 'automatic');

        # Create a textview using the system's preferred colours and fonts
        my $textView = Gtk3::TextView->new();
        $scroller->add_with_viewport($textView);
        my $buffer = Gtk3::TextBuffer->new();
        $textView->set_buffer($buffer);
        $textView->set_editable(FALSE);
        $textView->set_cursor_visible(FALSE);
        $axmud::CLIENT->desktopObj->setTextViewStyle($self->winType, $textView);

        # Fill the textview
        if (! $newlineFlag) {
            $buffer->set_text(join("", @list));
        } else {
            $buffer->set_text(join("\n", @list));
        }

        return 1
    }

    ##################
    # Accessors - set

    ##################
    # Accessors - get

    sub notebook
        { $_[0]->{notebook} }
    sub button
        { $_[0]->{button} }
}

{ package Games::Axmud::OtherWin::Connect;

    use strict;
    use warnings;
#   use diagnostics;

    use Glib qw(TRUE FALSE);

    our @ISA = qw(
        Games::Axmud::Generic::OtherWin Games::Axmud::Generic::FreeWin Games::Axmud::Generic::Win
        Games::Axmud
    );

    ##################
    # Constructors

    sub new {

        # Called by GA::Generic::Win->createFreeWin
        # Creates a new instance of the Connections window, which displays a list of world
        #   profiles and invites the user to connect to one of them
        #
        # Expected arguments
        #   $number         - Unique number for this window object
        #   $workspaceObj   - The GA::Obj::Workspace handling the workspace in which this window
        #                       should be created
        #   $owner          - The owner; a 'grid' window object (but not an 'external' window) or a
        #                       'free' window object. When this window opens/closes, the owner is
        #                       informed via calls to its ->add_childFreeWin / ->del_childFreeWin
        #                       functions
        #
        # Optional arguments
        #   $session        - The GA::Session from which this function was called. 'undef' if the
        #                       calling function didn't specify a session and $owner's ->session IV
        #                       is also 'undef'
        #   $title          - Ignored if set (all 'other' windows define their own title)
        #   $editObj        - Ignored if set

lib/Games/Axmud/OtherWin.pm  view on Meta::CPAN

            worldObj                    => undef,
            # The GA::Obj::MiniWorld that stores the changes being made by the user to the grid
            #   widgets. For an existing world profile, the mini-world exists in $self->worldHash;
            #   otherwise it's a temporary GA::Obj::MiniWorld that might (or might not) be stored as
            #   a world profile, at some point
            miniWorldObj                => undef,
            # Flag set to TRUE when $self->resetGridWidgets or $self->updateGridWidgets are
            #   changing the value displayed in the grid widgets; this stops the mini-world object
            #   from being modified (the mini-world object should only store changes made by the
            #   user)
            updateFlag                  => undef,

            # First line displayed in the Gtk3::TreeView
            newWorldString              => '<b><i>Create new world</i></b>',
            # First line displayed in the Gtk3::ComboBox
            noCharString                => '<no character>',
            # First line displayed in 'information' section
            noWebsiteString             => 'Websites: (no websites)',
            # Second line
            noConnectString             => 'Connections: 0',

            # Current search terms. If 'undef', all worlds are listed; otherwise, only those worlds
            #   matching one or both search terms are listed
            searchRegex                 => undef,
            searchLanguage              => undef,
        };

        # Bless the object into existence
        bless $self, $class;

        return $self;
    }

    ##################
    # Methods

    # Standard window object functions

#   sub winSetup {}         # Inherited from GA::Generic::FreeWin

    sub winEnable {

        # Called by GA::Generic::Win->createFreeWin, after the call to $self->winSetup
        # After the Gtk3::Window has been setup and moved into position, makes it visible
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 on success

        my ($self, $check) = @_;

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->winEnable', @_);
        }

        # Make the window appear on the desktop
        $self->winShowAll($self->_objClass . '->winEnable');
        $self->ivPoke('enabledFlag', TRUE);

        # Fill the treeview
        $self->resetTreeView($self->treeView);

        # The 'connect' button should have focus
        $self->connectButton->grab_focus();

        # This type of window is unique (only one can be open at any time); inform the GA::Client
        #   it has opened
        $axmud::CLIENT->set_connectWin($self);

        return 1;
    }

#   sub winDesengage {}     # Inherited from GA::Generic::FreeWin

    sub winDestroy {

        # Can be called by anything
        # Updates IVs
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments, if the window can't be destroyed or if it has already
        #       been destroyed
        #   1 on success

        my ($self, $check) = @_;

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->winDestroy', @_);
        }

        if (! $self->winBox) {

            # Window already destroyed in a previous call to this function
            return undef;
        }

        # Close any 'free' windows for which this window is a parent
        foreach my $winObj ($self->ivValues('childFreeWinHash')) {

            $winObj->winDestroy();
        }

        # Destroy the Gtk3::Window
        eval { $self->winBox->destroy(); };
        if ($@) {

            # Window can't be destroyed
            return undef;

        } else {

            $self->ivUndef('winWidget');
            $self->ivUndef('winBox');
        }

        # Inform the owner and the desktop object of this 'free' window's demise
        $axmud::CLIENT->desktopObj->del_freeWin($self);
        if ($self->owner) {

            $self->owner->del_childFreeWin($self);
        }

        # This type of window is unique (only one can be open at any time); inform the GA::Client
        #   it has closed
        $axmud::CLIENT->set_connectWin();

        return 1;
    }

#   sub winShowAll {}       # Inherited from GA::Generic::Win

    sub drawWidgets {

        # Called by $self->winSetup
        # Sets up the Connections window with its standard widgets
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $check) = @_;

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->drawWidgets', @_);
        }

        # Create a packing box
        my $packingBox = Gtk3::VBox->new(FALSE, $self->spacingPixels);
        $self->winBox->add($packingBox);
        $packingBox->set_border_width(0);

        # Update IVs immediately, for the benefit of any functions called
        $self->ivPoke('packingBox', $packingBox);

        # Create a horizontal pane to divide the window in two, with an image/treeview on the
        #   left, and everything else on the right
        my $hPaned = Gtk3::HPaned->new();
        $packingBox->pack_start($hPaned, TRUE, TRUE, 0);
        $hPaned->set_wide_handle(TRUE);

        # On the left, create a vertical packing box, with an image at the top, a strip of buttons
        #   in the middle and a treeview at the bottom
        my $vBox = Gtk3::VBox->new(FALSE, $self->spacingPixels);
        $hPaned->add1($vBox);

        # Create a frame containing an image
        my $frame = Gtk3::Frame->new(undef);
        $vBox->pack_start($frame, FALSE, FALSE, 0);
        $frame->set_shadow_type($axmud::CLIENT->constShadowType);

        my $image = Gtk3::Image->new_from_file($self->defaultIcon);
        $frame->add($image);

lib/Games/Axmud/OtherWin.pm  view on Meta::CPAN

            $treeSelection->select_iter($matchPointer);
        } else {
            $treeSelection->select_iter($model->get_iter_first());
        }

        return 1;
    }

    sub resetGridWidgets {

        # Called by $self->selectWorldCallback when the user clicks on the 'Create new world' line
        #   in the treeview
        # Resets IVs and resets the widgets in the window's Gtk3::Grid, ready for the user to enter
        #   details for a new world
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $check) = @_;

        # Check for improper arguments
        if (defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->resetGridWidgets', @_);
        }

        # Reset the IV which stores the currently displayed world
        $self->ivUndef('worldObj');
        # Create a temporary GA::Obj::MiniWorld to store the changes
        $self->ivPoke('miniWorldObj', Games::Axmud::Obj::MiniWorld->new());
        # Set a flag to TRUE to stop the mini-world object being updated, as we change the values
        #   displayed in the grid's widgets
        $self->ivPoke('updateFlag', TRUE);

        # Reset the 'create world' button's label (it gets modified by $self->updateGridWidgets)
        $self->createWorldButton->set_label('Create world');
        $self->createWorldButton->set_tooltip_text('Create a world profile');

        # Reset the grid widgets
        $self->entry->set_text('');
        $self->entry2->set_text('');
        $self->entry3->set_text('');
        $self->checkButton->set_active(FALSE);

        $self->radioButton->set_active(TRUE);

        my $comboBox = $self->resetComboBox(TRUE);
        $self->ivPoke('comboBox', $comboBox);

        $self->websiteLabel->set_text($self->noWebsiteString);
        $self->connectionLabel->set_text($self->noConnectString);
        $self->descripBuffer->set_text('');

        # (These calls eliminate flashing when the screenshot is updated rapidly, for example when
        #   the user scrolls through the list of worlds)
        $self->winShowAll($self->_objClass . '->resetGridWidgets');
        $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->resetGridWidgets');

        # Update the world screenshot, using the default logo
        # If a logo for this world exists, use it; otherwise use the default logo
        my $image = Gtk3::Image->new_from_file($self->defaultIcon);
        $axmud::CLIENT->desktopObj->removeWidget($self->frame, $self->image);
        $self->frame->add($image);
        $self->ivPoke('image', $image);

        # (A repeat of those calls eliminates it entirely)
        $self->winShowAll($self->_objClass . '->resetGridWidgets');
        $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->resetGridWidgets');

        # The entry box for the world's name must be made editable
        $self->entry->set_editable(TRUE);
        # The 'pwd' / 'account' buttons start insensitive, but can be sensitised if the user selects
        #   a character
        $self->editPwdButton->set_sensitive(FALSE);
        $self->editAccButton->set_sensitive(FALSE);
        # The 'reset world' button must be insensitive when there isn't a corresponding world
        #   profile
        $self->resetWorldButton->set_sensitive(FALSE);
        # The 'add', 'connect offline' and 'connect to world' buttons must be insensitive until the
        #   user at least types something in the 'host address' entry box
        $self->addCharButton->set_sensitive(FALSE);
        $self->offlineButton->set_sensitive(FALSE);
        $self->connectButton->set_sensitive(FALSE);

        # Update complete
        $self->ivPoke('updateFlag', FALSE);

        # The call to ->show_all() causes the image to appear
        $self->winShowAll($self->_objClass . '->resetGridWidgets');

        return 1;
    }

    sub updateGridWidgets {

        # Called by $self->selectWorldCallback when the user clicks on a line in the treeview
        #   corresponding to a world profile
        # Also called by $self->testModeLoginCallback
        #
        # Updates IVs and updates the widgets in the window's Gtk3::Grid, so they show details about
        #   the world
        #
        # Expected arguments
        #   $worldObj   - The GA::Profile::World object corresponding to the clicked line
        #   $line       - The text of the treeview line that the user clicked
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $worldObj, $line, $check) = @_;

        # Local variables
        my (
            $displayFlag, $modName, $host, $port, $website, $connections, $logoPath,
            @charList,
        );

        # Check for improper arguments
        if (! defined $worldObj || ! defined $line || defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->updateGridWidgets', @_);
        }

        # Decide which list should be displayed. Default display mode is 'undef', representing
        #   a list of world profiles
        if ($self->otherWorldButton->get_active()) {

lib/Games/Axmud/OtherWin.pm  view on Meta::CPAN

        if (! $displayFlag) {

            $connections = 'Connections: ' . $worldObj->numberConnects;
            if ($worldObj->lastConnectDate && $worldObj->lastConnectTime) {

                $connections .= ', most recent: ' . $worldObj->lastConnectDate . ' at '
                                    . $worldObj->lastConnectTime;
            }

        } else {

            $connections = 'Connections: (n/a)';
        }

        $self->connectionLabel->set_markup($connections);

        # (Descrip)
        if ($self->miniWorldObj->ivExists('propHash', 'descrip')) {

            $self->descripBuffer->set_text($self->miniWorldObj->ivShow('propHash', 'descrip'));

        } elsif (! $displayFlag && $worldObj->worldDescrip) {

            $self->descripBuffer->set_text($worldObj->worldDescrip);

        } elsif (! $displayFlag) {

            $self->descripBuffer->set_text('');

        } else {

            $self->descripBuffer->set_text(
                'This world profile won\'t be created until you click one of the buttons below',
            )
        }

        # Move the 'descrip' textview's scrollbar to the top, in case the user has been browsing
        #   another world's description
        $self->descripTextView->scroll_to_iter(
            $self->descripBuffer->get_start_iter(),
            0.0,
            TRUE,
            0,
            1,
        );

        # If a logo for this world exists, use it; otherwise use the default logo
        if (! $displayFlag) {

            $logoPath = $axmud::DATA_DIR . '/logos/' . $worldObj->name . '.png';
        }

        if ($displayFlag || ! (-e $logoPath)) {

            $logoPath = $axmud::CLIENT->getClientLogo($worldObj->adultFlag);
        }

        # (These calls eliminate flashing when the screenshot is updated rapidly, for example when
        #   the user scrolls through the list of worlds)
        $self->winShowAll($self->_objClass . '->updateGridWidgets');
        $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->updateGridWidgets');

        # Update the world screenshot
        my $image = Gtk3::Image->new_from_file($logoPath);
        $axmud::CLIENT->desktopObj->removeWidget($self->frame, $self->image);
        $self->frame->add($image);
        $self->ivPoke('image', $image);

        # (A repeat of those calls eliminates it entirely)
        $self->winShowAll($self->_objClass . '->updateGridWidgets');
        $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->updateGridWidgets');

        # The entry box for the world's name must not be changed
        $self->entry->set_editable(FALSE);
        # The 'add' button must be sensitive
        $self->addCharButton->set_sensitive(TRUE);
        # The 'pwd'/'account' buttons start sensitised if there's a selected character, but
        #   desensitised if not
        if ($self->miniWorldObj->selectChar) {

            $self->editPwdButton->set_sensitive(TRUE);
            $self->editAccButton->set_sensitive(TRUE);

        } else {

            $self->editPwdButton->set_sensitive(FALSE);
            $self->editAccButton->set_sensitive(FALSE);
        }

        # The 'reset world' button must be sensitive
        $self->resetWorldButton->set_sensitive(TRUE);
        # If the world profile doesn't have a ->dns, ->ipv4 or ->ipv6 value, Axmud obviously won't
        #   be able to connect to the world. Make the connect buttons desensitised until the user
        #   types something in the 'host address' entry box
        if (! $host) {

            $self->offlineButton->set_sensitive(FALSE);
            $self->connectButton->set_sensitive(FALSE);

        } else {

            # Otherwise, these two buttons start sensitised
            $self->offlineButton->set_sensitive(TRUE);
            $self->connectButton->set_sensitive(TRUE);
        }

        # Update complete
        $self->ivPoke('updateFlag', FALSE);

        # The call to ->show_all() causes the image to appear
        $self->winShowAll($self->_objClass . '->updateGridWidgets');

        return 1;
    }

    sub resetComboBox {

        # Called by $self->createGridWidgets, ->resetGridWidgets and ->updateGridWidgets
        # Not sure how to empty a Gtk3::ComboBox, so we'll just destroy the old one, and replace it
        #   with a new one
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Optional arguments
        #   $destroyFlag    - If TRUE, a combobox already exists, and must be deleted. If FALSE (or
        #                       'undef'), the combobox is being drawn for the first time
        #   @charList       - A list of characters to display in the combobpx. If empty, the
        #                       combobox will contain only the '<no character>' string. If not
        #                       empty, the '<no character>' is added to @charList as the first item
        #
        # Return values
        #   'undef' on improper arguments
        #   Otherwise returns the Gtk3::ComboBox created

        my ($self, $destroyFlag, @charList) = @_;

        # Local variables
        my ($count, $index);

        # (No improper arguments to check)

        # If a Gtk3::ComboBox already exists, destroy it
        if ($destroyFlag) {

            $axmud::CLIENT->desktopObj->removeWidget($self->grid, $self->comboBox);
       }

        # Create a new combobox
        unshift (@charList, $self->noCharString);
        my $comboBox = $self->addComboBox($self->grid, undef, \@charList, undef,
            4, 8, 7, 8);

        # If the current mini-world object specifies a character, make that the combobox's active
        #   item. Otherwise, make the '<no character>' string the active item
        $index = 0;
        if ($self->miniWorldObj && $self->miniWorldObj->selectChar) {

            $count = -1;
            OUTER: foreach my $string (@charList) {

                $count++;

                if ($string eq $self->miniWorldObj->selectChar) {

                    $index = $count;
                    last OUTER;
                }
            }
        }

        $comboBox->set_active($index);
        # Also, the 'pwd'/'account' buttons should only be sensitised when there's a selected
        #   character
        if ($self->miniWorldObj) {

            if ($self->miniWorldObj->selectChar) {

                $self->editPwdButton->set_sensitive(TRUE);
                $self->editAccButton->set_sensitive(TRUE);

            } else {

                $self->editPwdButton->set_sensitive(FALSE);
                $self->editAccButton->set_sensitive(FALSE);
            }
        }

        # Now we can add the combobox's ->signal_connect, which updates the mini-world object when
        #   a character is selected
        $comboBox->signal_connect('changed' => sub {

            my $char = $comboBox->get_active_text();

            if ($char eq $self->noCharString) {

                $self->miniWorldObj->ivUndef('selectChar');
                # When no character is selected, the 'pwd'/'account' buttons must be desensitised
                $self->editPwdButton->set_sensitive(FALSE);
                $self->editAccButton->set_sensitive(FALSE);

            } else {

                $self->miniWorldObj->ivPoke('selectChar', $char);
                # When no character is selected, the 'pwd'/'account' buttons must be desensitised
                $self->editPwdButton->set_sensitive(TRUE);

lib/Games/Axmud/OtherWin.pm  view on Meta::CPAN

            #   area)
            packingBox                  => undef,       # Gtk3::VBox

            # Standard IVs for 'free' windows

            # The window's default size, in pixels
            widthPixels                 => 600,
            heightPixels                => 300,
            # Default border/item spacing sizes used in the window, in pixels
            borderPixels                => $axmud::CLIENT->constFreeBorderPixels,
            spacingPixels               => $axmud::CLIENT->constFreeSpacingPixels,

            # A string to use as the window title. If 'undef', a generic title is used
            title                       => $axmud::SCRIPT . ' client console',
            # Hash containing any number of key-value pairs needed for this particular 'config'
            #   window; for example, for example, GA::PrefWin::TaskStart uses it to specify a task
            #   name and type. Set to an empty hash if not required
            configHash                  => {%configHash},

            # IVs for this window
            textView                    => undef,       # Gtk3::TextView
            buffer                      => undef,       # Gtk3::TextBuffer
        };

        # Bless the object into existence
        bless $self, $class;

        return $self;
    }

    ##################
    # Methods

    # Standard window object functions

#   sub winSetup {}         # Inherited from GA::Generic::FreeWin

    sub winEnable {

        # Called by GA::Generic::Win->createFreeWin, after the call to $self->winSetup
        # After the Gtk3::Window has been setup and moved into position, makes it visible
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 on success

        my ($self, $check) = @_;

        # Local variables
        my @list;

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->winEnable', @_);
        }

        # Make the window appear on the desktop
        $self->winShowAll($self->_objClass . '->winEnable');
        $self->ivPoke('enabledFlag', TRUE);

        # This type of window is unique (only one can be open at any time); inform the GA::Client
        #   it has opened
        $axmud::CLIENT->set_consoleWin($self);

        # If any system messages have been stored, we can display them now
        @list = $axmud::CLIENT->systemMsgList;
        if (@list) {

            do {

                my ($type, $msg);

                $type = shift @list;
                $msg = shift @list;

                $self->update($type, $msg);

            } until (! @list);
        }

        # Each system message is displayed here only once
        $axmud::CLIENT->reset_systemMsg();

        return 1;
    }

#   sub winDesengage {}     # Inherited from GA::Generic::FreeWin

    sub winDestroy {

        # Can be called by anything
        # Updates IVs
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments, if the window can't be destroyed or if it has already
        #       been destroyed
        #   1 on success

        my ($self, $check) = @_;

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->winDestroy', @_);
        }

        if (! $self->winBox) {

            # Window already destroyed in a previous call to this function
            return undef;
        }

        # Close any 'free' windows for which this window is a parent
        foreach my $winObj ($self->ivValues('childFreeWinHash')) {

            $winObj->winDestroy();
        }

        # Destroy the Gtk3::Window
        eval { $self->winBox->destroy(); };
        if ($@) {

            # Window can't be destroyed
            return undef;

        } else {

            $self->ivUndef('winWidget');
            $self->ivUndef('winBox');
        }

        # Inform the owner and the desktop object of this 'free' window's demise
        $axmud::CLIENT->desktopObj->del_freeWin($self);
        if ($self->owner) {

            $self->owner->del_childFreeWin($self);
        }

        # This type of window is unique (only one can be open at any time); inform the GA::Client
        #   it has closed
        $axmud::CLIENT->set_consoleWin();

        return 1;
    }

#   sub winShowAll {}       # Inherited from GA::Generic::Win

    sub drawWidgets {

        # Called by $self->winSetup
        # Sets up the Client Console window with its standard widgets
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $check) = @_;

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->drawWidgets', @_);
        }

        # Create a packing box
        my $packingBox = Gtk3::VBox->new(FALSE, 0);
        $self->winBox->add($packingBox);
        $packingBox->set_border_width(0);

        # Create a textview
        my $scroller = Gtk3::ScrolledWindow->new(undef, undef);
        $packingBox->pack_start($scroller, TRUE, TRUE, 0);
        $scroller->set_shadow_type($axmud::CLIENT->constShadowType);
        $scroller->set_policy('automatic', 'automatic');
        $scroller->set_border_width(5);

        # Use a textview with default colours/fonts
        my $textView = Gtk3::TextView->new();
        $scroller->add($textView);
        my $buffer = Gtk3::TextBuffer->new();
        $textView->set_buffer($buffer);
        $textView->set_editable(FALSE);
        $textView->set_cursor_visible(FALSE);
        $textView->set_can_focus(FALSE);
        $textView->set_wrap_mode('word-char');      # Wrap words if possible, characters if not
        $axmud::CLIENT->desktopObj->setTextViewStyle('main', $textView);

        # Create a mark at the end of the buffer, with right gravity, so that whenever text is
        #   inserted, we can scroll to that mark (and the mark stays at the end)
        my $endMark = $buffer->create_mark('end', $buffer->get_end_iter(), FALSE);

        # Update IVs
        $self->ivPoke('packingBox', $packingBox);
        $self->ivPoke('textView', $textView);
        $self->ivPoke('buffer', $buffer);

        # Create some colour tags, so that system messages can be displayed in their usual colours
        $self->createColourTags();

        return 1;
    }

#   sub redrawWidgets {}    # Inherited from GA::Generic::Win

    # ->signal_connects

    # Other functions

    sub createColourTags {

        # Called by $self->drawWidgets
        # Create some Gtk3::TextTags, so that system messages can be shown in their usual colours
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $check) = @_;

        # Check for improper arguments
        if (defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->update', @_);
        }

        $self->buffer->create_tag(
            'system',
            'foreground'
                => $axmud::CLIENT->returnRGBColour($axmud::CLIENT->customShowSystemTextColour),
        );

        $self->buffer->create_tag(
            'error',
            'foreground'
                => $axmud::CLIENT->returnRGBColour($axmud::CLIENT->customShowErrorColour),
        );

        $self->buffer->create_tag(
            'warning',
            'foreground'
                => $axmud::CLIENT->returnRGBColour($axmud::CLIENT->customShowWarningColour),
        );

lib/Games/Axmud/OtherWin.pm  view on Meta::CPAN

        return $self;
    }

    ##################
    # Methods

    # Standard window object functions

#   sub winSetup {}         # Inherited from GA::Generic::FreeWin

#   sub winEnable {}        # Inherited from GA::Generic::FreeWin

#   sub winDesengage {}     # Inherited from GA::Generic::FreeWin

#   sub winDestroy {}       # Inherited from GA::Generic::FreeWin

#   sub winShowAll {}       # Inherited from GA::Generic::Win

    sub drawWidgets {

        # Called by $self->winSetup
        # Sets up the Gtk3::Window by drawing the window's widgets
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $check) = @_;

        # Local variables
        my $title;

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->drawWidgets', @_);
        }

        # Create a packing box
        my $packingBox = Gtk3::VBox->new(FALSE, 0);
        $self->winBox->add($packingBox);
        $packingBox->set_border_width(0);

        # At the top, create a textview
        my $scroller = Gtk3::ScrolledWindow->new(undef, undef);
        $packingBox->pack_start($scroller, TRUE, TRUE, 0);
        $scroller->set_shadow_type($axmud::CLIENT->constShadowType);
        $scroller->set_policy('automatic', 'automatic');
        $scroller->set_border_width(0);

        # Create a textview with default colours/fonts
        my $textView = Gtk3::TextView->new();
        $scroller->add($textView);
        my $buffer = Gtk3::TextBuffer->new();
        $textView->set_buffer($buffer);
        $textView->set_editable(TRUE);
        $textView->set_cursor_visible(TRUE);
        $axmud::CLIENT->desktopObj->setTextViewStyle($self->winType, $textView);
        # ->signal_connect appears below

        # Set the initial contents of the textview
        $buffer->set_text($self->ivShow('configHash', 'mcp_content'));

        # At the bottom, create a button strip in a horizontal packing box
        my $hBox = Gtk3::HBox->new(FALSE, 0);
        $packingBox->pack_end($hBox, FALSE, FALSE, $self->spacingPixels);

        # Create some buttons
        my $cancelButton = Gtk3::Button->new('Cancel');
        $hBox->pack_start($cancelButton, TRUE, TRUE, $self->borderPixels);
        $cancelButton->get_child->set_width_chars(10);
        $cancelButton->signal_connect('clicked' => sub {

            $self->winDestroy();
        });
        $cancelButton->set_sensitive(FALSE);

        my $saveButton = Gtk3::Button->new('Save');
        $hBox->pack_start($saveButton, TRUE, TRUE, $self->borderPixels);
        $saveButton->get_child->set_width_chars(10);
        $saveButton->signal_connect('clicked' => sub {

            $self->doSave($buffer);
            $cancelButton->set_sensitive(FALSE);
        });

        my $saveCloseButton = Gtk3::Button->new('Save and close');
        $hBox->pack_end($saveCloseButton, TRUE, TRUE, $self->borderPixels);
        $saveCloseButton->get_child->set_width_chars(15);
        $saveCloseButton->signal_connect('clicked' => sub {

            $self->doSave($buffer);
            $self->winDestroy();
        });

        # ->signal_connect from above
        $buffer->signal_connect('changed' => sub {

            $cancelButton->set_sensitive(TRUE);
        });

        # Update IVs (not worth storing widgets other than the main packing box)
        $self->ivPoke('packingBox', $packingBox);

        return 1;
    }

#   sub redrawWidgets {}    # Inherited from GA::Generic::Win

    # ->signal_connects

    # Other functions

    sub doSave {

        # Called by $self->drawWidgets
        # 'Saves' the contents of the window by sending an MSP message to the world
        #
        # Expected arguments
        #   $buffer     - The window's Gtk3::Buffer
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $buffer, $check) = @_;

        # Local variables
        my (
            $text,
            @list,
        );

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->doSave', @_);
        }

        $text = $axmud::CLIENT->desktopObj->bufferGetText($buffer);
        @list = split(/\n/, $text);

        # Send the MCP message (one or more multiline parts)
        $self->session->mcpSendMultiLine(
            'dns-org-mud-moo-simpleedit-set',
                'reference',
                $self->ivShow('configHash', 'mcp_reference'),
                'content',
                \@list,
                'type',
                $self->ivShow('configHash', 'mcp_type'),
        );

        return 1;
    }

    ##################
    # Accessors - set

    ##################
    # Accessors - get
}

{ package Games::Axmud::OtherWin::PatternTest;

    use strict;
    use warnings;
#   use diagnostics;

    use Glib qw(TRUE FALSE);

    our @ISA = qw(
        Games::Axmud::Generic::OtherWin Games::Axmud::Generic::FreeWin Games::Axmud::Generic::Win
        Games::Axmud
    );

    ##################
    # Constructors

    sub new {

        # Called by GA::Generic::Win->createFreeWin
        # Creates a new instance of the Pattern Test window, which allows the user to to test
        #   patterns (regexes) on the fly
        #
        # Expected arguments
        #   $number         - Unique number for this window object
        #   $workspaceObj   - The GA::Obj::Workspace handling the workspace in which this window
        #                       should be created
        #   $owner          - The owner; a 'grid' window object (but not an 'external' window) or a
        #                       'free' window object. When this window opens/closes, the owner is
        #                       informed via calls to its ->add_childFreeWin / ->del_childFreeWin
        #                       functions
        #
        # Optional arguments
        #   $session        - The GA::Session from which this function was called. 'undef' if the
        #                       calling function didn't specify a session and $owner's ->session IV
        #                       is also 'undef'
        #   $title          - Ignored if set (all 'other' windows define their own title)
        #   $editObj        - Ignored if set

lib/Games/Axmud/OtherWin.pm  view on Meta::CPAN

        return $self;
    }

    ##################
    # Methods

    # Standard window object functions

#   sub winSetup {}         # Inherited from GA::Generic::FreeWin

#   sub winEnable {}        # Inherited from GA::Generic::FreeWin

#   sub winDesengage {}     # Inherited from GA::Generic::FreeWin

#   sub winDestroy {}       # Inherited from GA::Generic::FreeWin

#   sub winShowAll {}       # Inherited from GA::Generic::Win

    sub drawWidgets {

        # Called by $self->winSetup
        # Sets up the Gtk3::Window by drawing the window's widgets
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $check) = @_;

        # Local variables
        my $title;

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->drawWidgets', @_);
        }

        # Create a packing box
        my $packingBox = Gtk3::VBox->new(FALSE, 0);
        $self->winBox->add($packingBox);
        $packingBox->set_border_width(0);

        # At the top, create a textview
        my $scroller = Gtk3::ScrolledWindow->new(undef, undef);
        $packingBox->pack_start($scroller, TRUE, TRUE, 0);
        $scroller->set_shadow_type($axmud::CLIENT->constShadowType);
        $scroller->set_policy('automatic', 'automatic');
        $scroller->set_border_width(0);

        # Create a textview with default colours/fonts
        my $textView = Gtk3::TextView->new();
        $scroller->add($textView);
        my $buffer = Gtk3::TextBuffer->new();
        $textView->set_buffer($buffer);
        $textView->set_editable(TRUE);
        $textView->set_cursor_visible(TRUE);
        $axmud::CLIENT->desktopObj->setTextViewStyle($self->winType, $textView);

        # At the bottom, create several horizontal packing boxes for various widgets
        my $hBox = Gtk3::HBox->new(FALSE, 0);
        $packingBox->pack_start($hBox, FALSE, FALSE, $self->spacingPixels);

        my $radioButton = Gtk3::RadioButton->new_with_label(undef, 'Execute instructions');
        $hBox->pack_start($radioButton, TRUE, TRUE, $self->spacingPixels);

        my $checkButton = Gtk3::CheckButton->new_with_label('(ignore empty lines)');
        $hBox->pack_start($checkButton, TRUE, TRUE, $self->spacingPixels);

        my $radioButton2 = Gtk3::RadioButton->new_with_label(
            $radioButton->get_group(),
            'Run as a script',
        );

        $hBox->pack_start($radioButton2, TRUE, TRUE, $self->spacingPixels);

        # Second strip
        my $hBox2 = Gtk3::HBox->new(FALSE, 0);
        $packingBox->pack_start($hBox2, FALSE, FALSE, 0);

        my $label = Gtk3::Label->new('Prepend:');
        $hBox2->pack_start($label, FALSE, FALSE, $self->spacingPixels);

        my $entry = Gtk3::Entry->new();
        $hBox2->pack_start($entry, TRUE, TRUE, 0);
        $entry->set_tooltip_text('Prepend this to every world command');

        my $label2 = Gtk3::Label->new('Append:');
        $hBox2->pack_start($label2, FALSE, FALSE, $self->spacingPixels);

        my $entry2 = Gtk3::Entry->new();
        $hBox2->pack_start($entry2, TRUE, TRUE, 0);
        $entry2->set_tooltip_text('Append this to every world command');

        # Third strip
        my $hBox3 = Gtk3::HBox->new(FALSE, 0);
        $packingBox->pack_end($hBox3, FALSE, FALSE, $self->spacingPixels);

        my $okButton = Gtk3::Button->new('Send');
        $hBox3->pack_start($okButton, TRUE, TRUE, 0);

        my $clearButton = Gtk3::Button->new('Send and clear text');
        $hBox3->pack_start($clearButton, FALSE, FALSE, $self->spacingPixels);
        $clearButton->get_child->set_width_chars(20);

        my $closeButton = Gtk3::Button->new('Close window');
        $hBox3->pack_start($closeButton, FALSE, FALSE, 0);
        $closeButton->get_child->set_width_chars(15);

        # ->signal_connects for the buttons
        $radioButton->signal_connect('toggled' => sub {

            if ($radioButton->get_active()) {

                $checkButton->set_sensitive(TRUE);

                $entry->set_sensitive(TRUE);
                $entry2->set_sensitive(TRUE);

                $okButton->set_label('Send');
                $clearButton->set_label('   Send and clear text   ');
            }
        });

        $radioButton2->signal_connect('toggled' => sub {

            if ($radioButton2->get_active()) {

                $checkButton->set_active(FALSE);
                $checkButton->set_sensitive(FALSE);

                $entry->set_sensitive(FALSE);
                $entry->set_text('');
                $entry2->set_sensitive(FALSE);
                $entry2->set_text('');

                $okButton->set_label('Run');
                $clearButton->set_label('   Run and clear text   ');
            }
        });

        $okButton->signal_connect('clicked' => sub {

            if ($radioButton->get_active()) {

                # Execute instructions
                $self->executeInstructions(
                    $axmud::CLIENT->desktopObj->bufferGetText($buffer),
                    $entry->get_text(),
                    $entry2->get_text(),
                    $checkButton->get_active(),
                );

            } else {

                # Save the script as a temporary file, and execute it
                $self->runScript($axmud::CLIENT->desktopObj->bufferGetText($buffer));
            }
        });

        $clearButton->signal_connect('clicked' => sub {

            if ($radioButton->get_active()) {

                # Execute instructions
                $self->executeInstructions(
                    $axmud::CLIENT->desktopObj->bufferGetText($buffer),
                    $entry->get_text(),
                    $entry2->get_text(),
                    $checkButton->get_active(),
                );

            } else {

                # Save the script as a temporary file, and execute it
                $self->runScript($axmud::CLIENT->desktopObj->bufferGetText($buffer));
            }

            $buffer->set_text('');
        });

        $closeButton->signal_connect('clicked' => sub {

            $self->winDestroy();
        });

        # Update IVs (not worth storing widgets other than the main packing box)
        $self->ivPoke('packingBox', $packingBox);

        return 1;
    }

#   sub redrawWidgets {}    # Inherited from GA::Generic::Win

    # ->signal_connects

    # Other functions

    sub executeInstructions {

        # Called by $self->drawWidgets when the user clicks the 'Send' button
        # Executes the contents of the window as instructions, one line at a time
        #
        # Expected arguments
        #   $text       - The contents of the Gtk3::TextBuffer
        #   $preText    - Text to prepend to any world commands (or an empty string)
        #   $postText   - Text to append to any world commands (or an empty string)
        #   $ignoreFlag - TRUE if empty lines should be ignored, FALSE if they should be used (as
        #                   world commands)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $text, $preText, $postText, $ignoreFlag, $check) = @_;

        # Local variables
        my (@cmdList, @finalList);

        # Check for improper arguments
        if (
            ! defined $text || ! defined $preText || ! defined $postText || ! defined $ignoreFlag
            || defined $check
        ) {
             return $axmud::CLIENT->writeImproper($self->_objClass . '->executeInstructions', @_);
        }

        # Split $text into lines
        @cmdList = split(/\n/, $text);

        # Ignore empty lines, if required
        if (! $ignoreFlag) {

            @finalList = @cmdList;

        } else {

lib/Games/Axmud/OtherWin.pm  view on Meta::CPAN

            #   area)
            packingBox                  => undef,       # Gtk3::VBox

            # Standard IVs for 'free' windows

            # The window's default size, in pixels
            widthPixels                 => 600,
            heightPixels                => 300,
            # Default border/item spacing sizes used in the window, in pixels
            borderPixels                => $axmud::CLIENT->constFreeBorderPixels,
            spacingPixels               => $axmud::CLIENT->constFreeSpacingPixels,

            # A string to use as the window title. If 'undef', a generic title is used
            title                       => $axmud::SCRIPT . ' session console #' . $session->number,
            # Hash containing any number of key-value pairs needed for this particular 'config'
            #   window; for example, for example, GA::PrefWin::TaskStart uses it to specify a task
            #   name and type. Set to an empty hash if not required
            configHash                  => {%configHash},

            # IVs for this window
            textView                    => undef,       # Gtk3::TextView
            buffer                      => undef,       # Gtk3::TextBuffer
        };

        # Bless the object into existence
        bless $self, $class;

        return $self;
    }

    ##################
    # Methods

    # Standard window object functions

#   sub winSetup {}         # Inherited from GA::Generic::FreeWin

    sub winEnable {

        # Called by GA::Generic::Win->createFreeWin, after the call to $self->winSetup
        # After the Gtk3::Window has been setup and moved into position, makes it visible
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 on success

        my ($self, $check) = @_;

        # Local variables
        my @list;

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->winEnable', @_);
        }

        # Make the window appear on the desktop
        $self->winShowAll($self->_objClass . '->winEnable');
        $self->ivPoke('enabledFlag', TRUE);

        # This type of window is unique (only one can be open per session); inform the GA::Session
        #   it has opened
        $self->session->set_consoleWin($self);

        # If any system messages have been stored, we can display them now
        @list = $self->session->systemMsgList;
        if (@list) {

            do {

                my ($type, $msg);

                $type = shift @list;
                $msg = shift @list;

                $self->update($type, $msg);

            } until (! @list);
        }

        # Each system message is displayed here only once
        $self->session->reset_systemMsg();

        return 1;
    }

#   sub winDesengage {}     # Inherited from GA::Generic::FreeWin

    sub winDestroy {

        # Can be called by anything
        # Updates IVs
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments, if the window can't be destroyed or if it has already
        #       been destroyed
        #   1 on success

        my ($self, $check) = @_;

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->winDestroy', @_);
        }

        if (! $self->winBox) {

            # Window already destroyed in a previous call to this function
            return undef;
        }

        # Close any 'free' windows for which this window is a parent
        foreach my $winObj ($self->ivValues('childFreeWinHash')) {

            $winObj->winDestroy();
        }

        # Destroy the Gtk3::Window
        eval { $self->winBox->destroy(); };
        if ($@) {

            # Window can't be destroyed
            return undef;

        } else {

            $self->ivUndef('winWidget');
            $self->ivUndef('winBox');
        }

        # Inform the owner and the desktop object of this 'free' window's demise
        $axmud::CLIENT->desktopObj->del_freeWin($self);
        if ($self->owner) {

            $self->owner->del_childFreeWin($self);
        }

        # This type of window is unique (only one can be open per session); inform the GA::Session
        #   it has closed
        $self->session->set_consoleWin();

        return 1;
    }

#   sub winShowAll {}       # Inherited from GA::Generic::Win

#   sub drawWidgets {}      # Inherited from GA::OtherWin::ClientConsole

#   sub redrawWidgets {}    # Inherited from GA::Generic::Win

    # ->signal_connects

    # Other functions

#   sub createColourTags {} # Inherited from GA::OtherWin::ClientConsole

#   sub update {}           # Inherited from GA::OtherWin::ClientConsole

    ##################
    # Accessors - set

    ##################
    # Accessors - get
}

{ package Games::Axmud::OtherWin::Simulate;

    use strict;
    use warnings;
#   use diagnostics;

    use Glib qw(TRUE FALSE);

    our @ISA = qw(
        Games::Axmud::Generic::OtherWin Games::Axmud::Generic::FreeWin Games::Axmud::Generic::Win
        Games::Axmud
    );

    ##################
    # Constructors

    sub new {

        # Called by GA::Generic::Win->createFreeWin
        # Creates a new instance of the Simulate window (an 'other' window). The window contains a
        #   textview in which the user can type text. When the 'Simulate' button is clicked, the
        #   contents of the textview (if any) is combined into a single string (with multiple lines
        #   separated by newline characters). The string is then used in a ';simulateworld' command,
        #   and appears in the session's default textview, as if it had been received from the world
        #
        # Expected arguments
        #   $number         - Unique number for this window object

lib/Games/Axmud/OtherWin.pm  view on Meta::CPAN


        # Local variables
        my (
            $width, $height, $title, $sampleText, $sampleUnderlay,
            @tagList,
            %prettyHash, %reversePrettyHash, %ansiHash,
        );

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->drawWidgets', @_);
        }

        # Import the list of Axmud standard colour/style tags (which also includes the dummy tags
        #   like 'bold', 'reverse_off' and 'attribs_off')
        @tagList = $axmud::CLIENT->constColourStyleList;
        # (For convenience, add 'attribs_off' to both the beginning and end of the list)
        unshift(@tagList, 'attribs_off');
        # Also import the hash of pretty names for each standard tag, in which the keys are the
        #   items in @tagList
        %prettyHash = $axmud::CLIENT->constPrettyTagHash;
        # Use a reverse hash too, so we can work out which combobox item was selected
        %reversePrettyHash = reverse %prettyHash;

        # Prepare a hash of ANSI escape sequences which the user can insert at any place in the
        #   textview, in the form
        #       $ansiHash{tag} = number_of_ANSI_escape_sequence
        %ansiHash = (
            (reverse $axmud::CLIENT->constANSIColourHash),
            (reverse $axmud::CLIENT->constANSIStyleHash),
        );

        $ansiHash{'bold'} = 1;
        $ansiHash{'bold_off'} = 22;
        $ansiHash{'reverse'} = 7;
        $ansiHash{'reverse_off'} = 27;
        $ansiHash{'conceal'} = 8;
        $ansiHash{'conceal_off'} = 28;
        $ansiHash{'attribs_off'} = 0;

        # Create a packing box
        my $packingBox = Gtk3::VBox->new(FALSE, 0);
        $self->winBox->add($packingBox);
        $packingBox->set_border_width(0);

        # At the top, create a textview
        my $scroller = Gtk3::ScrolledWindow->new(undef, undef);
        $packingBox->pack_start($scroller, TRUE, TRUE, 0);
        $scroller->set_shadow_type($axmud::CLIENT->constShadowType);
        $scroller->set_policy('automatic', 'automatic');
        $scroller->set_border_width(0);

        # Create a textview with default colours/fonts
        my $textView = Gtk3::TextView->new();
        $scroller->add($textView);
        my $buffer = Gtk3::TextBuffer->new();
        $textView->set_buffer($buffer);
        $textView->set_editable(TRUE);
        $textView->set_cursor_visible(TRUE);
        $axmud::CLIENT->desktopObj->setTextViewStyle($self->winType, $textView);

        # At the bottom, create a button strip in a horizontal packing box
        my $hBox = Gtk3::HBox->new(FALSE, 0);
        $packingBox->pack_end($hBox, FALSE, FALSE, $self->spacingPixels);

        # Create a combo
        my $comboBox = Gtk3::ComboBoxText->new();
        $hBox->pack_start($comboBox, FALSE, FALSE, $self->borderPixels);
        $title = 'Add an ANSI escape sequence:';
        $sampleText = 'Sample xterm-256 text colour';
        $sampleUnderlay = 'Sample xterm-256 underlay colour';
        $comboBox->append_text($title);
        foreach my $tag (@tagList) {

            $comboBox->append_text($prettyHash{$tag});

            # GA::Client->constColourStyleList doesn't include xterm colour tags, so we'll insert
            #   a sample text and a sample underlay colour right just before the style tags
            if ($tag eq 'ul_white') {

                $comboBox->append_text($sampleText);
                $comboBox->append_text($sampleUnderlay);
            }
        }

        $comboBox->set_active(0);

        # Create the 'Apply' button
        my $addButton = Gtk3::Button->new('Apply');
        $hBox->pack_start($addButton, FALSE, FALSE, 0);
        $addButton->get_child->set_width_chars(8);
        $addButton->signal_connect('clicked' => sub {

            my ($prettyTag, $tag, $ansi);

            $prettyTag = $comboBox->get_active_text();
            if ($prettyTag) {

                if ($prettyTag eq $sampleText) {

                    # Use an example xterm-256 text colour (dark grey)
                    $ansi = chr(27) . '[38;5;234m';

                } elsif ($prettyTag eq $sampleUnderlay) {

                    # Use an example xterm-256 underlay colour (light orange)
                    $ansi = chr(27) . '[48;5;214m';

                } elsif ($prettyTag ne $title) {

                    # Get an Axmud colour/style tag (or one of the dummy tags like 'bold',
                    #   'reverse_off' and 'attribs_off')
                    $tag = $reversePrettyHash{$prettyTag};
                    # Convert it to an ANSI escape sequence
                    $ansi = chr(27) . '[' . $ansiHash{$tag} . 'm';
                }

                if ($ansi) {

                    $buffer->insert_at_cursor($ansi);
                }
            }
        });

        # Create the 'Simulate' button
        my $okButton = Gtk3::Button->new('Simulate');
        $hBox->pack_end($okButton, FALSE, FALSE, 0);
        $okButton->get_child->set_width_chars(8);
        $okButton->signal_connect('clicked' => sub {

            my ($text, $type);

            $text = $axmud::CLIENT->desktopObj->bufferGetText($buffer);
            $type = $self->ivShow('configHash', 'type');

            # If the textview contains some text, and if the calling GA::Session still exists...
            if ($text && $axmud::CLIENT->ivExists('sessionHash', $self->session->number)) {

                if ($type eq 'prompt') {

                    # Simulate a prompt received from the world. The TRUE argument means that the
                    #   'main' window's blinker shouldn't be turned on.
                    chomp $text;
                    $self->session->processIncomingData($text, TRUE);

                } else {

                    # Simulate text received from the world. The TRUE argument means that the main
                    #   window's blinker shouldn't be turned on.
                    $self->session->processIncomingData($text, TRUE);
                }
            }
        });

        # Create the 'Close' button
        my $cancelButton = Gtk3::Button->new('Close');
        $hBox->pack_end($cancelButton, FALSE, FALSE, $self->spacingPixels);
        $cancelButton->get_child->set_width_chars(8);
        $cancelButton->signal_connect('clicked' => sub {

            $self->winDestroy();
        });

        # Update IVs (not worth storing widgets other than the main packing box)
        $self->ivPoke('packingBox', $packingBox);

        return 1;
    }

#   sub redrawWidgets {}    # Inherited from GA::Generic::Win

    # ->signal_connects

    # Other functions

    ##################
    # Accessors - set

    ##################
    # Accessors - get
}

{ package Games::Axmud::OtherWin::SourceCode;

    use strict;
    use warnings;
#   use diagnostics;

    use Glib qw(TRUE FALSE);

    our @ISA = qw(
        Games::Axmud::Generic::OtherWin Games::Axmud::Generic::FreeWin Games::Axmud::Generic::Win
        Games::Axmud

lib/Games/Axmud/OtherWin.pm  view on Meta::CPAN

    # Methods

    # Standard window object functions

    sub winSetup {

        # Called by GA::Generic::Win->createFreeWin, after the call to $self->new
        # Creates the Gtk3::Window itself
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or if the window can't be opened
        #   1 on success

        my ($self, $check) = @_;

        # Local variables
        my $iv;

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->winSetup', @_);
        }

        # Before doing anything, try to read the file (and don't open this window if it can't be
        #   done)
        if (! $self->readFile()) {

            return undef;
        }

        # Create the Gtk3::Window
        my $winWidget = Gtk3::Window->new('toplevel');
        if (! $winWidget) {

            return undef;

        } else {

            # Store the IV now, as subsequent code needs it
            $self->ivPoke('winWidget', $winWidget);
            $self->ivPoke('winBox', $winWidget);
        }

        # Set up ->signal_connects
        $self->setDeleteEvent();            # 'delete-event'

        # Set the window title
        $winWidget->set_title($self->title);

        # Set the window's default size and position
        $winWidget->set_default_size($self->widthPixels, $self->heightPixels);
        $winWidget->set_border_width($self->borderPixels);
        $winWidget->set_position('center');

        # Set the icon list for this window
        $iv = $self->winType . 'WinIconList';
        $winWidget->set_icon_list($axmud::CLIENT->desktopObj->{$iv});

        # Draw the widgets used by this window
        if (! $self->drawWidgets()) {

            return undef;
        }

        # The calling function can now call $self->winEnable to make the window visible
        return 1;
    }

#   sub winEnable {}        # Inherited from GA::Generic::FreeWin

#   sub winDesengage {}     # Inherited from GA::Generic::FreeWin

#   sub winDestroy {}       # Inherited from GA::Generic::FreeWin

#   sub winShowAll {}       # Inherited from GA::Generic::Win

    sub drawWidgets {

        # Called by $self->winSetup
        # Sets up the Gtk3::Window by drawing the window's widgets
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $check) = @_;

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->drawWidgets', @_);
        }

        # Create a packing box
        my $packingBox = Gtk3::VBox->new(FALSE, 0);
        $self->winBox->add($packingBox);
        $packingBox->set_border_width(0);

        # At the top, create a textview
        my $frame = Gtk3::Frame->new($self->title);
        $packingBox->pack_start($frame, TRUE, TRUE, 0);
        # Update the frame label
        $frame->set_label(
            'World model room #' . $self->modelObj->number . ' (' .  $self->file . ')',
        );
        $frame->set_border_width(0);

        my $scroller = Gtk3::ScrolledWindow->new(undef, undef);
        $frame->add($scroller);
        $scroller->set_shadow_type($axmud::CLIENT->constShadowType);
        $scroller->set_policy('automatic', 'automatic');
        $scroller->set_border_width(5);

        # Create a textview with default colours/fonts
        my $textView = Gtk3::TextView->new();
        $scroller->add($textView);
        my $buffer = Gtk3::TextBuffer->new();
        $textView->set_buffer($buffer);
        $textView->set_editable(FALSE);
        $textView->set_cursor_visible(FALSE);
        $axmud::CLIENT->desktopObj->setTextViewStyle($self->winType, $textView);

        # Copy the contents of the file to the textview
        $buffer->set_text(join("\n", $self->lineList));

        # At the bottom, create a button strip in a horizontal packing box
        my $hBox = Gtk3::HBox->new(FALSE, 0);
        $packingBox->pack_end($hBox, FALSE, FALSE, $self->spacingPixels);

        # Add a single button
        my $button = Gtk3::Button->new(' Close ');
        $hBox->pack_end($button, FALSE, FALSE, $self->borderPixels);
        $button->get_child->set_width_chars(10);
        $button->signal_connect('clicked' => sub {

            $self->winDestroy();
        });

        # Update IVs (not worth storing widgets other than the main packing box)
        $self->ivPoke('packingBox', $packingBox);

        return 1;
    }

#   sub redrawWidgets {}    # Inherited from GA::Generic::Win

    # ->signal_connects

    # Other functions

    sub readFile {

        # Called by $self->winSetup before creating the Gtk3::Window
        # Performs a few checks, displaying a 'dialogue' window if the source code viewer window
        #   can't be opened for one reason or another
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or if the window should not be opened
        #   1 otherwise

        my ($self, $check) = @_;

        # Local variables
        my (
            $worldModelObj, $obj, $virtualFlag, $sourcePath, $errorMsg, $file, $fileName,
            $fileHandle,
            @lineList,
        );

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->readFile', @_);
        }

        # Import the world model (for convenience)
        $worldModelObj = $self->session->worldModelObj;
        # Import some values from $self->configHash (for convenience)

lib/Games/Axmud/OtherWin.pm  view on Meta::CPAN

            notebookCurrentHeader       => undef,
            # When the user double-clicks an item in the notebook's list, the reference of the
            #   function which should be called in response (usually to make double-clicking the
            #   equivalent of clicking on one of the buttons)
            # Set to 'undef' if nothing should happen
            notebookSelectRef           => undef,

            # Hash of treeview headers, in the form
            #   $headerHash{header_string} = ref_of_subroutine
            headerHash                  => {},

            # List of notebok 'tab_name's in the same order used in the window
            #   (match the keys of ->notebookTabHash)
            notebookTabList             => [],
            # Hash of notebook tabs, in the form
            #   $notebookTagHash{tab_name} = reference_to_Gtk3::Label_of_tab
            notebookTabHash             => {},
            # Hash of data lists displayed in the notebook, in the form
            #   $notebookDataHash{tab_name} = reference_to_GA::Obj::SimpleList
            notebookDataHash            => {},

            # To access Axbasic's default data, $self->setupTreeView creates a dummy
            #   Language::Axbasic::Script object, which is stored here so that functions like
            #   $self->axbasicHeader can use it
            dummyScriptObj              => undef,
        };

        # Bless the object into existence
        bless $self, $class;

        return $self;
    }

    ##################
    # Methods

    # Standard window object functions

#   sub winSetup {}         # Inherited from GA::Generic::FreeWin

    sub winEnable {

        # Called by GA::Generic::Win->createFreeWin, after the call to $self->winSetup
        # After the Gtk3::Window has been setup and moved into position, makes it visible
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 on success

        my ($self, $check) = @_;

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->winEnable', @_);
        }

        # Make the window appear on the desktop
        $self->winShowAll($self->_objClass . '->winEnable');
        $self->ivPoke('enabledFlag', TRUE);

        # This type of window is unique to its GA::Session (only one can be open at any time, per
        #   session); inform the session it has opened
        $self->session->set_viewerWin($self);

        return 1;
    }

#   sub winDesengage {}     # Inherited from GA::Generic::FreeWin

    sub winDestroy {

        # Can be called by anything
        # Updates IVs
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments, if the window can't be destroyed or if it has already
        #       been destroyed
        #   1 on success

        my ($self, $check) = @_;

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->winDestroy', @_);
        }

        if (! $self->winBox) {

            # Window already destroyed in a previous call to this function
            return undef;
        }

        # Close any 'free' windows for which this window is a parent
        foreach my $winObj ($self->ivValues('childFreeWinHash')) {

            $winObj->winDestroy();
        }

        # Destroy the Gtk3::Window
        eval { $self->winBox->destroy(); };
        if ($@) {

            # Window can't be destroyed
            return undef;

        } else {

            $self->ivUndef('winWidget');
            $self->ivUndef('winBox');
        }

        # Inform the owner and the desktop object of this 'free' window's demise
        $axmud::CLIENT->desktopObj->del_freeWin($self);
        if ($self->owner) {

            $self->owner->del_childFreeWin($self);
        }

        # This type of window is unique to its GA::Session (only one can be open at any time, per
        #   session); inform the session it has closed
        $self->session->set_viewerWin();

        return 1;
    }

#   sub winShowAll {}       # Inherited from GA::Generic::Win

    sub drawWidgets {

        # Called by $self->winSetup
        # Sets up the data viewer window with its standard widgets
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $check) = @_;

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->drawWidgets', @_);
        }

        # Create a packing box
        my $packingBox = Gtk3::VBox->new(FALSE, 0);
        $self->winBox->add($packingBox);
        $packingBox->set_border_width(0);

        # Create a horizontal pane to divide the window into two, with the treeview on the left, and
        #   everything else on the right
        my $hPaned = Gtk3::HPaned->new();
        $packingBox->pack_start($hPaned, TRUE, TRUE, 0);

        # Add a treeview on the left of the window
        my $treeViewModel = Gtk3::TreeStore->new( ['Glib::String'] );
        my $treeView = Gtk3::TreeView->new($treeViewModel);
        $treeView->set_enable_search(FALSE);
        # Append a single column to the treeview
        $treeView->append_column(
            Gtk3::TreeViewColumn->new_with_attributes(
                'Objects',
                Gtk3::CellRendererText->new,
                text => 0,
            )
        );

        # Make the treeview scrollable
        my $treeViewScroller = Gtk3::ScrolledWindow->new();
        $hPaned->pack1($treeViewScroller, FALSE, FALSE);

lib/Games/Axmud/OtherWin.pm  view on Meta::CPAN

        #                   [
        #                       [row0_cell0, row0_cell1, row0cell2...],
        #                       [row1_cell0, row1_cell1, row1cell2...],
        #                       [row2_cell0, row2_cell1, row2cell2...],
        #                       ...
        #                   ]
        #
        # Optional arguments
        #   $buttonListRef  - a reference to a list of buttons, in the format
        #                   [
        #                       'button_name', 'tooltip', 'callback_sub_ref',
        #                       'button_name', 'tooltip', 'callback_sub_ref',
        #                       'button_name', 'tooltip', 'callback_sub_ref',
        #                       ...
        #                   ]
        #                   - if $buttonListRef is 'undef', no buttons are displayed
        #
        #   $scrollFlag     - if set to TRUE, the Gtk3::ScrolledWindow is scrolled to the bottom;
        #                       if FALSE (or 'undef'), it remains scrolled to the top
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my (
            $self, $tabListRef, $columnListRef, $dataHashRef, $buttonListRef, $scrollFlag,
            $check,
        ) = @_;

        # Local variables
        my (
            $number, $dataListRef,
            @tabList, @columnList, @buttonList, @scrollerList,
            %dataHash,
        );

        # Check for improper arguments
        if (
            ! defined $tabListRef || ! defined $columnListRef || ! defined $dataHashRef
            || defined $check
        ) {
            return $axmud::CLIENT->writeImproper($self->_objClass . '->refreshNotebook', @_);
        }

        # Dereference the supplied arguments
        @tabList = @$tabListRef;
        @columnList = @$columnListRef;
        %dataHash = %$dataHashRef;
        if ($buttonListRef) {

            @buttonList = @$buttonListRef;
        };

        # Remove the existing tabs and buttons (if any)
        $self->resetNotebook();

        # If a button list was not visible, but should now be visible, do some repacking
        if (! $self->hPaned2 && $buttonListRef) {

            # Unpack existing widgets
            $axmud::CLIENT->desktopObj->removeWidget($self->hPaned, $self->treeViewScroller);
            $axmud::CLIENT->desktopObj->removeWidget($self->hPaned, $self->notebook);

            # Repack the treeview scroller
            $self->hPaned->pack1($self->treeViewScroller, FALSE, FALSE);

            # Add a second horizontal pane on the right of the first one
            # In this pane, a notebook is on the left, and a strip of buttons is on the right
            my $hPaned2 = Gtk3::HPaned->new();
            $self->hPaned->pack2($hPaned2, TRUE, FALSE);

            # Repack the notebook on the left of $hPaned2
            $hPaned2->pack1($self->notebook, TRUE, FALSE);

            # Add a second vertical packing box for the strip of buttons
            my $vBox = Gtk3::VBox->new(FALSE, 0);
            $hPaned2->pack2($vBox, FALSE, FALSE);

            $self->hPaned->set_position($self->leftWidth);
            $hPaned2->set_position($self->centreWidth);

            # Update IVs
            $self->ivPoke('hPaned2', $hPaned2);
            $self->ivPoke('vBox', $vBox);

        # Likewise, if the button list was visible and is no longer required, do some repacking
        } elsif ($self->hPaned2 && ! $buttonListRef) {

            $axmud::CLIENT->desktopObj->removeWidget($self->hPaned, $self->treeViewScroller);
            $axmud::CLIENT->desktopObj->removeWidget($self->hPaned2, $self->notebook);
            $axmud::CLIENT->desktopObj->removeWidget($self->hPaned, $self->hPaned2);

            # Repack the treeview scroller
            $self->hPaned->pack1($self->treeViewScroller, FALSE, FALSE);

            # Repack the notebook
            $self->hPaned->pack2($self->notebook, TRUE, FALSE);

            $self->hPaned->set_position($self->leftWidth);

            # Update IVs
            $self->ivUndef('hPaned2');
            $self->ivUndef('vBox');
        }

        # Create new tabs in the notebook
        do {

            my (
                $tab, $mnemonic, $slWidget, $scroller, $label, $slWidgetRef, $button, $count,
                $vAdjust,
                @ownColumnList,
            );

            $tab = shift @tabList;
            $mnemonic = shift @tabList;

            # Add a simple list
            $slWidget = Games::Axmud::Obj::SimpleList->new(@columnList);
            # Make each row double-clickable
            $slWidget->signal_connect('row_activated' => sub {

                # If doubling-clicking on a row is equivalent to something else, call the specified
                #   function to make it happen
                if ($self->notebookSelectRef) {

                    &{$self->notebookSelectRef};
                }
            });

            # Make the simple list scrollable
            $scroller = Gtk3::ScrolledWindow->new();
            $scroller->set_policy('automatic', 'automatic');
            $scroller->add($slWidget);
            push (@scrollerList, $scroller);

            # Fill the columns with data
            $dataListRef = $dataHash{$tab};
            @{$slWidget->{data}} = @$dataListRef;

            # Make all columns of type 'bool' (which are composed of checkbuttons) non-activatable,
            #   so that the user can't click them on and off
            if (@columnList) {

                $count = -1;
                @ownColumnList = @columnList;

                do {

                    my ($title, $type);

lib/Games/Axmud/OtherWin.pm  view on Meta::CPAN

        $self->ivPoke('notebookMode', 'list');

        # Add an strip of buttons at the top of $self->vBox
        if (@buttonList) {

            do {

                my ($name, $tip, $method, $btn);

                $name = shift @buttonList;
                $tip = shift @buttonList;
                $method = shift @buttonList;

                $btn = Gtk3::Button->new($name);
                $btn->signal_connect('clicked' => sub {

                    $self->$method();
                });
                $btn->set_tooltip_text($tip);

                $self->vBox->pack_start($btn, FALSE, FALSE, 0);
                $self->ivPush('buttonList', $btn);

            } until (! @buttonList);
        }

        # Add a separator just beneath these buttons
        my $separator = Gtk3::HSeparator->new();
        $self->vBox->pack_start($separator, FALSE, FALSE, 10);
        $self->ivPush('buttonList', $separator);

        # Add two standard buttons at the bottom of $self->vBox, regardless of the type of list
        #   displayed
        my $btn = Gtk3::Button->new('Refresh list');
        $btn->signal_connect('clicked' => sub {

            $self->updateNotebook();
        });
        $btn->set_tooltip_text('Refresh this list');
        $self->vBox->pack_start($btn, FALSE, FALSE, 0);
        $self->ivPush('buttonList', $btn);

        my $btn2 = Gtk3::Button->new('Exit viewer');
        $btn2->signal_connect('clicked' => sub {

            $self->winDestroy();
        });
        $btn2->set_tooltip_text('Close the ' . $axmud::SCRIPT . ' data viewer');
        $self->vBox->pack_start($btn2, FALSE, FALSE, 0);
        $self->ivPush('buttonList', $btn2);

        # Set the width of the notebook, depending on whether there are any buttons to display
        if ($buttonListRef) {
            $self->hPaned2->set_position($self->centreWidth);
        } else {
            $self->hPaned2->set_position($self->centreWidth + $self->rightWidth);
        }

        # Render the changes
        $self->winShowAll($self->_objClass . '->refreshNotebook');
        $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->refreshNotebook');

        # If $scrollFlag is set, we can now scroll the Gtk3::ScrolledWindow to the bottom
        if ($scrollFlag) {

            foreach my $scroller (@scrollerList) {

                my $vAdjust = $scroller->get_vadjustment();

                $vAdjust->set_value(
                    $vAdjust->get_lower()
                    + (($vAdjust->get_upper() - $vAdjust->get_page_size()) - $vAdjust->get_lower())
                );
            }

            $self->winShowAll($self->_objClass . '->refreshNotebook');
        }

        return 1;
    }

    sub resetNotebook {

        # Called by $self->refreshNotebook
        # Resets the notebook, removing tabs and buttons
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or if there is no notebook to reset
        #   1 otherwise

        my ($self, $check) = @_;

        # Local variables
        my $number;

        # Check for improper arguments
        if (defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->resetNotebook', @_);
        }

        # If the notebook doesn't exist, there's nothing to reset
        if (! $self->notebook) {

            return undef;
        }

        # Remove the existing tabs (if any)
        if (
            $self->notebookMode eq 'list'
            || $self->notebookMode eq 'text'
        ) {
            $number = $self->notebook->get_n_pages();
            if ($number) {

                for (my $count = 0; $count < $number; $count++) {

                    $self->notebook->remove_page(0);
                }
            }

            $self->ivEmpty('notebookTabHash');
            $self->ivEmpty('notebookTabList');
            $self->ivEmpty('notebookDataHash');
        }

        # Remove the existing buttons (if any)
        if ($self->buttonList) {

            foreach my $button ($self->buttonList) {

                $button->destroy;
            }

            $self->ivEmpty('buttonList');
        }

        return 1;
    }

    sub refreshTextView {

        # Called by $self->cmdHeader (etc)
        # Creates a Gtk3::TextView in the notebook, and writes the supplied text to it
        # (Header functions which need a simple list, rather than a textview, call
        #   ->refreshNotebook)
        #
        # Expected arguments
        #   $tab    - The title of the notebook tab
        #   @list   - A list of lines to display in the textview
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $tab, @list) = @_;

        # Local variables
        my $label;

        # Check for improper arguments
        if (! defined $tab) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->refreshTextView', @_);
        }

        # If the button strip is visible, do some repacking
        if ($self->hPaned2) {

            $axmud::CLIENT->desktopObj->removeWidget($self->hPaned2, $self->notebook);
            $axmud::CLIENT->desktopObj->removeWidget($self->hPaned, $self->hPaned2);

            $self->hPaned->add2($self->notebook);

            $self->hPaned->set_position($self->leftWidth);
        }

        # Update IVs
        $self->ivUndef('hPaned2');
        $self->ivUndef('vBox');

        # Remove the existing notebook content
        $self->resetNotebook();

        # Create a scrolled window
        my $scrolled = Gtk3::ScrolledWindow->new(undef, undef);
        $scrolled->set_shadow_type($axmud::CLIENT->constShadowType);
        $scrolled->set_policy('automatic', 'automatic');
        $scrolled->set_border_width(5);

        # Create a textview with default colours/fonts
        my $textView = Gtk3::TextView->new();
        my $buffer = Gtk3::TextBuffer->new();
        $textView->set_buffer($buffer);
        $textView->set_editable(FALSE);
        $axmud::CLIENT->desktopObj->setTextViewStyle($self->winType, $textView);

        # Copy the text into the textview
        $buffer->set_text(join("\n", @list));

        # Complete setup
        $scrolled->add($textView);

        # Add a label
        $label = Gtk3::Label->new_with_mnemonic($tab);
        $self->notebook->append_page($scrolled, $label);

        # Render the changes
        $self->winShowAll($self->_objClass . '->refreshTextView');

        # Update IVs
        $self->ivAdd('notebookTabHash', $tab, $label);
        $self->ivPoke('notebookMode', 'text');

        return 1;
    }

    sub updateNotebook {

        # Called whenever the current notebook changes (when a new profile or cage is created or
        #   deleted - eg by $self->refreshNotebook)
        # Updates the notebook by calling the same method called when a header in the treeview is
        #   selected
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Optional arguments
        #   @list   - The rows in the notebook's GA::Obj::SimpleList which should be marked as
        #               'selected' (as if the user had clicked on them). If the list is empty,
        #               no rows are marked as 'selected'
        #
        # Return values
        #   'undef' on improper arguments or if the notebook isn't displaying anything
        #   1 otherwise

        my ($self, @list) = @_;

        # Local variables
        my ($method, $currentTab);

        # (No improper arguments to check)

        if (
            $self->notebookCurrentHeader
            && $self->ivExists('headerHash', $self->notebookCurrentHeader)
        ) {
            # Remember the currently selected tab
            $currentTab = $self->notebook->get_current_page();

            # Call the method specified by $self->headerHash
            $method = $self->ivShow('headerHash', $self->notebookCurrentHeader);
            $self->$method($self->notebookCurrentHeader);

            # If @list isn't empty, mark some of the rows as selected
            if (@list) {



( run in 0.524 second using v1.01-cache-2.11-cpan-0d23b851a93 )