Games-Axmud

 view release on metacpan or  search on metacpan

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

        # Operation complete
        if (! $pwd) {

            return $self->complete(
                $session, $standardCmd,
                'Added ' . $category . ' profile \'' . $name . '\'',
            );

        } else {

            # Obfuscate the password, using the same number of asterisk characters as elsewhere
            return $self->complete(
                $session, $standardCmd,
                'Added ' . $category . ' profile \'' . $name . '\' with the password \'********\'',
            );
        }
    }

    sub setProfile {

        # Called by GA::Cmd::SetGuild->do, SetRace->do, SetChar->do
        # Sets the current guild/race/char profile
        #
        # Expected arguments
        #   $session        - The calling function's GA::Session
        #   $inputString    - The command actually typed, e.g. 'sg thief'
        #   $standardCmd    - Standard version of the client command, e.g. 'setguild'
        #   $name           - The profile's name
        #   $category       - The category of profile - 'guild', 'race' or 'char'
        #
        # Optional arguments
        #   $pwd            - For character profiles only, the password to set. If the character
        #                       also exists, the password is updated. If 'undef', don't set or
        #                       update a password
        #   $account        - For character profiles, only, the associated account name to set. If
        #                       'undef', don't set an account name
        #
        # Return values
        #   'undef' on improper arguments or if there's an error
        #   1 on success

        my (
            $self, $session, $inputString, $standardCmd, $name, $category, $pwd, $account, $check,
        ) = @_;

        # Local variables
        my (
            $iv, $currentObj, $obj, $package, $historyObj,
            %customProfHash,
        );

        # Check for improper arguments
        if (
            ! defined $session || ! defined $inputString || ! defined $standardCmd
            || ! defined $name || ! defined $category || defined $check
        ) {
            return $axmud::CLIENT->writeImproper($self->_objClass . '->setProfile', @_);
        }

        # Check there are no 'free' windows open
        if ($axmud::CLIENT->desktopObj->listSessionFreeWins($session, TRUE)) {

            return $self->error(
                $session, $inputString,
                'Can\'t set a current profile while there are \'free\' windows (such as edit,'
                . ' preference and wizard windows) open - try closing them first',
            );
        }

        # If the profile already exists, check it isn't already a current profile
        if ($session->ivExists('currentProfHash', $category)) {

            $currentObj = $session->ivShow('currentProfHash', $category);
            if ($currentObj->name eq $name) {

                # Error message depends on whether it's the right kind of profile
                if ($currentObj->category eq $category) {

                    return $self->error(
                        $session, $inputString,
                        'The profile \'' . $name . '\' is already the current ' . $category
                        . ' profile',
                    );

                } else {

                    return $self->error(
                        $session, $inputString,
                        'The profile \'' . $name . '\' is the current ' . $currentObj->category
                        . ' profile',
                    );
                }
            }
        }

        # For character profiles, need to check that another session isn't already using the same
        #   profile. The TRUE flag means 'ignore sessions whose ->status is 'disconnected')
        if (
            $category eq 'char'
            && $axmud::CLIENT->testSessions($session->currentWorld->name, $name, TRUE)
        ) {
            return $self->error(
                $session, $inputString,
                'You are already using \'' . $name . '\' as the current character profile in'
                . ' another session',
            );
        }

        # If the profile exists, use it
        if ($session->ivExists('profHash', $name)) {

            $obj = $session->ivShow('profHash', $name);

            # Check it's the right category of profile
            if ($obj->category ne $category) {

                return $self->error(
                    $session, $inputString,
                    'The profile \'' . $name . '\' is a ' . $obj->category . ' profile',
                );
            }

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

            }

            # If the Status task's counters are running, reset their values, and turn them off
            if ($session->statusTask) {

                $session->statusTask->update_profiles();
            }

            # Operation complete
            if (! $pwd) {

                return $self->complete(
                    $session, $standardCmd,
                    'Added \'' . $name . '\' as the current ' . $category . ' profile',
                );

            } else {

                return $self->complete(
                    $session, $standardCmd,
                    'Added \'' . $name . '\' with the password \'********\' as the current '
                    . $category . ' profile',
                );
            }
        }
    }

    sub unsetProfile {

        # Called by GA::Cmd::UnsetGuild->do, UnsetRace->do, UnsetChar->do
        # Unsets the current guild/race/char profile (so that it's not a current profile
        #   any more)
        #
        # Expected arguments
        #   $session        - The calling function's GA::Session
        #   $inputString    - The command actually typed, e.g. 'ug thief'
        #   $standardCmd    - Standard version of the client command, e.g. 'unsetguild'
        #   $category       - The category of profile - 'guild', 'race' or 'char'
        #
        # Return values
        #   'undef' on improper arguments or if there's an error
        #   1 on success

        my ($self, $session, $inputString, $standardCmd, $category, $check) = @_;

        # Local variables
        my (
            $obj,
            %customProfHash,
        );

        # Check for improper arguments
        if (
            ! defined $session || ! defined $inputString || ! defined $standardCmd
            || ! defined $category || defined $check
        ) {
            return $axmud::CLIENT->writeImproper($self->_objClass . '->unsetProfile', @_);
        }

        # Check there are no 'free' windows open in any session
        if ($axmud::CLIENT->desktopObj->freeWinHash) {

            return $self->error(
                $session, $inputString,
                'Can\'t delete a profile while there are \'free\' windows (such as edit,'
                . ' preference and wizard windows) open in any session - try closing them first',
            );
        }

        # Check there is a current profile of the right category
        if (! $session->ivExists('currentProfHash', $category)) {

            return $self->error(
                $session, $inputString,
                'There is no current ' . $category . ' profile',
            );

        } else {

            $obj = $session->ivShow('currentProfHash', $category);
        }

        # Guild/race profiles only - if there's a current character, inform it it has lost its
        #   guild/race
        if ($category eq 'guild' || $category eq 'char') {

            if ($session->currentChar) {

                if ($category eq 'guild') {
                    $session->currentChar->ivUndef('guild');
                } elsif ($category eq 'race') {
                    $session->currentChar->ivUndef('race');
                }
            }

        # Character profiles only - if the character profile specifies a guild, race or custom
        #   profiles as current profile, unset them, too
        } elsif ($category eq 'char') {

            if ($obj->guild) {

                $obj->ivUndef('guild');
                $session->pseudoCmd('unsetguild', 'hide_complete');
            }

            if ($obj->race) {

                $obj->ivUndef('race');
                $session->pseudoCmd('unsetrace', 'hide_complete');
            }

            if ($obj->customProfHash) {

                %customProfHash = $obj->customProfHash;
                foreach my $customProf (keys %customProfHash) {

                    $session->pseudoCmd('unsetcustomprofile ' . $customProf, 'hide_complete');
                }

                $obj->ivEmpty('customProfHash');
            }

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

                'Could not create the cloned ' . $category .' profile \'' . $copy
                . '\' - errors while cloning cages',
            );

        } else {

            # Updates IVs
            $session->add_prof($copyObj);
            # Tell the current world it's acquired a new associated definiton
            $session->currentWorld->ivAdd('profHash', $copy, $category);
            # If it's a character profile, update more IVs
            if ($category eq 'char') {

                # Character's password and associated account names not yet known
                $session->currentWorld->ivAdd('passwordHash', $copy, undef);
                $session->currentWorld->ivAdd('accountHash', $copy, undef);
            }

            return $self->complete(
                $session, $standardCmd,
                'Created cloned ' . $category . ' profile \'' . $copy . '\'',
            );
        }
    }

    sub deleteProfile {

        # Called by GA::Cmd::DeleteGuild->do, DeleteRace->do, DeleteChar->do or
        #   DeleteCustomProfile->do
        # Deletes a guild/race/char or custom profile
        #
        # Expected arguments
        #   $session        - The calling function's GA::Session
        #   $inputString    - The command actually typed, e.g. 'ug thief'
        #   $standardCmd    - Standard version of the client command, e.g. 'unsetguild'
        #   $name           - The profile's name
        #   $category       - The category of profile - 'guild', 'race' or 'char' or a custom
        #                       profile category
        #
        # Return values
        #   'undef' on improper arguments or if there's an error
        #   1 on success

        my ($self, $session, $inputString, $standardCmd, $name, $category, $check) = @_;

        # Local variables
        my (
            $obj,
            %customProfHash,
        );

        # Check for improper arguments
        if (
            ! defined $session || ! defined $inputString || ! defined $standardCmd
            || ! defined $name || ! defined $category || defined $check
        ) {
            return $axmud::CLIENT->writeImproper($self->_objClass . '->deleteProfile', @_);
        }

        # Check there are no 'free' windows open in any session
        if ($axmud::CLIENT->desktopObj->freeWinHash) {

            return $self->error(
                $session, $inputString,
                'Can\'t delete a profile while there are \'free\' windows (such as edit,'
                . ' preference and wizard windows) open in any session - try closing them first',
            );
        }

        # Check the profile exists
        if (! $session->ivExists('profHash', $name)) {

            return $self->error(
                $session, $inputString,
                'The profile \'' . $name . '\' doesn\'t exist',
            );

        } else {

            $obj = $session->ivShow('profHash', $name);
        }

        # Check the profile is the right category
        if ($obj->category ne $category) {

            return $self->error(
                $session, $inputString,
                'The profile \'' . $name . '\' is a ' . $obj->category . ' profile',
            );
        }

        # Delete a current profile
        if (defined $session->currentGuild && $session->currentGuild eq $obj) {

            # Remove this profile's interfaces
            $session->resetProfileInterfaces($obj->name);
            # Destroy its cages
            $session->destroyCages($obj, TRUE);
            # Unset the profile as a current defintition
            $session->del_currentProf($category);
            # Remove the profile
            $session->del_prof($obj);

            # Guild/race profiles only - if there's a current character, inform it that it has lost
            #   its associated profile
            if ($category eq 'guild' || $category eq 'race') {

                if ($session->currentChar) {

                    if ($category eq 'guild') {
                        $session->currentChar->ivUndef('guild');
                    } elsif ($category eq 'race') {
                        $session->currentChar->ivUndef('race');
                    }
                }

            # Character profiles only - if the character profile specifies a guild, race or custom
            #   profiles as current profiles, unset them
            } elsif ($category eq 'char') {

                if ($obj->guild) {

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

            #   ->editHash{iv_name} = list_reference;
            #   ->editHash{iv_name} = hash_reference;
            # For 'pref' windows, a hash of key-value pairs set by the window's widgets;
            #   $self->enableButtons can access this hash to perform any necessary actions
            #   ('pref' windows don't make a call to ->saveChanges)
            editHash                    => {},
            # Hash containing any number of key-value pairs needed for this particular
            #   'edit'/'pref' window; for example, GA::PrefWin::TaskStart uses it to specify a task
            #   name and type. Set to an empty hash if not required
            editConfigHash              => \%configHash,

            # In the index on the left-hand side of the window, the pointer (Gtk3::TreeIter) for
            #   the $self->notebook tab that's currently being drawn
            indexPointer                => undef,
            # A hash of inner Gtk3::Notebooks. Keys are a tab number in the outer notebook (first
            #   tab is #0); the corresponding values are the inner notebook for that tab, or
            #   'undef' if the tab has no inner notebook
            innerNotebookHash           => {},
        };

        # 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 or if $self->editObj is the wrong type of object
        #   1 on success

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

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

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

        # For 'edit' windows, check that $self->editObj is of the right type. For 'pref' windows,
        #   $self->editObj is not set, so the check always succeeds
        if (! $self->checkEditObj()) {

            return undef;
        }

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

        return 1;
    }

#   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 'edit'/'pref' 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', @_);
        }

        if (! $axmud::CLIENT->configWinIndexFlag) {

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

            # Add a notebook at the top
            my $notebook = Gtk3::Notebook->new();
            $packingBox->pack_start($notebook, TRUE, TRUE, 0);
            $notebook->set_scrollable(TRUE);
            $notebook->popup_enable();

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

            # Create Reset/Save/Cancel/OK buttons
            my ($okButton, $cancelButton, $resetButton, $saveButton) = $self->enableButtons($hBox);

            # Update IVs
            $self->ivPoke('packingBox', $packingBox);
            $self->ivPoke('notebook', $notebook);
            $self->ivPoke('hBox', $hBox);
            $self->ivPoke('okButton', $okButton);
            $self->ivPoke('cancelButton', $cancelButton);
            $self->ivPoke('resetButton', $resetButton);
            $self->ivPoke('saveButton', $saveButton);

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

        ) = @_;

        # Check for improper arguments
        if (
            ! defined $grid || ! defined $editableFlag || ! defined $leftAttach
            || ! defined $rightAttach || ! defined $topAttach || defined $check
        ) {
            return $axmud::CLIENT->writeImproper($self->_objClass . '->addTextView', @_);
        }

        # Check that the position in the grid makes sense
        if (! $self->checkPosn($leftAttach, $rightAttach, $topAttach, $bottomAttach)) {

            return undef;
        }

        # Set defaults
        if (! defined $listFlag) {

            $listFlag = TRUE;
        }

        if (! defined $removeEmptyFlag) {

            $removeEmptyFlag = TRUE;
        }

        if (! defined $removeSpaceFlag) {

            $removeSpaceFlag = TRUE;
        }

        if (! defined $width) {

            $width = -1;    # Let Gtk3 set the width
        }

        if (! defined $height) {

            $height = -1;   # Let Gtk3 set the height
        }

        # Creating a containing Gtk3::Frame and Gtk3::ScrolledWindow
        my $scroll = Gtk3::ScrolledWindow->new(undef, undef);
        $scroll->set_shadow_type($axmud::CLIENT->constShadowType);
        $scroll->set_policy('automatic', 'automatic');
        $scroll->set_size_request($width, $height);
#        $scroll->set_border_width($self->spacingPixels);

        # Create a textview with default colours/fonts
        my $textView = Gtk3::TextView->new();
        $scroll->add($textView);
        my $buffer = Gtk3::TextBuffer->new();
        $textView->set_buffer($buffer);
        if ($noScrollFlag) {

            $textView->set_wrap_mode('word-char');  # Wrap words if possible, characters if not
            $textView->set_justification('left');
        }

        $axmud::CLIENT->desktopObj->setTextViewStyle($self->winType, $textView);

        if ($iv) {

            # Display the existing value of the IV (the call to $buffer->set_text requires a
            #   defined value, so don't try to display 'undef')
            if ($self->editObj->$iv) {

                if ($listFlag) {

                    # The call to ->ivPeek returns the contents of the IV as a flat list
                    $buffer->set_text(join("\n", $self->editObj->ivPeek($iv)));

                } else {

                    $buffer->set_text($self->editObj->$iv);
                }
            }

            $buffer->signal_connect('changed' => sub {

                my (
                    $text,
                    @list, @finalList,
                );

                $text = $axmud::CLIENT->desktopObj->bufferGetText($buffer);

                if ($listFlag) {

                    # Split the contents of the textview into a list of lines, separated by
                    #   newline characters
                    @list = split("\n", $text);
                    # Remove any empty lines and leading/trailing whitespace, if allowed
                    foreach my $line (@list) {

                        if ($line && $removeSpaceFlag) {

                            $line =~ s/^\s+//;  # Remove leading whitespace
                            $line =~ s/\s+$//;  # Remove trailing whitespace
                        }

                        if ($line || ! $removeEmptyFlag) {

                            push (@finalList, $line);
                        }
                    }

                    # Set the IV
                    $self->ivAdd('editHash', $iv, \@finalList);

                } else {

                    # Treat the contents of the IV as a single string
                    $self->ivAdd('editHash', $iv, $text);
                }
            });
        }

        # Make the textview editable or not editable
        $textView->set_editable($editableFlag);

        # Add the textview to the grid
        $scroll->set_hexpand(TRUE);
        $scroll->set_vexpand(TRUE);
        $grid->attach(
            $scroll,
            $leftAttach,
            $topAttach,
            ($rightAttach - $leftAttach),
            ($bottomAttach - $topAttach),
        );

        return $textView;
    }

    sub addSimpleList {

        # Adds a GA::Obj::SimpleList at the specified position in the window's Gtk3::Grid
        #
        # Example calls:
        #   my $slWidget = $self->addSimpleList($grid, 'some_IV', \@columnList,
        #       0, 6, 0, 1);
        #   my $slWidget = $self->addSimpleList($grid, 'some_IV', \@columnList,
        #       0, 6, 0, 1,
        #       -1, -1,
        #       GA::Client->getMethodRef('function_name'));

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

                                } else {
                                    push (@patternList, $key);
                                }
                            }
                        }
                    }

                } until (! @list);

                if (! @patternList) {

                    $self->showMsgDialogue(
                        'Check patterns',
                        'info',
                        'There are no patterns on this page',
                        'ok',
                    );

                } else {

                    # Check every pattern in turn. GA::Client->regexCheck returns 'undef' if the
                    #   regex is valid, or an error message if it's not valid
                    foreach my $pattern (@patternList) {

                        my $error = $axmud::CLIENT->regexCheck($pattern);
                        if (defined $error) {

                            push (@errorList, $pattern, $error);
                        }
                    }

                    if (! @errorList) {

                        $self->showMsgDialogue(
                            'Check patterns',
                            'info',
                            'All patterns on this page are valid',
                            'ok',
                        );

                    } else {

                        $msg = 'Patterns checked: ' . (scalar @patternList) . ', errors: '
                                . ((scalar @errorList) / 2);

                        $count = 0;
                        do {

                            my ($pattern, $error);

                            $pattern = shift @errorList;
                            $error = shift @errorList;
                            $count++;

                            chomp $pattern;
                            chomp $error;

                            $msg .= "\n\n$pattern\n$error";

                            # (Only show the first 2 errors - don't want a dialogue window as big as
                            #   the desktop)
                            if ($count >= 2 && @errorList) {

                                $msg .= "\n\n...";
                                @errorList = ();
                            }

                        } until (! @errorList);

                        $self->showMsgDialogue(
                            'Check patterns',
                            'error',
                            $msg,
                            'ok',
                            undef,
                            TRUE,           # Preserve newline characters in $msg
                        );
                    }
                }
            });
        }

        # Add the button to the grid
        $button->set_hexpand(TRUE);
        $button->set_vexpand(FALSE);
        $grid->attach(
            $button,
            $leftAttach,
            $topAttach,
            ($rightAttach - $leftAttach),
            ($bottomAttach - $topAttach),
        );

        return $button;
    }

    # Add widgets - special functions for GA::EditWin::Generic::Interface and
    #   GA::EditWin::Interface::Active

    sub useCheckButton {

        # Adapted from $self->addCheckButton
        # Adds a Gtk3::CheckButton with an accompnaying label at the specified position in the
        #   window's Gtk3::Grid. Instead of setting an IV in $self->editHash, sets a key-value pair
        #   in $self->attribHash
        #
        # Example calls:
        #   my $checkButton = $self->useCheckButton($grid, 'Click me', 'some_attribute', TRUE,
        #       0, 6, 0, 1);
        #   my $checkButton = $self->useCheckButton($grid, undef, 'some_attribute', TRUE,
        #       0, 6, 0, 1);
        #
        # Expected arguments
        #   $grid       - The tab's Gtk3::Grid object
        #   $name       - A 'name' for the checkbutton (displayed next to the button); if 'undef',
        #                   no name is displayed
        #   $attrib     - The name of the attribute set when the check button is toggled (matches
        #                   a key in $self->attribHash and GA::Interface::Trigger->attribHash)
        #   $stateFlag  - Flag set to FALSE if the checkbutton's state should be 'insensitive',
        #                   TRUE if it should be 'normal'
        #   $leftAttach, $rightAttach, $topAttach, $bottomAttach

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

        #   $readOnlyFlag
        #           - If set to TRUE, the scalar is read-only (so can't be edited); otherwise set
        #               to FALSE (or 'undef')
        #   $callFunc
        #           - If specified, $self->$funcName is called when the IV is updated instead of the
        #               usual call to $self->updateListDataWithKey (useful if we don't want the
        #               simple list sorted alphabetically, as normally happens); otherwise set to
        #               'undef'
        #   @callFuncArgs
        #           - Optional list of args to be passed if $callFunc is called; otherwise an empty
        #               list
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $iv, $key, $deRefFlag, $slWidget, $readOnlyFlag, $callFunc, @callFuncArgs) = @_;

        # Local variables
        my (
            $title, $labelText, $outerHashRef, $value, $response,
            %outerHash,
        );

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

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

        # Set the window title
        if ($readOnlyFlag) {
            $title = 'View Scalar';
        } else {
            $title = 'Edit Scalar';
        }

        # Show the 'dialogue' window
        my $dialogueWin;
        if (! $readOnlyFlag) {

            $dialogueWin = Gtk3::Dialog->new(
                $title,
                $self->winWidget,
                Gtk3::DialogFlags->new([qw/modal destroy-with-parent/]),
                'gtk-cancel' => 'reject',
                'gtk-ok'     => 'accept',
            );

        } else {

            $dialogueWin = Gtk3::Dialog->new(
                $title,
                $self->winWidget,
                Gtk3::DialogFlags->new([qw/modal destroy-with-parent/]),
                'gtk-ok'     => 'accept',
            );
        }

        $dialogueWin->set_position('center-always');
        $dialogueWin->set_icon_list($axmud::CLIENT->desktopObj->{dialogueWinIconList});

        $dialogueWin->signal_connect('delete-event' => sub {

            $dialogueWin->destroy();
            $self->restoreFocus();

            # (In case TTS is being used and another 'dialogue' window is about to open, make sure
            #   the window is visibly closed)
            $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->promptScalar');
        });

        # Add widgets to the 'dialogue' window
        my $vBox = $dialogueWin->get_content_area();
        # The call to ->addDialogueIcon splits $vBox in two, with an icon on the left, and a new
        #   Gtk3::VBox on the right, into which we put everything
        my $vBox2 = $self->addDialogueIcon($vBox);

        # Create a label, which will shortly show which IV is being edited (a second label,
        #   $undefLabel, is sometimes used immediately below it)
        my $label = Gtk3::Label->new();
        $label->set_alignment(0, 0.5);

        # Create an entry, in which the user supplies a new value
        my $entry = Gtk3::Entry->new;
        if ($readOnlyFlag) {

            $entry->set_state('insensitive');
        }

        # Set the contents of the entry box, and prepare the label text
        if (! defined $key) {

            # Mode 1 - edit the value of $self->editObj->$iv or $self->editHash->$iv
            $labelText = 'Value of IV \'' . $iv . '\'';

            # If the IV hasn't yet been edited, use the original value
            if (! $self->ivExists('editHash', $iv)) {

                if (defined $self->editObj->{$iv}) {

                    # Put the current value in the entry box
                    $entry->set_text($self->editObj->{$iv});

                } else {

                    # Add more text to the label, to show that the value is 'undef'
                    $labelText .= ' <i>(currently \'undef\')</i>';
                }

            # If the IV has been edited, use the edited value
            } else {

                # Use an edited value
                if (defined $self->ivShow('editHash', $iv)) {

                    $entry->set_text($self->ivShow('editHash', $iv));

                } else {

                    # Add more text to the label, to show that the value is 'undef'
                    $labelText .= ' <i>(currently \'undef\')</i>';
                }
            }

        } else {

            # Mode 2 - edit the scalar stored in a key-value pair; the pair is in the hash
            #   stored as $self->editObj->$iv or $self->editHash->$iv
            $labelText = 'Value of key \'' . $key . '\' stored in IV \'' . $iv . '\'';

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

        # Pack the label and entry box
        $vBox2->pack_start($label, TRUE, TRUE, $self->spacingPixels);
        $vBox2->pack_start($entry, TRUE, TRUE, $self->spacingPixels);

        # Optionally add a button strip in the lower area, containing a single button
        if (! $readOnlyFlag) {

            my $hBox = Gtk3::HBox->new(FALSE, 0);
            $vBox2->pack_start($hBox, TRUE, TRUE, $self->spacingPixels);

            # Create the 'use undef' button
            my $button3 = Gtk3::Button->new('Use \'undef\' value');
            $hBox->pack_end($button3, FALSE, FALSE, $self->spacingPixels);

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

                # Store an 'undef' scalar value

                # Mode 1 - edit the scalar stored in $self->editObj->$iv or $self->editHash->$iv
                if (! defined $key) {

                    $self->ivAdd('editHash', $iv, undef);

                    # Update the simple list displayed in the parent window, if a list was specified
                    if ($slWidget) {

                        $self->updateListDataWithKey($slWidget, $iv, undef);
                    }

                # Mode 2 - edit the scalar stored in a key-value pair; the pair is in the hash
                #   stored as $self->editObj->$iv or $self->editHash->$iv
                } else {

                    # Update the hash stored in the IV with a new key-value pair. The key is $key,
                    #   the corresponding value is the new scalar
                    $outerHash{$key} = undef;

                    # Store the modified outer hash as the IV
                    $self->ivAdd('editHash', $iv, \%outerHash);

                    # Update the simple list displayed in the parent window, if a list was specified
                    if ($slWidget) {

                        $self->updateListDataWithKey($slWidget, $key, undef);
                    }
                }

                # Call a function to re-display the simple list in the parent window, if a function
                #   was specified
                if ($callFunc) {

                    $self->$callFunc(@callFuncArgs);
                }

                # Close the window and return success value
                $dialogueWin->destroy();
                $self->restoreFocus();

                # (In case TTS is being used and another 'dialogue' window is about to open, make
                #   sure the window is visibly closed)
                $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->promptScalar');

                return 1;
            });
        }

        # Widget drawing complete
        $vBox->show_all();

        # Get the response
        $response = $dialogueWin->run();
        if ($response eq 'accept') {

            my $scalar;

            # Don't save anything for read-only values
            if (! $readOnlyFlag) {

                $scalar = $entry->get_text();

                # Mode 1 - edit the value stored in of $self->editObj->$iv or $self->editHash->$iv
                if (! defined $key) {

                    # Store the edited scalar
                    $self->ivAdd('editHash', $iv, $scalar);

                    # Update the simple list displayed in the parent window, if a list was specified
                    if ($slWidget) {

                        $self->updateListDataWithKey($slWidget, $iv, $scalar);
                    }

                # Mode 2 - edit the scalar stored in a key-value pair; the pair is in the hash
                #   stored as $self->editObj->$iv or $self->editHash->$iv
                } else {

                    # Update the hash stored in the IV with a new key-value pair. The key is $key,
                    #   the corresponding value is the new scalar
                    if ($deRefFlag) {

                        # Cage masks: $value is a scalar reference
                        $value = \$scalar;

                    } else {

                        # Everything else: $value is a normal scalar
                        $value = $scalar;
                    }

                    $outerHash{$key} = $value;

                    # Store the modified outer hash as the IV
                    $self->ivAdd('editHash', $iv, \%outerHash);

                    # Update the simple list displayed in the parent window, if a list was specified
                    if ($slWidget) {

                        $self->updateListDataWithKey($slWidget, $key, $value);
                    }
                }

                # Call a function to re-display the simple list in the parent window, if a function
                #   was specified
                if ($callFunc) {

                    $self->$callFunc(@callFuncArgs);
                }
            }
        }

        # Destroy the window
        $dialogueWin->destroy();
        $self->restoreFocus();

        # (In case TTS is being used and another 'dialogue' window is about to open, make sure the
        #   window is visibly closed)
        $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->promptScalar');

        # Operation complete
        return 1;
    }

    sub promptList {

        # Creates a 'dialogue' window to prompt the user to view, enter (or edit) a list of values
        # If the user supplies any values, sets the IV then closes the window
        #
        # Operates in two modes
        #   Mode 1  - the supplied IV is a list, i.e.
        #               $self->editObj->$iv = [some_list]
        #               $self->editHash->$iv = [some_list]
        #           - the values supplied by the user are saved in place of [some_list]
        #
        #   Mode 2  - the supplied IV is a hash, i.e.
        #               $self->editObj->$iv = {some_hash}
        #               $self->editHash->$iv = {some_hash}
        #           - the calling function also supplies a key in {some_hash}
        #           - a reference to the list supplied by the user is saved as the key's
        #               corresponding value
        #
        # Expected arguments
        #   $iv - The IV to set in $self->editHash
        #
        # Optional arguments
        #   $key    - In mode 1, 'undef'; in mode 2, a key in {some_hash}
        #   $slWidget
        #           - In mode 1, 'undef'; in mode 2, 'undef' or the GA::Obj::SimpleList in which the
        #               IV's data is being displayed (if specified, the simple list is updated when
        #               this window is closed)
        #   $readOnlyFlag
        #           - If set to TRUE, the list is read-only (so can't be edited); otherwise set
        #               to FALSE (or 'undef')
        #   $callFunc
        #           - If specified, $self->$funcName is called when the IV is updated instead of the
        #               usual call to $self->updateListDataWithKey (useful if we don't want the
        #               simple list sorted alphabetically, as normally happens); otherwise set to
        #               'undef'
        #   @callFuncArgs
        #           - Optional list of args to be passed if $callFunc is called; otherwise an empty
        #               list
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $iv, $key, $slWidget, $readOnlyFlag, $callFunc, @callFuncArgs) = @_;

        # Local variables
        my (
            $title, $replaceListRef, $outerHashRef, $response,
            %outerHash,
        );

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

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

        # Set the window title
        if ($readOnlyFlag) {
            $title = 'View List';
        } else {
            $title = 'Edit List';
        }

        # Show the 'dialogue' window
        my $dialogueWin;
        if (! $readOnlyFlag) {

            $dialogueWin = Gtk3::Dialog->new(
                $title,
                $self->winWidget,
                Gtk3::DialogFlags->new([qw/modal destroy-with-parent/]),
                'gtk-cancel' => 'reject',
                'gtk-ok'     => 'accept',
            );

        } else {

            $dialogueWin = Gtk3::Dialog->new(
                $title,
                $self->winWidget,
                Gtk3::DialogFlags->new([qw/modal destroy-with-parent/]),
                'gtk-ok'     => 'accept',
            );
        }

        $dialogueWin->set_position('center-always');
        $dialogueWin->set_icon_list($axmud::CLIENT->desktopObj->{dialogueWinIconList});

        $dialogueWin->signal_connect('delete-event' => sub {

            $dialogueWin->destroy();
            $self->restoreFocus();

            # (In case TTS is being used and another 'dialogue' window is about to open, make sure
            #   the window is visibly closed)
            $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->promptList');
        });

        # Add widgets to the 'dialogue' window
        my $vBox = $dialogueWin->get_content_area();
        # The call to ->addDialogueIcon splits $vBox in two, with an icon on the left, and a new
        #   Gtk3::VBox on the right, into which we put everything
        my $vBox2 = $self->addDialogueIcon($vBox);

        # Create a label, which will shortly show which IV is being edited
        my $label = Gtk3::Label->new();
        $vBox2->pack_start($label, TRUE, TRUE, $self->spacingPixels);
        $label->set_alignment(0, 0.5);

        # Create a textview, in which the user supplies a list of values
        my $scroll = Gtk3::ScrolledWindow->new(undef, undef);
        $vBox2->pack_start($scroll, TRUE, TRUE, $self->spacingPixels);
        $scroll->set_shadow_type($axmud::CLIENT->constShadowType);
        $scroll->set_policy('automatic', 'automatic');
        $scroll->set_size_request(200, 150);            # Minimum size
        $scroll->set_border_width($self->spacingPixels);

        # Create a textview with default colours/fonts
        my $textView = Gtk3::TextView->new();
        $scroll->add($textView);

        my $buffer = Gtk3::TextBuffer->new();
        $textView->set_buffer($buffer);

        if ($readOnlyFlag) {
            $textView->set_editable(FALSE);
        } else {
            $textView->set_editable(TRUE);
        }

        $axmud::CLIENT->desktopObj->setTextViewStyle($self->winType, $textView);

        # Set the contents of the label and the textview
        if (! defined $key) {

            # Mode 1 - edit the list stored in $self->editObj->$iv or $self->editHash->$iv
            $label->set_markup('List stored in IV \'' . $iv . '\'');

            # If the IV hasn't yet been edited, use the original stored list
            if (! $self->ivExists('editHash', $iv)) {

                $replaceListRef = $self->editObj->{$iv};    # (Profile templates have no accessors)
                $buffer->set_text(join("\n", @$replaceListRef));

            # If the IV has been edited, use the edited list
            } else {

                $replaceListRef = $self->ivShow('editHash', $iv);
                $buffer->set_text(join("\n", @$replaceListRef));
            }

        } else {

            # Mode 2 - edit the list stored in a key-value pair; the pair is in the hash
            #   stored as $self->editObj->$iv or $self->editHash->$iv
            $label->set_markup('List stored in key \'' . $key . '\' in IV \'' . $iv . '\'');

            # Get a copy of the hash being edited
            if (! $self->ivExists('editHash', $iv)) {

                $outerHashRef = $self->editObj->{$iv};      # (Profile templates have no accessors)
                %outerHash = %$outerHashRef;

            } else {

                $outerHashRef = $self->ivShow('editHash', $iv);
                %outerHash = %$outerHashRef;
            }

            # Put the current contents of the list in the textview
            if (exists $outerHash{$key} && defined $outerHash{$key}) {

                $replaceListRef = $outerHash{$key};
                $buffer->set_text(join("\n", @$replaceListRef));
            }
        }

        # Widget drawing complete
        $vBox->show_all();

        # Get the response
        $response = $dialogueWin->run();
        if ($response eq 'accept') {

            my (
                $text,
                @dataList,
            );

            # Don't save anything for read-only values
            if (! $readOnlyFlag) {

                $text = $axmud::CLIENT->desktopObj->bufferGetText($buffer);

                # Split the contents of the textview into a list of lines, separated by
                #   newline characters
                @dataList = split("\n", $text);

                # Mode 1 - edit the list stored in of $self->editObj->$iv or $self->editHash->$iv
                if (! defined $key) {

                    # Store the list we've been editing
                    $self->ivAdd('editHash', $iv, \@dataList);

                    # Update the simple list displayed in the parent window, if a list was specified
                    if ($slWidget) {

                        $self->updateListDataWithKey($slWidget, $iv, \@dataList);
                    }

                # Mode 2 - edit the list stored in a key-value pair; the pair is in the hash
                #   stored as $self->editObj->$iv or $self->editHash->$iv
                } else {

                    # Update the hash stored in the IV with a new key-value pair. The key is
                    #   $key, the corresponding value is a reference to the list we've
                    #   been editing, @dataList
                    $outerHash{$key} = \@dataList;

                    # Store the modified outer hash as the IV
                    $self->ivAdd('editHash', $iv, \%outerHash);

                    # Update the simple list displayed in the parent window, if a list was specified
                    if ($slWidget) {

                        $self->updateListDataWithKey($slWidget, $key, \@dataList);
                    }
                }

                # Call a function to re-display the simple list in the parent window, if a function
                #   was specified
                if ($callFunc) {

                    $self->$callFunc(@callFuncArgs);
                }
            }
        }

        # Destroy the window
        $dialogueWin->destroy();
        $self->restoreFocus();

        # (In case TTS is being used and another 'dialogue' window is about to open, make sure the
        #   window is visibly closed)
        $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->promptList');

        # Operation complete
        return 1;
    }

    sub promptHash {

        # Creates a 'dialogue' window to prompt the user to view, enter (or edit) a hash of
        #   key-value pairs
        # If the user supplies any pairs, sets the IV then closes the window
        #
        # Operates in two modes
        #   Mode 1  - the supplied IV is a hash, i.e.
        #               $self->editObj->$iv = {some_hash}
        #               $self->editHash->$iv = {some_hash}
        #           - the key-value pairs supplied by the user are saved in place of {some_hash}
        #
        #   Mode 2  - the supplied IV is a hash, i.e.
        #               $self->editObj->$iv = {some_hash}
        #               $self->editHash->$iv = {some_hash}
        #           - the calling function also supplies a key in {some_hash}
        #           - a reference to the hash supplied by the user is saved as the key's
        #               corresponding value
        #
        # Expected arguments
        #   $iv     - The IV to set in $self->editObj
        #
        # Optional arguments
        #   $key    - In mode 1, 'undef'; in mode 2, a key in {some_hash}
        #   $slWidget
        #           - In mode 1, 'undef'; in mode 2, 'undef' or the GA::Obj::SimpleListin which the
        #               IV's data is being displayed (if specified, the simple list is updated when
        #               this window is closed)
        #   $readOnlyFlag
        #           - If set to TRUE, the scalar is read-only (so can't be edited); otherwise set to
        #               FALSE (or 'undef')
        #   $callFunc
        #           - If specified, $self->$funcName is called when the IV is updated instead of the
        #               usual call to $self->updateListDataWithKey (useful if we don't want the
        #               simple list sorted alphabetically, as normally happens); otherwise set to
        #               'undef'
        #   @callFuncArgs
        #           - Optional list of args to be passed if $callFunc is called; otherwise an empty
        #               list
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $iv, $key, $slWidget, $readOnlyFlag, $callFunc, @callFuncArgs) = @_;

        # Local variables
        my (
            $title, $response, $outerHashRef,
            %outerHash, %replaceHash, %innerHash,
        );

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

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

        # Set the window title
        if ($readOnlyFlag) {
            $title = 'View Hash';
        } else {
            $title = 'Edit Hash';
        }

        # Show the 'dialogue' window
        my $dialogueWin;
        if (! $readOnlyFlag) {

            $dialogueWin = Gtk3::Dialog->new(
                $title,
                $self->winWidget,
                Gtk3::DialogFlags->new([qw/modal destroy-with-parent/]),
                'gtk-cancel' => 'reject',
                'gtk-ok'     => 'accept',
            );

        } else {

            $dialogueWin = Gtk3::Dialog->new(
                $title,
                $self->winWidget,
                Gtk3::DialogFlags->new([qw/modal destroy-with-parent/]),
                'gtk-ok'     => 'accept',
            );
        }

        $dialogueWin->set_position('center-always');
        $dialogueWin->set_icon_list($axmud::CLIENT->desktopObj->{dialogueWinIconList});

        $dialogueWin->signal_connect('delete-event' => sub {

            $dialogueWin->destroy();
            $self->restoreFocus();

            # (In case TTS is being used and another 'dialogue' window is about to open, make sure
            #   the window is visibly closed)
            $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->promptHash');
        });

        # Add widgets to the 'dialogue' window
        my $vBox = $dialogueWin->get_content_area();
        # The call to ->addDialogueIcon splits $vBox in two, with an icon on the left, and a new
        #   Gtk3::VBox on the right, into which we put everything
        my $vBox2 = $self->addDialogueIcon($vBox);

        # Create a label, which will shortly show which IV is being edited
        my $label = Gtk3::Label->new();
        $vBox2->pack_start($label, TRUE, TRUE, $self->spacingPixels);
        $label->set_alignment(0, 0.5);

        # Create a scroller
        my $scroller = Gtk3::ScrolledWindow->new;
        $vBox2->pack_start($scroller, TRUE, TRUE, $self->spacingPixels);
        $scroller->set_policy('automatic', 'automatic');
        $scroller->set_size_request(200, 150);          # Minimum size

        # Create a simple list with two columns representing a hash, for which the user
        #   supplies key-value pairs
        my $slWidget2 = Games::Axmud::Obj::SimpleList->new(
            # Give the first column a minimum width; don't want the columns moving around too
            #   much when the user enter new key-value pairs
            'Key           ' => 'text',
            'Value'          => 'text',
        );
        $scroller->add($slWidget2);

        # Set the contents of the label and the simple list. We don't want to overwrite the hash
        #   until the user clicks the 'ok' button, so we'll edit a copy of the hash
        if (! defined $key) {

            # Mode 1 - edit the hash stored in $self->editObj->$iv or $self->editHash->$iv
            $label->set_markup('Hash stored in IV \'' . $iv . '\'');
            %outerHash = $self->promptHash_displayDataMode1($slWidget2, $iv);
            # Edit a copy, so that we can revert to the original copy if we want to
            %replaceHash = %outerHash;

        } else {

            # Mode 2 - edit the hash stored in a key-value pair; the pair is in the hash
            #   stored as $self->editObj->$iv or $self->editHash->$iv
            $label->set_markup('Hash stored in key \'' . $key . "\'\nin IV \'" . $iv . "\'");
            %innerHash = $self->promptHash_displayDataMode2($slWidget2, $iv, $key);
            # Edit a copy, so that we can revert to the original copy if we want to
            %replaceHash = %innerHash;
        }

        # If the hash is editable, we have a lot more widgets to add
        if (! $readOnlyFlag) {

            # Add a grid containing entry boxes for new key-value pairs. The grid ensures that
            #   the two entry boxes are aligned with each other
            my $grid = Gtk3::Grid->new();
            $vBox2->pack_start($grid, TRUE, TRUE, $self->spacingPixels);

            my $label2 = Gtk3::Label->new();
            $label2->set_alignment(0, 0.5);
            $label2->set_markup('Key');

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

            # Don't save anything for read-only values
            if (! $readOnlyFlag) {

                # Mode 1 - edit the hash stored in $self->editObj->$iv or $self->editHash->$iv
                if (! defined $key) {

                    # Store the hash we've been editing
                    $self->ivAdd('editHash', $iv, \%replaceHash);

                    # Update the simple list displayed in the parent window, if a list was specified
                    if ($slWidget) {

                        $self->updateListDataWithKey($slWidget, $iv, \%replaceHash);
                    }

                # Mode 2 - edit the hash stored in a key-value pair; the pair is in the hash stored
                #   as $self->editObj->$iv or $self->editHash->$iv
                } else {

                    # Get a copy of the hash stored in the IV
                    if (! $self->ivExists('editHash', $iv)) {

                        %outerHash = $self->editObj->$iv;

                    # If the IV has been edited, use the edited hash
                    } else {

                        $outerHashRef = $self->ivShow('editHash', $iv);
                        %outerHash = %$outerHashRef;
                    }

                    # Now update that hash with a new key-value pair. The key is $key, the
                    #   corresponding value is a reference to the hash we've been editing,
                    #   %replaceHash
                    $outerHash{$key} = \%replaceHash;

                    # Store the modified outer hash as the IV
                    $self->ivAdd('editHash', $iv, \%outerHash);

                    # Update the displayed list, if one was specified
                    if ($slWidget) {

                        $self->updateListDataWithKey($slWidget, $key, \%replaceHash);
                    }
                }

                # Call a function to re-display the simple list, if one was specified
                if ($callFunc) {

                    $self->$callFunc(@callFuncArgs);
                }
            }
        }

        # Destroy the window
        $dialogueWin->destroy();
        $self->restoreFocus();

        # (In case TTS is being used and another 'dialogue' window is about to open, make sure the
        #   window is visibly closed)
        $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->promptHash');

        # Operation complete
        return 1;
    }

    sub promptHash_displayDataMode1 {

        # Called by $self->promptHash when we want to edit a hash IV in $self->editObj or
        #   $self->editHash
        # The calling function had created a GA::Obj::SimpleList; this function's job is to fill
        #   it with data
        #
        # Expected arguments
        #   $slWidget   - The GA::Obj::SimpleList where the IV's data is displayed
        #   $iv         - The IV being edited
        #
        # Return values
        #   An empty hash on improper arguments
        #   Otherwise returns a copy of the hash being edited

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

        # Local variables
        my (
            $hashRef,
            %emptyHash, %dataHash,
        );

        # Check for improper arguments
        if (! defined $slWidget || ! defined $iv || defined $check) {

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

            return %emptyHash;
        }

        # If the IV hasn't yet been edited, use the original stored hash
        if (! $self->ivExists('editHash', $iv)) {

            $hashRef = $self->editObj->{$iv};        # (Profile templates have no accessors)
            %dataHash = %$hashRef;

        # If the IV has been edited, use the edited hash
        } else {

            $hashRef = $self->ivShow('editHash', $iv);
            %dataHash = %$hashRef;
        }

        # Update the GA::Obj::SimpleList (which currently stores no data)
        foreach my $key (sort {lc($a) cmp lc($b)} (keys %dataHash)) {

            push (
                @{$slWidget->{data}},
                [$key, $dataHash{$key}],
            );
        }

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


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

#   sub new {}              # Defined in window objects which inherit this one

    ##################
    # 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', @_);
        }

        # 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 {

        # 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);

        return 1;
    }

    sub winDisengage {

        # Should not be called, in general (provides compatibility with other types of windows,
        #   whose window objects can be destroyed without closing the windows themselves)
        # If called, this function just calls $self->winDestroy and returns the result
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or if the window can't be disengaged
        #   1 on success

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

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

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

        return $self->winDestroy();
    }

    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);
        }

        return 1;
    }

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

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

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

    # ->signal_connects

    sub setDeleteEvent {

        # Called by $self->winSetup
        # Set up a ->signal_connect to watch out for the user manually closing the 'free' window
        #
        # 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 . '->setDeleteEvent', @_);
        }

        $self->winBox->signal_connect('delete-event' => sub {

            # Prevent Gtk3 from taking action directly. Instead redirect the request to
            #   $self->winDestroy
            return $self->winDestroy();
        });

        return 1;
    }

    # Other functions

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

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

    sub widthPixels
        { $_[0]->{widthPixels} }
    sub heightPixels
        { $_[0]->{heightPixels} }
    sub borderPixels
        { $_[0]->{borderPixels} }

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


        # Called by GA::Obj::Workspace->createGridWin or ->createSimpleGridWin
        # This generic function merely creates a Gtk3::Window
        # Your own window object code should either create a function based on
        #   GA::Win::Internal->winSetup, or your code should inherit that function directly
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Optional arguments
        #   $title      - The window title or 'undef'. Ignored in this generic function
        #   $listRef    - Reference to a list of functions to call, just after the Gtk3::Window is
        #                   created (can be used to set up further ->signal_connects, if this
        #                   window needs them)
        #
        # Return values
        #   'undef' on improper arguments or if the window can't be opened
        #   1 on success

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

        # Local variables
        my $iv;

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

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

        # Don't create a new window, if it already exists
        if ($self->enabledFlag) {

            return undef;
        }

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

            return undef;

        } else {

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

        # Set up ->signal_connects specified by the calling function, if any
        if ($listRef) {

            foreach my $func (@$listRef) {

                $self->$func();
            }
        }

        # 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($winWidget)) {

            return undef;
        }

        # The calling function can now move the window into position, before calling
        #   $self->winEnable to make it visible, and to set up any more ->signal_connects()
        return 1;
    }

    sub winEnable {

        # Called by GA::Obj::Workspace->createGridWin or ->createSimpleGridWin
        # This generic function merely makes the Gtk3::Window visible
        # Your own window object code should either create a function based on
        #   GA::Win::Internal->winEnable, or your code should inherit that function directly
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Optional arguments
        #   $listRef    - Reference to a list of functions to call, just after the Gtk3::Window is
        #                   created (can be used to set up further ->signal_connects, if this
        #                   window needs them)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 on success

        my ($self, $title, $listRef, $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);

        # For windows about to be placed on a grid, briefly minimise the window so it doesn't
        #   appear in the centre of the desktop before being moved to its correct workspace, size
        #   and position
        if ($self->workspaceGridObj && $self->winWidget eq $self->winBox) {

            $self->minimise();
        }

        # Set up ->signal_connects specified by the calling function, if any
        if ($listRef) {

            foreach my $func (@$listRef) {

                $self->$func();
            }
        }

        return 1;
    }

