diff --git a/projectfiles/CodeBlocks/wesnoth.cbp b/projectfiles/CodeBlocks/wesnoth.cbp index e22212fc163b..a1fec4f42b5b 100644 --- a/projectfiles/CodeBlocks/wesnoth.cbp +++ b/projectfiles/CodeBlocks/wesnoth.cbp @@ -1123,6 +1123,8 @@ + + diff --git a/projectfiles/VC9/wesnoth.vcproj b/projectfiles/VC9/wesnoth.vcproj index 5f79bf2aff65..3826354dd26a 100644 --- a/projectfiles/VC9/wesnoth.vcproj +++ b/projectfiles/VC9/wesnoth.vcproj @@ -21288,6 +21288,14 @@ RelativePath="..\..\src\notifications\windows_tray_notification.hpp" > + + + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 36fe06ab6596..0b64b3a8a103 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -916,6 +916,7 @@ set(wesnoth-main_SRC whiteboard/side_actions.cpp whiteboard/suppose_dead.cpp whiteboard/utility.cpp + wmi_pager.cpp ${network_implementation_files} # network.cpp and network_worker.cpp are included by default (without USE_ANA_NETWORK) ) diff --git a/src/SConscript b/src/SConscript index 5f0e2398967e..8acc52c4dced 100644 --- a/src/SConscript +++ b/src/SConscript @@ -553,6 +553,7 @@ wesnoth_sources = Split(""" widgets/combo_drag.cpp widgets/drop_target.cpp widgets/scrollpane.cpp + wmi_pager.cpp """) if env["PLATFORM"] == "win32": diff --git a/src/game_events/menu_item.hpp b/src/game_events/menu_item.hpp index e6904472d18b..937dee2991ad 100644 --- a/src/game_events/menu_item.hpp +++ b/src/game_events/menu_item.hpp @@ -41,6 +41,8 @@ class wml_menu_item wml_menu_item(const std::string& id, const vconfig & definition, const wml_menu_item & original); + /// The id of this item. + const std::string & id() const { return item_id_; } /// The image associated with this menu item. const std::string & image() const; /// If true, allow using the menu to trigger this item. diff --git a/src/game_events/wmi_container.cpp b/src/game_events/wmi_container.cpp index 0e6848c0e5e6..d8188188d82e 100644 --- a/src/game_events/wmi_container.cpp +++ b/src/game_events/wmi_container.cpp @@ -34,9 +34,6 @@ static lg::log_domain log_engine("engine"); #define WRN_NG LOG_STREAM(warn, log_engine) #define LOG_NG LOG_STREAM(info, log_engine) -static const size_t MAX_WML_COMMANDS = 7; - - // This file is in the game_events namespace. namespace game_events { @@ -104,16 +101,14 @@ bool wmi_container::fire_item(const std::string & id, const map_location & hex) /** * Returns the menu items that can be shown for the given location. - * The number of items returned is limited by MAX_WML_COMMANDS. + * Should be used with a wmi_pager to limit the number of items displayed at once. * @param[out] items Pointers to applicable menu items will be pushed onto @a items. * @param[out] descriptions Menu item text will be pushed onto @descriptions (in the same order as @a items). */ void wmi_container::get_items(const map_location& hex, std::vector > & items, - std::vector & descriptions) const + std::vector & descriptions, const_iterator start, const_iterator finish) const { - size_t item_count = 0; - if ( empty() ) // Nothing to do (skip setting game variables). return; @@ -124,7 +119,7 @@ void wmi_container::get_items(const map_location& hex, scoped_xy_unit highlighted_unit("unit", hex.x, hex.y, *resources::units); // Check each menu item. - BOOST_FOREACH( const item_ptr & item, *this ) + BOOST_FOREACH( const item_ptr & item, std::make_pair (start, finish) ) { // Can this item be shown? if ( item->use_wml_menu() && item->can_show(hex) ) @@ -132,10 +127,6 @@ void wmi_container::get_items(const map_location& hex, // Include this item. items.push_back(item); descriptions.push_back(item->menu_text()); - - // Limit how many items can be returned. - if ( ++item_count >= MAX_WML_COMMANDS ) - return; } } } diff --git a/src/game_events/wmi_container.hpp b/src/game_events/wmi_container.hpp index 02f28e566b7e..c8a6167927be 100644 --- a/src/game_events/wmi_container.hpp +++ b/src/game_events/wmi_container.hpp @@ -76,8 +76,15 @@ class wmi_container{ bool fire_item(const std::string & id, const map_location & hex) const; /// Returns the menu items that can be shown for the given location. void get_items(const map_location& hex, - std::vector > & items, - std::vector & descriptions) const; + std::vector > & items, + std::vector & descriptions, + const_iterator start, const_iterator finish) const; + /// Range over all items by default + void get_items(const map_location& hex, + std::vector > & items, + std::vector & descriptions) const { + get_items(hex, items, descriptions, begin(), end()); + } /// Initializes the implicit event handlers for inlined [command]s. void init_handlers() const; void to_config(config& cfg) const; @@ -99,6 +106,7 @@ class wmi_container{ const_iterator begin() const { return const_iterator(wml_menu_items_.begin()); } const_iterator end() const { return const_iterator(wml_menu_items_.end()); } + size_t size() const { return wml_menu_items_.size(); } private: // data map_t wml_menu_items_; }; diff --git a/src/play_controller.cpp b/src/play_controller.cpp index 5a69da83bcc8..f16f071061d2 100644 --- a/src/play_controller.cpp +++ b/src/play_controller.cpp @@ -54,6 +54,7 @@ #include "unit.hpp" #include "unit_id.hpp" #include "whiteboard/manager.hpp" +#include "wmi_pager.hpp" #include "wml_exception.hpp" #include @@ -125,6 +126,7 @@ play_controller::play_controller(const config& level, saved_game& state_of_game, init_side_done_(level["init_side_done"].to_bool(true)), savenames_(), wml_commands_(), + wml_command_pager_(new wmi_pager()), victory_when_enemies_defeated_(true), remove_from_carryover_on_defeat_(true), end_level_data_(), @@ -724,7 +726,9 @@ bool play_controller::execute_command(const hotkey::hotkey_command& cmd, int ind throw game::load_game_exception(savenames_[i],false,false,false,""); } else if ( i < wml_commands_.size() && wml_commands_[i] ) { - wml_commands_[i]->fire_event(mouse_handler_.get_last_hex()); + if (!wml_command_pager_->capture(*wml_commands_[i])) { + wml_commands_[i]->fire_event(mouse_handler_.get_last_hex()); + } return true; } } @@ -1072,7 +1076,8 @@ void play_controller::expand_wml_commands(std::vector& items) // Replace this placeholder entry with available menu items. items.erase(items.begin() + i); - gamestate_.gamedata_.get_wml_menu_items().get_items(mouse_handler_.get_last_hex(), + wml_command_pager_->update_ref(&gamestate_.gamedata_.get_wml_menu_items()); + wml_command_pager_->get_items(mouse_handler_.get_last_hex(), wml_commands_, newitems); items.insert(items.begin()+i, newitems.begin(), newitems.end()); // End the "for" loop. diff --git a/src/play_controller.hpp b/src/play_controller.hpp index 7a1e7ab4fb80..f7a632e78856 100644 --- a/src/play_controller.hpp +++ b/src/play_controller.hpp @@ -35,6 +35,7 @@ class saved_game; class game_data; class team; class unit; +class wmi_pager; namespace actions { class undo_list; @@ -279,6 +280,7 @@ class play_controller : public controller_base, public events::observer, public void expand_wml_commands(std::vector& items); std::vector wml_commands_; + boost::scoped_ptr wml_command_pager_; bool victory_when_enemies_defeated_; bool remove_from_carryover_on_defeat_; diff --git a/src/wmi_pager.cpp b/src/wmi_pager.cpp new file mode 100644 index 000000000000..dceb0987a1a0 --- /dev/null +++ b/src/wmi_pager.cpp @@ -0,0 +1,131 @@ +/* + Copyright (C) 2014 by Chris Beck + Part of the Battle for Wesnoth Project http://www.wesnoth.org/ + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY. + + See the COPYING file for more details. +*/ + + +#include "wmi_pager.hpp" +#include "global.hpp" + +#include "config.hpp" +#include "game_events/menu_item.hpp" +#include "game_events/wmi_container.hpp" +#include "gettext.hpp" + +#include +#include +#include //std::advance +#include +#include + +struct map_location; + +static const char * next_id = "__wml_items_next_page"; +static const char * prev_id = "__wml_items_prev_page"; + +static void add_next_page_item( std::vector > & items, + std::vector & descriptions) +{ + std::string desc = _("More Items"); + config temp; + temp["description"] = desc; + items.push_back(boost::make_shared(next_id, temp)); + descriptions.push_back(desc); +} + +static void add_prev_page_item( std::vector > & items, + std::vector & descriptions) +{ + std::string desc = _("Earlier Items"); + config temp; + temp["description"] = desc; + items.push_back(boost::make_shared(prev_id, temp)); + descriptions.push_back(desc); +} + +bool wmi_pager::capture ( const game_events::wml_menu_item & item ) +{ + if (item.id() == next_id) { + page_num_++; + return true; + } else if (item.id() == prev_id) { + page_num_--; + return true; + } + return false; +} + +typedef game_events::wmi_container::const_iterator wmi_it; + +void wmi_pager::get_items(const map_location& hex, + std::vector > & items, + std::vector & descriptions) +{ + if (!foo_) { + return; + } + + assert(page_size_ > 2u); //if we dont have at least 3 items, we can't display anything... + + if (foo_->size() <= page_size_) { //In this case the first page is sufficient and we don't have to do anything. + foo_->get_items(hex, items, descriptions); + page_num_ = 0; //reset page num in case there are more items later. + return; + } + + if (page_num_ < 0) //should never happen but maybe some wierd gui thing happens idk + { + page_num_ = 0; + } + + if (page_num_ == 0) { //we are on the first page, so show page_size_-1 items and a next button + wmi_it end_first_page = foo_->begin(); + std::advance(end_first_page, page_size_ - 1); + + foo_->get_items(hex, items, descriptions, foo_->begin(), end_first_page); + add_next_page_item(items, descriptions); + return; + } + + add_prev_page_item(items, descriptions); //this will be necessary since we aren't on the first page + + // first page has page_size_ - 1. + // last page has page_size_ - 1. + // all other pages have page_size_ - 2; + + size_t first_displayed_index = (page_size_ - 2) * page_num_ + 1; //this is the 0-based index of the first item displayed on this page. + //alternatively, the number of items displayed on earlier pages + + while (first_displayed_index >= foo_->size()) + { + page_num_--; //The list must have gotten shorter and our page counter is now off the end, so decrement + first_displayed_index = (page_size_ - 2) * page_num_ + 1; //recalculate + } + // ^ This loop terminates with first_displayed_index > 0, because foo_->size() > page_size_ or else we exited earlier, and we only decrease by (page_size_-2) each time. + + wmi_it start_range = foo_->begin(); + std::advance(start_range, first_displayed_index); // <-- get an iterator to the start of our range. begin() + n doesn't work because map is not random access + //^ = foo_->begin() + first_displayed_index + + if (first_displayed_index + page_size_-1 >= foo_->size()) //if this can be the last page, then we won't put next page at the bottom. + { + foo_->get_items(hex, items, descriptions, start_range, foo_->end()); // display all of the remaining items + return; + } else { //we are in a middle page + wmi_it end_range = start_range; + std::advance(end_range, page_size_-2); + + foo_->get_items(hex, items, descriptions, start_range, end_range); + add_next_page_item(items, descriptions); + return; + } +} diff --git a/src/wmi_pager.hpp b/src/wmi_pager.hpp new file mode 100644 index 000000000000..86276f8e083e --- /dev/null +++ b/src/wmi_pager.hpp @@ -0,0 +1,49 @@ +/* + Copyright (C) 2014 by Chris Beck + Part of the Battle for Wesnoth Project http://www.wesnoth.org/ + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY. + + See the COPYING file for more details. +*/ + + +/** This class manages the paging of WML menu items results, from a + * container. It is an adapter, managing the production of items lists + * from the container, and screening the "fire" signals coming back + * in to intercept the paging signals. + */ + +struct map_location; +namespace game_events { class wml_menu_item; } +namespace game_events { class wmi_container; } + +#include "global.hpp" + +#include +#include +#include + +class wmi_pager { +private: + int page_num_; //!< Current page number + size_t page_size_; //!< Current size of a page + const game_events::wmi_container * foo_; //!< Internal pointer to the collection of wml menu items + +public: + wmi_pager() : page_num_(0), page_size_(7), foo_(NULL) {} + + void update_ref(game_events::wmi_container * ptr) { foo_ = ptr; } //!< Updates the internal wmi_container pointer + + /** Adds the currently paged range of menu items to the given lists */ + void get_items(const map_location& hex, //!< Game hex related to this context menu + std::vector > & items, //!< List of accumulated menu items so far. + std::vector & descriptions); //!< List of menu item descriptions + + bool capture(const game_events::wml_menu_item & item); //!< Captures a page up / page down event in the case that it is fired. +};