Games-Axmud

 view release on metacpan or  search on metacpan

lib/Games/Axmud/Win/Map.pm  view on Meta::CPAN

            my $item_alignVertical = Gtk3::CheckMenuItem->new('Align _vertically');
            $item_alignVertical->set_active($self->worldModelObj->mapLabelAlignYFlag);
            $item_alignVertical->signal_connect('toggled' => sub {

                # Use $alignFlag to avoid an infinite loop, if we have to toggle the button back to
                #   its original state because the user declined to confirm the operation
                if (! $alignFlag) {

                    if (! $self->toggleAlignCallback('vertical')) {

                        $alignFlag = TRUE;
                        if (! $item_alignVertical->get_active()) {
                            $item_alignVertical->set_active(TRUE);
                        } else {
                            $item_alignVertical->set_active(FALSE);
                        }

                        $alignFlag = FALSE;
                    }
                }
            });
            $subMenu_alignment->append($item_alignVertical);

        my $item_alignment = Gtk3::MenuItem->new('_Label alignment');
        $item_alignment->set_submenu($subMenu_alignment);
        $column_labels->append($item_alignment);

        $column_labels->append(Gtk3::SeparatorMenuItem->new()); # Separator

        my $item_deleteLabel = Gtk3::ImageMenuItem->new('_Delete labels');
        my $img_deleteLabel = Gtk3::Image->new_from_stock('gtk-delete', 'menu');
        $item_deleteLabel->set_image($img_deleteLabel);
        $item_deleteLabel->signal_connect('activate' => sub {

            # Callback to prompt for confirmation, before deleting multiple labels
            $self->deleteLabelsCallback();
        });
        $column_labels->append($item_deleteLabel);
        # (Requires $self->currentRegionmap & either $self->selectedLabel or
        #   $self->selectedLabelHash)
        $self->ivAdd('menuToolItemHash', 'delete_label', $item_deleteLabel);

        my $item_quickDelete = Gtk3::ImageMenuItem->new('_Quick label deletion...');
        my $img_quickDelete = Gtk3::Image->new_from_stock('gtk-delete', 'menu');
        $item_quickDelete->set_image($img_quickDelete);
        $item_quickDelete->signal_connect('activate' => sub {

            $self->session->pseudoCmd('quicklabeldelete', $self->pseudoCmdMode);
        });
        $column_labels->append($item_quickDelete);

        # Setup complete
        return $column_labels;
    }

    # Popup menu widget methods

    sub enableCanvasPopupMenu {

        # Called by $self->canvasEventHandler
        # Creates a popup-menu for the Gtk3::Canvas when no rooms, exits, room tags or labels are
        #   selected
        #
        # Expected arguments
        #   $clickXPosPixels, $clickYPosPixels
        #       - Coordinates of the pixel that was right-clicked on the map
        #   $clickXPosBlocks, $clickYPosBlocks
        #       - Coordinates of the gridblock that was right-clicked on the map
        #
        # Return values
        #   'undef' on improper arguments or if the widget can't be created
        #   Otherwise returns the Gtk3::Menu created

        my (
            $self, $clickXPosPixels, $clickYPosPixels, $clickXPosBlocks, $clickYPosBlocks, $check,
        ) = @_;

        # Check for improper arguments
        if (
            ! defined $clickXPosPixels || ! defined $clickYPosPixels
            || ! defined $clickXPosBlocks || ! defined $clickYPosBlocks || defined $check
        ) {
            return $axmud::CLIENT->writeImproper($self->_objClass . '->enableCanvasPopupMenu', @_);
        }

        # Set up the popup menu
        my $menu_canvas = Gtk3::Menu->new();
        if (! $menu_canvas) {

            return undef;
        }

        # (Everything here assumes $self->currentRegionmap)

        my $item_addFirstRoom = Gtk3::ImageMenuItem->new('Add _first room');
        my $img_addFirstRoom = Gtk3::Image->new_from_stock('gtk-add', 'menu');
        $item_addFirstRoom->set_image($img_addFirstRoom);
        $item_addFirstRoom->signal_connect('activate' => sub {

            $self->addFirstRoomCallback();
        });
        $menu_canvas->append($item_addFirstRoom);
        # (Also requires empty $self->currentRegionmap->gridRoomHash)
        if ($self->currentRegionmap->gridRoomHash) {

            $item_addFirstRoom->set_sensitive(FALSE);
        }

        my $item_addRoomHere = Gtk3::ImageMenuItem->new('Add _room here');
        my $img_addRoomHere = Gtk3::Image->new_from_stock('gtk-add', 'menu');
        $item_addRoomHere->set_image($img_addRoomHere);
        $item_addRoomHere->signal_connect('activate' => sub {

            my $roomObj;

            # The 'Add room at click' operation from the main menu resets the value of
            #   ->freeClickMode; we must do the same here
            $self->reset_freeClickMode();

            # Create the room
            $roomObj = $self->mapObj->createNewRoom(
                $self->currentRegionmap,
                $clickXPosBlocks,
                $clickYPosBlocks,
                $self->currentRegionmap->currentLevel,
            );

            # When using the 'Add room at block' menu item, the new room is selected to make it
            #   easier to see where it was drawn. To make things consistent, select this new room,
            #   too
            if ($roomObj) {

                $self->setSelectedObj(
                    [$roomObj, 'room'],
                    FALSE,      # Select this object; unselect all other objects
                );
            }
        });
        $menu_canvas->append($item_addRoomHere);

        $menu_canvas->append(Gtk3::SeparatorMenuItem->new());  # Separator

        my $item_addLabelHere = Gtk3::ImageMenuItem->new('Add _label here');
        my $img_addLabelHere = Gtk3::Image->new_from_stock('gtk-add', 'menu');
        $item_addLabelHere->set_image($img_addLabelHere);
        $item_addLabelHere->signal_connect('activate' => sub {

lib/Games/Axmud/Win/Map.pm  view on Meta::CPAN

        $menu_canvas->append($item_addLabelHere);

        $menu_canvas->append(Gtk3::SeparatorMenuItem->new());  # Separator

        my $item_centreMap = Gtk3::MenuItem->new('_Centre map here');
        $item_centreMap->signal_connect('activate' => sub {

            $self->centreMapOverRoom(
                undef,              # Centre the map, not over a room...
                $clickXPosBlocks,   # ...but over this gridblock
                $clickYPosBlocks,
            );
        });
        $menu_canvas->append($item_centreMap);

        $menu_canvas->append(Gtk3::SeparatorMenuItem->new());  # Separator

        my $item_editRegionmap = Gtk3::ImageMenuItem->new('_Edit regionmap...');
        my $img_editRegionmap = Gtk3::Image->new_from_stock('gtk-edit', 'menu');
        $item_editRegionmap->set_image($img_editRegionmap);
        $item_editRegionmap->signal_connect('activate' => sub {

            # Open an 'edit' window for the regionmap
            $self->createFreeWin(
                'Games::Axmud::EditWin::Regionmap',
                $self,
                $self->session,
                'Edit \'' . $self->currentRegionmap->name . '\' regionmap',
                $self->currentRegionmap,
                FALSE,                          # Not temporary
            );
        });
        $menu_canvas->append($item_editRegionmap);

        my $item_preferences = Gtk3::ImageMenuItem->new('Edit world _model...');
        my $img_preferences = Gtk3::Image->new_from_stock('gtk-edit', 'menu');
        $item_preferences->set_image($img_preferences);
        $item_preferences->signal_connect('activate' => sub {

            # Open an 'edit' window for the world model
            $self->createFreeWin(
                'Games::Axmud::EditWin::WorldModel',
                $self,
                $self->session,
                'Edit world model',
                $self->session->worldModelObj,
                FALSE,                          # Not temporary
            );
        });
        $menu_canvas->append($item_preferences);

        # Setup complete
        $menu_canvas->show_all();

        return $menu_canvas;
    }

    sub enableRoomsPopupMenu {

        # Called by $self->canvasObjEventHandler
        # Creates a popup-menu for the selected room
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or if the widget can't be created
        #   Otherwise returns the Gtk3::Menu created

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

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

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

        # Set up the popup menu
        my $menu_rooms = Gtk3::Menu->new();
        if (! $menu_rooms) {

            return undef;
        }

        # (Everything here assumes $self->currentRegionmap and $self->selectedRoom)

        my $item_setCurrentRoom = Gtk3::MenuItem->new('_Set current room');
        $item_setCurrentRoom->signal_connect('activate' => sub {

            $self->mapObj->setCurrentRoom($self->selectedRoom);
        });
        $menu_rooms->append($item_setCurrentRoom);

        my $item_centreMap = Gtk3::MenuItem->new('_Centre map over room');
        $item_centreMap->signal_connect('activate' => sub {

            $self->centreMapOverRoom($self->selectedRoom);
        });
        $menu_rooms->append($item_centreMap);

        my $item_executeScripts = Gtk3::MenuItem->new('Run _Axbasic scripts');
        $item_executeScripts->signal_connect('activate' => sub {

            $self->executeScriptsCallback();
        });
        $menu_rooms->append($item_executeScripts);
        # (Also requires $self->mapObj->currentRoom that's the same as $self->selectedRoom)
        if (! $self->mapObj->currentRoom || $self->mapObj->currentRoom ne $self->selectedRoom) {

            $item_executeScripts->set_sensitive(FALSE);
        }

        $menu_rooms->append(Gtk3::SeparatorMenuItem->new());  # Separator

            # 'Pathfinding' submenu
            my $subMenu_pathFinding = Gtk3::Menu->new();

            my $item_highlightPath = Gtk3::MenuItem->new('_Highlight path');
            $item_highlightPath->signal_connect('activate' => sub {

                $self->processPathCallback('select_room');
            });
            $subMenu_pathFinding->append($item_highlightPath);

            my $item_displayPath = Gtk3::MenuItem->new('_Edit path...');
            $item_displayPath->signal_connect('activate' => sub {

                $self->processPathCallback('pref_win');
            });
            $subMenu_pathFinding->append($item_displayPath);

            my $item_goToRoom = Gtk3::MenuItem->new('_Go to room');
            $item_goToRoom->signal_connect('activate' => sub {

                $self->processPathCallback('send_char');
            });
            $subMenu_pathFinding->append($item_goToRoom);

lib/Games/Axmud/Win/Map.pm  view on Meta::CPAN


                            # Edit virtual area file
                            $self->editFileCallback(TRUE);
                        }
                    }
                });
                $subSubMenu_sourceCode->append($item_editSource);
                # (Also requires either $self->selectedRoom->sourceCodePath or
                #   $self->selectedRoom->virtualAreaPath)
                if (
                    ! $self->selectedRoom->sourceCodePath
                    && ! $self->selectedRoom->virtualAreaPath
                ) {
                    $item_editSource->set_sensitive(FALSE);
                }

            my $item_sourceCode = Gtk3::MenuItem->new('Source _code');
            $item_sourceCode->set_submenu($subSubMenu_sourceCode);
            $subMenu_roomFeatures->append($item_sourceCode);

            $subMenu_roomFeatures->append(Gtk3::SeparatorMenuItem->new());  # Separator

            my $item_setInteriorOffsets = Gtk3::MenuItem->new('_Synchronise grid coordinates...');
            $item_setInteriorOffsets->signal_connect('activate' => sub {

                $self->setInteriorOffsetsCallback();
            });
            $subMenu_roomFeatures->append($item_setInteriorOffsets);

            my $item_resetInteriorOffsets = Gtk3::MenuItem->new('_Reset grid coordinates');
            $item_resetInteriorOffsets->signal_connect('activate' => sub {

                $self->resetInteriorOffsetsCallback();
            });
            $subMenu_roomFeatures->append($item_resetInteriorOffsets);

        my $item_roomFeatures = Gtk3::MenuItem->new('Ot_her room features');
        $item_roomFeatures->set_submenu($subMenu_roomFeatures);
        $menu_rooms->append($item_roomFeatures);

        $menu_rooms->append(Gtk3::SeparatorMenuItem->new());  # Separator

        my $item_deleteRoom = Gtk3::ImageMenuItem->new('_Delete room');
        my $img_deleteRoom = Gtk3::Image->new_from_stock('gtk-delete', 'menu');
        $item_deleteRoom->set_image($img_deleteRoom);
        $item_deleteRoom->signal_connect('activate' => sub {

            $self->deleteRoomsCallback();
        });
        $menu_rooms->append($item_deleteRoom);

        # Setup complete
        $menu_rooms->show_all();

        return $menu_rooms;
    }

    sub enableRoomTagsPopupMenu {

        # Called by $self->canvasObjEventHandler
        # Creates a popup-menu for the selected room tag
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or if the widget can't be created
        #   Otherwise returns the Gtk3::Menu created

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

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

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

        # Set up the popup menu
        my $menu_tags = Gtk3::Menu->new();
        if (! $menu_tags) {

            return undef;
        }

        # (Everything here assumes $self->currentRegionmap and $self->selectedRoomTag)

        my $item_editTag = Gtk3::MenuItem->new('_Set room tag...');
        $item_editTag->signal_connect('activate' => sub {

            $self->setRoomTagCallback();
        });
        $menu_tags->append($item_editTag);

        my $item_resetPosition = Gtk3::MenuItem->new('_Reset position');
        $item_resetPosition->signal_connect('activate' => sub {

            if ($self->selectedRoomTag) {

                $self->worldModelObj->resetRoomOffsets(
                    TRUE,                       # Update Automapper windows now
                    1,                          # Mode 1 - reset room tag only
                    $self->selectedRoomTag,     # Set to the parent room's blessed reference
                );
            }
        });
        $menu_tags->append($item_resetPosition);

        # Setup complete
        $menu_tags->show_all();

        return $menu_tags;
    }

    sub enableRoomGuildsPopupMenu {

        # Called by $self->canvasObjEventHandler
        # Creates a popup-menu for the selected room guild
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or if the widget can't be created
        #   Otherwise returns the Gtk3::Menu created

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

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

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

        # Set up the popup menu
        my $menu_guilds = Gtk3::Menu->new();
        if (! $menu_guilds) {

            return undef;
        }

        # (Everything here assumes $self->currentRegionmap and $self->selectedRoomGuild)

        my $item_editGuild = Gtk3::MenuItem->new('_Set room guild...');
        $item_editGuild->signal_connect('activate' => sub {

            $self->setRoomGuildCallback();
        });
        $menu_guilds->append($item_editGuild);

        my $item_resetPosition = Gtk3::MenuItem->new('_Reset position');
        $item_resetPosition->signal_connect('activate' => sub {

            if ($self->selectedRoomGuild) {

                $self->worldModelObj->resetRoomOffsets(
                    TRUE,                       # Update Automapper windows now
                    2,                          # Mode 2 - reset room guild only
                    $self->selectedRoomGuild,   # Set to the parent room's blessed reference
                );
            }
        });
        $menu_guilds->append($item_resetPosition);

        # Setup complete
        $menu_guilds->show_all();

        return $menu_guilds;
    }

    sub enableExitsPopupMenu {

        # Called by $self->canvasObjEventHandler
        # Creates a popup-menu for the selected exit
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or if the widget can't be created
        #   Otherwise returns the Gtk3::Menu created

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

        # Local variables
        my @titleList;

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

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

        # Set up the popup menu
        my $menu_exits = Gtk3::Menu->new();
        if (! $menu_exits) {

            return undef;
        }

        # (Everything here assumes $self->currentRegionmap and $self->selectedExit)

            # 'Allocate map direction' submenu
            my $subMenu_setDir = Gtk3::Menu->new();

            my $item_changeDir = Gtk3::MenuItem->new('_Change direction...');
            $item_changeDir->signal_connect('activate' => sub {

                $self->changeDirCallback();
            });
            $subMenu_setDir->append($item_changeDir);
            # (Also requires $self->selectedExit->drawMode is 'primary' or 'perm_alloc'
            if (
                $self->selectedExit->drawMode ne 'primary'
                && $self->selectedExit->drawMode ne 'perm_alloc'
            ) {
                $item_changeDir->set_sensitive(FALSE);
            }

            my $item_altDir = Gtk3::MenuItem->new('Set _alternative direction(s)...');
            $item_altDir->signal_connect('activate' => sub {

                $self->setAltDirCallback();
            });
            $subMenu_setDir->append($item_altDir);

        my $item_setDir = Gtk3::MenuItem->new('Set di_rection');
        $item_setDir->set_submenu($subMenu_setDir);
        $menu_exits->append($item_setDir);

        my $item_setAssisted = Gtk3::MenuItem->new('Set assisted _move...');
        $item_setAssisted->signal_connect('activate' => sub {

            $self->setAssistedMoveCallback();
        });
        $menu_exits->append($item_setAssisted);
        # (Also requires $self->selectedExit->drawMode 'primary', 'temp_unalloc' or 'perm_unalloc')
        if ($self->selectedExit->drawMode eq 'temp_alloc') {

            $item_setAssisted->set_sensitive(FALSE);
        }

            # 'Allocate map direction' submenu
            my $subMenu_allocateMapDir = Gtk3::Menu->new();

            my $item_allocatePrimary = Gtk3::MenuItem->new('Choose _direction...');
            $item_allocatePrimary->signal_connect('activate' => sub {

                $self->allocateMapDirCallback();
            });
            $subMenu_allocateMapDir->append($item_allocatePrimary);


            my $item_confirmTwoWay = Gtk3::MenuItem->new('Confirm _two-way exit...');

lib/Games/Axmud/Win/Map.pm  view on Meta::CPAN

                $self->editExitTagCallback();
            });
            $subMenu_exitTags->append($item_editTag);

            my $item_toggleExitTag = Gtk3::MenuItem->new('_Toggle exit tag');
            $item_toggleExitTag->signal_connect('activate' => sub {

                $self->toggleExitTagCallback();
            });
            $subMenu_exitTags->append($item_toggleExitTag);

            $subMenu_exitTags->append(Gtk3::SeparatorMenuItem->new());    # Separator

            my $item_resetPosition = Gtk3::MenuItem->new('_Reset text position');
            $item_resetPosition->signal_connect('activate' => sub {

                $self->resetExitOffsetsCallback();
            });
            $subMenu_exitTags->append($item_resetPosition);

        my $item_exitTags = Gtk3::MenuItem->new('Exit _tags');
        $item_exitTags->set_submenu($subMenu_exitTags);
        $menu_exits->append($item_exitTags);
        # (Also requires either a $self->selectedExit which is a region exit)
        if (! $self->selectedExit->regionFlag) {

            $item_exitTags->set_sensitive(FALSE);
        }

        $menu_exits->append(Gtk3::SeparatorMenuItem->new());  # Separator

        my $item_editExit = Gtk3::ImageMenuItem->new('Edit e_xit...');
        my $img_editExit = Gtk3::Image->new_from_stock('gtk-edit', 'menu');
        $item_editExit->set_image($img_editExit);
        $item_editExit->signal_connect('activate' => sub {

            $self->editExitCallback();
        });
        $menu_exits->append($item_editExit);

        $menu_exits->append(Gtk3::SeparatorMenuItem->new());  # Separator

        my $item_deleteExit = Gtk3::ImageMenuItem->new('_Delete exit');
        my $img_deleteExit = Gtk3::Image->new_from_stock('gtk-add', 'menu');
        $item_deleteExit->set_image($img_deleteExit);
        $item_deleteExit->signal_connect('activate' => sub {

            $self->deleteExitCallback();
        });
        $menu_exits->append($item_deleteExit);

        # Setup complete
        $menu_exits->show_all();

        return $menu_exits;
    }

    sub enableExitTagsPopupMenu {

        # Called by $self->canvasObjEventHandler
        # Creates a popup-menu for the selected exit tag
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or if the widget can't be created
        #   Otherwise returns the Gtk3::Menu created

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

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

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

        # Set up the popup menu
        my $menu_tags = Gtk3::Menu->new();
        if (! $menu_tags) {

            return undef;
        }

        # (Everything here assumes $self->currentRegionmap and $self->selectedExitTag)

        my $item_editTag = Gtk3::MenuItem->new('_Edit exit tag');
        $item_editTag->signal_connect('activate' => sub {

            $self->editExitTagCallback();
        });
        $menu_tags->append($item_editTag);

        my $item_cancelTag = Gtk3::MenuItem->new('_Cancel exit tag');
        $item_cancelTag->signal_connect('activate' => sub {

            $self->toggleExitTagCallback();
        });
        $menu_tags->append($item_cancelTag);

        $menu_tags->append(Gtk3::SeparatorMenuItem->new());  # Separator

        my $item_viewDestination = Gtk3::MenuItem->new('_View destination');
        $item_viewDestination->signal_connect('activate' => sub {

            $self->viewExitDestination();
        });
        $menu_tags->append($item_viewDestination);

        $menu_tags->append(Gtk3::SeparatorMenuItem->new());  # Separator

        my $item_resetPosition = Gtk3::MenuItem->new('_Reset position');
        $item_resetPosition->signal_connect('activate' => sub {

            $self->resetExitOffsetsCallback();
        });
        $menu_tags->append($item_resetPosition);

        # Setup complete
        $menu_tags->show_all();

        return $menu_tags;
    }

    sub enableLabelsPopupMenu {

        # Called by $self->canvasObjEventHandler
        # Creates a popup-menu for the selected label
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or if the widget can't be created
        #   Otherwise returns the Gtk3::Menu created

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

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

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

        # Set up the popup menu
        my $menu_labels = Gtk3::Menu->new();
        if (! $menu_labels) {

            return undef;
        }

        # (Everything here assumes $self->currentRegionmap and $self->selectedLabel)

        my $item_setLabel = Gtk3::ImageMenuItem->new('_Set label...');
        my $img_setLabel = Gtk3::Image->new_from_stock('gtk-edit', 'menu');
        $item_setLabel->set_image($img_setLabel);
        $item_setLabel->signal_connect('activate' => sub {

            $self->setLabelCallback(FALSE)
        });
        $menu_labels->append($item_setLabel);

        my $item_customiseLabel = Gtk3::ImageMenuItem->new('_Customise label...');
        my $img_customiseLabel = Gtk3::Image->new_from_stock('gtk-edit', 'menu');
        $item_customiseLabel->set_image($img_customiseLabel);
        $item_customiseLabel->signal_connect('activate' => sub {

            $self->setLabelCallback(TRUE);
        });
        $menu_labels->append($item_customiseLabel);

        $menu_labels->append(Gtk3::SeparatorMenuItem->new());  # Separator

            # 'Set label style' submenu
            my $subMenu_setStyle = Gtk3::Menu->new();

            foreach my $style (
                sort {lc($a) cmp lc($b)} ($self->worldModelObj->ivKeys('mapLabelStyleHash'))
            ) {
                my $item_thisStyle = Gtk3::MenuItem->new($style);
                $item_thisStyle->signal_connect('activate' => sub {

                    $self->setLabelDirectCallback($style);
                });
                $subMenu_setStyle->append($item_thisStyle);
            }

        my $item_setStyle = Gtk3::MenuItem->new('S_et label style');
        $item_setStyle->set_submenu($subMenu_setStyle);
        $menu_labels->append($item_setStyle);
        # (Also requires at least one label style)
        if (! $self->worldModelObj->mapLabelStyleHash) {

            $item_setStyle->set_sensitive(FALSE);
        }

        $menu_labels->append(Gtk3::SeparatorMenuItem->new());  # Separator

        my $item_deleteLabel = Gtk3::ImageMenuItem->new('_Delete label');
        my $img_deleteLabel = Gtk3::Image->new_from_stock('gtk-delete', 'menu');
        $item_deleteLabel->set_image($img_deleteLabel);
        $item_deleteLabel->signal_connect('activate' => sub {

            if ($self->selectedLabel) {

lib/Games/Axmud/Win/Map.pm  view on Meta::CPAN

        OUTER: foreach my $set (@afterList, @beforeList) {

            if (! $self->ivShow('buttonSetHash', $set)) {

                $nextSet = $set;
                last OUTER;
            }
        }

        if (! $nextSet) {

            # All button sets are visible; cannot switch set
            return undef;
        }

        # Remove the existing button set (preserving the switcher and add buttons, and the separator
        #   that follows them)
        foreach my $widget ($self->toolbarButtonList) {

            $axmud::CLIENT->desktopObj->removeWidget($toolbar, $widget);
        }

        # After the separator, we draw the specified button set. This function decides which
        #   specific function to call, and returns the result
        @buttonList = $self->chooseButtonSet($toolbar, $nextSet);

        # Add the buttons/separators to the toolbar
        foreach my $button (@buttonList) {

            my $label;

            # (Separators don't have labels, so we need to check for that)
            if (! $axmud::CLIENT->toolbarLabelFlag && $button->isa('Gtk3::ToolButton')) {

                $button->set_label(undef);
            }

            $toolbar->insert($button, -1);
        }

        # Sensitise/desensitise menu bar/toolbar items, depending on current conditions
        $self->restrictWidgets();

        # Update IVs
        $self->ivAdd('buttonSetHash', $currentSet, FALSE);
        $self->ivAdd('buttonSetHash', $nextSet, TRUE);
        $self->ivAdd('toolbarHash', $toolbar, $nextSet);
        $self->ivPoke('toolbarButtonList', @buttonList);
        $self->ivPoke('toolbarOriginalSet', $nextSet);

        # Not worth calling $self->redrawWidgets, so must do a ->show_all()
        $toolbar->show_all();

        return 1;
    }

    sub addToolbar {

        # Called by a ->signal_connect in $self->drawToolbar whenever the user clicks the original
        #   toolbar's add button
        # Creates a popup menu containing all of the button sets that aren't currently visible, then
        #   imlements the user's choice
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or if there's an error
        #   1 otherwise

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

        # Local variables
        my (
            @list,
            %hash,
        );

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

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

        # Get a list of button sets that aren't already visible
        # NB The 'default' set can only be viewed in the original (first) toolbar, so it's not added
        #   to this list
        foreach my $set ($self->constButtonSetList) {

            my $descrip = $self->ivShow('constButtonDescripHash', $set);

            if ($set ne $self->constToolbarDefaultSet && ! $self->ivShow('buttonSetHash', $set)) {

                push (@list, $descrip);
                $hash{$descrip} = $set;
            }
        }

        if (! @list) {

            # All button sets are visible (this shouldn't happen)
            return undef;
        }

        # Set up the popup menu
        my $popupMenu = Gtk3::Menu->new();
        if (! $popupMenu) {

            return undef;
        }

        # Add a title menu item, which does nothing
        my $title_item = Gtk3::MenuItem->new('Add button set:');
        $title_item->signal_connect('activate' => sub {

            return undef;
        });
        $title_item->set_sensitive(FALSE);
        $popupMenu->append($title_item);

        $popupMenu->append(Gtk3::SeparatorMenuItem->new());     # Separator

        # Fill the popup menu with button sets
        foreach my $descrip (@list) {

            my $menu_item = Gtk3::MenuItem->new($descrip);
            $menu_item->signal_connect('activate' => sub {

                # Add the set to the world model's list of button sets...
                $self->worldModelObj->add_buttonSet($hash{$descrip});
                # ...then redraw the window component containing the toolbar(s)
                $self->redrawWidgets('toolbar');
            });
            $popupMenu->append($menu_item);
        }

        # Also add a 'Cancel' menu item, which does nothing
        $popupMenu->append(Gtk3::SeparatorMenuItem->new());     # Separator

        my $cancel_item = Gtk3::MenuItem->new('Cancel');
        $cancel_item->signal_connect('activate' => sub {

            return undef;
        });
        $popupMenu->append($cancel_item);

        # Display the popup menu
        $popupMenu->popup(
            undef, undef, undef, undef,
            1,                              # Left mouse button
            Gtk3::get_current_event_time(),
        );

        $popupMenu->show_all();

        # Operation complete. Now wait for the user's response
        return 1;
    }

    sub removeToolbar {

        # Called by a ->signal_connect in $self->drawToolbar whenever the user clicks on the remove
        #   button in any toolbar except the original one
        # Removes the specified toolbar and updates IVs
        #
        # Expected arguments
        #   $toolbar    - The toolbar widget to be removed
        #
        # Return values
        #   'undef' on improper arguments or if there's an error
        #   1 otherwise

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

        # Local variables
        my (
            $set,
            @modList,
        );

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

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

        # Check the toolbar widget still exists (no reason it shouldn't, but it doesn't hurt to
        #   check)
        if (! $self->ivExists('toolbarHash', $toolbar)) {

            return undef;

        } else {

            # Get the button set that was drawn in this toolbar
            $set = $self->ivShow('toolbarHash', $toolbar);
        }

        # Add the set to the world model's list of button sets...
        $self->worldModelObj->del_buttonSet($set);
        # ...then redraw the window component containing the toolbar(s)
        $self->redrawWidgets('toolbar');

        return 1;
    }

    sub chooseButtonSet {

        # Called by $self->drawToolbar and ->switchToolbarButtons
        # Calls the right function for the specified button set, and returns the result
        #
        # Expected arguments
        #   $toolbar    - The toolbar widget on which the buttons are drawn
        #   $set        - The button set to use (one of the items in $self->constButtonSetList)

lib/Games/Axmud/Win/Map.pm  view on Meta::CPAN


            push (@interiorList, $mode);
            $interiorHash{$mode} = $descrip;
            $iconHash{$mode} = $icon;

        } until (! @initList);

        for (my $count = 0; $count < (scalar @interiorList); $count++) {

            my ($icon, $mode);

            $mode = $interiorList[$count];

            # (For $count = 0, $buttonGroup is 'undef')
            my $radioButton;
            if ($mode eq 'none') {
                $radioButton = Gtk3::RadioToolButton->new(undef);
            } else {
                $radioButton = Gtk3::RadioToolButton->new_from_widget($lastButton);
            }

            if ($self->worldModelObj->roomInteriorMode eq $mode) {

                $radioButton->set_active(TRUE);
            }
            $radioButton->set_icon_widget(
                Gtk3::Image->new_from_file($axmud::SHARE_DIR . '/icons/map/' . $iconHash{$mode}),
            );
            $radioButton->set_label($interiorHash{$mode});
            $radioButton->set_tooltip_text($interiorHash{$mode});

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

                if (! $self->ignoreMenuUpdateFlag && $radioButton->get_active()) {

                    $self->worldModelObj->switchRoomInteriorMode($mode);
                }
            });
            push (@buttonList, $radioButton);
            # (Never desensitised)
            $self->ivAdd('menuToolItemHash', 'icon_interior_mode_' . $mode, $radioButton);

            $lastButton = $radioButton;

            # (Add a separator after the first toolbar button)
            if ($mode eq 'none') {

                # Separator
                my $separator = Gtk3::SeparatorToolItem->new();
                push (@buttonList, $separator);
            }
        }

        return @buttonList;
    }

    sub addRoomFlagButton {

        # Called by a ->signal_connect in $self->drawPaintingButtonSet whenever the user clicks the
        #   'add room flag' button in the 'painting' button set
        # Creates a popup menu containing all room flags, then implements the user's choice
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or if there's an error
        #   1 otherwise

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

        # Local variables
        my %checkHash;

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

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

        # Compile a hash of existing preferred room flags (we don't want the user to add the same
        #   room flag twice)
        foreach my $roomFlag ($self->worldModelObj->preferRoomFlagList) {

            $checkHash{$roomFlag} = undef;
        }

        # Set up the popup menu
        my $popupMenu = Gtk3::Menu->new();
        if (! $popupMenu) {

            return undef;
        }

        # Add a title menu item, which does nothing
        my $title_item = Gtk3::MenuItem->new('Add preferred room flag:');
        $title_item->signal_connect('activate' => sub {

            return undef;
        });
        $title_item->set_sensitive(FALSE);
        $popupMenu->append($title_item);

        $popupMenu->append(Gtk3::SeparatorMenuItem->new());     # Separator

        # Fill the popup menu with room flags
        foreach my $filter ($axmud::CLIENT->constRoomFilterList) {

            # A sub-sub menu for $filter
            my $subSubMenu_filter = Gtk3::Menu->new();

            my @nameList = $self->worldModelObj->getRoomFlagsInFilter($filter);
            foreach my $name (@nameList) {

                my $obj = $self->worldModelObj->ivShow('roomFlagHash', $name);
                if ($obj) {

                    my $menuItem = Gtk3::MenuItem->new($obj->descrip);
                    $menuItem->signal_connect('activate' => sub {

                        # Add the room flag to the world model's list of preferred room flags...
                        $self->worldModelObj->add_preferRoomFlag($name);
                        # ...then redraw the window component containing the toolbar(s), toggling
                        #   the button for the new room flag
                        $self->redrawWidgets('toolbar');
                    });
                    $subSubMenu_filter->append($menuItem);
                }
            }

            if (! @nameList) {

                my $menuItem = Gtk3::MenuItem->new('(No flags in this filter)');
                $menuItem->set_sensitive(FALSE);
                $subSubMenu_filter->append($menuItem);
            }

            my $menuItem = Gtk3::MenuItem->new(ucfirst($filter));
            $menuItem->set_submenu($subSubMenu_filter);
            $popupMenu->append($menuItem);
        }

        # Also add a 'Cancel' menu item, which does nothing
        $popupMenu->append(Gtk3::SeparatorMenuItem->new());     # Separator

        my $cancel_item = Gtk3::MenuItem->new('Cancel');
        $cancel_item->signal_connect('activate' => sub {

            return undef;
        });
        $popupMenu->append($cancel_item);

        # Display the popup menu
        $popupMenu->popup(
            undef, undef, undef, undef,
            1,                              # Left mouse button
            Gtk3::get_current_event_time(),
        );

        $popupMenu->show_all();

        # Operation complete. Now wait for the user's response
        return 1;
    }

    sub removeRoomFlagButton {

        # Called by a ->signal_connect in $self->drawPaintingButtonSet whenever the user clicks the
        #   'remove room flag' button in the 'painting' button set
        # Removes the specified room flag from the toolbar and updates IVs
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or if there's an error
        #   1 otherwise

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

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

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

        # Set up the popup menu
        my $popupMenu = Gtk3::Menu->new();
        if (! $popupMenu) {

            return undef;
        }

        # Add a title menu item, which does nothing
        my $title_item = Gtk3::MenuItem->new('Remove preferred room flag:');
        $title_item->signal_connect('activate' => sub {

            return undef;
        });
        $title_item->set_sensitive(FALSE);
        $popupMenu->append($title_item);

        $popupMenu->append(Gtk3::SeparatorMenuItem->new());     # Separator

        # Fill the popup menu with room flags
        foreach my $roomFlag ($self->worldModelObj->preferRoomFlagList) {

            my $menu_item = Gtk3::MenuItem->new($roomFlag);
            $menu_item->signal_connect('activate' => sub {

                # Remove the room flag from the world model's list of preferred room flags...
                $self->worldModelObj->del_preferRoomFlag($roomFlag);
                # ...and from the painter object iself...
                $self->worldModelObj->painterObj->ivDelete('roomFlagHash', $roomFlag);
                # ...then redraw the window component containing the toolbar(s)
                $self->redrawWidgets('toolbar');
            });
            $popupMenu->append($menu_item);
        }

        # Add a 'remove all' menu item
        $popupMenu->append(Gtk3::SeparatorMenuItem->new());     # Separator

        my $remove_all_item = Gtk3::MenuItem->new('Remove all');
        $remove_all_item->signal_connect('activate' => sub {

            my ($total, $choice);

            $total = scalar $self->worldModelObj->preferRoomFlagList;

            # If there's more than one colour, prompt the user for confirmation
            if ($total > 1) {

                $choice = $self->showMsgDialogue(
                    'Remove all room flag buttons',
                    'question',
                    'Are you sure you want to remove all ' . $total . ' room flag buttons?',
                    'yes-no',
                );

            } else {

                $choice = 'yes';
            }

            if (defined $choice && $choice eq 'yes') {

                # Reset the world model's list of preferred room flags
                $self->worldModelObj->reset_preferRoomFlagList();

                # Update the painter object (which might contain room flags not added with these
                #   tools)
                foreach my $roomFlag ($self->worldModelObj->preferRoomFlagList) {

                    $self->worldModelObj->ivDelete('roomFlagHash', $roomFlag);
                }

                # Then redraw the window component containing the toolbar(s)
                $self->redrawWidgets('toolbar');
            }
        });
        $popupMenu->append($remove_all_item);

        # Also add a 'Cancel' menu item, which does nothing
        my $cancel_item = Gtk3::MenuItem->new('Cancel');
        $cancel_item->signal_connect('activate' => sub {

            return undef;
        });
        $popupMenu->append($cancel_item);

        # Display the popup menu
        $popupMenu->popup(
            undef, undef, undef, undef,
            1,                              # Left mouse button
            Gtk3::get_current_event_time(),
        );

        $popupMenu->show_all();

        # Operation complete. Now wait for the user's response
        return 1;
    }

    sub addBGColourButton {

        # Called by a ->signal_connect in $self->drawBackgroundButtonSet whenever the user clicks
        #   the 'add background colour' button in the 'background' button set
        # Prompts the user for a new RGB colour tag, then implements the user's choice
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or if there's an error
        #   1 otherwise

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

        # Local variables
        my $colour;

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

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

        $colour = $self->showColourSelectionDialogue('Add preferred background colour');
        if (defined $colour) {

            # Add the room flag to the world model's list of preferred background colours...
            $self->worldModelObj->add_preferBGColour($colour);
            # ...then redraw the window component containing the toolbar(s), selecting the new
            #   colour
            $self->ivPoke('bgColourChoice', $colour);
            $self->redrawWidgets('toolbar');
        }

        return 1;
    }

    sub removeBGColourButton {

        # Called by a ->signal_connect in $self->drawBackgroundButtonSet whenever the user clicks
        #   the 'remove background colour' button in the 'background' button set
        # Removes the specified colour from the toolbar and updates IVs
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or if there's an error
        #   1 otherwise

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

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

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

        # Set up the popup menu
        my $popupMenu = Gtk3::Menu->new();
        if (! $popupMenu) {

            return undef;
        }

        # Add a title menu item, which does nothing
        my $title_item = Gtk3::MenuItem->new('Remove preferred background colour:');
        $title_item->signal_connect('activate' => sub {

            return undef;
        });
        $title_item->set_sensitive(FALSE);
        $popupMenu->append($title_item);

        $popupMenu->append(Gtk3::SeparatorMenuItem->new());     # Separator

        # Fill the popup menu with colours
        foreach my $colour ($self->worldModelObj->preferBGColourList) {

            my $menu_item = Gtk3::MenuItem->new($colour);
            $menu_item->signal_connect('activate' => sub {

                # Remove the colour from the world model's list of preferred background colours...
                $self->worldModelObj->del_preferBGColour($colour);
                # ...then redraw the window component containing the toolbar(s)
                $self->redrawWidgets('toolbar');
            });
            $popupMenu->append($menu_item);
        }

        # Add a 'remove all' menu item
        $popupMenu->append(Gtk3::SeparatorMenuItem->new());     # Separator

        my $remove_all_item = Gtk3::MenuItem->new('Remove all');
        $remove_all_item->signal_connect('activate' => sub {

            my ($total, $choice);

            $total = scalar $self->worldModelObj->preferBGColourList;

            # If there's more than one colour, prompt the user for confirmation
            if ($total > 1) {

                $choice = $self->showMsgDialogue(
                    'Remove all colour buttons',
                    'question',
                    'Are you sure you want to remove all ' . $total . ' colour buttons?',
                    'yes-no',
                );

            } else {

                $choice = 'yes';
            }

            if (defined $choice && $choice eq 'yes') {

                # Reset the world model's list of preferred background colour...
                $self->worldModelObj->reset_preferBGColourList();
                # ...then redraw the window component containing the toolbar(s)
                $self->redrawWidgets('toolbar');
            }
        });
        $popupMenu->append($remove_all_item);

        # Also add a 'Cancel' menu item, which does nothing
        my $cancel_item = Gtk3::MenuItem->new('Cancel');
        $cancel_item->signal_connect('activate' => sub {

            return undef;
        });
        $popupMenu->append($cancel_item);

        # Display the popup menu
        $popupMenu->popup(
            undef, undef, undef, undef,
            1,                              # Left mouse button
            Gtk3::get_current_event_time(),
        );

        $popupMenu->show_all();

        # Operation complete. Now wait for the user's response
        return 1;
    }

    # Treeview widget methods

    sub enableTreeView {

        # Called by $self->drawWidgets
        # Sets up the Automapper window's treeview widget
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or if the widget can't be created
        #   Otherwise returns the Gtk3::ScrolledWindow containing the Gtk3::TreeView created

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

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

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

        # Create the treeview
        my $objectModel = Gtk3::TreeStore->new( ['Glib::String'] );
        my $treeView = Gtk3::TreeView->new($objectModel);
        if (! $objectModel || ! $treeView) {

            return undef;
        }

        # No interactive searches required
        $treeView->set_enable_search(FALSE);

        # Append a single column to the treeview
        $treeView->append_column(
            Gtk3::TreeViewColumn->new_with_attributes(
                'Regions',
                Gtk3::CellRendererText->new,
                markup => 0,
            )
        );

        # Make the treeview scrollable
        my $treeViewScroller = Gtk3::ScrolledWindow->new;
        $treeViewScroller->add($treeView);
        $treeViewScroller->set_policy(qw/automatic automatic/);

        # Make the branches of the list tree clickable, so the rows can be expanded and collapsed
        $treeView->signal_connect('row_activated' => sub {

            my ($treeView, $path, $column) = @_;

            $self->treeViewRowActivatedCallback();
        });

lib/Games/Axmud/Win/Map.pm  view on Meta::CPAN

        $canvasFrame->set_border_width(3);

        # Create a scrolled window
        my $canvasScroller = Gtk3::ScrolledWindow->new();
        my $canvasHAdjustment = $canvasScroller->get_hadjustment();
        my $canvasVAdjustment = $canvasScroller->get_vadjustment();
        $canvasScroller->set_border_width(3);
        # Set the scrolling policy
        $canvasScroller->set_policy('always','always');

        # Add the scrolled window to the frame
        $canvasFrame->add($canvasScroller);

        # The only way to scroll the map to the correct position, is to store the scrolled window's
        #   size allocation whenever it is set
        $canvasScroller->signal_connect('size-allocate' => sub {

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

            $self->ivPoke('canvasScrollerWidth', $$hashRef{width});
            $self->ivPoke('canvasScrollerHeight', $$hashRef{height});
        });

        # Store the remaining widgets
        $self->ivPoke('canvasFrame', $canvasFrame);
        $self->ivPoke('canvasScroller', $canvasScroller);
        $self->ivPoke('canvasHAdjustment', $canvasHAdjustment);
        $self->ivPoke('canvasVAdjustment', $canvasVAdjustment);

        # Set up tooltips
        $self->enableTooltips();
        # Draw the empty background map (default is white)
        $self->resetMap();

        # Setup complete
        return $canvasFrame;
    }

    sub enableTooltips {

        # Called by $self->enableCanvas (only)
        # Sets up tooltips
        #
        # 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 . '->enableTooltips', @_);
        }

        # Create a Gtk3::Window to act as a tooltip, being visible (or not) as appropriate
        my $tooltipLabel = Gtk3::Label->new();
        my $tooltipWin = Gtk3::Window->new('popup');
        $tooltipWin->set_decorated(FALSE);
        $tooltipWin->set_position('mouse');
        $tooltipWin->set_border_width(2);
        $tooltipWin->modify_fg('normal', [Gtk3::Gdk::Color::parse('black')]->[1]);
        $tooltipWin->modify_bg('normal', [Gtk3::Gdk::Color::parse('yellow')]->[1]);
        $tooltipWin->add($tooltipLabel);

        # Update IVs
        $self->ivPoke('canvasTooltipObj', undef);
        $self->ivPoke('canvasTooltipObjType', undef);
        $self->ivPoke('canvasTooltipFlag', FALSE);

        # Setup complete
        return 1;
    }

    sub setMapPosn {

        # Can be called by anything
        # Scroll the canvas to the desired position, revealing a portion of the map
        #
        # Expected arguments
        #   $xPos   - Value between 0 (far left) and 1 (far right)
        #   $yPos   - Value between 0 (far top) and 1 (far bottom)
        #
        # Return values
        #   'undef' on improper arguments or if there is no current regionmap
        #   1 otherwise

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

        # Local variables
        my ($canvasWidget, $xBlocks, $yBlocks, $xPixels, $yPixels, $scrollX, $scrollY);

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

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

        # Do nothing if there is no current regionmap
        if (! $self->currentRegionmap) {

            return undef;
        }

        # Get the canvas widget to be scrolled
        $canvasWidget = $self->currentParchment->ivShow(
            'canvasWidgetHash',
            $self->currentRegionmap->currentLevel,
        );

        # The code in this function, which uses GooCanvas2::Canvas->scroll_to, produces a slightly
        #   different value to the code in $self->getMapPosn, which uses scrollbar positions
        # When moving up and down through map levels, this causes the scroll position to drift from
        #   its original position
        # The only way to deal with this is to adjust $xPos and $yPos so that they represent the
        #   middle of a gridblock. In that way, the first change of level might adjust the map's
        #   scroll position (slightly), but subsequent changes preserve the exact same scroll
        #   position

lib/Games/Axmud/Win/Map.pm  view on Meta::CPAN

                # Continue the drag operation by re-drawing the object(s) at their new position
                $self->continueDrag($event, $event->x_root, $event->y_root);
            }
        });

        $canvasObj->signal_connect('enter_notify_event' => sub {

            my ($item, $target, $event) = @_;

            if ($modelObj && $self->worldModelObj->showTooltipsFlag && ! $self->canvasTooltipObj) {

                # Show the tooltips window
                $self->showTooltips($type, $canvasObj, $modelObj);
            }
        });

        $canvasObj->signal_connect('leave_notify_event' => sub {

            my ($item, $target, $event) = @_;

            if (
                $modelObj
                && $self->canvasTooltipFlag
                && $self->canvasTooltipObj eq $canvasObj
                && $self->canvasTooltipObjType eq $type
            ) {
                # Hide the tooltips window
                $self->hideTooltips();
            }
        });

        # Setup complete
        return 1;
    }

    sub canvasEventHandler {

        # Handles events on the map background (i.e. clicking on an empty part of the background
        #   which doesn't contain a room, room tag, room guild, exit, exit tag, label, or checked
        #   direction)
        # The calling function, an anonymous sub defined in $self->setupCanvasEvent, filters out the
        #   signals we don't want
        # At the moment, the signals let through the filter are:
        #   button_press, 2button_press, 3button_press, button_release
        #
        # Expected arguments
        #   $canvasObj  - The canvas object which intercepted an event signal
        #   $event      - The Gtk3::Gdk::Event that caused the signal
        #
        # Return values
        #   'undef' on improper arguments, if there is no region map or if the signal $event is one
        #       that this function doesn't handle
        #   1 otherwise

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

        # Local variables
        my (
            $clickXPosPixels, $clickYPosPixels, $clickType, $button, $shiftFlag, $ctrlFlag,
            $clickXPosBlocks, $clickYPosBlocks, $newRoomObj, $roomNum, $roomObj, $exitObj, $listRef,
            $result, $twinExitObj, $result2, $popupMenu,
        );

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

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

        # Don't do anything if there is no current regionmap
        if (! $self->currentRegionmap) {

            return undef;
        }

        # In case the previous click on the canvas was a right-click on an exit, we no longer need
        #   the coordinates of the click
        $self->ivUndef('exitClickXPosn');
        $self->ivUndef('exitClickYPosn');

        # Get the coordinates on the map of the clicked pixel. If the map is magnified we might get
        #   fractional values, so we need to use int()
        ($clickXPosPixels, $clickYPosPixels) = (int($event->x), int($event->y));

        # For mouse button clicks, get the click type and whether or not the SHIFT and/or CTRL keys
        #   were held down
        ($clickType, $button, $shiftFlag, $ctrlFlag) = $self->checkMouseClick($event);
        if (! $clickType) {

            # Not an event in which we're interested
            return undef;
        }

        # Work out which gridblock is underneath the mouse click
        ($clickXPosBlocks, $clickYPosBlocks) = $self->findGridBlock(
            $clickXPosPixels,
            $clickYPosPixels,
            $self->currentRegionmap,
        );

        # If $self->freeClickMode and/or $self->bgColourMode aren't set to 'default', left-clicking
        #   on empty space causes something unusual to happen
        if (
            $clickType eq 'single'
            && $button eq 'left'
            && ($self->freeClickMode ne 'default' || $self->bgColourMode ne 'default')
        ) {
            # Free click mode 'add_room' - 'Add room at click' menu option
            # (NB If this code is altered, the equivalent code in ->enableCanvasPopupMenu must also
            #   be altered)
            if ($self->freeClickMode eq 'add_room') {

                # Only add one new room
                $self->reset_freeClickMode();

                $newRoomObj = $self->mapObj->createNewRoom(
                    $self->currentRegionmap,
                    $clickXPosBlocks,
                    $clickYPosBlocks,
                    $self->currentRegionmap->currentLevel,
                );

lib/Games/Axmud/Win/Map.pm  view on Meta::CPAN

            # Free click mode 'add_label' - 'Add label at click' menu option
            } elsif ($self->freeClickMode eq 'add_label') {

                $self->addLabelAtClickCallback($clickXPosPixels, $clickYPosPixels);

                # Only add one new label
                $self->reset_freeClickMode();

            # Free click mode 'move_room' - 'Move selected rooms to click' menu option
            } elsif ($self->freeClickMode eq 'move_room') {

                $self->moveRoomsToClick($clickXPosBlocks, $clickYPosBlocks);

                # Only do it once
                $self->reset_freeClickMode();

            # Background colour mode 'square_start' (no menu option)
            } elsif ($self->freeClickMode eq 'default' && $self->bgColourMode eq 'square_start') {

                $self->setColouredSquare($clickXPosBlocks, $clickYPosBlocks);

            # Background colour mode 'rect_start' (no menu option)
            } elsif ($self->freeClickMode eq 'default' && $self->bgColourMode eq 'rect_start') {

                # Store the coordinates of the click, and wait for the second click
                $self->ivPoke('bgColourMode', 'rect_stop');
                $self->ivPoke('bgRectXPos', $clickXPosBlocks);
                $self->ivPoke('bgRectYPos', $clickYPosBlocks);

            # Background colour mode 'rect_stop' (no menu option)
            } elsif ($self->freeClickMode eq 'default' && $self->bgColourMode eq 'rect_stop') {

                $self->setColouredRect($clickXPosBlocks, $clickYPosBlocks);
            }

            # Non-default operation complete
            return 1;
        }

        # Otherwise, see if there's a room inside the gridblock that was clicked (if there is, we
        #   will be able to detect clicks near exits)
        $roomNum = $self->currentRegionmap->fetchRoom(
            $clickXPosBlocks,
            $clickYPosBlocks,
            $self->currentRegionmap->currentLevel,
        );

        if (defined $roomNum) {

            $roomObj = $self->worldModelObj->ivShow('modelHash', $roomNum);
        }

        if ($roomObj && $clickType eq 'single' && $self->currentRegionmap->gridExitHash) {

            # Usually, when we click on the map on an empty pixel, all selected objects are
            #   unselected
            # However, because exits are often drawn only 1 pixel wide, they're quite difficult to
            #   click on. This section checks whether the mouse click occured close enough to an
            #   exit
            # A left-click near an exit causes the exit to be selected/unselected. A right-click
            #   selects the exit (unselecting everything else) and opens a popup menu for that exit.
            #   If the click isn't close enough to an exit, the user is deemed to have clicked in
            #   open space
            # (NB If no exits have been drawn, don't bother checking)

            # Now we check if they clicked near an exit, or in open space
            $exitObj = $self->findClickedExit(
                $clickXPosPixels,
                $clickYPosPixels,
                $roomObj,
                $self->currentRegionmap,
            );

            if ($exitObj) {

                if ($button eq 'left' && $event->state =~ m/mod5-mask/) {

                    # This is a drag operation on the nearby exit
                    $listRef = $self->currentParchment->getDrawnExit($exitObj);
                    if (defined $listRef) {

                        $self->startDrag(
                            'exit',
                            $$listRef[0],        # The exit's canvas object
                            $exitObj,
                            $event,
                            $clickXPosPixels,
                            $clickYPosPixels,
                        );
                    }

                } elsif ($button eq 'left') {

                    # If this exit (and/or its twin) is a selected exit, unselect them
                    $result = $self->unselectObj($exitObj);
                    if ($exitObj->twinExit) {

                        $twinExitObj
                            = $self->worldModelObj->ivShow('exitModelHash', $exitObj->twinExit);

                        if ($twinExitObj) {

                            $result2 = $self->unselectObj($twinExitObj);
                        }
                    }

                    if (! $result && ! $result2) {

                        # The exit wasn't already selected, so select it
                        $self->setSelectedObj(
                            [$exitObj, 'exit'],
                            # Retain other selected objects if CTRL key held down
                            $ctrlFlag,
                        );
                    }

                } elsif ($button eq 'right') {

                    # Select the exit, unselecting all other selected objects
                   $self->setSelectedObj(
                        [$exitObj, 'exit'],
                        FALSE,          # Select this object; unselect all other objects
                    );

                    # Create the popup menu
                    if ($self->selectedExit) {

                        $popupMenu = $self->enableExitsPopupMenu();
                        if ($popupMenu) {

                            $popupMenu->popup(
                                undef, undef, undef, undef,
                                $event->button,
                                $event->time,
                            );
                        }
                    }
                }

                return 1;
            }
        }

        # Otherwise, the user clicked in open space

        # If it was a right-click, open a popup menu
        if ($clickType eq 'single' && $button eq 'right') {

            $popupMenu = $self->enableCanvasPopupMenu(
                $clickXPosPixels,
                $clickYPosPixels,
                $clickXPosBlocks,
                $clickYPosBlocks,
            );

            if ($popupMenu) {

                $popupMenu->popup(
                    undef, undef, undef, undef,
                    $event->button,
                    $event->time,
                );
            }

        # If it was a left-click, it's potentially a selection box operation
        } elsif ($clickType eq 'single' && $button eq 'left') {

            # The selection box isn't actually drawn until the user moves their mouse. If they
            #   release the button instead, at that point we unselect all selected objects
            $self->startSelectBox($clickXPosPixels, $clickYPosPixels);

        # If it's a mouse button release, handle the end of any selection box operation
        } elsif ($clickType eq 'release' && $button eq 'left' && $self->selectBoxFlag) {

            $self->stopSelectBox($event, $clickXPosPixels, $clickYPosPixels);

        # Otherwise, if it's a button click (not a button release), just unselect all selected
        #   objects
        } elsif ($clickType ne 'release') {

            $self->setSelectedObj();
        }

        return 1;
    }

    sub canvasObjEventHandler {

        # Handles events on canvas object (i.e. clicking on a room, room tag, room guild, exit,
        #   exit tag or label). Note that clicks on canvas objects for checked directions are
        #   ignored; they are not handled by this function nor by $self->canvasEventHandler
        # The calling function, an anonymous sub defined in $self->setupCanvasObjEvent, filters out
        #   the signals we don't want
        # At the moment, the signals let through the filter are:
        #   button_press, 2button_press
        #
        # Expected arguments
        #   $objType    - 'room', 'room_tag', 'room_guild', 'exit', 'exit_tag' or 'label'
        #   $canvasObj  - The canvas object which intercepted an event signal
        #   $modelObj   - The GA::ModelObj::Room, GA::Obj::Exit or GA::Obj::MapLabel which is
        #                   represented by this canvas object
        #   $event      - The Gtk3::Gdk::Event that caused the signal
        #
        # Return values
        #   'undef' on improper arguments or if the signal $event is one that this function doesn't
        #       handle
        #   1 otherwise

        my ($self, $objType, $canvasObj, $modelObj, $event, $check) = @_;

        # Local variables
        my (
            $clickType, $button, $shiftFlag, $ctrlFlag, $selectFlag, $clickTime, $otherRoomObj,
            $startX, $stopX, $startY, $stopY, $result, $twinExitObj, $result2, $popupMenu,
        );

        # Check for improper arguments
        if (
            ! defined $objType || ! defined $canvasObj || ! defined $modelObj || ! defined $event
            || defined $check
        ) {
            return $axmud::CLIENT->writeImproper($self->_objClass . '->canvasObjEventHandler', @_);
        }

        # In case the previous click on the canvas was a right-click on an exit, we no longer need
        #   the coordinates of the click
        $self->ivUndef('exitClickXPosn');
        $self->ivUndef('exitClickYPosn');

        # If $self->freeClickMode has been set to 'add_room' or 'add_label' by the 'Add room at
        #   click' or 'Add label at click' menu options, since this part of the grid is already
        #   occupied, we can go back to normal
        if ($self->freeClickMode eq 'add_room' || $self->freeClickMode eq 'add_label') {

            $self->reset_freeClickMode();
        }

        # For mouse button clicks, get the click type and whether or not the SHIFT and/or CTRL keys
        #   were held down
        ($clickType, $button, $shiftFlag, $ctrlFlag) = $self->checkMouseClick($event);
        if (! $clickType) {

            # Not an event in which we're interested
            return undef;
        }

        # Various parts of the function check that these hashes contain at least one item between
        #   them
        if (
            $self->selectedRoomHash || $self->selectedRoomTagHash || $self->selectedRoomGuildHash
            || $self->selectedExitHash || $self->selectedExitTagHash || $self->selectedLabelHash
        ) {
            $selectFlag = TRUE;
        }

        # For capturing double-clicks on rooms, we need to compare the times at which each click is
        #   received
        $clickTime = $axmud::CLIENT->getTime();

        # Process single left clicks
        if ($clickType eq 'single' && $button eq 'left') {

            # Process a left-clicked room differently, if ->freeClickMode has been set to
            #   'connect_exit' by the 'Connect to click' menu option (ignoring the SHIFT/CTRL keys)
            if ($self->freeClickMode eq 'connect_exit' && $objType eq 'room') {

                # Occasionally get an error, when there's no selected exit. $self->freeClickMode
                #   should get reset, but not in these situations
                if (! $self->selectedExit) {

                    $self->reset_freeClickMode();

                } else {

lib/Games/Axmud/Win/Map.pm  view on Meta::CPAN

                        );
                    }
                }
            }

        # Process right-clicks
        } elsif ($clickType eq 'single' && $button eq 'right') {

            if ($objType eq 'exit') {

                # For twin exits - which share a canvas object - use the exit whose parent room is
                #   closest to the click
                $modelObj = $self->chooseClickedExit($modelObj, int($event->x), int($event->y));
            }

            # If a group of things are already selected, unselect them all and select the object
            #   that was clicked
            if ($selectFlag) {

                # Select this room/label, unselecting all other objects
                $self->setSelectedObj(
                    [$modelObj, $objType],
                    # Retain other selected objects if CTRL key held down
                    $ctrlFlag,
                );

            } else {

                # If this object isn't already selected, select it (but don't unselect something
                #   as we would for a left-click)
                if ($objType eq 'room_tag') {

                    $self->setSelectedObj(
                        [$modelObj, 'room_tag'],
                        FALSE,          # Select this object; unselect all other objects
                    );

                } elsif ($objType eq 'room_guild') {

                    $self->setSelectedObj(
                        [$modelObj, 'room_guild'],
                        FALSE,          # Select this object; unselect all other objects
                    );

                } elsif ($objType eq 'exit_tag') {

                    $self->setSelectedObj(
                        [$modelObj, 'exit_tag'],
                        FALSE,          # Select this object; unselect all other objects
                    );

                } else {

                    $self->setSelectedObj(
                        [$modelObj, $objType],
                        FALSE,          # Select this object; unselect all other objects
                    );
                }
            }

            # Create the popup menu
            if ($objType eq 'room' && $self->selectedRoom) {
                $popupMenu = $self->enableRoomsPopupMenu();
            } elsif ($objType eq 'room_tag' && $self->selectedRoomTag) {
                $popupMenu = $self->enableRoomTagsPopupMenu();
            } elsif ($objType eq 'room_guild' && $self->selectedRoomGuild) {
                $popupMenu = $self->enableRoomGuildsPopupMenu();
            } elsif ($objType eq 'exit_tag' && $self->selectedExitTag) {
                $popupMenu = $self->enableExitTagsPopupMenu();
            } elsif ($objType eq 'exit' && $self->selectedExit) {

                # Store the position of the right-click, in case the user wants to add a bend from
                #   the popup menu
                $self->ivPoke('exitClickXPosn', int($event->x));
                $self->ivPoke('exitClickYPosn', int($event->y));
                # Now we can open the poup menu
                $popupMenu = $self->enableExitsPopupMenu();

            } elsif ($objType eq 'label' && $self->selectedLabel) {

                $popupMenu = $self->enableLabelsPopupMenu();
            }

            if ($popupMenu) {

                $popupMenu->popup(undef, undef, undef, undef, $event->button, $event->time);
            }
        }

        return 1;
    }

    sub deleteCanvasObj {

        # Called by numerous functions
        #
        # When a region object, room object, room tag, room guild, exit, exit tag or label is being
        #   drawn, redrawn or deleted from the world model, this function must be called
        # The function checks whether the model object is currently drawn on a map as one or more
        #   canvas objects and, if it is, destroys the canvas objects
        #
        # This function also handles coloured blocks and rectangles on the background map, details
        #   of which are stored in the regionmap object (GA::Obj::Regionmap), not the world model
        # The function checks whether a canvas object for the coloured block/rectangle is currently
        #   displayed on the map as a canvas object and, if so, destroys the canvas object
        #
        # Expected arguments
        #   $type       - Set to 'region', 'room', 'room_tag', 'room_guild', 'exit', 'exit_tag' or
        #                   'label' for world model objects, 'checked_dir' for checked directions
        #                   and 'square', 'rect' for coloured blocks/rectangles
        #   $modelObj   - The GA::ModelObj::Region, GA::ModelObj::Room, GA::Obj::Exit or
        #                   GA::Obj::MapLabel being drawn /redrawn / deleted
        #               - For checked directions, the GA::ModelObj::Room in which the checked
        #                   direction is stored
        #               - For coloured squares, it's not a blessed reference, but a coordinate in
        #                   the form 'x_y' (to delete canvas objects on all levels), or 'x_y_z' (to
        #                   delete the canvas object on one level)
        #               - For coloured rectangles, it's not a blessed reference, but a key in the
        #                   form 'object-number' (to delete canvas objects on all levels), or
        #                   'object-number_level' (to delete the canvas object on one level)
        #
        # Optional arguments
        #   $regionmapObj, $parchmentObj
        #               - The regionmap and parchment object for $modelObj. If not set, this
        #                   function fetches them. Both must be specified if $type is 'square' or
        #                   'rect')
        #   $deleteFlag - Set to TRUE if the object is being deleted from the world model, FALSE
        #                   (or 'undef') if not. Never TRUE for coloured blocks/rectangles which
        #                   are not stored in the world model
        #
        # Return values
        #   'undef' on improper arguments, if there's an error or if there are no canvas objects to
        #       destroy
        #   1 otherwise

        my ($self, $type, $modelObj, $regionmapObj, $parchmentObj, $deleteFlag, $check) = @_;

        # Local variables
        my (
            $roomObj,
            @redrawList,
            %redrawHash,
        );

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

lib/Games/Axmud/Win/Map.pm  view on Meta::CPAN

                    my $exitObj = $self->worldModelObj->ivShow('exitModelHash', $exitNum);

                    if ($exitObj->superFlag) {

                        $exitCount++;
                    }
                }

                # If there are ten super-region exits, each individual exit has nine region paths
                #   joining it to every other super-region exit. We then double the number, because
                #   safe region paths are stored separately. So the estimated number of region
                #   paths is ((n-1) ^ 2 ), all multiplied by 2
                $estimate += (2 * (($exitCount - 1) ** 2));
            }

            # If the estimated number of paths is above the limit set by the world model, make the
            #   pause window visible for the duration of the recalculation
            if ($estimate > $self->worldModelObj->recalculatePauseNum) {

                $self->showPauseWin();
            }

            # Recalculate region paths for each region added to our list
            $count = 0;
            foreach my $regionmapObj (@regionmapList) {

                my $number = $self->worldModelObj->recalculateRegionPaths(
                    $self->session,
                    $regionmapObj,
                );

                if ($number) {

                    $count += $number;
                }
            }

            # Make the pause window invisible
            $self->hidePauseWin();

        } else {

            # Recalculate paths to/from the selected exit.
            $count = $self->worldModelObj->recalculateSpecificPaths(
                $self->session,
                $self->currentRegionmap,
                $self->selectedExit,
            );

            # In case the called function returns 'undef', $count still needs to be an integer
            if (! $count) {

                $count = 0;
            }

            # For the message we're about to compose, @regionmapList must contain the affected
            #   regionmap
            push (@regionmapList, $self->currentRegionmap);
        }

        # Display a popup showing the results
        $msg = 'Recalculation complete: ';

        if (! $count) {
            $msg .= 'no region paths found';
        } elsif ($count == 1) {
            $msg .= '1 region path found';
        } else {
            $msg .= $count . ' region paths found';
        }

        if (@regionmapList == 1) {
            $msg .= ' in 1 region.';
        } else {
            $msg .= ' in ' . scalar @regionmapList . ' regions.';
        }

        $self->showMsgDialogue(
            'Recalculate region paths',
            'info',
            $msg,
            'ok',
        );

        return 1;
    }

    sub locateCurrentRoomCallback {

        # Called by $self->enableRegionsColumn
        # Tries to find the current room by comparing the Locator task's current room with every
        #   room in the current region, in a specified region, or in all regions
        # If there's a single matching room, that room is set as the current room. If the single
        #   matching room is in a different region or level to the current one, the map is redrawn
        # If there are multiple matching rooms, those rooms are selected. If they are all in a
        #   different region or at a different level to the current one, the map is redrawn
        #
        # Expected arguments
        #   $type   - Where to search: 'current' for the current regionmap, 'select' to prompt the
        #               user for a regionmap, or 'all' to search in all regionmaps
        #
        # Return values
        #   'undef' on improper arguments, if the standard callback check fails, if there is no
        #       current regionmap, or if there is no Locator task (or the task doesn't know the
        #       current location), if the Locator's current room is dark or unspecified or if the
        #       user declines to continue
        #   1 otherwise

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

        # Local variables
        my (
            $taskObj, $msg, $regionName, $regionmapObj, $choice, $matchObj, $regionObj,
            @roomList, @list, @regionList, @selectList, @modList, @newRegionList, @sortedList,
            %regionmapHash,
        );

        # Check for improper arguments
        if (
            ! defined $type || ($type ne 'current' && $type ne 'select' && $type ne 'all')
            || defined $check

lib/Games/Axmud/Win/Map.pm  view on Meta::CPAN


        # Called by $self->enableRoomsColumn (also called by GA::Cmd::Go->do)
        # Performs the A* algorithm to find a path between the current room and the selected room,
        #   and then does something with it
        #
        # Expected arguments
        #   $mode   - Set to one of the following:
        #               'select_room' - shows the path by selecting every room along the route
        #               'pref_win' - shows the path in a 'pref' window, allowing the user to store
        #                   it as a pre-defined route (using the ';addroute' command)
        #               'send_char' - sends the character to the selected room
        #
        # Return values
        #   'undef' on improper arguments, if the standard callback check fails or if no path can be
        #       found between the current and selected rooms
        #   1 otherwise

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

        # Local variables
        my (
            $dictObj, $text, $count, $maxChars, $string, $lastExitObj, $roomListRef, $exitListRef,
            $response,
            @roomList, @exitList, @cmdList, @reverseCmdList, @highlightList, @modList,
        );

        # Check for improper arguments
        if (
            ! defined $mode
            || ($mode ne 'select_room' && $mode ne 'pref_win' && $mode ne 'send_char')
            || defined $check
        ) {
            return $axmud::CLIENT->writeImproper($self->_objClass . '->processPathCallback', @_);
        }

        # Standard callback check
        if (
            ! $self->currentRegionmap
            || ! $self->mapObj->currentRoom
            || ! $self->selectedRoom
            || $self->mapObj->currentRoom eq $self->selectedRoom
        ) {
            return undef;
        }

        # Import the current dictionary (for speed)
        $dictObj = $self->session->currentDict;

        # Use the universal version of the A* algorithm to find a path between the current and
        #   selected rooms (if they're in the same region, the call is automatically redirected to
        #   ->findPath)
        ($roomListRef, $exitListRef) = $self->worldModelObj->findUniversalPath(
            $self->session,
            $self->mapObj->currentRoom,
            $self->selectedRoom,
            $self->worldModelObj->avoidHazardsFlag,
        );

        if (! defined $roomListRef || ! @$roomListRef) {

            # There is no path between the current and selected room. Notify the user with a popup
            $self->showMsgDialogue(
                'No path found',
                'warning',
                'There is no known path between the current room (#'
                . $self->mapObj->currentRoom->number . ') and the selected room (#'
                . $self->selectedRoom->number . ')',
                'ok',
            );

            return undef;
        }

        # Apply post-processing to the path to remove jagged edges (if allowed)
        if ($self->worldModelObj->postProcessingFlag) {

            ($roomListRef, $exitListRef) = $self->worldModelObj->smoothPath(
                $self->session,
                $roomListRef,
                $exitListRef,
                $self->worldModelObj->avoidHazardsFlag,
            );
        }

        # Convert the list references returned by the called functions into lists
        @roomList = @$roomListRef;
        @exitList = @$exitListRef;

        # Compile a list of commands to get from one end of the route to the other. If assisted
        #   moves are turned on, use them; otherwise, use each exit's nominal direction
        # At the same time, try to compile a list of directions that lead from the end of the
        #   route back to the start
        @cmdList = $self->worldModelObj->convertExitList($self->session, @exitList);
        # Attempt to find the reverse list of directions, if possible (but only bother in
        #   'select_room' mode)
        if ($mode eq 'pref_win') {

            @reverseCmdList = $self->worldModelObj->findPathCmds($self->session, -1, @roomList);
        }

        # 'select_room' - select each room in the path, in order to highlight the route (but don't
        #   select the current room)
        # 'pref_win' - show the route/reverse route in a 'pref' window
        if ($mode eq 'select_room' || $mode eq 'pref_win') {

            foreach my $roomObj (@roomList) {

                if ($roomObj ne $self->mapObj->currentRoom) {

                    push (@highlightList, $roomObj, 'room');
                }
            }

            $self->setSelectedObj(
                \@highlightList,
                TRUE,           # Select multiple objects, including the currently selected room
            );
        }

        # 'pref_win' - show the route/reverse route in a 'pref' window, allowing the user to store
        #   it as a pre-defined route (using the ';addroute' command)

lib/Games/Axmud/Win/Map.pm  view on Meta::CPAN

            @reducedList = @sortedList;
        }

        # Prepare the message to show in the window
        if ($self->mapObj->currentRoom) {

            $msg = "Current room:\n";
            $msg .= "   #" . $self->mapObj->currentRoom->number . " '";

            # '<unnamed room>' will cause a Pango error, so replace that string
            # GA::ModelObj::Room->name has already been cut down to a maximum of 32 characters. By
            #   checking for a length longer than 31, we can be certain we're not adding an ellipsis
            #   to a room title that was exactly 32 characters long
            $roomName = $self->mapObj->currentRoom->name;
            if ($roomName eq '<unnamed room>') {
                $roomName = '(unnamed room)';
            } elsif (length($roomName) > 31) {
                $roomName = substr($roomName, 0, 29) . '...';
            }

            $msg .= $roomName . "'\n\n";

        } else {

            $msg = '';
        }

        if (@reducedList) {

            if (scalar @sortedList != scalar @reducedList) {

                $msg .= "Selected rooms (first " . $limit . " rooms of " . scalar @sortedList
                        . ")";

            } elsif (scalar @sortedList == 1) {

                $msg .= "Selected rooms (1 room)";

            } else {

                $msg .= "Selected rooms (" . scalar @sortedList . " rooms)";
            }

            foreach my $obj (@reducedList) {

                my $roomName;

                $msg .= "\n   #" . $obj->number . " '";

                $roomName = $obj->name;
                if ($roomName eq '<unnamed room>') {
                    $roomName = '(unnamed room)';
                } elsif (length($roomName) > 31) {
                    $roomName = substr($roomName, 0, 29) . '...';
                }

                $msg .= $roomName . "'";
            }
        }

        # Display a popup to show the results
        $self->showMsgDialogue(
            'Identify rooms',
            'info',
            $msg,
            'ok',
            undef,
            TRUE,           # Preserve newline characters in $msg
        );

        return 1;
    }

    sub updateVisitsCallback {

        # Called by $self->enableRoomsColumn, ->enableRoomsPopupMenu and ->drawMiscButtonSet
        # Adjusts the number of character visits shown in the selected room(s)
        # Normally, the current character's visits are changed. However, if $self->showChar is set,
        #   that character's visits are changed
        #
        # Expected arguments
        #   $mode   - 'increase' to increase the number of visits by one, 'decrease' to decrease the
        #               visits by one, 'manual' to let the user enter a value manually, 'reset' to
        #               reset the number to zero
        #
        # Return values
        #   'undef' on improper arguments, if the standard callback check fails, if the user clicks
        #       the 'cancel' button on a 'dialogue' window or for any other error
        #   1 otherwise

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

        # Local variables
        my (
            $char, $current, $result, $matchFlag,
            @roomList, @drawList,
        );

        # Check for improper arguments
        if (
            ! defined $mode
            || ($mode ne 'increase' && $mode ne 'decrease' && $mode ne 'manual' && $mode ne 'reset')
            || defined $check
        ) {
            return $axmud::CLIENT->writeImproper($self->_objClass . '->updateVisitsCallback', @_);
        }

        # Standard callback check
        if (! $self->currentRegionmap || (! $self->selectedRoom && ! $self->selectedRoomHash)) {

            return undef;
        }

        # Get a list of selected room(s)
        @roomList = $self->compileSelectedRooms();

        # Decide which character to use
        if ($self->showChar) {

            $char = $self->showChar;

lib/Games/Axmud/Win/Map.pm  view on Meta::CPAN

            @sortedList, @reducedList,
        );

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

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

        # Standard callback check
        if (! $self->currentRegionmap || (! $self->selectedExit && ! $self->selectedExitHash)) {

            return undef;
        }

        # Compile a list of selected exits and sort by exit model number
        @sortedList = sort {$a->number <=> $b->number} ($self->compileSelectedExits());

        # Reduce the size of the list to a maximum of $limit
        $limit = 10;
        if (@sortedList > $limit) {
            @reducedList = @sortedList[0..($limit - 1)];
        } else {
            @reducedList = @sortedList;
        }

        # Prepare the message to show in the 'dialogue' window
        if (scalar @sortedList != scalar @reducedList) {

            $msg = "Selected exits (first " . $limit . " exits of " . scalar @sortedList . ")\n";

        } elsif (scalar @sortedList == 1) {

            $msg = "Selected exits (1 exit)\n";

        } else {

            $msg = "Selected exits (" . scalar @sortedList . " exits)\n";
        }

        foreach my $exitObj (@reducedList) {

            my $customDir;

            # Convert the exit's map direction, ->mapDir (a standard primary direction) into a
            #   custom primary direction, so that we can compare it with the exit's nominal
            #   direction
            $customDir = $self->session->currentDict->ivShow('primaryDirHash', $exitObj->mapDir);
            if (! $customDir || $customDir eq $exitObj->dir) {

                $msg .= "   #" . $exitObj->number . " '" . $exitObj->dir . "' (room #"
                            . $exitObj->parent . ")\n";

            } else {

                $msg .= "   #" . $exitObj->number . " '" . $exitObj->dir . "' [" . $exitObj->mapDir
                            . "] (room #" . $exitObj->parent . ")\n";
            }
        }

        # Display a popup to show the results
        $self->showMsgDialogue(
            'Identify exits',
            'info',
            $msg,
            'ok',
            undef,
            TRUE,           # Preserve newline characters in $msg
        );

        return 1;
    }

    sub editExitCallback {

        # Called by $self->enableExitsColumn
        # Opens a GA::EditWin::Exit for the selected exit. If the selected exit could be confused
        #   with others occupying (roughly) the same space, opens a 'dialogue' window so the user
        #   can choose one
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or if the standard callback check fails
        #   1 otherwise

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

        # Local variables
        my (
            $comboListRef, $exitHashRef, $choice, $exitObj,
            @comboList,
            %exitHash,
        );

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

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

        # Standard callback check
        if (! $self->currentRegionmap || ! $self->selectedExit) {

            return undef;
        }

        # If the selected exit has a twin exit and/or a shadow exit, we need to prompt the user to
        #   ask which of them should be edited
        if ($self->selectedExit->twinExit || $self->selectedExit->shadowExit) {

            ($comboListRef, $exitHashRef) = $self->compileExitList();
            if (! defined $comboListRef) {

                return undef;
            }

            @comboList = @$comboListRef;
            %exitHash = %$exitHashRef;

lib/Games/Axmud/Win/Map.pm  view on Meta::CPAN

        ($stringListRef, $exitHashRef) = $self->compileExitList();
        if (! defined $stringListRef) {

            return undef;
        }

        @stringList = @$stringListRef;
        %exitHash = %$exitHashRef;

        # If there is more than one exit in the list, prompt the user to specify which one to delete
        if (scalar @stringList > 1) {

            # Compile the combo list
            if (@stringList == 2) {
                @comboList = ($bothString, @stringList);
            } elsif (@stringList > 2) {
                @comboList = ($allString, @stringList);
            } else {
                @comboList = @stringList;
            }

            # Prompt the user to choose which exit to delete
            $choice = $self->showComboDialogue(
                'Select exit',
                'Select which exit to delete',
                \@comboList,
            );

            if (! $choice) {

                return undef;

            } elsif ($choice eq $bothString || $choice eq $allString) {

                @finalList = values %exitHash;

            } else {

                push (@finalList, $exitHash{$choice});
            }

        } else {

            # There's only one exit on which to operate
            push (@finalList, $self->selectedExit);
        }

        # Delete the exit object(s) and instruct the world model to update its Automapper windows
        $self->worldModelObj->deleteExits(
            $self->session,
            TRUE,           # Update Automapper windows now
            @finalList,
        );

        return 1;
    }

    sub addBendCallback {

        # Called by $self->enableExitsPopupMenu (only)
        # After a right-click on an exit, when the user has selected 'add bend' in the popup menu,
        #   add a bend at the same position
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments, if the standard callback check fails or if the bend is
        #       not added
        #   1 otherwise

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

        # Local variables
        my (
            $startXPos, $startYPos, $clickXPos, $clickYPos, $stopXPos, $stopYPos, $resultType,
            $twinExitObj,
        );

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

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

        # Standard callback check
        if (
            ! $self->currentRegionmap
            || ! $self->selectedExit
            || (! $self->selectedExit->oneWayFlag && ! $self->selectedExit->twinExit)
            || $self->selectedExit->regionFlag
            || ! defined $self->exitClickXPosn
            || ! defined $self->exitClickYPosn
        ) {
            return undef;
        }

        # Get the absolute coordinates of the start of the middle (bending) section of the
        #   exit
        # At the same time, convert the absolute coordinates of the right-mouse click on the exit,
        #   and the absolute coordinates of the end of the bending section, into coordinates
        #   relative to the start of the bending section of the eixt
        ($startXPos, $startYPos, $clickXPos, $clickYPos, $stopXPos, $stopYPos, $resultType)
            = $self->findExitClick(
                $self->selectedExit,
                $self->exitClickXPosn,
                $self->exitClickYPosn,
            );

        # If the click wasn't in the parent room's gridblock, in the destination room's gridblock
        #   or too close to an existing bend...
        if (! $resultType) {

            # Add a bend to the exit
            $self->worldModelObj->addExitBend(
                FALSE,                          # Don't update Automapper windows yet
                $self->selectedExit,
                $startXPos, $startYPos,
                $clickXPos, $clickYPos,
                $stopXPos, $stopYPos,
            );

            # Repeat the process for the selected exit's twin (if there is one)
            if ($self->selectedExit->twinExit) {

                $twinExitObj = $self->worldModelObj->ivShow(
                    'exitModelHash',
                    $self->selectedExit->twinExit,
                );

                ($startXPos, $startYPos, $clickXPos, $clickYPos, $stopXPos, $stopYPos)
                    = $self->findExitClick(
                        $twinExitObj,
                        $self->exitClickXPosn,
                        $self->exitClickYPosn,
                    );

                $self->worldModelObj->addExitBend(
                    FALSE,                          # Don't update Automapper windows yet
                    $twinExitObj,
                    $startXPos, $startYPos,
                    $clickXPos, $clickYPos,
                    $stopXPos, $stopYPos,
                );
            }

            # Now we can redraw the exit
            $self->worldModelObj->updateMapExit(
                $self->selectedExit,
                $twinExitObj,               # May be 'undef'
            );

            return 1;

        } else {

            # If the click was too close to an existing bend, show a message explaining why nothing
            #   has happened (don't bother showing a message for other values of $resultType, which
            #   probably can't be returned to this function anyway)
            if ($resultType eq 'near_bend') {

                $self->showMsgDialogue(
                    'Add bend',
                    'error',
                    'Cannot add a bend - you clicked too close to an existing bend',
                    'ok',
                );
            }

            return undef;
        }
    }

    sub removeBendCallback {

        # Called by $self->enableExitsPopupMenu (only)
        # After a right-click on an exit, when the user has selected 'remove bend' in the popup
        #   menu, remove the bend closest to the clicked position
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments, if the standard callback check fails or if the mouse
        #       click was not near a bend
        #   1 otherwise

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

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

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

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

        # Standard callback check
        if (
            ! $self->currentRegionmap
            || ! $self->selectedExit
            || (! $self->selectedExit->oneWayFlag && ! $self->selectedExit->twinExit)
            || ! $self->selectedExit->bendOffsetList
            || ! defined $self->exitClickXPosn
            || ! defined $self->exitClickYPosn
        ) {
            return undef;
        }

        # Find the number of the bend which is closest to the the clicked position
        $index = $self->findExitBend(
            $self->selectedExit,
            $self->exitClickXPosn,
            $self->exitClickYPosn,
        );

        if (! defined $index) {

            $self->showMsgDialogue(
                'Remove bend',
                'error',
                'Please right-click on the bend that you want to remove',
                'ok',
            );

            return undef;

        } else {

            # Remove this bend
            $self->worldModelObj->removeExitBend(
                $self->session,
                TRUE,                   # Update Automapper windows now
                $self->selectedExit,
                $index,                 # Remove this bend (first bend is numbered 0)
            );

lib/Games/Axmud/Win/Map.pm  view on Meta::CPAN

        if ($exitMode eq 'no_exit') {

            # Draw exit mode 'no_exit': The room takes up the whole gridblock
            ($startX, $startY) = (0, 0);

        } else {

            # Draw exit mode 'simple_exit'/'complex_exit': The room takes up the central part of the
            #   gridblock
            ($startX, $startY) = $self->getBorderCorner(
                0,  # $roomObj->xPosBlocks,
                0,  # $roomObj->yPosBlocks,
                0,  # $blockCornerXPosPixels,
                0,  # $blockCornerYPosPixels,
                $self->currentRegionmap,
            );
        }

        # Find the coordinates of the pixel at the bottom-right of the room's border
        if ($exitMode eq 'no_exit') {

            # Delete 2 pixels to allow a 1-pixel border on each side of the room box; otherwise,
            #   the room's borders touch and will look like double-width lines
            $stopX = $startX + $self->currentRegionmap->blockWidthPixels - 3;
            $stopY = $startY + $self->currentRegionmap->blockHeightPixels - 3;

        # In draw exit modes 'simple_exit'/'complex_exit', the room takes up the middle part of the
        #   gridblock
        } else {

            $stopX = $startX + $self->currentRegionmap->roomWidthPixels - 1;
            $stopY = $startY + $self->currentRegionmap->roomHeightPixels - 1;
        }

        # The mouse button was released over a gridblock containing a suitable room. Was the mouse
        #   over the room itself, or in the surrounding empty space?
        if (
            $clickXPosPixels >= (
                $startX + ($xBlocks * $self->currentRegionmap->blockWidthPixels)
            ) && $clickXPosPixels <= (
                $stopX + ($xBlocks * $self->currentRegionmap->blockWidthPixels)
            ) && $clickYPosPixels >= (
                $startY + ($yBlocks * $self->currentRegionmap->blockHeightPixels)
            ) && $clickYPosPixels <= (
                $stopY + ($yBlocks * $self->currentRegionmap->blockHeightPixels)
            )
        ) {
            # Mouse button was released over a suitable destination room
            return $destRoomObj;

        } else {

            # Mouse button was not release over a suitable destination room
            return undef;
        }
    }

    sub findExitClick {

        # Called by $self->addBendCallback and ->findExitBend
        # After a right-click on an exit, when the user has selected 'add bend' in the popup menu,
        #   find the position of the click relative to the start of the middle (bending) section of
        #   the drawn exit (the bending section starts at the same position, that an uncertain exit
        #   would end). Returns the relative position of the click, as well as the absolute
        #   position of the start and end of the bending section
        # (When called by ->findExitBend, process a left-mouse click)
        #
        # Expected arguments
        #   $exitObj                - The exit that was right-clicked
        #   $clickXPos, $clickYPos  - The position of the mouse click on the canvas
        #
        # Return values
        #   An empty list on improper arguments or if the relative coordinates can't be found
        #   Otherwise returns a list of coordinates in the form
        #       (
        #           start_section_x, start_section_y,
        #           mouse_click_x, mouse_click_y,
        #           end_section_x, end_section_y,
        #           result_type,
        #       )
        #   ...where 'start_section_x', 'start_section_y' are absolute coordinates of the start
        #       of the bending section, and where 'mouse_click_x', 'mouse_click_y', 'end_section_x'
        #       and 'end_section_y' are coordinates relative to the start, and 'result_type' gives
        #       more information about the click:
        #
        #       'parent_block' if the click took place in the parent room's gridblock
        #       'dest_block' if the click took place in the destination room's gridblock
        #       'near_bend' if the click took place close enough to an existing bend, that no
        #           additional bend can be added there
        #       'undef' otherwise

        my ($self, $exitObj, $clickXPos, $clickYPos, $check) = @_;

        # Local variables
        my (
            $mapDir, $exitMode, $roomObj, $xPos, $yPos, $posnListRef, $destRoomObj, $destXPos,
            $destYPos, $oppDir, $oppPosnListRef, $resultType, $bendSize,
            @emptyList, @returnList, @offsetList,
        );

        # Check for improper arguments
        if (! defined $exitObj || ! defined $clickXPos || ! defined $clickYPos || defined $check) {

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

        # Fetch the equivalent primary direction (the direction in which the exit is drawn on the
        #   map)
        $mapDir = $exitObj->mapDir;
        if (! $mapDir) {

            return @emptyList;
        }

        # Get the current exit drawing mode. GA::Obj::WorldModel->drawExitMode is one of the values
        #   'ask_regionmap', 'no_exit', 'simple_exit' and 'complex_exit'. The regionmap's
        #   ->drawExitMode is any of those values except 'ask_regionmap'
        if ($self->worldModelObj->drawExitMode eq 'ask_regionmap') {
            $exitMode = $self->currentRegionmap->drawExitMode;
        } else {



( run in 1.780 second using v1.01-cache-2.11-cpan-364913b4093 )