#   sub winDisengage {}         # Defined in window objects which inherit this one

#   sub winDestroy {}           # Defined in window objects which inherit this one

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

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

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

    # ->signal_connects

    sub setDeleteEvent {

        # Called by GA::Obj::Workspace->createGridWin (for 'external' windows only)
        # Called by $self->winSetup (for other 'grid' windows)
        # This generic function doesn't actually create any ->signal_connects
        #
        # 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 . '->setDeleteEvent', @_);
        }

        # (Do nothing)

        return 1;
    }

    sub setKeyPressEvent {

        # Called by $self->winSetup
        # This generic function doesn't actually create any ->signal_connects

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

        }

        # Open a task window, giving up after the first successful attempt
        if (! @preferList) {

            @preferList = $self->winPreferList;
        }

        OUTER: foreach my $item (@preferList) {

            if ($item eq 'grid') {
                $result = $self->openGridWin($winmapObj);
            } elsif ($item eq 'pane') {
                $result = $self->openPaneWin($winmapObj, FALSE);
            } elsif ($item eq 'entry') {
                $result = $self->openPaneWin($winmapObj, TRUE);
            } elsif ($item eq 'pseudo') {
                $result = $self->openPseudoWin($winmapObj);
            }

            if ($result) {

                last OUTER;
            }
        }

        return $result;
    }

    sub openGridWin {

        # Called by the $self->openWin (only)
        # Tries to open a task window as a 'grid' window (in response to 'grid' in
        #   $self->winPreferList)
        #
        # Expected arguments
        #   $winmapObj  - The winmap object to use in the task window (matches a value in
        #                   GA::Client->winmapHash)
        #
        # Return values
        #   'undef' on improper arguments or if a 'grid' window is not opened
        #   1 if a 'grid' window is opened

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

        # Local variables
        my (
            $winObj,
            @workspaceList,
        );

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

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

        # Compile a list of workspaces, with the workspace containing the parent session's 'main'
        #   window first
        @workspaceList
            = $axmud::CLIENT->desktopObj->listWorkspaces($self->session->mainWin->workspaceObj);

        OUTER: foreach my $workspaceObj (@workspaceList) {

            $winObj = $workspaceObj->createGridWin(
                'custom',                   # All task windows are 'custom' windows
                $self->name,                # Window name is the same as the task name
                $self->prettyName,          # Window title
                $winmapObj->name,
                'Games::Axmud::Win::Internal',
                                            # Package name
                undef,                      # No windows exists yet
                undef,                      # Ditto
                $self,                      # Owner
                $self->session,
                $workspaceObj->findWorkspaceGrid($self->session),
                                            # Session's workspace grid object
            );

            if ($winObj) {

                last OUTER;
            }
        }

        if (! $winObj) {

            # Window not opened
            return undef;

        } else {

            # Window created and enabled
            $self->ivPoke('winObj', $winObj);
            $self->ivPoke('taskWinFlag', TRUE);
            # Set its title
            $self->setTaskWinTitle();

            # In Axmud 'blind' mode, make sure the session's 'main' window is not obscured by the
            #   newly-opened task window
            if ($axmud::BLIND_MODE_FLAG) {

                $self->session->mainWin->restoreFocus();
            }
        }

        # Add a tab, if required. The TRUE argument indicates window setup
        $self->addTab(undef, TRUE);
        # Set up the entry box, if present
        $self->setupEntry();

        return 1;
    }

    sub openPaneWin {

        # Called by the $self->openWin (only)
        # Tries to open a task window as a pane object (GA::Table::Pane) inside the session's 'main'
        #   window (in response to 'pane' or 'entry' in $self->winPreferList)
        #
        # Expected arguments

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


{ package Games::Axmud::Generic::Win;

    use strict;
    use warnings;
#   use diagnostics;

    use Glib qw(TRUE FALSE);

    our @ISA = qw(Games::Axmud);

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

#   sub new {}                  # Defined in window objects which inherit this one

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

    # Standard window object functions

#   sub winSetup {}             # Defined in window objects which inherit this one

#   sub winEnable {}            # Defined in window objects which inherit this one

#   sub winDisengage {}         # Defined in window objects which inherit this one

#   sub winDestroy {}           # Defined in window objects which inherit this one

    sub winShowAll {

        # Generic function to update the window itself to make any changes visible
        # If some code has called $self->setInvisible, then nothing happens (and the window
        #   remains invisible)
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Optional arguments
        #   $string    - For debugging purposes. Describes the calling function, e.g.
        #                   ->winShowAll($self->_objClass . '->callingFunction');
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

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

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

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

        if ($self->winWidget && $self->visibleFlag) {

            $self->winWidget->show_all();

            # Any textview objects (GA::Obj::TextView) which are waiting to update their size IVs
            #   can now do so
            foreach my $textViewObj ($axmud::CLIENT->desktopObj->ivValues('textViewHash')) {

                if ($textViewObj->sizeUpdateFlag && $textViewObj->winObj eq $self) {

                    $textViewObj->updateVisibleSize();
                }
            }

            # Optionally, write information about the calling function to the terminal (for
            #   debugging)
#           if ($string) {
#
#               print "->winShowAll() call from " . $string . " at " . $axmud::CLIENT->getTime()
#                       . "\n";
#
#           } else {
#
#               print "->winShowAll() call from unspecified function at "
#                       . $axmud::CLIENT->getTime() . "\n";
#           }
        }

        return 1;
    }

    sub drawWidgets {

        # Generic function to draw widgets within the window, usually called by $self->winSetup or
        #   $self->winEnable
        #
        # 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 . '->drawWidgets', @_);
        }

        # (Do nothing)

        return 1;
    }

    sub redrawWidgets {

        # Generic function to redraw widgets within the window
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 on success

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

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

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

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

        if (
            $self->winWidget eq $self->winBox
            && (! $flag || $self->winWidget->get_urgency_hint())
        ) {
            $self->winWidget->set_urgency_hint(FALSE);
            # The line above doesn't work (in Linux Mint with Cinnamon), so we'll do it the brutal
            #   way, too
            $self->restoreFocus();
            if ($self->session) {

                $self->session->mainWin->restoreFocus();
            }

            return 1;

        } else {

            # Hint not reset
            return undef;
        }
    }

    sub restoreFocus {

        # Can be called by any function (often after creating a 'dialogue' window or after
        #   re-stacking 'grid' windows)
        # Activates this window object's Gtk3::Window, if it is known. For 'internal' windows,
        #   returns the focus to the entry box in the 'Games::Axmud::Strip::Entry' strip object, if
        #   there is one
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or $self->winWidget is not set
        #   1 otherwise

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

        # Local variables
        my $stripObj;

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

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

        # Activate the Gtk3::Window, if it is known (for pseudo-windows, activate the parent window)
        if ($self->winType eq 'external') {

            # Activate the window
            $axmud::CLIENT->desktopObj->wmCtrlObj->wmctrl(
                '-a',
                $self->internalID,
                '-i',
            );

        } else {

            if (! $self->winWidget) {

                return undef;

            } else {

                $self->winWidget->present();
            }

            # For 'internal' windows, returns the focus to the entry box in the
            #   'Games::Axmud::Strip::Entry' strip object, if there is one
            if (
                $self->winType eq 'main'
                || $self->winType eq 'protocol'
                || $self->winType eq 'custom'
            ) {
                $stripObj = $self->ivShow('firstStripHash', 'Games::Axmud::Strip::Entry');
                if ($stripObj) {

                    $stripObj->entry->grab_focus();
                }
            }
        }

        return 1;
    }

    sub setVisible {

        # Can be called by any function
        # Makes the Gtk3::Window itself visible, and sets a flag so that calls to $self->winShowAll
        #   are carried out
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or $self->winWidget is not set
        #   1 otherwise

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

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

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

        if (! $self->winWidget) {

            return undef;

        } else {

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

        #                       added to any registry (usually because the user needs to name it
        #                       first). Set to FALSE (or 'undef') otherwise. Ignored if $editObj is
        #                       not specified
        #   %configHash     - Hash containing any number of key-value pairs needed for this
        #                       particular 'free' 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
        #
        # Return values
        #   'undef' on improper arguments or if the child 'free' window can't be created
        #   Otherwise returns the blessed reference to the child window

        my ($self, $packageName, $owner, $session, $title, $editObj, $tempFlag, %configHash) = @_;

        # Local variables
        my ($pluginName, $pluginObj, $class, $winObj);

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

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

        # All windows except 'external' and 'dialogue' windows can have a child 'free' window
        if ($self->winType eq 'external' || $self->winType eq 'dialogue') {

            return undef;
        }

        # If the window package was added by a plugin and the plugin is disabled, don't create the
        #   window
        $pluginName = $axmud::CLIENT->ivShow('pluginFreeWinHash', $packageName);
        if ($pluginName) {

            $pluginObj = $axmud::CLIENT->ivShow('pluginHash', $pluginName);
            if ($pluginObj && ! $pluginObj->enabledFlag) {

                return undef;
            }
        }

        # If no owner is specified, it's this window
        if (! $owner) {

            $owner = $self;

        # If an owner is specified, it must be a window object (inheriting from GA::Generic::Win)
        } elsif (! $owner->isa('Games::Axmud::Generic::Win')) {

            return undef;
        }

        # If no session is specified, it's the owner's session
        if (! $session) {

            $session = $owner->session;
        }

        # Create the 'free' window object
        $winObj = $packageName->new(
            $axmud::CLIENT->desktopObj->freeWinCount,
            $self->workspaceObj,
            $owner,
            $session,
            $title,
            $editObj,
            $tempFlag,
            %configHash,
        );

        # Check it's any 'free' window besides a 'dialogue' window
        if (! $winObj || $winObj->winCategory ne 'free' || $winObj->winType eq 'dialogue') {

            return undef;
        }

        # Make the window visible
        if (! $winObj->winSetup()) {

            return undef;
        }

        if (! $winObj->winEnable()) {

            return undef;
        }

        # Update IVs
        $axmud::CLIENT->desktopObj->add_freeWin($winObj);
        $self->add_childFreeWin($winObj);

        return $winObj;
    }

    sub quickFreeWin {

        # Shortcut to $self->createFreeWin, allowing the calling code to specify only the bare
        #   minimum of arguments
        #
        # Expected arguments
        #   $packageName    - The Perl object for the child 'free' window
        #
        # Optional arguments
        #   $session        - The GA::Session from which this function was called. If 'undef',
        #                       the new window's ->session is the same as this window's session
        #                       (which might be 'undef', too)
        #   %configHash     - Hash containing any number of key-value pairs needed for this
        #                       particular 'free' 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
        #
        # Return values
        #   'undef' on improper arguments or if the child 'free' window can't be created
        #   Otherwise returns the blessed reference to the child window

        my ($self, $packageName, $session, %configHash) = @_;

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

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

        return $self->createFreeWin(
            $packageName,       # Compulsory
            undef,
            $session,           # May be 'undef'
            undef,
            undef,
            undef,
            %configHash,
        );
    }

    # 'dialogue' windows

    sub closeDialogueWin {

        # Can be called by anything to close a 'dialogue' window early (especially one that won't
        #   close itself)
        # For example, called by GA::Client->start and GA::Session->setupProfiles after an earlier
        #   call to the 'dialogue' window created by $self->showBusyWin
        #
        # Expected arguments
        #   $dialogueWin    - The 'dialogue' window to close
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

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

            @argList, @list,
            %buttonHash,
        );

        # Check for improper arguments
        if (
            ! defined $title || ! defined $icon || ! defined $text || ! defined $buttonType
            || defined $check
        ) {
            return $axmud::CLIENT->writeImproper($self->_objClass . '->showMsgDialogue', @_);
        }

        # If an earlier call to $self->showBusyWin created a popup window, close it (otherwise it'll
        #   be visible above the new dialogue window)
        if ($axmud::CLIENT->busyWin) {

            $self->closeDialogueWin($axmud::CLIENT->busyWin);
        }

        # Set the correct spacing size for 'dialogue' windows
        $spacing = $axmud::CLIENT->constFreeSpacingPixels;

        # Check that $icon and $buttonType are valid values
        if (
            $icon ne 'info' && $icon ne 'warning' && $icon ne 'error' && $icon ne 'question'
        ) {
            return $axmud::CLIENT->writeError(
                'Unrecognised value \'' . $icon . '\' for icon argument',
                $self->_objClass . '->showMsgDialogue',
            );
        }

        # Convert $buttonType into an argument list
        if ($buttonType eq 'ok') {
            @argList = ('gtk-ok', 'ok');
        } elsif ($buttonType eq 'close') {
            @argList = ('gtk-close', 'close');
        } elsif ($buttonType eq 'cancel') {
            @argList = ('gtk-cancel', 'cancel');
        } elsif ($buttonType eq 'yes-no') {
            @argList = ('gtk-no', 'no', 'gtk-yes', 'yes');
        } elsif ($buttonType eq 'ok-cancel') {
            @argList = ('gtk-cancel', 'cancel', 'gtk-ok', 'ok');
        } elsif ($buttonType ne 'none') {

            return $axmud::CLIENT->writeError(
                'Unrecognised value \'' . $icon . '\' for button type argument',
                $self->_objClass . '->showMsgDialogue',
            );
        }

        # Show the 'dialogue' window
        my $dialogueWin = Gtk3::Dialog->new(
            $title,
            $self->winWidget,
            Gtk3::DialogFlags->new([qw/modal destroy-with-parent/]),
            @argList,
        );

        $dialogueWin->set_position('center-always');
        $dialogueWin->set_icon_list($axmud::CLIENT->desktopObj->{dialogueWinIconList});

        $dialogueWin->signal_connect('delete-event' => sub {

            $dialogueWin->destroy();
            $self->restoreFocus();

            # (In case TTS is being used and another 'dialogue' window is about to open, make sure
            #   the window is visibly closed)
            $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->showMsgDialogue');
        });

        # Set the default response, if specified
        if (defined $defaultResponse) {

            $dialogueWin->set_default_response($defaultResponse);
        }

        # Add widgets to the 'dialogue' window
        my $vBox = $dialogueWin->get_content_area();
        # The call to ->addDialogueIcon splits $vBox in two, with an icon on the left, and a new
        #   Gtk3::VBox on the right, into which we put everything
        my $vBox2 = $self->addDialogueIcon(
            $vBox,
            $axmud::SHARE_DIR . '/icons/replace/dialogue_replace_' . $icon . '.png',
            TRUE,               # Don't draw the image inside a frame
        );

        my $label = Gtk3::Label->new();
        $vBox2->pack_start($label, FALSE, FALSE, $spacing);
        $label->set_alignment(0, 0);

        # If $text is long, it produces a wider window than was produced in earlier version of Gtk.
        #   This doesn't look very nice, so split $text into lines with a maximum length
        # At the same time, we need to escape any < or > characters, or we'll get a Pango error)
        if (! $noSplitFlag) {

            $label->set_markup(
                Glib::Markup::escape_text(
                    $axmud::CLIENT->splitText(
                        $text,
                        0,                  # No maximum rows
                        $axmud::CLIENT->constDialogueLabelSize,
                                            # Maximum characters per line
                        FALSE,              # No ellipsis required
                        TRUE,               # Don't use hyphens when splitting words
                    )
                ),
            );

        } else {

            # Calling function has already added newline characters, so no split required
            $label->set_markup(Glib::Markup::escape_text($text));
        }

        # For the benefit of visually-impaired users who are using the 'tab' key to switch buttons,
        #   don't allow the label to receive focus
        $label->set_can_focus(FALSE);

        # Display the 'dialogue' window. Without this combination of Gtk calls, the window is not
        #   consistently active (don't know why this works; it just does)
        $dialogueWin->show_all();
        $dialogueWin->present();
        $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->showComboDialogue');

        # Prepare text-to-speech (TTS) code. Get a hash of possible response buttons, in the form
        #   $buttonHash{'response'} = Gtk3::Button (if the button is used), or 'undef' (if not)
        %buttonHash = (
            'ok', $dialogueWin->get_widget_for_response('ok'),
            'close', $dialogueWin->get_widget_for_response('close'),
            'cancel', $dialogueWin->get_widget_for_response('cancel'),
            'yes', $dialogueWin->get_widget_for_response('yes'),
            'no', $dialogueWin->get_widget_for_response('no'),
        );

        if ($axmud::CLIENT->systemAllowTTSFlag && $axmud::CLIENT->ttsDialogueFlag) {

            # Perform TTS for this window
            $axmud::CLIENT->ttsAddUrgentJob($title, 'dialogue');
            $axmud::CLIENT->ttsAddUrgentJob($text, 'dialogue');

            foreach my $response (keys %buttonHash) {

                my $button = $buttonHash{$response};

                if (defined $button) {

                    # Handy list of responses that are available in this dialogue
                    push (@list, $response);

                    $button->signal_connect('grab-focus' => sub {

                        my $label = $button->get_label();

                        # $label is in the form 'gtk-yes', 'gtk-no' etc
                        $axmud::CLIENT->ttsAddUrgentJob(
                            substr($label, 4) . ' button',
                            'dialogue',
                            # Override other TTS urgent jobs, such as the $title and $text above
                            TRUE,
                        );
                    });
                }
            }

            # (No need to read this message, if there's only one button)
            if (@list == 1) {

                $axmud::CLIENT->ttsAddUrgentJob(
                    $list[0] . ' button selected',
                    'dialogue',
                );

            } else {

                $axmud::CLIENT->ttsAddUrgentJob(
                    'Select ' . join (', or, ', @list),
                    'dialogue',
                );
            }
        }
        # (end of TTS code)

        # Get the response
        $response = $dialogueWin->run();
        if ($axmud::CLIENT->systemAllowTTSFlag && $axmud::CLIENT->ttsDialogueFlag) {

            if ($response && exists $buttonHash{$response}) {

                $axmud::CLIENT->ttsAddUrgentJob(
                    $response . ' selected',
                    'dialogue',
                    TRUE,
                );

            } else {

                $axmud::CLIENT->ttsAddUrgentJob(
                    'Cancelled',
                    'dialogue',
                    TRUE,
                );
            }
        }

        # Destroy the window and return the response
        $dialogueWin->destroy();
        $self->restoreFocus();

        # (In case TTS is being used and another 'dialogue' window is about to open, make sure the
        #   window is visibly closed)
        $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->showMsgDialogue');

        return $response;
    }

    sub showFileChooser {

        # Can be called by any function
        # Creates a standard Gtk3::FileChooserDialog and returns the response (if any)
        #
        # Expected arguments
        #   $title          - The title of the window, e.g. 'Select file to load'
        #   $type           - 'open', 'save', 'select-folder', 'create-folder'
        #
        # Optional arguments
        #   $arg            - If $type = 'open', set the current folder (this behaviour is
        #                       discouraged, but it's sometimes appropriate for Axmud code). If
        #                       $type = 'save', suggest a filename using $arg. Ignored if 'undef' or
        #                       if $type is not 'open' or 'save'
        #
        # Return values
        #   'undef' on improper arguments, if $type is invalid, if the file chooser window can't be
        #       opened or if no file is selected
        #   Otherwise returns a path to the selected file

        my ($self, $title, $type, $arg, $check) = @_;

        # Local variables
        my $fileName;

        # Check for improper arguments
        if (! defined $title || ! defined $type || defined $check) {

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

        # If an earlier call to $self->showBusyWin created a popup window, close it (otherwise it'll
        #   be visible above the new dialogue window)
        if ($axmud::CLIENT->busyWin) {

            $self->closeDialogueWin($axmud::CLIENT->busyWin);
        }

        # Check that $type is a valid type
        if (
            $type ne 'open' && $type ne 'save' && $type ne 'select-folder'
            && $type ne 'create-folder'
        ) {
            return $self->writeError(
                'Unrecognised file choose type \'' . $type . '\'',
                $self->_objClass . '->showFileChooser',
            );
        }

        # Open the file chooser window
        my $dialogueWin = Gtk3::FileChooserDialog->new(
            $title,
            $self->winWidget,
            $type,
            'gtk-cancel' => 'cancel',
            'gtk-ok' => 'ok'
        );

        if (! $dialogueWin) {

            return undef;
        }

        $dialogueWin->set_position('center-always');
        $dialogueWin->set_icon_list($axmud::CLIENT->desktopObj->{dialogueWinIconList});

        $dialogueWin->signal_connect('delete-event' => sub {

            $dialogueWin->destroy();
            $self->restoreFocus();
        });

        if (defined $arg) {

            # If loading a file, set the current folder
            if ($type eq 'open') {

                $dialogueWin->set_current_folder($arg);

            # If saving a file, suggest a filename
            } elsif ($type eq 'save' && defined $arg) {

                $dialogueWin->set_current_name($arg);
            }
        }

        # Get the file
        if ($dialogueWin->run eq 'ok') {

            $fileName = $dialogueWin->get_filename();
        }

        # Close the window
        $dialogueWin->destroy();
        $self->restoreFocus();

        # For saving, show a confirmation message
        if (defined $fileName){

            if (-f $fileName && $type eq 'save') {

                my $choice = $self->showMsgDialogue(
                    'Replace existing file',
                    'question',
                    'Overwrite existing file ' . $fileName . ' ?',
                    'yes-no',
                );

                # If the user selects 'no', return false
                if ($choice eq 'no') {

                    return undef;
                }
            }

            # Return the path of the selected file
            return $fileName;

        } else {

            # No file selected
            return undef;
        }
    }

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

        #                       $text is split into lines of no more than 40 characters
        #   $singleFlag     - Set when called by GA::CLIENT->connectBlind (or by any other code that
        #                       might want to remove the 'Cancel' button). If TRUE, only an 'OK'
        #                       button is used. If FALSE (or 'undef'), both an 'OK' and 'Cancel'
        #                       buttons are used
        #
        # Return values
        #   'undef' on improper arguments or if the user doesn't enter some text
        #   Otherwise returns the user response (the contents of the entry box)

        my (
            $self, $title, $text, $maxChars, $entryText, $obscureModeFlag, $noSplitFlag,
            $singleFlag, $check
        ) = @_;

        # Local variables
        my (
            $spacing, $starText, $lastThing, $response, $responseText, $confirmMsg,
            %buttonHash,
        );

        # Check for improper arguments
        if (! defined $title || ! defined $text || defined $check) {

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

        # If an earlier call to $self->showBusyWin created a popup window, close it (otherwise it'll
        #   be visible above the new dialogue window)
        if ($axmud::CLIENT->busyWin) {

            $self->closeDialogueWin($axmud::CLIENT->busyWin);
        }

        # Set the correct spacing size for 'dialogue' windows
        $spacing = $axmud::CLIENT->constFreeSpacingPixels;

        # Show the dialogue window
        my $dialogueWin;
        if ($singleFlag) {

            $dialogueWin = Gtk3::Dialog->new(
                $title,
                $self->winWidget,
                Gtk3::DialogFlags->new([qw/modal destroy-with-parent/]),
                'gtk-ok'     => 'accept',
            );

        } else {

            $dialogueWin = Gtk3::Dialog->new(
                $title,
                $self->winWidget,
                Gtk3::DialogFlags->new([qw/modal destroy-with-parent/]),
                'gtk-cancel' => 'reject',
                'gtk-ok'     => 'accept',
            );
        }

        $dialogueWin->set_position('center-always');
        $dialogueWin->set_icon_list($axmud::CLIENT->desktopObj->{dialogueWinIconList});

        $dialogueWin->signal_connect('delete-event' => sub {

            $dialogueWin->destroy();
            $self->restoreFocus();

            # (In case TTS is being used and another 'dialogue' window is about to open, make sure
            #   the window is visibly closed)
            $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->showEntryDialogue');
        });

        # Add widgets to the 'dialogue' window
        my $vBox = $dialogueWin->get_content_area();
        # The call to ->addDialogueIcon splits $vBox in two, with an icon on the left, and a new
        #   Gtk3::VBox on the right, into which we put everything
        my $vBox2 = $self->addDialogueIcon($vBox);

        my $label = Gtk3::Label->new();
        $vBox2->pack_start($label, FALSE, FALSE, $spacing);
        $label->set_alignment(0, 0);
        if (! $noSplitFlag) {

            $label->set_markup(
                Glib::Markup::escape_text(
                    $axmud::CLIENT->splitText(
                        $text,
                        0,                  # No maximum rows
                        $axmud::CLIENT->constDialogueLabelSize,
                                            # Maximum characters per line
                        FALSE,              # No ellipsis required
                        TRUE,               # Don't use hyphens when splitting words
                    )
                ),
            );

        } else {

            $label->set_markup(Glib::Markup::escape_text($text));
        }

        my $entry = Gtk3::Entry->new();
        $vBox2->pack_start($entry, FALSE, FALSE, $spacing);
        if (defined $maxChars && $axmud::CLIENT->intCheck($maxChars, 1)) {

            $entry->set_max_length($maxChars);
        }

        if ($obscureModeFlag) {

            $entry->set_visibility(FALSE);

            if ($entryText) {

                # Set the string to be used to disguise the number of characters in $entryText
                $starText = '********';
                # Just in case $entryText happens to be the same string, use a different string!
                if ($entryText eq $starText) {

                    $starText = 'xxxxxxxx';
                }

                $entry->set_text($starText);

            } else {

                # (Don't hide the fact that $entryText is an empty string, if it is so)
                $starText = '';
            }

        } elsif ($entryText) {

            # $obscureModeFlag is not set, so display $entryText
            $entry->set_text($entryText);
        }

        $entry->signal_connect('button_press_event' => sub {

            # In obscure mode, user can change the entry box's text in two ways - clicking on the
            #   box itself, in which case this event occurs (and we need to empty the box), or by
            #   tabbing focus through widgets, until the focus falls onto the box, in which case
            #   the box is emptied and replaced with the first keypress
            if ($obscureModeFlag && defined $starText) {

                $entry->set_text('');
                # When this function returns a value (below), we need to know whether the
                #   obscured text has been modified. Only need to do this once
                $starText = undef;
            }
        });

        $entry->signal_connect('changed' => sub {

            # If the text in the entry box has been modified, then we reset $starText (for the
            #   reasons described just above)
            $starText = undef;
        });

        $entry->signal_connect('activate' => sub {

            # Get the entry's text, because the code at the bottom of this function won't be
            #   able to retrieve it...
            $responseText = $entry->get_text();
            # ...after we destroy the 'dialogue' window
            $dialogueWin->destroy();
            $self->restoreFocus();
        });

        # Display the 'dialogue' window. Without this combination of Gtk calls, the window is not
        #   consistently active (don't know why this works; it just does)
        $dialogueWin->show_all();
        $dialogueWin->present();
        $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->showEntryDialogue');

        # Prepare text-to-speech (TTS) code. Get a hash of the response buttons, in the form
        #   $buttonHash{'response'} = Gtk3::Button
        $buttonHash{'ok'} =  $dialogueWin->get_widget_for_response('accept');
        if (! $singleFlag) {

            $buttonHash{'cancel'} =  $dialogueWin->get_widget_for_response('reject');
        }

        if ($axmud::CLIENT->systemAllowTTSFlag && $axmud::CLIENT->ttsDialogueFlag) {

            # Perform TTS for this window
            $axmud::CLIENT->ttsAddUrgentJob($title, 'dialogue');
            $axmud::CLIENT->ttsAddUrgentJob($text, 'dialogue');

            # Read out buttons, when in focus
            foreach my $response (keys %buttonHash) {

                my $button = $buttonHash{$response};

                $button->signal_connect('grab-focus' => sub {

                    my $label = $button->get_label();

                    if (! defined $lastThing || $lastThing ne $button) {

                        $axmud::CLIENT->ttsAddUrgentJob(
                            # ($label is in the form 'gtk-yes', 'gtk-no' etc)
                            substr($label, 4) . ' button',
                            'dialogue',
                            # Override other TTS urgent jobs, such as the $title and $text above
                            TRUE,
                        );
                    }

                    # Don't use TTS to read out the same widget consecutively
                    $lastThing = $button;
                });
            }

            $entry->signal_connect('grab-focus' => sub {

                # (Don't read anything out the first time, but if the user cycles through the keys
                #   and returns to the entry, using the tab key, read out something)
                if ($lastThing) {

                    if (! $entry->get_text()) {

                        $axmud::CLIENT->ttsAddUrgentJob(
                            'Type something here',
                            'dialogue',
                            TRUE,
                        );

                    } else {

                        $axmud::CLIENT->ttsAddUrgentJob(
                            'Entered: ' . $entry->get_text(),
                            'dialogue',
                            TRUE,
                        );
                    }
                }

                # Don't use TTS to read out the same widget consecutively
                $lastThing = $entry;
            });
        }

        # Get the response
        $response = $dialogueWin->run();
        if ($response eq 'accept' || $response eq 'none') {

            # If the user pressed the ENTER key, the entry's ->signal_connect for 'activate' stored
            #   the entry's text in $responseText, before destroying the window
            if (! $responseText) {

                $responseText = $entry->get_text();
            }

            if ($axmud::CLIENT->systemAllowTTSFlag && $axmud::CLIENT->ttsDialogueFlag) {

                if ($obscureModeFlag) {

                    $axmud::CLIENT->ttsAddUrgentJob(
                        'Text accepted',
                        'dialogue',
                        TRUE,
                    );

                } else {

                    $axmud::CLIENT->ttsAddUrgentJob(
                        'Entered: ' . $responseText,
                        'dialogue',
                        TRUE,
                    );
                }
            }

        } else {

            if ($axmud::CLIENT->systemAllowTTSFlag && $axmud::CLIENT->ttsDialogueFlag) {

                $axmud::CLIENT->ttsAddUrgentJob('Cancelled', 'dialogue', TRUE);
            }
        }

        # Destroy the window
        $dialogueWin->destroy();
        $self->restoreFocus();

        # (In case TTS is being used and another 'dialogue' window is about to open, make sure the
        #   window is visibly closed)
        $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->showEntryDialogue');

        # Return the response
        if ($obscureModeFlag && defined $starText) {

            # The obscured '********' text has not been modified, so we can return the original
            #   unmodified $entryText
            return $entryText;

        } else {

            # Otherwise, return the contents of the entry box
            return $responseText;
        }
    }

    sub showDoubleEntryDialogue {

        # Can be called by any function
        # Similar to $self->showEntryDialogue, but contains two entry boxes; returns the contents of
        #   both boxes
        #
        # Expected arguments
        #   $title          - The title to display, e.g. 'File Save'
        #   $labelText      - The label above the first entry box. Can be pango markup text, or just
        #                       plain text
        #
        # Optional arguments
        #   $labelText2     - The label above the second entry box. If 'undef', no second label is
        #                       used (but the second entry box is still used)
        #   $maxChars       - The maximum number of chars allowed in both entry boxes (if 'undef',
        #                       no maximum)
        #   $obscureMode    - Sets which of the entry boxes has its text obscured
        #                       - 'default' (or 'undef') - no text is obscured
        #                       - 'first'                - first box is obscured
        #                       - 'second'               - second box is obscured
        #                       - 'both'                 - both boxes are obscured
        #   $noSplitFlag    - If TRUE, the message $text is not automatically split into shorter
        #                       lines (because the calling function has already added newline
        #                       characters as it requires). If FALSE (or 'undef'), the message
        #                       $text is split into lines of no more than 40 characters
        #
        # Return values
        #   An empty list on improper arguments or if the user doesn't enter some text in either
        #       entry box
        #   Otherwise a list of two elements, containing the text in both entry boxes

        my (
            $self, $title, $labelText, $labelText2, $maxChars, $obscureMode, $noSplitFlag, $check
        ) = @_;

        # Local variables
        my (
            $spacing, $response, $responseText, $responseText2,
            @emptyList,
        );

        # If an earlier call to $self->showBusyWin created a popup window, close it (otherwise it'll
        #   be visible above the new dialogue window)
        if ($axmud::CLIENT->busyWin) {

            $self->closeDialogueWin($axmud::CLIENT->busyWin);
        }

        # Set the correct spacing size for 'dialogue' windows
        $spacing = $axmud::CLIENT->constFreeSpacingPixels;

        # Check for improper arguments
        if (! defined $title || ! defined $labelText || defined $check) {

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

        # Show the 'dialogue' window
        my $dialogueWin = Gtk3::Dialog->new(
            $title,
            $self->winWidget,
            Gtk3::DialogFlags->new([qw/modal destroy-with-parent/]),
            'gtk-cancel' => 'reject',
            'gtk-ok'     => 'accept',
        );

        $dialogueWin->set_position('center-always');
        $dialogueWin->set_icon_list($axmud::CLIENT->desktopObj->{dialogueWinIconList});

        $dialogueWin->signal_connect('delete-event' => sub {

            $dialogueWin->destroy();
            $self->restoreFocus();

            return @emptyList;
        });

        # Add widgets to the 'dialogue' window
        my $vBox = $dialogueWin->get_content_area();
        # The call to ->addDialogueIcon splits $vBox in two, with an icon on the left, and a new
        #   Gtk3::VBox on the right, into which we put everything
        my $vBox2 = $self->addDialogueIcon($vBox);

        # First label and entry
        my $label = Gtk3::Label->new();
        $vBox2->pack_start($label, FALSE, FALSE, $spacing);
        $label->set_alignment(0, 0);
        if (! $noSplitFlag) {

            $label->set_markup(
                Glib::Markup::escape_text(
                    $axmud::CLIENT->splitText(
                        $labelText,
                        0,                  # No maximum rows
                        $axmud::CLIENT->constDialogueLabelSize,
                                            # Maximum characters per line
                        FALSE,              # No ellipsis required
                        TRUE,               # Don't use hyphens when splitting words
                    )
                ),
            );

        } else {

            $label->set_markup(Glib::Markup::escape_text($labelText));
        }

        my $entry = Gtk3::Entry->new();
        $vBox2->pack_start($entry, FALSE, FALSE, $spacing);
        if (defined $maxChars && $axmud::CLIENT->intCheck($maxChars, 1)) {

            $entry->set_max_length($maxChars);
        }

        # Second label and entry
        if ($labelText2) {

            my $label2 = Gtk3::Label->new();
            $vBox2->pack_start($label2, FALSE, FALSE, $spacing);
            $label2->set_alignment(0, 0);

            if (! $noSplitFlag) {

                $label2->set_markup(
                    Glib::Markup::escape_text(
                        $axmud::CLIENT->splitText(
                            $labelText2,
                            0,                  # No maximum rows
                            $axmud::CLIENT->constDialogueLabelSize,
                                                # Maximum characters per line
                            FALSE,              # No ellipsis required
                            TRUE,               # Don't use hyphens when splitting words
                        )
                    ),
                );

            } else {

                $label2->set_markup(Glib::Markup::escape_text($labelText2));
            }
        }

        my $entry2 = Gtk3::Entry->new();
        $vBox2->pack_start($entry2, FALSE, FALSE, $spacing);
        if (defined $maxChars && $axmud::CLIENT->intCheck($maxChars, 1)) {

            $entry2->set_max_length($maxChars);
        }

        # Obscure text in the entry boxes, if necessary
        if ($obscureMode && $obscureMode ne 'default') {

            if ($obscureMode eq 'first' || $obscureMode eq 'both') {

                $entry->set_visibility(FALSE);
            }

            if ($obscureMode eq 'second' || $obscureMode eq 'both') {

                $entry2->set_visibility(FALSE);
            }
        }

        # Display the 'dialogue' window. Without this combination of Gtk calls, the window is not
        #   consistently active (don't know why this works; it just does)
        $dialogueWin->show_all();
        $dialogueWin->present();
        $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->showDoubleEntryDialogue');

        # Get the response. If the user clicked 'cancel', $response will be 'reject'
        # Otherwise, user clicked 'ok', and we need to get the contents of the two boxes
        $response = $dialogueWin->run();
        if ($response eq 'accept') {

            $responseText = $entry->get_text();
            $responseText2 = $entry2->get_text();

            # Destroy the window
            $dialogueWin->destroy();
            $self->restoreFocus();

            # Return the response
            return ($responseText, $responseText2);

        } else {

            # Destroy the window
            $dialogueWin->destroy();
            $self->restoreFocus();

            # Return the response
            return @emptyList;
        }
    }

    sub showTripleEntryDialogue {

        # Can be called by any function
        # Similar to $self->showEntryDialogue, but contains three entry boxes; returns the contents
        #   of all three boxes
        #
        # Expected arguments
        #   $title          - The title to display, e.g. 'File Save'
        #   $labelText      - The label above the first entry box. Can be pango markup text, or just
        #                       plain text
        #
        # Optional arguments
        #   $labelText2     - The label above the second entry box. If 'undef', no second label is
        #                       used (but the second entry box is still used)
        #   $labelText3     - The label above the third entry box. If 'undef', no third label is
        #                       used (but the third entry box is still used)
        #   $maxChars       - The maximum number of chars allowed in all entry boxes (if 'undef',
        #                       no maximum)
        #   $obscureMode    - Sets which of the entry boxes has its text obscured
        #                       - 0 (or 'undef')    - no text is obscured (000)
        #                       - 1                 - first box is obscured (001)
        #                       - 2                 - second box is obscured (010)
        #                       - 3                 - first/second boxes are obscured (011)
        #                       - 4                 - third box is obscured (100)
        #                       - 5                 - first/third boxes are obscured (101)
        #                       - 6                 - second/third boxes are obscured (110)
        #                       - 7                 - all boxes are obscured (111)
        #   $noSplitFlag    - If TRUE, the message $text is not automatically split into shorter
        #                       lines (because the calling function has already added newline
        #                       characters as it requires). If FALSE (or 'undef'), the message
        #                       $text is split into lines of no more than 40 characters
        #
        # Return values
        #   An empty list on improper arguments or if the user doesn't enter some text in either
        #       entry box
        #   Otherwise a list of three elements, containing the text in both entry boxes

        my (
            $self, $title, $labelText, $labelText2, $labelText3, $maxChars, $obscureMode,
            $noSplitFlag, $check,
        ) = @_;

        # Local variables
        my (
            $spacing, $response, $responseText, $responseText2, $responseText3,
            @emptyList,
        );

        # Check for improper arguments
        if (! defined $title || ! defined $labelText || defined $check) {

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

        # If an earlier call to $self->showBusyWin created a popup window, close it (otherwise it'll
        #   be visible above the new dialogue window)
        if ($axmud::CLIENT->busyWin) {

            $self->closeDialogueWin($axmud::CLIENT->busyWin);
        }

        # Set the correct spacing size for 'dialogue' windows
        $spacing = $axmud::CLIENT->constFreeSpacingPixels;

        # Show the 'dialogue' window
        my $dialogueWin = Gtk3::Dialog->new(
            $title,
            $self->winWidget,
            Gtk3::DialogFlags->new([qw/modal destroy-with-parent/]),
            'gtk-cancel' => 'reject',
            'gtk-ok'     => 'accept',
        );

        $dialogueWin->set_position('center-always');
        $dialogueWin->set_icon_list($axmud::CLIENT->desktopObj->{dialogueWinIconList});

        $dialogueWin->signal_connect('delete-event' => sub {

            $dialogueWin->destroy();
            $self->restoreFocus();

            return @emptyList;
        });

        # Add widgets to the 'dialogue' window
        my $vBox = $dialogueWin->get_content_area();
        # The call to ->addDialogueIcon splits $vBox in two, with an icon on the left, and a new
        #   Gtk3::VBox on the right, into which we put everything
        my $vBox2 = $self->addDialogueIcon($vBox);

        # First label and entry
        my $label = Gtk3::Label->new();
        $vBox2->pack_start($label, FALSE, FALSE, $spacing);
        $label->set_alignment(0, 0);
        if (! $noSplitFlag) {

            $label->set_markup(
                Glib::Markup::escape_text(
                    $axmud::CLIENT->splitText(
                        $labelText,
                        0,                  # No maximum rows
                        $axmud::CLIENT->constDialogueLabelSize,
                                            # Maximum characters per line
                        FALSE,              # No ellipsis required
                        TRUE,               # Don't use hyphens when splitting words
                    )
                ),
            );

        } else {

            $label->set_markup(Glib::Markup::escape_text($labelText));
        }

        my $entry = Gtk3::Entry->new();
        $vBox2->pack_start($entry, FALSE, FALSE, $spacing);
        if (defined $maxChars && $axmud::CLIENT->intCheck($maxChars, 1)) {

            $entry->set_max_length($maxChars);
        }

        # Second label and entry
        if ($labelText2) {

            my $label2 = Gtk3::Label->new();
            $vBox2->pack_start($label2, FALSE, FALSE, $spacing);
            $label2->set_alignment(0, 0);

            if (! $noSplitFlag) {

                $label2->set_markup(
                    Glib::Markup::escape_text(
                        $axmud::CLIENT->splitText(
                            $labelText2,
                            0,                  # No maximum rows

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

        }

        # Third label and entry
        if ($labelText3) {

            my $label3 = Gtk3::Label->new();
            $vBox2->pack_start($label3, FALSE, FALSE, $spacing);
            $label3->set_alignment(0, 0);

            if (! $noSplitFlag) {

                $label3->set_markup(
                    Glib::Markup::escape_text(
                        $axmud::CLIENT->splitText(
                            $labelText3,
                            0,                  # No maximum rows
                            $axmud::CLIENT->constDialogueLabelSize,
                                                # Maximum characters per line
                            FALSE,              # No ellipsis required
                            TRUE,               # Don't use hyphens when splitting words
                        )
                    ),
                );

            } else {

                $label3->set_markup(Glib::Markup::escape_text($labelText3));
            }
        }

        my $entry3 = Gtk3::Entry->new();
        $vBox2->pack_start($entry3, FALSE, FALSE, $spacing);
        if (defined $maxChars && $axmud::CLIENT->intCheck($maxChars, 1)) {

            $entry3->set_max_length($maxChars);
        }

        # Obscure text in the entry boxes, if necessary
        if ($obscureMode) {

            if ($obscureMode == 1 || $obscureMode == 3 || $obscureMode == 5 || $obscureMode == 7) {

                $entry->set_visibility(FALSE);
            }

            if ($obscureMode == 2 || $obscureMode == 3 || $obscureMode >= 6) {

                $entry2->set_visibility(FALSE);
            }

            if ($obscureMode >= 4) {

                $entry3->set_visibility(FALSE);
            }
        }

        # Display the 'dialogue' window. Without this combination of Gtk calls, the window is not
        #   consistently active (don't know why this works; it just does)
        $dialogueWin->show_all();
        $dialogueWin->present();
        $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->showTripleComboDialogue');

        # Get the responses. If the user clicked 'cancel', $response will be 'reject'
        # Otherwise, user clicked 'ok', and we need to get the contents of the two boxes
        $response = $dialogueWin->run();
        if ($response eq 'accept') {

            $responseText = $entry->get_text();
            $responseText2 = $entry2->get_text();
            $responseText3 = $entry3->get_text();

            # Destroy the window
            $dialogueWin->destroy();
            $self->restoreFocus();

            # Return the response
            return ($responseText, $responseText2, $responseText3);

        } else {

            # Destroy the window
            $dialogueWin->destroy();
            $self->restoreFocus();

            # Return the response
            return @emptyList;
        }
    }

    sub showComboDialogue {

        # Can be called by any function
        # Shows a short message in a 'dialogue' window with the buttons 'ok' and 'cancel'
        # Prompts the user to choose a line from a combobox; returns the chosen line if the 'ok'
        #   button is pressed, but 'undef' if either the cancel button is pressed or the window is
        #   closed
        #
        # Expected arguments
        #   $title          - The title to display, e.g. 'File Save'
        #   $text           - The message to display. Can be pango markup text, or just plain text
        #
        # Optional arguments
        #   $listRef        - Reference to a list of scalars to be used in the combo box. If
        #                       'undef', the combo box will be empty
        #   $singleFlag     - Set when called by GA::CLIENT->connectBlind (or by any other code that
        #                       might want to remove the 'Cancel' button). If TRUE, only an 'OK'
        #                       button is used. If FALSE (or 'undef'), both an 'OK' and 'Cancel'
        #                       buttons are used
        #   $noSplitFlag    - If TRUE, the message $text is not automatically split into shorter
        #                       lines (because the calling function has already added newline
        #                       characters as it requires). If FALSE (or 'undef'), the message
        #                       $text is split into lines of no more than 40 characters
        #
        # Return values
        #   'undef' on improper arguments, if the user doesn't choose a line or if @lineList is
        #       empty
        #   Otherwise returns the user response (the text of the selected line)

        my ($self, $title, $text, $listRef, $singleFlag, $noSplitFlag, $check) = @_;

        # Local variables
        my (
            $spacing, $lastThing, $response, $responseText,
            %buttonHash,
        );

        # Check for improper arguments
        if (! defined $title || ! defined $text || defined $check) {

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

        # If an earlier call to $self->showBusyWin created a popup window, close it (otherwise it'll
        #   be visible above the new dialogue window)
        if ($axmud::CLIENT->busyWin) {

            $self->closeDialogueWin($axmud::CLIENT->busyWin);
        }

        # Set the correct spacing size for 'dialogue' windows
        $spacing = $axmud::CLIENT->constFreeSpacingPixels;

        # If $listRef was not specified, use an empty list
        if (! defined $listRef) {

            @$listRef = ();
        }

        # Show the 'dialogue' window. If $listRef is empty, don't show a 'cancel' button
        my $dialogueWin;
        if (! @$listRef || $singleFlag) {

            $dialogueWin = Gtk3::Dialog->new(
                $title,
                $self->winWidget,
                Gtk3::DialogFlags->new([qw/modal destroy-with-parent/]),
                'gtk-ok'     => 'accept',
            );

        } else {

            $dialogueWin = Gtk3::Dialog->new(
                $title,
                $self->winWidget,
                Gtk3::DialogFlags->new([qw/modal destroy-with-parent/]),
                'gtk-cancel' => 'reject',
                'gtk-ok'     => 'accept',
            );
        }

        $dialogueWin->set_position('center-always');
        $dialogueWin->set_icon_list($axmud::CLIENT->desktopObj->{dialogueWinIconList});

        $dialogueWin->signal_connect('delete-event' => sub {

            $dialogueWin->destroy();
            $self->restoreFocus();

            # (In case TTS is being used and another 'dialogue' window is about to open, make sure
            #   the window is visibly closed)
            $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->showComboDialogue');
        });

        # Add widgets to the 'dialogue' window
        my $vBox = $dialogueWin->get_content_area();
        # The call to ->addDialogueIcon splits $vBox in two, with an icon on the left, and a new
        #   Gtk3::VBox on the right, into which we put everything
        my $vBox2 = $self->addDialogueIcon($vBox);

        my $label = Gtk3::Label->new();
        $vBox2->pack_start($label, FALSE, FALSE, $spacing);
        $label->set_alignment(0, 0);

        if (! $noSplitFlag) {

            $label->set_markup(
                Glib::Markup::escape_text(
                    $axmud::CLIENT->splitText(
                        $text,
                        0,                  # No maximum rows
                        $axmud::CLIENT->constDialogueLabelSize,
                                            # Maximum characters per line
                        FALSE,              # No ellipsis required
                        TRUE,               # Don't use hyphens when splitting words
                    )
                ),
            );

        } else {

            $label->set_markup(Glib::Markup::escape_text($text));
        }

        my $comboBox = Gtk3::ComboBoxText->new();
        $vBox2->pack_start($comboBox, FALSE, FALSE, $spacing);
        # Fill the combobox with the specified lines, and display the first line
        foreach my $line (@$listRef) {

            $comboBox->append_text($line);
        }
        $comboBox->set_active(0);

        # Display the 'dialogue' window. Without this combination of Gtk calls, the window is not
        #   consistently active (don't know why this works; it just does)
        $dialogueWin->show_all();
        $dialogueWin->present();
        $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->showComboDialogue');

        # Prepare text-to-speech (TTS) code. Get a hash of the response buttons, in the form
        #   $buttonHash{'response'} = Gtk3::Button
        $buttonHash{'ok'} = $dialogueWin->get_widget_for_response('accept');
        if (@$listRef && ! $singleFlag) {

            $buttonHash{'cancel'} = $dialogueWin->get_widget_for_response('reject');
        }

        if ($axmud::CLIENT->systemAllowTTSFlag && $axmud::CLIENT->ttsDialogueFlag) {

            # Perform TTS for this window
            $axmud::CLIENT->ttsAddUrgentJob($title, 'dialogue');
            $axmud::CLIENT->ttsAddUrgentJob($text, 'dialogue');

            # Read out buttons, when in focus
            foreach my $response (keys %buttonHash) {

                my $button = $buttonHash{$response};

                $button->signal_connect('grab-focus' => sub {

                    my $label = $button->get_label();

                    if (! defined $lastThing || $lastThing ne $button) {

                        $axmud::CLIENT->ttsAddUrgentJob(
                            # ($label is in the form 'gtk-yes', 'gtk-no' etc)
                            substr($label, 4) . ' button',
                            'dialogue',
                            # Override other TTS urgent jobs, such as the $title and $text above
                            TRUE,
                        );
                    }

                    # Don't use TTS to read out the same button label consecutively
                    $lastThing = $button;
                });
            }

            # Intercept page up/page down, and make it skip 10 lines, rather than going to the
            #   top/bottom
            $comboBox->signal_connect('key-press-event' => sub {

                my ($widget, $event) = @_;

                # Local variables
                my ($keycode, $standard, $index);

                # Get the system keycode for this keypress
                $keycode = Gtk3::Gdk::keyval_name($event->keyval);
                # Translate it into a standard Axmud keycode
                $standard = $axmud::CLIENT->reverseKeycode($keycode);

                if ($standard eq 'page_up' || $standard eq 'page_down') {

                    $index = $comboBox->get_active();
                    if ($index > -1) {

                        if ($standard eq 'page_up') {

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

                $lastThing = $text;

                return undef;
            });

            # Make sure that the first item in the combobox has been read out
            if (! defined $lastThing || (@$listRef && $lastThing ne $$listRef[0])) {

                if (@$listRef) {

                    $axmud::CLIENT->ttsAddUrgentJob(
                        $$listRef[0] . ' selected',
                        'dialogue',
                    );

                } else {

                    $axmud::CLIENT->ttsAddUrgentJob(
                        'There is nothing to select',
                        'dialogue',
                    );
                }
            }

            # Don't use TTS to read out the same combo item consecutively
            if (@$listRef) {

                $lastThing = $$listRef[0];
            }
        }

        # Get the response
        $response = $dialogueWin->run();
        if ($response eq 'accept') {

            $responseText = $comboBox->get_active_text();

            if ($axmud::CLIENT->systemAllowTTSFlag && $axmud::CLIENT->ttsDialogueFlag) {

                $axmud::CLIENT->ttsAddUrgentJob(
                    $responseText . ' entered',
                    'dialogue',
                    TRUE,
                );
            }

        } else {

            if ($axmud::CLIENT->systemAllowTTSFlag && $axmud::CLIENT->ttsDialogueFlag) {

                $axmud::CLIENT->ttsAddUrgentJob('Cancelled', 'dialogue', TRUE);
            }
        }

        # Destroy the window
        $dialogueWin->destroy();
        $self->restoreFocus();

        # (In case TTS is being used and another 'dialogue' window is about to open, make sure the
        #   window is visibly closed)
        $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->showComboDialogue');

        return $responseText;
    }

    sub showDoubleComboDialogue {

        # Can be called by any function
        # Similar to $self->showDoubleEntryDialogue, but contains two combo boxes; returns the
        #   contents of both boxes
        #
        # Expected arguments
        #   $title          - The title to display, e.g. 'File Save'
        #
        # Optional arguments
        #   $labelText      - The label above the first combo box. Can be pango markup text, or just
        #                       plain text. If 'undef', no first label is used (but the combo box is
        #                       still used)
        #   $labelText2     - The label above the second combo box. If 'undef', no second label is
        #                       used (but the combo box is still used)
        #   $listRef        - Reference to a list of scalars to be used in the first combo box. If
        #                       'undef', the first combo box will be empty
        #   $listRef2       - Reference to a list of scalars to be used in the second combo box. If
        #                       'undef', the second combo box will be empty
        #   $noSplitFlag    - If TRUE, the message $text is not automatically split into shorter
        #                       lines (because the calling function has already added newline
        #                       characters as it requires). If FALSE (or 'undef'), the message
        #                       $text is split into lines of no more than 40 characters
        #
        # Return values
        #   An empty list on improper arguments
        #   Otherwise a list of two elements, containing the contents of the two combo boxes

        my ($self, $title, $labelText, $labelText2, $listRef, $listRef2, $noSplitFlag, $check) = @_;

        # Local variables
        my (
            $spacing, $response, $responseText, $responseText2,
            @emptyList,
        );

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

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

        # If an earlier call to $self->showBusyWin created a popup window, close it (otherwise it'll
        #   be visible above the new dialogue window)
        if ($axmud::CLIENT->busyWin) {

            $self->closeDialogueWin($axmud::CLIENT->busyWin);
        }

        # Set the correct spacing size for 'dialogue' windows
        $spacing = $axmud::CLIENT->constFreeSpacingPixels;

        # If $listRef/$listRef2 were not specified, use empty lists
        if (! defined $listRef) {

            @$listRef = ();
        }

        if (! defined $listRef2) {

            @$listRef2 = ();
        }

        # Show the 'dialogue' window
        my $dialogueWin = Gtk3::Dialog->new(
            $title,
            $self->winWidget,
            Gtk3::DialogFlags->new([qw/modal destroy-with-parent/]),
            'gtk-cancel' => 'reject',
            'gtk-ok'     => 'accept',
        );

        $dialogueWin->set_position('center-always');
        $dialogueWin->set_icon_list($axmud::CLIENT->desktopObj->{dialogueWinIconList});

        $dialogueWin->signal_connect('delete-event' => sub {

            $dialogueWin->destroy();
            $self->restoreFocus();

            return @emptyList;
        });

        # Add widgets to the 'dialogue' window
        my $vBox = $dialogueWin->get_content_area();
        # The call to ->addDialogueIcon splits $vBox in two, with an icon on the left, and a new
        #   Gtk3::VBox on the right, into which we put everything
        my $vBox2 = $self->addDialogueIcon($vBox);

        # First label (optional) and combo (not optional)
        my $label;
        if ($labelText) {

            $label = Gtk3::Label->new();
            $label->set_alignment(0, 0);

            if (! $noSplitFlag) {

                $label->set_markup(
                    Glib::Markup::escape_text(
                        $axmud::CLIENT->splitText(
                            $labelText,
                            0,                  # No maximum rows
                            $axmud::CLIENT->constDialogueLabelSize,
                                                # Maximum characters per line
                            FALSE,              # No ellipsis required
                            TRUE,               # Don't use hyphens when splitting words
                        )
                    ),
                );

            } else {

                $label->set_markup(Glib::Markup::escape_text($labelText));
            }

            $vBox2->pack_start($label, FALSE, FALSE, $spacing);
        }

        my $combo = Gtk3::ComboBoxText->new();
        $vBox2->pack_start($combo, FALSE, FALSE, $spacing);

        # Fill the combo box with the specified lines, and display the first line
        if (@$listRef) {

            foreach my $line (@$listRef) {

                $combo->append_text($line);
            }

            $combo->set_active(0);
        }

        # Second label (optional) and combo (not optional)

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

        #                       'undef'), shows an entry above a combo box
        #   $quickFlag      - If set to TRUE, pressing the ENTER key while the cursor is in the
        #                       entry box closes the window; FALSE or 'undef' if the user must
        #                       actually click the 'OK' or 'Cancel' buttons
        #                       buttons are used
        #   $noSplitFlag    - If TRUE, the message $text is not automatically split into shorter
        #                       lines (because the calling function has already added newline
        #                       characters as it requires). If FALSE (or 'undef'), the message
        #                       $text is split into lines of no more than 40 characters
        #
        # Return values
        #   An empty list on improper arguments or if the user doesn't enter some text in the entry
        #       box
        #   Otherwise a list of two elements, containing the contents of the entry box and the
        #       active contents of the combo box

        my (
            $self, $title, $labelText, $labelText2, $listRef, $maxChars, $reverseFlag, $quickFlag,
            $noSplitFlag, $check,
        ) = @_;

        # Local variables
        my (
            $spacing, $response, $responseText, $responseText2,
            @emptyList,
        );

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

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

        # If an earlier call to $self->showBusyWin created a popup window, close it (otherwise it'll
        #   be visible above the new dialogue window)
        if ($axmud::CLIENT->busyWin) {

            $self->closeDialogueWin($axmud::CLIENT->busyWin);
        }

        # Set the correct spacing size for 'dialogue' windows
        $spacing = $axmud::CLIENT->constFreeSpacingPixels;

        # If $listRef was not specified, use an empty list
        if (! defined $listRef) {

            @$listRef = ();
        }

        # Show the 'dialogue' window
        my $dialogueWin = Gtk3::Dialog->new(
            $title,
            $self->winWidget,
            Gtk3::DialogFlags->new([qw/modal destroy-with-parent/]),
            'gtk-cancel' => 'reject',
            'gtk-ok'     => 'accept',
        );

        $dialogueWin->set_position('center-always');
        $dialogueWin->set_icon_list($axmud::CLIENT->desktopObj->{dialogueWinIconList});

        $dialogueWin->signal_connect('delete-event' => sub {

            $dialogueWin->destroy();
            $self->restoreFocus();

            return @emptyList;
        });

        # Add widgets to the 'dialogue' window
        my $vBox = $dialogueWin->get_content_area();
        # The call to ->addDialogueIcon splits $vBox in two, with an icon on the left, and a new
        #   Gtk3::VBox on the right, into which we put everything
        my $vBox2 = $self->addDialogueIcon($vBox);

        # First label (optional) and entry (not optional)
        my $label;
        if ($labelText) {

            $label = Gtk3::Label->new();
            $label->set_alignment(0, 0);

            if (! $noSplitFlag) {

                $label->set_markup(
                    Glib::Markup::escape_text(
                        $axmud::CLIENT->splitText(
                            $labelText,
                            0,                  # No maximum rows
                            $axmud::CLIENT->constDialogueLabelSize,
                                                # Maximum characters per line
                            FALSE,              # No ellipsis required
                            TRUE,               # Don't use hyphens when splitting words
                        )
                    ),
                );

            } else {

                $label->set_markup(Glib::Markup::escape_text($labelText));
            }

        }

        my $entry = Gtk3::Entry->new();
        if (defined $maxChars && $axmud::CLIENT->intCheck($maxChars, 1)) {

            $entry->set_max_length($maxChars);
        }

        # Second label (optional) and combo (not optional)
        my $label2;
        if ($labelText2) {

            $label2 = Gtk3::Label->new();
            $label2->set_alignment(0, 0);

            if (! $noSplitFlag) {

                $label2->set_markup(

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

        if (@$listRef) {

            foreach my $line (@$listRef) {

                $combo->append_text($line);
            }

            $combo->set_active(0);
        }

        # Arrange the entry and combo in the specified order
        if (! $reverseFlag) {

            if ($labelText) {

                $vBox2->pack_start($label, FALSE, FALSE, $spacing);
            }

            $vBox2->pack_start($entry, FALSE, FALSE, $spacing);

            if ($labelText2) {

                $vBox2->pack_start($label2, FALSE, FALSE, $spacing);
            }

            $vBox2->pack_start($combo, FALSE, FALSE, $spacing);

        } else {

            if ($labelText2) {

                $vBox2->pack_start($label2, FALSE, FALSE, $spacing);
            }

            $vBox2->pack_start($combo, FALSE, FALSE, $spacing);

            if ($labelText) {

                $vBox2->pack_start($label, FALSE, FALSE, $spacing);
            }

            $vBox2->pack_start($entry, FALSE, FALSE, $spacing);
        }

        if ($quickFlag) {

            $entry->signal_connect('activate' => sub {

                $responseText = $entry->get_text();
                $responseText2 = $combo->get_active_text();

                # Destroy the window
                $dialogueWin->destroy();
            });
        }

        # Display the 'dialogue' window. Without this combination of Gtk calls, the window is not
        #   consistently active (don't know why this works; it just does)
        $dialogueWin->show_all();
        $dialogueWin->present();
        $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->showEntryComboDialogue');

        # Get the response. If the user clicked 'cancel', $response will be 'reject'
        # Otherwise, user clicked 'ok', and we need to get the contents of the two boxes
        $response = $dialogueWin->run();
        if (defined $responseText) {

            # (User pressed their ENTER key when $quickFlag is TRUE)

            # Destroy the window
            $dialogueWin->destroy();
            $self->restoreFocus();

            # Return the response
            return ($responseText, $responseText2);

        } elsif ($response eq 'accept' || defined $responseText) {

            $responseText = $entry->get_text();
            $responseText2 = $combo->get_active_text();

            # Destroy the window
            $dialogueWin->destroy();
            $self->restoreFocus();

            # Return the response
            return ($responseText, $responseText2);

        } else {

            # Destroy the window
            $dialogueWin->destroy();
            $self->restoreFocus();

            # Return the response
            return @emptyList;
        }
    }

    sub showColourSelectionDialogue {

        # Can be called by any function
        # Creates a standard Gtk3::ColorSelectionDialog and returns the response (if any)
        #
        # Expected arguments
        #   $title          - The title to display, e.g. 'Select colour'
        #
        # Optional arguments
        #   $initialColour  - The initial colour to use, in the form '#FFFFFF'. If not specified,
        #                       the dialogue's default colour ('#FFFFFF') is used
        #
        # Return values
        #   'undef' on expected arguments or if the user doesn't close the 'dialogue' window by
        #       clicking the 'ok' button
        #   Otherwise, returns the colour selected, in the form '#FFFFFF'

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

        # Local variables
        my ($colorSelectionObj, $red, $green, $blue, $colorObj, $response, $hex);

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

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

        # If an earlier call to $self->showBusyWin created a popup window, close it (otherwise it'll
        #   be visible above the new dialogue window)
        if ($axmud::CLIENT->busyWin) {

            $self->closeDialogueWin($axmud::CLIENT->busyWin);
        }

        # Show the 'dialogue' window
        my $dialogueWin = Gtk3::ColorSelectionDialog->new($title);
        $dialogueWin->set_transient_for($self->winWidget);

        $dialogueWin->set_position('center-always');
        $dialogueWin->set_icon_list($axmud::CLIENT->desktopObj->{dialogueWinIconList});

        $colorSelectionObj = $dialogueWin->get_color_selection();

        if ($initialColour) {

            # Split a string like '#FFFFFF' into three seperate colours (red, green and blue),
            #   convert them to decimals (in the range 0-255), and then convert that to a range of
            #   0-65535 - which is what Gtk3::Gdk::Color expects
            $red = hex(substr($initialColour, 1, 2)) * 257;
            $green = hex(substr($initialColour, 3, 2)) * 257;
            $blue = hex(substr($initialColour, 5, 2)) * 257;

            $colorObj = Gtk3::Gdk::Color->new($red, $green, $blue, 0);

            # Tell the Gtk3::ColorSelectionDialog to use this colour, initially
            $colorSelectionObj->set_current_color($colorObj);
        }

        # Get the response
        $response = $dialogueWin->run();
        if ($response eq 'ok') {

            # This is probably not the best way of converting #ffff25812581 to #FF2525, but it will
            #   have to do, for now
            $hex = '#' . uc(
                sprintf('%02x', int($colorSelectionObj->get_current_color->red() / 256))
                . sprintf('%02x', int($colorSelectionObj->get_current_color->green() / 256))
                . sprintf('%02x', int($colorSelectionObj->get_current_color->blue() / 256))
            );
        }

        # Destroy the window
        $dialogueWin->destroy();

        # Return the colour (or 'undef' if no colour was selected)
        return $hex;
    }

    sub showFontSelectionDialogue {

        # Can be called by any function
        # Creates a standard Gtk3::FontSelectionDialog and returns the response (if any)
        #
        # Expected arguments
        #   $title          - The title to display, e.g. 'Select font'
        #
        # Optional arguments
        #   $initialFont    - The initial font and size to use, a string in the form 'Monospace 10'
        #
        # Return values
        #   'undef' on expected arguments or if the user doesn't close the 'dialogue' window by
        #       clicking the 'ok' button
        #   Otherwise, returns the font selected as a string in the form 'Monospace 10'

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

        # Local variables
        my ($response, $newFont);

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

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

        # If an earlier call to $self->showBusyWin created a popup window, close it (otherwise it'll
        #   be visible above the new dialogue window)
        if ($axmud::CLIENT->busyWin) {

            $self->closeDialogueWin($axmud::CLIENT->busyWin);
        }

        # Show the 'dialogue' window
        my $dialogueWin = Gtk3::FontChooserDialog->new($title, $self->winWidget);
        $dialogueWin->set_position('center-always');
        $dialogueWin->set_icon_list($axmud::CLIENT->desktopObj->{dialogueWinIconList});

        if ($initialFont) {

            $dialogueWin->set_font($initialFont);
        }

        # Get the response
        $response = $dialogueWin->run();
        if ($response eq 'ok') {

            # Get the selected font
            $newFont = $dialogueWin->get_font();
        }

        # Close the 'dialogue' window
        $dialogueWin->destroy();

        # Return the font (or 'undef' if no font was selected)
        return $newFont;
    }

    sub promptRoomFlag {

        # Called by GA::EditWin::WorldModel->roomFlags1Tab
        # Prompts the user for the attributes of a new custom room flag
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   An empty list on improper arguments or if the user closes the window without clicking
        #       the 'OK' button
        #   Otherwise returns a list in the form
        #       (name, short_name, descrip, colour)
        #   ...roughly corresponding to IVs in the new GA::Obj::RoomFlag object

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

        # Local variables
        my (
            $colour, $response,
            @emptyList, @returnList,
        );

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

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

        # Show the 'dialogue' window
        my $dialogueWin = Gtk3::Dialog->new(
            'Add custom room flag',
            $self->winWidget,
            Gtk3::DialogFlags->new([qw/modal destroy-with-parent/]),
            'gtk-cancel' => 'reject',
            'gtk-ok'     => 'accept',
        );

        $dialogueWin->set_position('center-always');
        $dialogueWin->set_icon_list($axmud::CLIENT->desktopObj->{dialogueWinIconList});

        $dialogueWin->signal_connect('delete-event' => sub {

            $dialogueWin->destroy();
            $self->restoreFocus();

            return @emptyList;
        });

        # Add widgets to the 'dialogue' window
        my $vBox = $dialogueWin->get_content_area();
        # The call to ->addDialogueIcon splits $vBox in two, with an icon on the left, and a new
        #   Gtk3::VBox on the right, into which we put everything
        my $vBox2 = $self->addDialogueIcon($vBox);

        # Need a grid as it's the quicket way to draw the room flag colour
        my $grid = Gtk3::Grid->new();
        $vBox2->pack_start($grid, TRUE, TRUE, $axmud::CLIENT->constFreeSpacingPixels);
        $grid->set_column_spacing($axmud::CLIENT->constFreeSpacingPixels);
        $grid->set_row_spacing($axmud::CLIENT->constFreeSpacingPixels);

        # Name
        my $label = Gtk3::Label->new();
        $grid->attach($label, 0, 0, 3, 1);
        $label->set_alignment(0, 0);
        $label->set_markup('Room flag name (max 16 chars)');

        my $entry = Gtk3::Entry->new();
        $grid->attach($entry, 0, 1, 3, 1);
        $entry->set_width_chars(16);
        $entry->set_max_length(16);

        # Short name
        my $label2 = Gtk3::Label->new();
        $grid->attach($label2, 0, 2, 3, 1);
        $label2->set_alignment(0, 0);
        $label2->set_markup('Short name (max 2 chars)');

        my $entry2 = Gtk3::Entry->new();
        $grid->attach($entry2, 0, 3, 3, 1);
        $entry2->set_width_chars(2);
        $entry2->set_max_length(2);

        # Description
        my $label3 = Gtk3::Label->new();
        $grid->attach($label3, 0, 4, 3, 1);
        $label3->set_alignment(0, 0);
        $label3->set_markup('Description');

        my $entry3 = Gtk3::Entry->new();
        $grid->attach($entry3, 0, 5, 3, 1);

        # Colour
        $colour = '#FFFFFF';            # Default new colour is white

        my $label4 = Gtk3::Label->new();
        $grid->attach($label4, 0, 6, 1, 1);
        $label4->set_markup('Use colour');
        $label4->set_alignment(0, 0.5);

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

        # If the user clicked 'cancel', $response will be 'reject'
        $response = $dialogueWin->run();

        if ($response ne 'accept') {

            $dialogueWin->destroy();
            $self->restoreFocus();

            return @emptyList;

        # Otherwise, user clicked 'ok'
        } else {

            @returnList = ($entry->get_text(), $entry2->get_text(), $entry3->get_text(), $colour);

            $dialogueWin->destroy();
            $self->restoreFocus();

            return @returnList;
        }
    }

    sub showIrreversibleTest {

        # Called by GA::Cmd::ToggleIrreversible->do
        # Shows a 'dialogue' window with a non-functional button that contains both an icon and
        #   some text, to test whether the user's system allows both (some systems will show only
        #   the text)
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or if the user closes the 'dialogue' window
        #   1 otherwise

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

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

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

        # If an earlier call to $self->showBusyWin created a popup window, close it (otherwise it'll
        #   be visible above the new dialogue window)
        if ($axmud::CLIENT->busyWin) {

            $self->closeDialogueWin($axmud::CLIENT->busyWin);
        }

        # Show the 'dialogue' window
        my $dialogueWin = Gtk3::Dialog->new(
            'Irreversible icon test',
            $self->winWidget,
            Gtk3::DialogFlags->new([qw/modal destroy-with-parent/]),
            'gtk-ok'     => 'accept',
        );

        $dialogueWin->set_position('center-always');
        $dialogueWin->set_icon_list($axmud::CLIENT->desktopObj->{dialogueWinIconList});

        $dialogueWin->signal_connect('delete-event' => sub {

            $dialogueWin->destroy();
            $self->restoreFocus();

            return undef;
        });

        # Add widgets to the 'dialogue' window
        my $vBox = $dialogueWin->get_content_area();
        # The call to ->addDialogueIcon splits $vBox in two, with an icon on the left, and a new
        #   Gtk3::VBox on the right, into which we put everything
        my $vBox2 = $self->addDialogueIcon($vBox);

        my $label = Gtk3::Label->new();
        $vBox2->pack_start($label, FALSE, FALSE, $axmud::CLIENT->constFreeSpacingPixels);
        $label->set_alignment(0, 0);
        $label->set_markup(
            "<i>If button icons are available on your\nsystem, the button below contains\nboth an"
            . " icon and some text</i>"
        );

        my $button = Gtk3::Button->new('Hello world!');
        $vBox2->pack_start($button, FALSE, FALSE, $axmud::CLIENT->constFreeSpacingPixels);

        my $image = Gtk3::Image->new_from_file(
            $axmud::SHARE_DIR . '/icons/system/irreversible.png',
        );

        $button->set_image($image);

        my $label2 = Gtk3::Label->new();
        $vBox2->pack_start($label2, FALSE, FALSE, $axmud::CLIENT->constFreeSpacingPixels);
        $label2->set_alignment(0, 0);
        $label2->set_markup(
            "<i>Click 'OK' to end the test</i>"
        );

        # Display the 'dialogue' window. Without this combination of Gtk calls, the window is not
        #   consistently active (don't know why this works; it just does)
        $dialogueWin->show_all();
        $dialogueWin->present();
        $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->showIrreversibleTest');

        # If the user clicked 'cancel', $response will be 'reject'
        # Otherwise, user clicked 'ok', and we might need to add initial tasks
        $dialogueWin->run();
        $dialogueWin->destroy();
        $self->restoreFocus();

        return 1;
    }

    sub showBusyWin {

        # Displays a temporary popup window (still an Axmud 'dialogue' window)
        # By default, displays the Axmud icon and the caption 'Loading...', but the calling function
        #   can specify a different logo and caption, if required
        # The popup window must be closed by the calling function, when no longer required (via a
        #   call to $self->closeDialogueWin)
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Optional arguments
        #   $path       - Path of the file containing the image to show as an icon. If not
        #                   specified, the standard Axmud icon
        #   $caption    - A short piece of text to show next to the image. If not specified, the
        #                   caption 'Loading...' is used
        #
        # Return values
        #   'undef' on improper arguments or if the window is not opened
        #   1 otherwise

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

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

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

        # Don't show the popup window at all, if not allowed
        if (! $axmud::CLIENT->allowBusyWinFlag) {

            return undef;
        }

        # Only one of these temporary popup windows can exist at a time. If one already exists,
        #   close it
        if ($axmud::CLIENT->busyWin) {

            $self->closeDialogueWin($axmud::CLIENT->busyWin);
        }

        # Set the file path and caption text, if not specified
        if (! defined $path || ! (-e $path)) {

            $path = $axmud::CLIENT->getDialogueIcon('medium');
        }

        if (! $caption) {

            $caption = 'Loading...';
        }

        # Show the window widget
        my $dialogueWin = Gtk3::Window->new('popup');
        $dialogueWin->set_position('center-always');
        $dialogueWin->set_icon_list($axmud::CLIENT->desktopObj->{dialogueWinIconList});
        $dialogueWin->set_title($axmud::SCRIPT);
        $dialogueWin->set_border_width(0);
        $dialogueWin->set_transient_for($self->winWidget);

        $dialogueWin->signal_connect('delete-event' => sub {

            $dialogueWin->destroy();
            $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->showBusyWin');
        });

        # Add widgets to the 'dialogue' window
        my $frame = Gtk3::Frame->new();
        $dialogueWin->add($frame);

        my $hBox = Gtk3::HBox->new(FALSE, 0);
        $frame->add($hBox);
        $hBox->set_border_width(10);

        my $image = Gtk3::Image->new_from_file($path);
        $hBox->pack_start($image, FALSE, FALSE, 5);

        my $label = Gtk3::Label->new();
        $hBox->pack_start($label, FALSE, FALSE, 5);
        $label->set_markup('<i><big>' . $caption . '</big></i>');
        $label->set_alignment(0.5, 0.5);

        $dialogueWin->show_all();

        # For some reason, during certain operations the icon and text are not shown in the
        #   window; the following lines make them appear
        $dialogueWin->present();
        # Update Gtk3's events queue
        $axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->showBusyWin');
        # Update the Client IV
        $axmud::CLIENT->set_busyWin($dialogueWin);

        return $dialogueWin;
    }

    # Functions to add widgets to a Gtk3::Grid

    sub addLabel {

        # Adds a Gtk3::Label at the specified position in a Gtk3::Grid
        #
        # Example calls:
        #   my $label = $self->addLabel($grid, 'Some plain text',
        #       0, 6, 0, 1);
        #   my $label = $self->addLabel($grid, '<b>Some pango markup text</b>',
        #       0, 6, 0, 1,
        #       0, 0.5);
        #
        # Expected arguments
        #   $grid       - The Gtk3::Grid itself
        #   $text       - The text to display (plain text or pango markup text)
        #   $leftAttach, $rightAttach, $topAttach, $bottomAttach
        #               - The position of the label in the table
        #
        # Optional arguments
        #   $alignLeft, $alignRight
        #               - Used in the call to ->set_alignment; two values in the range 0-1
        #               - If not specified, $alignLeft is set to 0, $alignRight to 0.5
        #
        # Return values
        #   'undef' on improper arguments or if the widget's position in the Gtk3::Grid is invalid
        #   Otherwise the Gtk3::Label created

        my (
            $self, $grid, $text, $leftAttach, $rightAttach, $topAttach, $bottomAttach, $alignLeft,
            $alignRight, $check
        ) = @_;

        # Check for improper arguments
        if (
            ! defined $grid || ! defined $text || ! defined $leftAttach || ! defined $rightAttach
            || ! defined $topAttach || ! defined $bottomAttach || defined $check
        ) {
            return $axmud::CLIENT->writeImproper($self->_objClass . '->addLabel', @_);
        }

        # Check that the position in the table makes sense
        if (! $self->checkPosn($leftAttach, $rightAttach, $topAttach, $bottomAttach)) {

            return undef;
        }

        # Set default alignment, if none specified
        if (! defined $alignLeft) {

            $alignLeft = 0;
        }

        if (! defined $alignRight) {

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

        #
        # Return values
        #   'undef' on improper arguments or if the widget's position in the Gtk3::Grid is invalid
        #   Otherwise the Gtk3::TextView created (inside a Gtk::ScrolledWindow)

        my (
            $self, $grid, $colourScheme, $funcRef, $string, $editableFlag, $leftAttach,
            $rightAttach, $topAttach, $bottomAttach, $width, $height, $check,
        ) = @_;

        # Check for improper arguments
        if (
            ! defined $grid || ! defined $editableFlag || ! defined $leftAttach
            || ! defined $rightAttach || ! defined $topAttach || ! defined $bottomAttach
            || defined $check
        ) {
            return $axmud::CLIENT->writeImproper($self->_objClass . '->addTextView', @_);
        }

        # Check that the position in the table makes sense
        if (! $self->checkPosn($leftAttach, $rightAttach, $topAttach, $bottomAttach)) {

            return undef;
        }

        # Set defaults
        if (! defined $width) {

            $width = -1;    # Let Gtk3 set the width
        }

        if (! defined $height) {

            $height = -1;   # Let Gtk3 set the height
        }

        # Creating a containing Gtk3::ScrolledWindow
        my $scroll = Gtk3::ScrolledWindow->new(undef, undef);
        $scroll->set_shadow_type($axmud::CLIENT->constShadowType);
        $scroll->set_policy('automatic', 'automatic');
        $scroll->set_size_request($width, $height);
#        $scroll->set_border_width($self->spacingPixels);

        # Create a textview and apply a CSS style
        my $textView = Gtk3::TextView->new();
        $scroll->add($textView);
        my $buffer = Gtk3::TextBuffer->new();
        $textView->set_buffer($buffer);
        $textView->set_cursor_visible(FALSE);

        if ($string) {

            $buffer->set_text(join("\n", $string));
        }

        # Make the textview editable or not editable
        $textView->set_editable($editableFlag);

        # Apply a CSS style to the textview
        if (defined $colourScheme && $axmud::CLIENT->ivExists('colourSchemeHash', $colourScheme)) {
            $axmud::CLIENT->desktopObj->setTextViewStyle($colourScheme, $textView);
        } else {
            $axmud::CLIENT->desktopObj->setTextViewStyle($self->winType, $textView);
        }

        # If a callback function was specified, apply it
        if ($funcRef) {

            $buffer->signal_connect('changed' => sub {

                &$funcRef(
                    $self,
                    $textView,
                    $buffer,
                    $axmud::CLIENT->desktopObj->bufferGetText($buffer),
                );
            });
        }

        # Add the textview to the grid
        $scroll->set_hexpand(TRUE);
        $scroll->set_vexpand(TRUE);
        $grid->attach(
            $scroll,
            $leftAttach,
            $topAttach,
            ($rightAttach - $leftAttach),
            ($bottomAttach - $topAttach),
        );

        return $textView;
    }

    sub addSimpleList {

        # Adds a GA::Obj::SimpleList at the specified position in a Gtk3::Grid
        # NB This function does not contain a ->signal_connect method - the calling function must
        #   specify its own one
        #
        # Example calls:
        #   my $slWidget = $self->addSimpleList($grid, \@columnList, $dataRef,
        #       0, 6, 0, 1);
        #   my $slWidget = $self->addSimpleList($grid, \@columnList, undef,
        #       0, 6, 0, 1,
        #       -1, 120);
        #   my $slWidget = $self->addSimpleList($grid, \@columnList, $dataRef,
        #       0, 6, 0, 1,
        #       -1, -1,
        #       GA::Client->getMethodRef('function_name'));
        #
        # Expected arguments
        #   $grid           - The Gtk3::Grid itself
        #   $columnListRef  - Reference to a list of column headings and types, in the form
        #                       ('heading', 'column_type', 'heading', 'column_type'...)
        #                   - 'column_type' is one of the column types specified by
        #                       GA::Obj::SimpleList, e.g. 'scalar', 'int'
        #                   - 'column_type' can also be 'bool' for a non-clickable checkbox, or
        #                       'bool_editable' for a clickable checkbox
        #   $dataRef        - Reference to a list of values, used to fill the simple list. If
        #                       'undef', it's up to the calling function to add data
        #   $leftAttach, $rightAttach, $topAttach, $bottomAttach
        #                   - The position of the simple list in the table
        #
        # Optional arguments
        #   $width, $height - The width and height (in pixels) of the scroller containing the list.
        #                       If specified, values of -1 mean 'don't set this value'. The default
        #                       values are (-1, -1)
        #   $funcRef        - Reference to the function to call when a (sensitised) checkbutton is
        #                       clicked. If 'undef', it's up to the calling function to create a
        #                       ->signal_connect method. Function references can be obtained by a
        #                       call to GA::Client->getMethodRef
        #
        # Return values
        #   'undef' on improper arguments or if the widget's position in the Gtk3::Grid is invalid
        #   Otherwise the GA::Obj::SimpleList created

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


        # Add the frame to the table (even if a Gtk3::Image wasn't created)
        $frame->set_hexpand(FALSE);
        $frame->set_vexpand(FALSE);
        $grid->attach(
            $frame,
            $leftAttach,
            $topAttach,
            ($rightAttach - $leftAttach),
            ($bottomAttach - $topAttach),
        );

        return ($image, $frame, $viewPort);
    }

    sub changeImage {

        # Changes the image shown as the result of a call to $self->addImage
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Optional arguments
        #   $viewPort   - The Gtk3::Viewport which contains the image ('undef' if no scrolling
        #                   viewport was used)
        #   $frame      - The Gtk3::Frame which contains the image ('undef' if a scrolling
        #                   viewport was used; ignored if $viewPort is defined)
        #   $oldImage   - The Gtk3::Image it currently contains. If it contains no image, set to
        #                   'undef'
        #   $filePath   - Full path to the file containing the image to be displayed (or 'undef' if
        #                   not using a file)
        #   $pixBuffer  - A Gtk3::Gdk::Pixbuf  containing the image to be displayed (or 'undef'
        #                   if not using a pixbuf)
        #
        # Return values
        #   'undef' on improper arguments, if the specified file doesn't exist or if a Gtk3::Image
        #       can't be created
        #   Otherwise returns the Gtk3::Image created, or 'undef' if none is created

        my ($self, $viewPort, $frame, $oldImage, $filePath, $pixBuffer, $check) = @_;

        # Check for improper arguments
        if ((! defined $viewPort && ! defined $frame) || defined $check) {

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

        # Create a new Gtk3::Image
        my $newImage;
        if ($filePath) {
            $newImage = Gtk3::Image->new_from_file($filePath);
        } elsif ($pixBuffer) {
            $newImage = Gtk3::Image->new_from_pixbuf($pixBuffer);
        }

        if ($viewPort) {

            # Remove the old image from its viewport, if an old image was specified
            if ($oldImage) {

                $axmud::CLIENT->desktopObj->removeWidget($viewPort, $oldImage);
            }

            # Add the new image to the viewport, if a new image was created
            if ($newImage) {

                $viewPort->add($newImage);
            }

        } else {

            # Remove the old image from its frame, if an old image was specified
            if ($oldImage) {

                $axmud::CLIENT->desktopObj->removeWidget($frame, $oldImage);
            }

            # Add the new image to the frame, if a new image was created
            if ($newImage) {

                $frame->add($newImage);
            }
        }

        # Update the window to show the changes
        $self->winShowAll($self->_objClass . '->changeImage');

        return $newImage;       # May be 'undef'
    }

    sub addSimpleImage {

        # Adds a Gtk3::Image from a specified file at the specified position in a Gtk3::Grid (but
        #   not inside a frame: call ->addImage to do that)
        #
        # Example calls:
        #   my $image = $self->addImage($grid, $filePath, $pixBuffer,
        #       0, 12, 1, 12);
        #   my $image = $self->addImage($grid, undef, undef,
        #       0, 12, 1, 12);
        #
        # Expected arguments
        #   $grid           - The Gtk3::Grid itself
        #   $filePath       - Full path to the file containing the image to be displayed (or 'undef'
        #                       if not using a file)
        #   $pixBuffer      - A Gtk3::Gdk::Pixbuf  containing the image to be displayed (or 'undef'
        #                       if not using a pixbuf)
        #   $leftAttach, $rightAttach, $topAttach, $bottomAttach
        #                   - The position of the frame in the table
        #
        # Return values
        #   'undef' on improper arguments or if a $filePath is specified which doesn't exist
        #   Otherwise the Gtk3::Image created

        my (
            $self, $grid, $filePath, $pixBuffer, $leftAttach, $rightAttach, $topAttach,
            $bottomAttach, $check,
        ) = @_;

        # Check for improper arguments
        if (
            ! defined $grid || ! defined $leftAttach || ! defined $rightAttach
            || ! defined $topAttach || ! defined $bottomAttach || defined $check
        ) {
            return $axmud::CLIENT->writeImproper($self->_objClass . '->addSimpleImage', @_);
        }

        # Check that the position in the table makes sense, and that filename (if specified) exists
        if (
            ! $self->checkPosn($leftAttach, $rightAttach, $topAttach, $bottomAttach)
            || (defined $filePath && ! -e $filePath)
        ) {
            return undef;
        }

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


            # Three flags that can be set by any page, to prevent one of three buttons from being
            #   made sensitive (temporarily)
            disableNextButtonFlag       => FALSE,
            disablePreviousButtonFlag   => FALSE,
            disableCancelButtonFlag     => FALSE,

            # The names of pages, in order of appearance
            pageList                    => [
#               'example',       # Corresponds to function $self->examplePage
#               'example2',
#               'example3',
            ],
            # The number of the current page (first page is 0)
            currentPage                 => 0,

            # Two hashes for using the 'Next' / 'Previous' buttons to skip around the pages, rather
            #   than going to the actual next or previous page (as normal)
            # The current page should add an entry to the hash, if necessary; the entry is removed
            #   by ->buttonPrevious or ->buttonNext as soon as either button is clicked
            # Hashes in the form
            #   $hash{current_page_number} = page_number_if_button_clicked
            # NB The first page's number is 0, so the fourth page will be page 3, not page 4
            specialNextButtonHash       => {},
            specialPreviousButtonHash   => {},
        };

        # 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 usually unique to its GA::Session (only one can be open at any
        #   time, per session); inform the session it has opened
        # Exception - if $self->session isn't set at all (presumably because there are no sessions
        #   running), then there's no-one to inform
        if ($self->session) {

            $self->session->set_wizWin($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 usually unique to its GA::Session (only one can be open at any
        #   time, per session); inform the session it has closed
        # Exception - if $self->session isn't set at all (presumably because there are no sessions
        #   running), then there's no-one to inform
        if ($self->session) {

            $self->session->set_wizWin();
        }

        return 1;
    }

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

    sub drawWidgets {

        # Called by $self->winSetup
        # Sets up the 'wiz' 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);

        # Add a grid (inside a scrolled window) in the higher area
        my $frame = Gtk3::Frame->new(undef);
        $packingBox->pack_start($frame, TRUE, TRUE, 0);
        $frame->set_border_width($self->spacingPixels);

        my $scroller = Gtk3::ScrolledWindow->new(undef, undef);
        $frame->add($scroller);
        $scroller->set_policy('automatic', 'automatic');
        $scroller->set_border_width(0);

        my $grid = Gtk3::Grid->new();
        $scroller->add_with_viewport($grid);
        $grid->set_column_spacing($self->spacingPixels);
        $grid->set_row_spacing($self->spacingPixels);
        $grid->set_border_width($self->borderPixels);

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


    sub setupGrid {

        # Called by $self->winEnable
        # Creates the first page for the wizard (not really necessary to have a whole function
        #   dedicated to this task, but having one keeps the design of 'wiz' windows consistent
        #   with the design of 'edit'/'pref' windows)
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

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

        # Local variables
        my ($func, $rows);

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

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

        # Get the name of the function for the first page
        $func = $self->ivIndex('pageList', $self->currentPage) . 'Page';
        # Call the function
        $rows = $self->$func();

        return 1;
    }

    sub updateGrid {

        # Called by $self->buttonPrevious and ->buttonNext
        # Changes the page currently visible in the 'wiz' window
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

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

        # Local variables
        my ($func, $rows);

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

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

        # Empty the grid used for the existing page
        foreach my $widget ($self->grid->get_children()) {

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

        # Get the name of the function for the new current page
        $func = $self->ivIndex('pageList', $self->currentPage) . 'Page';
        # Call the function
        $rows = $self->$func();

        # Set button sensititives ($self->disableNextButtonFlag, etc, override the usual rules, if
        #   they are set)

        # If it's the first page, the 'Previous' button must not be sensitive
        if ($self->currentPage == 0) {

            $self->previousButton->set_sensitive(FALSE);

            if (! $self->disableNextButtonFlag) {
                $self->nextButton->set_sensitive(TRUE);
            } else {
                $self->nextButton->set_sensitive(FALSE);
            }

            # Make sure the 'Next' button has the right label
            $self->nextButton->set_label('Next');
            $self->nextButton->get_child->set_width_chars(10);

        # If it's the last page, the 'Next' button must be converted to a 'Finish' button
        } elsif ($self->currentPage >= ((scalar $self->pageList) - 1)) {

            if (! $self->disablePreviousButtonFlag) {
                $self->previousButton->set_sensitive(TRUE);
            } else {
                $self->previousButton->set_sensitive(FALSE);
            }

            $self->nextButton->set_sensitive(TRUE);
            $self->nextButton->set_label('Finish');
            $self->nextButton->get_child->set_width_chars(10);

        # For all other pages, both buttons are sensitised
        } else {

            if (! $self->disableNextButtonFlag) {
                $self->nextButton->set_sensitive(TRUE);
            } else {
                $self->nextButton->set_sensitive(FALSE);
            }

            if (! $self->disablePreviousButtonFlag) {
                $self->previousButton->set_sensitive(TRUE);
            } else {
                $self->previousButton->set_sensitive(FALSE);
            }

            # Make sure the 'Next' button has the right label
            $self->nextButton->set_label('Next');
            $self->nextButton->get_child->set_width_chars(10);
        }

        # Reset the disable flags. It's up to individual pages to set them, when they're needed
        $self->ivPoke('disableNextButtonFlag', FALSE);



( run in 1.094 second using v1.01-cache-2.11-cpan-f56aa216473 )