made menu widgets sortable
This commit is contained in:
parent
d4062f31a6
commit
1943ac5873
31 changed files with 853 additions and 131 deletions
|
@ -1,6 +1,7 @@
|
|||
CVS HEAD:
|
||||
* balancing change: reduced cost of dwarvish fighter by 1 gold; made Lieutenant more powerful
|
||||
* user interface improvements:
|
||||
* made menu widgets sortable
|
||||
* added %-to-kill to Damage Calculations
|
||||
* made it so a unit's defense in a terrain is displayed over the terrain when choosing to move the unit
|
||||
* added 'Advanced' preferences section with 'binary save files' and 'show combat' as initial options
|
||||
|
|
|
@ -57,7 +57,7 @@ void checksumstreambuf::sum(unsigned char* begin, unsigned char* end)
|
|||
}
|
||||
|
||||
|
||||
checksumstream::checksumstream() : std::ostream(&sbuf)
|
||||
checksumstream::checksumstream() : std::basic_ostream<char>(this), sbuf(*this)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -39,13 +39,13 @@ private:
|
|||
unsigned short csumb;
|
||||
};
|
||||
|
||||
class checksumstream : public std::basic_ostream<char>
|
||||
class checksumstream : private checksumstreambuf, public std::basic_ostream<char>
|
||||
{
|
||||
public:
|
||||
checksumstream();
|
||||
unsigned long checksum();
|
||||
private:
|
||||
checksumstreambuf sbuf;
|
||||
checksumstreambuf& sbuf;
|
||||
};
|
||||
|
||||
#endif // CHECKSUM_HPP_INCLUDED
|
||||
|
|
|
@ -392,6 +392,51 @@ void save_preview_pane::draw_contents()
|
|||
font::draw_text(&video(), area, font::SIZE_SMALL, font::NORMAL_COLOUR, str.str(), area.x, ypos, true);
|
||||
}
|
||||
|
||||
std::string format_time_summary(time_t t)
|
||||
{
|
||||
time_t curtime = time(NULL);
|
||||
const struct tm* timeptr = localtime(&curtime);
|
||||
if(timeptr == NULL) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const struct tm current_time = *timeptr;
|
||||
|
||||
timeptr = localtime(&t);
|
||||
if(timeptr == NULL) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const struct tm save_time = *timeptr;
|
||||
|
||||
const char* format_string = _("%b %d %y");
|
||||
|
||||
if(current_time.tm_year == save_time.tm_year) {
|
||||
const int days_apart = current_time.tm_yday - save_time.tm_yday;
|
||||
if(days_apart == 0) {
|
||||
//save is from today
|
||||
format_string = _("%H:%M");
|
||||
} else if(days_apart > 0 && days_apart <= current_time.tm_wday) {
|
||||
//save is from this week
|
||||
format_string = _("%A, %H:%M");
|
||||
} else {
|
||||
//save is from current year
|
||||
format_string = _("%b %d");
|
||||
}
|
||||
} else {
|
||||
//save is from a different year
|
||||
format_string = _("%b %d %y");
|
||||
}
|
||||
|
||||
char buf[40];
|
||||
const size_t res = strftime(buf,sizeof(buf),format_string,&save_time);
|
||||
if(res == 0) {
|
||||
buf[0] = 0;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
} //end anon namespace
|
||||
|
||||
std::string load_game_dialog(display& disp, const config& game_config, const game_data& data, bool* show_replay)
|
||||
|
@ -495,13 +540,23 @@ std::string load_game_dialog(display& disp, const config& game_config, const gam
|
|||
}
|
||||
|
||||
std::vector<std::string> items;
|
||||
std::ostringstream heading;
|
||||
heading << HEADING_PREFIX << _("Name") << COLUMN_SEPARATOR << _("Date");
|
||||
items.push_back(heading.str());
|
||||
|
||||
for(i = games.begin(); i != games.end(); ++i) {
|
||||
std::string name = i->name;
|
||||
name.resize(minimum<size_t>(name.size(),40));
|
||||
|
||||
items.push_back(name);
|
||||
std::ostringstream str;
|
||||
str << name << COLUMN_SEPARATOR << format_time_summary(i->time_modified);
|
||||
|
||||
items.push_back(str.str());
|
||||
}
|
||||
|
||||
gui::menu::basic_sorter sorter;
|
||||
sorter.set_alpha_sort(0).set_id_sort(1);
|
||||
|
||||
gamemap map_obj(game_config,"");
|
||||
|
||||
std::vector<gui::preview_pane*> preview_panes;
|
||||
|
@ -517,7 +572,7 @@ std::string load_game_dialog(display& disp, const config& game_config, const gam
|
|||
const int res = gui::show_dialog(disp,NULL,
|
||||
_("Load Game"),
|
||||
_("Choose the game to load"),
|
||||
gui::OK_CANCEL,&items,&preview_panes,"",NULL,-1,NULL,&options,-1,-1,NULL,&buttons);
|
||||
gui::OK_CANCEL,&items,&preview_panes,"",NULL,-1,NULL,&options,-1,-1,NULL,&buttons,"",&sorter);
|
||||
|
||||
if(res == -1)
|
||||
return "";
|
||||
|
|
15
src/font.cpp
15
src/font.cpp
|
@ -492,6 +492,8 @@ text_surface &text_cache::find(text_surface const &t)
|
|||
return cache_.front();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
surface render_text(const std::string& text, int fontsize, const SDL_Color& colour, int style)
|
||||
{
|
||||
const std::vector<std::string> lines = utils::split(text, '\n', utils::REMOVE_EMPTY);
|
||||
|
@ -547,6 +549,8 @@ surface render_text(const std::string& text, int fontsize, const SDL_Color& colo
|
|||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
//function which will parse the markup tags at the front of a string
|
||||
std::string::const_iterator parse_markup(std::string::const_iterator i1, std::string::const_iterator i2,
|
||||
int* font_size, SDL_Color* colour, int* style)
|
||||
|
@ -816,9 +820,20 @@ int get_max_height(int size)
|
|||
|
||||
bool is_format_char(char c)
|
||||
{
|
||||
//side coloring
|
||||
if(c > 0 && c <= 10) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch(c) {
|
||||
case LARGE_TEXT:
|
||||
case SMALL_TEXT:
|
||||
case GOOD_TEXT:
|
||||
case BAD_TEXT:
|
||||
case NORMAL_TEXT:
|
||||
case BLACK_TEXT:
|
||||
case BOLD_TEXT:
|
||||
case NULL_MARKUP:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
|
24
src/game.cpp
24
src/game.cpp
|
@ -875,17 +875,20 @@ void game_controller::download_campaigns()
|
|||
}
|
||||
|
||||
std::vector<std::string> campaigns, options;
|
||||
|
||||
std::string sep(1, COLUMN_SEPARATOR);
|
||||
options.push_back(sep + _("Name") +
|
||||
sep + _("Version") +
|
||||
sep + _("Author") +
|
||||
sep + _("Downloads") +
|
||||
sep + _("Size"));
|
||||
|
||||
std::stringstream heading;
|
||||
heading << HEADING_PREFIX << sep << _("Name") << sep << _("Version") << sep
|
||||
<< _("Author") << sep << _("Downloads") << sep << _("Size");
|
||||
|
||||
const config::child_list& cmps = campaigns_cfg->get_children("campaign");
|
||||
const std::vector<std::string>& publish_options = available_campaigns();
|
||||
|
||||
std::vector<std::string> delete_options;
|
||||
|
||||
std::vector<int> sizes;
|
||||
|
||||
for(config::child_list::const_iterator i = cmps.begin(); i != cmps.end(); ++i) {
|
||||
const std::string& name = (**i)["name"];
|
||||
campaigns.push_back(name);
|
||||
|
@ -914,6 +917,9 @@ void game_controller::download_campaigns()
|
|||
if(author.size() > 16) {
|
||||
author.resize(16);
|
||||
}
|
||||
|
||||
//add negative sizes to reverse the sort order
|
||||
sizes.push_back(-atoi((**i)["size"].c_str()));
|
||||
|
||||
options.push_back(IMAGE_PREFIX + (**i)["icon"].str() + COLUMN_SEPARATOR +
|
||||
title + COLUMN_SEPARATOR +
|
||||
|
@ -922,6 +928,8 @@ void game_controller::download_campaigns()
|
|||
(**i)["downloads"].str() + COLUMN_SEPARATOR +
|
||||
format_file_size((**i)["size"]));
|
||||
}
|
||||
|
||||
options.push_back(heading.str());
|
||||
|
||||
for(std::vector<std::string>::const_iterator j = publish_options.begin(); j != publish_options.end(); ++j) {
|
||||
options.push_back(sep + _("Publish campaign: ") + *j);
|
||||
|
@ -936,7 +944,11 @@ void game_controller::download_campaigns()
|
|||
return;
|
||||
}
|
||||
|
||||
const int index = gui::show_dialog(disp(),NULL,_("Get Campaign"),_("Choose the campaign to download."),gui::OK_CANCEL,&options) - 1;
|
||||
gui::menu::basic_sorter sorter;
|
||||
sorter.set_alpha_sort(1).set_alpha_sort(2).set_alpha_sort(3).set_numeric_sort(4).set_position_sort(5,sizes);
|
||||
|
||||
const int index = gui::show_dialog(disp(),NULL,_("Get Campaign"),_("Choose the campaign to download."),gui::OK_CANCEL,&options,
|
||||
NULL,"",NULL,0,NULL,NULL,-1,-1,NULL,NULL,"",&sorter) - 1;
|
||||
if(index < 0) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace game_config
|
|||
int recall_cost = 20;
|
||||
int kill_experience = 8;
|
||||
int leadership_bonus = 25;
|
||||
const std::string version = VERSION;
|
||||
const std::string version = "0.9.1"; //VERSION;
|
||||
bool debug = false, editor = false, ignore_replay_errors = false;
|
||||
|
||||
std::string game_icon = "wesnoth-icon.png", game_title, game_logo, title_music;
|
||||
|
|
|
@ -234,7 +234,13 @@ bool show_intro_part(display &disp, const config& part,
|
|||
//to find out if the next word will fit, or if it has to be wrapped
|
||||
utils::utf8_iterator start_word = itor;
|
||||
++start_word;
|
||||
utils::utf8_iterator end_word = std::find(start_word, utils::utf8_iterator::end(story), ' ');
|
||||
utils::utf8_iterator end_word = start_word;
|
||||
const utils::utf8_iterator end_story = utils::utf8_iterator::end(story);
|
||||
for(; end_word != end_story; ++end_word) {
|
||||
if(*end_word == ' ') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string word;
|
||||
for(; start_word != end_word; ++start_word) {
|
||||
|
|
|
@ -20,8 +20,86 @@
|
|||
#include "game_config.hpp"
|
||||
#include "gettext.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
std::string games_menu_heading()
|
||||
{
|
||||
std::ostringstream str;
|
||||
str << HEADING_PREFIX << _("Map") << COLUMN_SEPARATOR << _("Name")
|
||||
<< COLUMN_SEPARATOR << _("Status");
|
||||
return str.str();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace mp {
|
||||
|
||||
lobby::lobby_sorter::lobby_sorter(const config& cfg) : cfg_(cfg)
|
||||
{
|
||||
set_alpha_sort(1);
|
||||
}
|
||||
|
||||
bool lobby::lobby_sorter::column_sortable(int column) const
|
||||
{
|
||||
switch(column)
|
||||
{
|
||||
case MAP_COLUMN:
|
||||
case STATUS_COLUMN:
|
||||
return true;
|
||||
default:
|
||||
return basic_sorter::column_sortable(column);
|
||||
}
|
||||
}
|
||||
|
||||
bool lobby::lobby_sorter::less(int column, const gui::menu::item& row1, const gui::menu::item& row2) const
|
||||
{
|
||||
const config* const list = cfg_.child("gamelist");
|
||||
if(list == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const config::child_list& games = list->get_children("game");
|
||||
if(row1.id >= games.size() || row2.id >= games.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const config& game1 = *games[row1.id];
|
||||
const config& game2 = *games[row2.id];
|
||||
|
||||
if(column == MAP_COLUMN) {
|
||||
size_t mapsize1 = game1["map_data"].size();
|
||||
if(mapsize1 == 0) {
|
||||
mapsize1 = game1["map"].size();
|
||||
}
|
||||
|
||||
size_t mapsize2 = game2["map_data"].size();
|
||||
if(mapsize2 == 0) {
|
||||
mapsize2 = game2["map"].size();
|
||||
}
|
||||
|
||||
return mapsize1 < mapsize2;
|
||||
|
||||
} else if(column == STATUS_COLUMN) {
|
||||
const int nslots1 = atoi(game1["slots"].c_str());
|
||||
const int nslots2 = atoi(game2["slots"].c_str());
|
||||
|
||||
const int turn1 = atoi(game1["turn"].c_str());
|
||||
const int turn2 = atoi(game2["turn"].c_str());
|
||||
|
||||
if(nslots1 > nslots2) {
|
||||
return true;
|
||||
} else if(nslots1 < nslots2) {
|
||||
return false;
|
||||
} else {
|
||||
return turn1 < turn2;
|
||||
}
|
||||
} else {
|
||||
return basic_sorter::less(column,row1,row2);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
lobby::lobby(display& disp, const config& cfg, chat& c, config& gamelist) :
|
||||
mp::ui(disp, cfg, c, gamelist),
|
||||
|
||||
|
@ -29,7 +107,8 @@ lobby::lobby(display& disp, const config& cfg, chat& c, config& gamelist) :
|
|||
join_game_(disp.video(), _("Join Game")),
|
||||
create_game_(disp.video(), _("Create Game")),
|
||||
quit_game_(disp.video(), _("Quit")),
|
||||
games_menu_(disp.video(), std::vector<std::string>()),
|
||||
sorter_(gamelist),
|
||||
games_menu_(disp.video(), std::vector<std::string>(1,games_menu_heading()),false,-1,-1,&sorter_),
|
||||
current_game_(0)
|
||||
{
|
||||
game_config::debug = false;
|
||||
|
|
|
@ -39,6 +39,18 @@ protected:
|
|||
virtual void gamelist_updated(bool silent=true);
|
||||
private:
|
||||
|
||||
class lobby_sorter : public gui::menu::basic_sorter
|
||||
{
|
||||
const config& cfg_;
|
||||
|
||||
bool column_sortable(int column) const;
|
||||
bool less(int column, const gui::menu::item& row1, const gui::menu::item& row2) const;
|
||||
|
||||
enum { MAP_COLUMN = 0, STATUS_COLUMN = 2};
|
||||
public:
|
||||
lobby_sorter(const config& cfg);
|
||||
};
|
||||
|
||||
std::vector<bool> game_vacant_slots_;
|
||||
std::vector<bool> game_observers_;
|
||||
|
||||
|
@ -47,6 +59,7 @@ private:
|
|||
gui::button create_game_;
|
||||
gui::button quit_game_;
|
||||
|
||||
lobby_sorter sorter_;
|
||||
gui::menu games_menu_;
|
||||
int current_game_;
|
||||
};
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "global.hpp"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "playcampaign.hpp"
|
||||
|
|
|
@ -1700,13 +1700,17 @@ void turn_info::status_table()
|
|||
{
|
||||
std::vector<std::string> items;
|
||||
std::stringstream heading;
|
||||
heading << _("Leader") << COLUMN_SEPARATOR << ' ' << COLUMN_SEPARATOR
|
||||
heading << HEADING_PREFIX << _("Leader") << COLUMN_SEPARATOR << ' ' << COLUMN_SEPARATOR
|
||||
<< _("Gold") << COLUMN_SEPARATOR
|
||||
<< _("Villages") << COLUMN_SEPARATOR
|
||||
<< _("Units") << COLUMN_SEPARATOR
|
||||
<< _("Upkeep") << COLUMN_SEPARATOR
|
||||
<< _("Income");
|
||||
|
||||
gui::menu::basic_sorter sorter;
|
||||
sorter.set_redirect_sort(0,1).set_alpha_sort(1).set_numeric_sort(2).set_numeric_sort(3)
|
||||
.set_numeric_sort(4).set_numeric_sort(5).set_numeric_sort(6).set_numeric_sort(7);
|
||||
|
||||
if(game_config::debug)
|
||||
heading << COLUMN_SEPARATOR << _("Gold");
|
||||
|
||||
|
@ -1763,7 +1767,8 @@ void turn_info::status_table()
|
|||
items.push_back(IMAGE_PREFIX + std::string("random-enemy.png") + COLUMN_SEPARATOR +
|
||||
IMAGE_PREFIX + "random-enemy.png");
|
||||
|
||||
gui::show_dialog(gui_,NULL,"","",gui::CLOSE_ONLY,&items);
|
||||
gui::show_dialog(gui_,NULL,"","",gui::CLOSE_ONLY,&items,
|
||||
NULL,"",NULL,0,NULL,NULL,-1,-1,NULL,NULL,"",&sorter);
|
||||
}
|
||||
|
||||
void turn_info::recruit()
|
||||
|
@ -1969,14 +1974,26 @@ void turn_info::recall()
|
|||
gui::show_dialog(gui_,NULL,"",msg.str());
|
||||
} else {
|
||||
std::vector<std::string> options;
|
||||
|
||||
std::ostringstream heading;
|
||||
heading << HEADING_PREFIX << COLUMN_SEPARATOR << _("Type")
|
||||
<< COLUMN_SEPARATOR << _("Name")
|
||||
<< COLUMN_SEPARATOR << _("Level")
|
||||
<< COLUMN_SEPARATOR << _("XP");
|
||||
|
||||
gui::menu::basic_sorter sorter;
|
||||
sorter.set_alpha_sort(1).set_alpha_sort(2).set_id_sort(3).set_numeric_sort(4);
|
||||
|
||||
options.push_back(heading.str());
|
||||
|
||||
for(std::vector<unit>::const_iterator u = recall_list.begin(); u != recall_list.end(); ++u) {
|
||||
std::stringstream option;
|
||||
const std::string& description = u->description().empty() ? "-" : u->description();
|
||||
option << IMAGE_PREFIX << u->type().image() << COLUMN_SEPARATOR
|
||||
<< u->type().language_name() << COLUMN_SEPARATOR
|
||||
<< description << COLUMN_SEPARATOR
|
||||
<< _("level: ") << u->type().level() << COLUMN_SEPARATOR
|
||||
<< _("XP: ") << u->experience() << "/";
|
||||
<< u->type().level() << COLUMN_SEPARATOR
|
||||
<< u->experience() << "/";
|
||||
|
||||
if(u->can_advance() == false) {
|
||||
option << "-";
|
||||
|
@ -2004,7 +2021,7 @@ void turn_info::recall()
|
|||
_("Select unit:") + std::string("\n"),
|
||||
gui::OK_CANCEL,&options,
|
||||
&preview_panes,"",NULL,-1,
|
||||
NULL,NULL,-1,-1,NULL,&buttons);
|
||||
NULL,NULL,-1,-1,NULL,&buttons,"",&sorter);
|
||||
}
|
||||
|
||||
if(res >= 0) {
|
||||
|
@ -2135,7 +2152,8 @@ void turn_info::objectives()
|
|||
|
||||
void turn_info::unit_list()
|
||||
{
|
||||
const std::string heading = _("Type") + std::string(1, COLUMN_SEPARATOR) +
|
||||
const std::string heading = std::string(1,HEADING_PREFIX) +
|
||||
_("Type") + std::string(1, COLUMN_SEPARATOR) +
|
||||
_("Name") + COLUMN_SEPARATOR +
|
||||
_("HP") + COLUMN_SEPARATOR +
|
||||
_("XP") + COLUMN_SEPARATOR +
|
||||
|
@ -2143,6 +2161,10 @@ void turn_info::unit_list()
|
|||
_("Moves") + COLUMN_SEPARATOR +
|
||||
_("Location");
|
||||
|
||||
gui::menu::basic_sorter sorter;
|
||||
sorter.set_alpha_sort(0).set_alpha_sort(1).set_numeric_sort(2).set_numeric_sort(3)
|
||||
.set_alpha_sort(4).set_numeric_sort(5).set_numeric_sort(6);
|
||||
|
||||
std::vector<std::string> items;
|
||||
items.push_back(heading);
|
||||
|
||||
|
@ -2190,7 +2212,8 @@ void turn_info::unit_list()
|
|||
preview_panes.push_back(&unit_preview);
|
||||
|
||||
selected = gui::show_dialog(gui_,NULL,_("Unit List"),"",
|
||||
gui::OK_ONLY,&items,&preview_panes);
|
||||
gui::OK_ONLY,&items,&preview_panes,
|
||||
"",NULL,0,NULL,NULL,-1,-1,NULL,NULL,"",&sorter);
|
||||
}
|
||||
|
||||
if(selected > 0 && selected < int(locations_list.size())) {
|
||||
|
|
|
@ -1277,12 +1277,21 @@ void show_hotkeys_dialog (display & disp, config *save_config)
|
|||
std::stringstream str,name;
|
||||
name << i->get_description();
|
||||
str << name.str();
|
||||
str << COLUMN_SEPARATOR << " : " << COLUMN_SEPARATOR;
|
||||
str << COLUMN_SEPARATOR;
|
||||
str << i->get_name();
|
||||
menu_items.push_back (str.str ());
|
||||
menu_items.push_back(str.str());
|
||||
}
|
||||
|
||||
gui::menu menu_(disp.video(), menu_items, false, height);
|
||||
std::ostringstream heading;
|
||||
heading << HEADING_PREFIX << _("Action") << COLUMN_SEPARATOR << _("Binding");
|
||||
menu_items.push_back(heading.str());
|
||||
|
||||
gui::menu::basic_sorter sorter;
|
||||
sorter.set_alpha_sort(0).set_alpha_sort(1);
|
||||
|
||||
gui::menu menu_(disp.video(), menu_items, false, height, -1, &sorter);
|
||||
menu_.sort_by(0);
|
||||
menu_.reset_selection();
|
||||
menu_.set_width(font::relative_size(400));
|
||||
menu_.set_location(xpos + font::relative_size(20), ypos);
|
||||
|
||||
|
@ -1340,7 +1349,7 @@ void show_hotkeys_dialog (display & disp, config *save_config)
|
|||
newhk.set_key(key, (mod & KMOD_SHIFT) != 0,
|
||||
(mod & KMOD_CTRL) != 0, (mod & KMOD_ALT) != 0, (mod & KMOD_LMETA) != 0);
|
||||
|
||||
menu_.change_item(menu_.selection(), 2, newhk.get_name());
|
||||
menu_.change_item(menu_.selection(), 1, newhk.get_name());
|
||||
};
|
||||
}
|
||||
if (save_button.pressed()) {
|
||||
|
|
|
@ -11,6 +11,11 @@
|
|||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "global.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "config.hpp"
|
||||
#include "random.hpp"
|
||||
#include "wassert.hpp"
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "global.hpp"
|
||||
|
||||
#include "config.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "serialization/binary_wml.hpp"
|
||||
|
|
|
@ -89,7 +89,7 @@ preprocessor_streambuf::preprocessor_streambuf(preproc_map *def)
|
|||
}
|
||||
|
||||
preprocessor_streambuf::preprocessor_streambuf(preprocessor_streambuf const &t)
|
||||
: std::streambuf(), current_(NULL), defines_(t.defines_),
|
||||
: current_(NULL), defines_(t.defines_),
|
||||
textdomain_(PACKAGE), depth_(t.depth_), quoted_(t.quoted_)
|
||||
{
|
||||
}
|
||||
|
@ -676,10 +676,10 @@ bool preprocessor_data::get_chunk()
|
|||
return true;
|
||||
}
|
||||
|
||||
struct preprocessor_deleter: std::istream
|
||||
struct preprocessor_deleter: std::basic_istream<char>
|
||||
{
|
||||
preprocessor_streambuf *buf_;
|
||||
preprocessor_deleter(preprocessor_streambuf *buf): std::istream(buf), buf_(buf) {}
|
||||
preprocessor_deleter(preprocessor_streambuf *buf) : std::basic_istream<char>(buf), buf_(buf) {}
|
||||
~preprocessor_deleter() { rdbuf(NULL); delete buf_->defines_; delete buf_; }
|
||||
};
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ std::ostream& operator<<(std::ostream& out, metrics& met)
|
|||
const int seconds = time_up%60;
|
||||
const int minutes = (time_up/60)%60;
|
||||
const int hours = (time_up/(60*60))%24;
|
||||
const int days = time_up/(60*60*14);
|
||||
const int days = time_up/(60*60*24);
|
||||
const int requests_immediate = met.nrequests_ - met.nrequests_waited_;
|
||||
const int percent_immediate = (requests_immediate*100)/(met.nrequests_ > 0 ? met.nrequests_ : 1);
|
||||
out << "METRICS\n----\nUp " << days << " days, " << hours << " hours, "
|
||||
|
|
|
@ -322,7 +322,7 @@ int show_dialog(display& disp, surface image,
|
|||
int text_widget_max_chars,
|
||||
dialog_action* action, std::vector<check_item>* options, int xloc, int yloc,
|
||||
const std::string* dialog_style, std::vector<dialog_button>* action_buttons,
|
||||
const std::string& help_topic)
|
||||
const std::string& help_topic, const menu::sorter* sorter)
|
||||
{
|
||||
if(disp.update_locked())
|
||||
return -1;
|
||||
|
@ -372,7 +372,7 @@ int show_dialog(display& disp, surface image,
|
|||
text_widget_height = text_widget.location().h + message_font_size;
|
||||
}
|
||||
|
||||
menu menu_(screen,menu_items,type == MESSAGE);
|
||||
menu menu_(screen,menu_items,type == MESSAGE,-1,-1,sorter);
|
||||
|
||||
menu_.set_numeric_keypress_selection(use_textbox == false);
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ class CVideo;
|
|||
#include "tooltips.hpp"
|
||||
|
||||
#include "widgets/button.hpp"
|
||||
#include "widgets/menu.hpp"
|
||||
|
||||
#include "SDL.h"
|
||||
|
||||
|
@ -133,7 +134,8 @@ int show_dialog(display &screen, surface image,
|
|||
std::vector<check_item>* options=NULL, int xloc=-1, int yloc=-1,
|
||||
const std::string* dialog_style=NULL,
|
||||
std::vector<dialog_button>* buttons=NULL,
|
||||
const std::string& help_topic=""
|
||||
const std::string& help_topic="",
|
||||
const menu::sorter* sorter=NULL
|
||||
);
|
||||
|
||||
void show_error_message(display &disp, std::string const &message);
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "global.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
|
|
|
@ -42,6 +42,8 @@ public:
|
|||
bool translatable_;
|
||||
};
|
||||
|
||||
friend class walker;
|
||||
|
||||
t_string();
|
||||
t_string(const t_string&);
|
||||
t_string(const std::string& string);
|
||||
|
|
|
@ -163,8 +163,6 @@ void unit::generate_traits()
|
|||
traits.push_back(candidate_traits[num]);
|
||||
candidate_traits.erase(candidate_traits.begin()+num);
|
||||
}
|
||||
|
||||
std::vector<std::string> description;
|
||||
|
||||
for(std::vector<config*>::const_iterator j = traits.begin(); j != traits.end(); ++j) {
|
||||
modifications_.add_child("trait",**j);
|
||||
|
|
|
@ -80,6 +80,7 @@ std::string str_cast(From a)
|
|||
}
|
||||
|
||||
inline bool chars_equal_insensitive(char a, char b) { return tolower(a) == tolower(b); }
|
||||
inline bool chars_less_insensitive(char a, char b) { return tolower(a) < tolower(b); }
|
||||
|
||||
//a definition of 'push_back' for strings, since some implementations
|
||||
//don't support string::push_back
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "global.hpp"
|
||||
|
||||
#include "variable.hpp"
|
||||
#include "gamestatus.hpp"
|
||||
#include "config.hpp"
|
||||
|
@ -78,7 +80,8 @@ const config vconfig::get_parsed_config() const
|
|||
vconfig::child_list vconfig::get_children(const std::string& key) const
|
||||
{
|
||||
const config::child_list& list = cfg_->get_children(key);
|
||||
vconfig::child_list res(list.begin(), list.end());
|
||||
vconfig::child_list res(list.size());
|
||||
std::copy(list.begin(), list.end(),res.begin());
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
class config;
|
||||
class t_string;
|
||||
class game_state;
|
||||
struct game_state;
|
||||
|
||||
/**
|
||||
* A variable-expanding proxy for the config class. This class roughly behaves
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include "../wml_separators.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <numeric>
|
||||
|
||||
namespace {
|
||||
|
@ -19,59 +21,240 @@ const size_t menu_cell_padding = font::SIZE_NORMAL * 3/5;
|
|||
|
||||
namespace gui {
|
||||
|
||||
menu::basic_sorter& menu::basic_sorter::set_alpha_sort(int column)
|
||||
{
|
||||
alpha_sort_.insert(column);
|
||||
return *this;
|
||||
}
|
||||
|
||||
menu::basic_sorter& menu::basic_sorter::set_numeric_sort(int column)
|
||||
{
|
||||
numeric_sort_.insert(column);
|
||||
return *this;
|
||||
}
|
||||
|
||||
menu::basic_sorter& menu::basic_sorter::set_id_sort(int column)
|
||||
{
|
||||
id_sort_.insert(column);
|
||||
return *this;
|
||||
}
|
||||
|
||||
menu::basic_sorter& menu::basic_sorter::set_redirect_sort(int column, int to)
|
||||
{
|
||||
if(column != to) {
|
||||
redirect_sort_.insert(std::pair<int,int>(column,to));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
menu::basic_sorter& menu::basic_sorter::set_position_sort(int column, const std::vector<int>& pos)
|
||||
{
|
||||
pos_sort_[column] = pos;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool menu::basic_sorter::column_sortable(int column) const
|
||||
{
|
||||
const std::map<int,int>::const_iterator redirect = redirect_sort_.find(column);
|
||||
if(redirect != redirect_sort_.end()) {
|
||||
return column_sortable(redirect->second);
|
||||
}
|
||||
|
||||
return alpha_sort_.count(column) == 1 || numeric_sort_.count(column) == 1 ||
|
||||
pos_sort_.count(column) == 1 || id_sort_.count(column) == 1;
|
||||
}
|
||||
|
||||
bool menu::basic_sorter::less(int column, const item& row1, const item& row2) const
|
||||
{
|
||||
const std::map<int,int>::const_iterator redirect = redirect_sort_.find(column);
|
||||
if(redirect != redirect_sort_.end()) {
|
||||
return less(redirect->second,row1,row2);
|
||||
}
|
||||
|
||||
if(column < 0 || column >= int(row2.fields.size())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(column >= int(row1.fields.size())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string& item1 = row1.fields[column];
|
||||
const std::string& item2 = row2.fields[column];
|
||||
if(alpha_sort_.count(column) == 1) {
|
||||
std::string::const_iterator begin1 = item1.begin(), end1 = item1.end(),
|
||||
begin2 = item2.begin(), end2 = item2.end();
|
||||
while(begin1 != end1 && (font::is_format_char(*begin1) || is_wml_separator(*begin1))) {
|
||||
++begin1;
|
||||
}
|
||||
|
||||
while(begin2 != end2 && (font::is_format_char(*begin2) || is_wml_separator(*begin2))) {
|
||||
++begin2;
|
||||
}
|
||||
|
||||
return std::lexicographical_compare(begin1,end1,begin2,end2,chars_less_insensitive);
|
||||
} else if(numeric_sort_.count(column) == 1) {
|
||||
const char* a = item1.c_str();
|
||||
const char* b = item2.c_str();
|
||||
|
||||
while(*a != 0 && !isdigit(*a)) {
|
||||
++a;
|
||||
}
|
||||
|
||||
while(*b != 0 && !isdigit(*b)) {
|
||||
++b;
|
||||
}
|
||||
|
||||
return atoi(a) > atoi(b);
|
||||
} else if(id_sort_.count(column) == 1) {
|
||||
return row1.id < row2.id;
|
||||
}
|
||||
|
||||
const std::map<int,std::vector<int> >::const_iterator itor = pos_sort_.find(column);
|
||||
if(itor != pos_sort_.end()) {
|
||||
const std::vector<int>& pos = itor->second;
|
||||
if(row1.id >= pos.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(row2.id >= pos.size()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return pos[row1.id] < pos[row2.id];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
menu::menu(CVideo& video, const std::vector<std::string>& items,
|
||||
bool click_selects, int max_height, int max_width)
|
||||
bool click_selects, int max_height, int max_width,
|
||||
const sorter* sorter_obj)
|
||||
: scrollarea(video),
|
||||
max_height_(max_height), max_width_(max_width), max_items_(-1), item_height_(-1),
|
||||
heading_height_(-1),
|
||||
cur_help_(-1,-1), help_string_(-1),
|
||||
selected_(0), click_selects_(click_selects),
|
||||
previous_button_(true), show_result_(false),
|
||||
double_clicked_(false),
|
||||
num_selects_(true),
|
||||
ignore_next_doubleclick_(false),
|
||||
last_was_doubleclick_(false)
|
||||
last_was_doubleclick_(false),
|
||||
sorter_(sorter_obj), sortby_(-1), highlight_heading_(-1)
|
||||
{
|
||||
fill_items(items, true);
|
||||
}
|
||||
|
||||
void menu::fill_items(const std::vector<std::string>& items, bool strip_spaces)
|
||||
{
|
||||
for(std::vector<std::string>::const_iterator item = items.begin();
|
||||
item != items.end(); ++item) {
|
||||
items_.push_back(utils::quoted_split(*item, COLUMN_SEPARATOR, !strip_spaces));
|
||||
for(std::vector<std::string>::const_iterator itor = items.begin();
|
||||
itor != items.end(); ++itor) {
|
||||
|
||||
if(itor->empty() == false && (*itor)[0] == HEADING_PREFIX) {
|
||||
heading_ = utils::quoted_split(itor->substr(1),COLUMN_SEPARATOR, !strip_spaces);
|
||||
continue;
|
||||
}
|
||||
|
||||
const size_t id = items_.size();
|
||||
item_pos_.push_back(id);
|
||||
const item new_item(utils::quoted_split(*itor, COLUMN_SEPARATOR, !strip_spaces),id);
|
||||
items_.push_back(new_item);
|
||||
|
||||
//make sure there is always at least one item
|
||||
if(items_.back().empty())
|
||||
items_.back().push_back(" ");
|
||||
if(items_.back().fields.empty()) {
|
||||
items_.back().fields.push_back(" ");
|
||||
}
|
||||
|
||||
//if the first character in an item is an asterisk,
|
||||
//it means this item should be selected by default
|
||||
std::string& first_item = items_.back().front();
|
||||
std::string& first_item = items_.back().fields.front();
|
||||
if(first_item.empty() == false && first_item[0] == DEFAULT_ITEM) {
|
||||
selected_ = items_.size()-1;
|
||||
selected_ = id;
|
||||
first_item.erase(first_item.begin());
|
||||
}
|
||||
}
|
||||
|
||||
create_help_strings();
|
||||
do_sort();
|
||||
update_size();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class sort_func
|
||||
{
|
||||
public:
|
||||
sort_func(const menu::sorter& pred, int column) : pred_(&pred), column_(column)
|
||||
{}
|
||||
|
||||
bool operator()(const menu::item& a, const menu::item& b) const
|
||||
{
|
||||
return pred_->less(column_,a,b);
|
||||
}
|
||||
|
||||
private:
|
||||
const menu::sorter* pred_;
|
||||
int column_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void menu::do_sort(SORT_TYPE type)
|
||||
{
|
||||
if(sortby_ < 0 || sorter_ == NULL || sorter_->column_sortable(sortby_) == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int selectid = selection();
|
||||
|
||||
if(type == NORMAL_SORT) {
|
||||
std::stable_sort(items_.begin(),items_.end(),sort_func(*sorter_,sortby_));
|
||||
} else if(type == INVERT_SORT) {
|
||||
std::reverse(items_.begin(),items_.end());
|
||||
}
|
||||
|
||||
recalculate_pos();
|
||||
|
||||
if(selectid >= 0 && selectid < int(item_pos_.size())) {
|
||||
move_selection(selectid,NO_MOVE_VIEWPORT);
|
||||
}
|
||||
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void menu::recalculate_pos()
|
||||
{
|
||||
item_pos_.resize(items_.size());
|
||||
for(std::vector<item>::const_iterator i = items_.begin(); i != items_.end(); ++i) {
|
||||
item_pos_[i->id] = i - items_.begin();
|
||||
}
|
||||
|
||||
assert_pos();
|
||||
}
|
||||
|
||||
void menu::assert_pos()
|
||||
{
|
||||
assert(item_pos_.size() == items_.size());
|
||||
for(size_t n = 0; n != item_pos_.size(); ++n) {
|
||||
assert(n == items_[item_pos_[n]].id);
|
||||
}
|
||||
}
|
||||
|
||||
void menu::create_help_strings()
|
||||
{
|
||||
help_.clear();
|
||||
for(std::vector<std::vector<std::string> >::iterator i = items_.begin(); i != items_.end(); ++i) {
|
||||
help_.resize(help_.size()+1);
|
||||
for(std::vector<std::string>::iterator j = i->begin(); j != i->end(); ++j) {
|
||||
for(std::vector<item>::iterator i = items_.begin(); i != items_.end(); ++i) {
|
||||
i->help.clear();
|
||||
for(std::vector<std::string>::iterator j = i->fields.begin(); j != i->fields.end(); ++j) {
|
||||
if(std::find(j->begin(),j->end(),static_cast<char>(HELP_STRING_SEPARATOR)) == j->end()) {
|
||||
help_.back().push_back("");
|
||||
i->help.push_back("");
|
||||
} else {
|
||||
const std::vector<std::string>& items = utils::split(*j, HELP_STRING_SEPARATOR, 0);
|
||||
if(items.size() >= 2) {
|
||||
*j = items.front();
|
||||
help_.back().push_back(items.back());
|
||||
i->help.push_back(items.back());
|
||||
} else {
|
||||
help_.back().push_back("");
|
||||
i->help.push_back("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,8 +267,9 @@ void menu::update_scrollbar_grip_height()
|
|||
set_shown_size(max_items_onscreen());
|
||||
}
|
||||
|
||||
void menu::update_size() {
|
||||
unsigned h = 0;
|
||||
void menu::update_size()
|
||||
{
|
||||
unsigned int h = heading_height();
|
||||
for(size_t i = get_position(),
|
||||
i_end = minimum(items_.size(), i + max_items_onscreen());
|
||||
i != i_end; ++i)
|
||||
|
@ -106,7 +290,14 @@ void menu::update_size() {
|
|||
set_measurements(w, h);
|
||||
}
|
||||
|
||||
int menu::selection() const { return selected_; }
|
||||
int menu::selection() const
|
||||
{
|
||||
if(selected_ < 0 || selected_ >= int(items_.size())) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return items_[selected_].id;
|
||||
}
|
||||
|
||||
void menu::set_inner_location(SDL_Rect const &rect)
|
||||
{
|
||||
|
@ -115,10 +306,14 @@ void menu::set_inner_location(SDL_Rect const &rect)
|
|||
bg_register(rect);
|
||||
}
|
||||
|
||||
void menu::change_item(int pos1, int pos2,std::string str)
|
||||
void menu::change_item(int pos1, int pos2,const std::string& str)
|
||||
{
|
||||
items_[pos1][pos2] = str;
|
||||
//undrawn_items_.insert(pos1);
|
||||
if(pos1 < 0 || pos1 >= int(item_pos_.size()) ||
|
||||
pos2 < 0 || pos2 >= int(items_[item_pos_[pos1]].fields.size())) {
|
||||
return;
|
||||
}
|
||||
|
||||
items_[item_pos_[pos1]].fields[pos2] = str;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
|
@ -129,7 +324,17 @@ void menu::erase_item(size_t index)
|
|||
return;
|
||||
|
||||
clear_item(nb_items - 1);
|
||||
items_.erase(items_.begin() + index);
|
||||
|
||||
const size_t pos = item_pos_[index];
|
||||
|
||||
item_pos_.erase(item_pos_.begin() + index);
|
||||
for(std::vector<size_t>::iterator i = item_pos_.begin(); i != item_pos_.end(); ++i) {
|
||||
if(*i > pos) {
|
||||
*i = *i-1;
|
||||
}
|
||||
}
|
||||
|
||||
items_.erase(items_.begin() + pos);
|
||||
if (selected_ >= nb_items - 1)
|
||||
selected_ = nb_items - 2;
|
||||
|
||||
|
@ -139,9 +344,21 @@ void menu::erase_item(size_t index)
|
|||
set_dirty();
|
||||
}
|
||||
|
||||
void menu::set_heading(const std::vector<std::string>& heading)
|
||||
{
|
||||
itemRects_.clear();
|
||||
column_widths_.clear();
|
||||
|
||||
heading_ = heading;
|
||||
max_items_ = -1;
|
||||
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void menu::set_items(const std::vector<std::string>& items, bool strip_spaces, bool keep_viewport)
|
||||
{
|
||||
items_.clear();
|
||||
item_pos_.clear();
|
||||
itemRects_.clear();
|
||||
column_widths_.clear();
|
||||
//undrawn_items_.clear();
|
||||
|
@ -153,10 +370,15 @@ void menu::set_items(const std::vector<std::string>& items, bool strip_spaces, b
|
|||
}
|
||||
|
||||
fill_items(items, strip_spaces);
|
||||
if (!keep_viewport)
|
||||
if(!keep_viewport) {
|
||||
set_position(0);
|
||||
}
|
||||
|
||||
update_scrollbar_grip_height();
|
||||
adjust_viewport_to_selection();
|
||||
|
||||
if(!keep_viewport) {
|
||||
adjust_viewport_to_selection();
|
||||
}
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
|
@ -174,7 +396,7 @@ size_t menu::max_items_onscreen() const
|
|||
return size_t(max_items_);
|
||||
}
|
||||
|
||||
const size_t max_height = max_height_ == -1 ? (video().gety()*66)/100 : max_height_;
|
||||
const size_t max_height = (max_height_ == -1 ? (video().gety()*66)/100 : max_height_) - heading_height();
|
||||
std::vector<int> heights;
|
||||
size_t n;
|
||||
for(n = 0; n != items_.size(); ++n) {
|
||||
|
@ -202,25 +424,39 @@ void menu::adjust_viewport_to_selection()
|
|||
|
||||
void menu::move_selection_up(size_t dep)
|
||||
{
|
||||
move_selection(selected_ > dep ? selected_ - dep : 0);
|
||||
set_selection_pos(selected_ > dep ? selected_ - dep : 0);
|
||||
}
|
||||
|
||||
void menu::reset_selection()
|
||||
{
|
||||
set_selection_pos(0);
|
||||
}
|
||||
|
||||
void menu::move_selection_down(size_t dep)
|
||||
{
|
||||
size_t nb_items = items_.size();
|
||||
move_selection(selected_ + dep >= nb_items ? nb_items - 1 : selected_ + dep);
|
||||
set_selection_pos(selected_ + dep >= nb_items ? nb_items - 1 : selected_ + dep);
|
||||
}
|
||||
|
||||
void menu::move_selection(size_t new_selected)
|
||||
void menu::set_selection_pos(size_t new_selected, SELECTION_MOVE_VIEWPORT move_viewport)
|
||||
{
|
||||
if (new_selected == selected_ || new_selected >= items_.size())
|
||||
return;
|
||||
|
||||
//undrawn_items_.insert(selected_);
|
||||
//undrawn_items_.insert(new_selected);
|
||||
set_dirty();
|
||||
invalidate_row_pos(selected_);
|
||||
invalidate_row_pos(new_selected);
|
||||
selected_ = new_selected;
|
||||
adjust_viewport_to_selection();
|
||||
|
||||
if(move_viewport == MOVE_VIEWPORT) {
|
||||
adjust_viewport_to_selection();
|
||||
}
|
||||
}
|
||||
|
||||
void menu::move_selection(size_t id, SELECTION_MOVE_VIEWPORT move_viewport)
|
||||
{
|
||||
if(id < item_pos_.size()) {
|
||||
set_selection_pos(item_pos_[id],move_viewport);
|
||||
}
|
||||
}
|
||||
|
||||
void menu::key_press(SDLKey key)
|
||||
|
@ -240,10 +476,10 @@ void menu::key_press(SDLKey key)
|
|||
move_selection_down(max_items_onscreen());
|
||||
break;
|
||||
case SDLK_HOME:
|
||||
move_selection(0);
|
||||
set_selection_pos(0);
|
||||
break;
|
||||
case SDLK_END:
|
||||
move_selection(items_.size() - 1);
|
||||
set_selection_pos(items_.size() - 1);
|
||||
break;
|
||||
//case SDLK_RETURN:
|
||||
// double_clicked_ = true;
|
||||
|
@ -254,7 +490,7 @@ void menu::key_press(SDLKey key)
|
|||
}
|
||||
|
||||
if (num_selects_ && key >= SDLK_1 && key <= SDLK_9)
|
||||
move_selection(key - SDLK_1);
|
||||
set_selection_pos(key - SDLK_1);
|
||||
}
|
||||
|
||||
void menu::handle_event(const SDL_Event& event)
|
||||
|
@ -303,10 +539,26 @@ void menu::handle_event(const SDL_Event& event)
|
|||
last_was_doubleclick_ = false;
|
||||
}
|
||||
}
|
||||
} else if(event.type == SDL_MOUSEMOTION && click_selects_) {
|
||||
const int item = hit(event.motion.x,event.motion.y);
|
||||
if (item != -1)
|
||||
move_selection(item);
|
||||
|
||||
|
||||
if(sorter_ != NULL) {
|
||||
const int heading = hit_heading(x,y);
|
||||
if(heading >= 0 && sorter_->column_sortable(heading)) {
|
||||
sort_by(heading);
|
||||
}
|
||||
}
|
||||
} else if(event.type == SDL_MOUSEMOTION) {
|
||||
if(click_selects_) {
|
||||
const int item = hit(event.motion.x,event.motion.y);
|
||||
if (item != -1)
|
||||
move_selection(item);
|
||||
}
|
||||
|
||||
const int heading_item = hit_heading(event.motion.x,event.motion.y);
|
||||
if(heading_item != highlight_heading_) {
|
||||
highlight_heading_ = heading_item;
|
||||
invalidate_heading();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,6 +579,11 @@ bool menu::double_clicked()
|
|||
return old;
|
||||
}
|
||||
|
||||
void menu::set_click_selects(bool value)
|
||||
{
|
||||
click_selects_ = value;
|
||||
}
|
||||
|
||||
void menu::set_numeric_keypress_selection(bool value)
|
||||
{
|
||||
num_selects_ = value;
|
||||
|
@ -338,6 +595,15 @@ void menu::scroll(int)
|
|||
set_dirty();
|
||||
}
|
||||
|
||||
void menu::sort_by(int column)
|
||||
{
|
||||
const bool already_sorted = (column == sortby_);
|
||||
sortby_ = column;
|
||||
do_sort(already_sorted ? INVERT_SORT : NORMAL_SORT);
|
||||
itemRects_.clear();
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
namespace {
|
||||
SDL_Rect item_size(const std::string& item) {
|
||||
SDL_Rect res = {0,0,0,0};
|
||||
|
@ -368,19 +634,25 @@ namespace {
|
|||
}
|
||||
}
|
||||
|
||||
void menu::column_widths_item(const std::vector<std::string>& row, std::vector<int>& widths) const
|
||||
{
|
||||
for(size_t col = 0; col != row.size(); ++col) {
|
||||
const SDL_Rect res = item_size(row[col]);
|
||||
|
||||
if(col == widths.size()) {
|
||||
widths.push_back(res.w + menu_cell_padding);
|
||||
} else if(res.w > widths[col] - menu_cell_padding) {
|
||||
widths[col] = res.w + menu_cell_padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<int>& menu::column_widths() const
|
||||
{
|
||||
if(column_widths_.empty()) {
|
||||
column_widths_item(heading_,column_widths_);
|
||||
for(size_t row = 0; row != items_.size(); ++row) {
|
||||
for(size_t col = 0; col != items_[row].size(); ++col) {
|
||||
const SDL_Rect res = item_size(items_[row][col]);
|
||||
|
||||
if(col == column_widths_.size()) {
|
||||
column_widths_.push_back(res.w + menu_cell_padding);
|
||||
} else if(res.w > column_widths_[col] - menu_cell_padding) {
|
||||
column_widths_[col] = res.w + menu_cell_padding;
|
||||
}
|
||||
}
|
||||
column_widths_item(items_[row].fields,column_widths_);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -395,29 +667,50 @@ void menu::clear_item(int item)
|
|||
bg_restore(rect);
|
||||
}
|
||||
|
||||
void menu::draw_item(int item)
|
||||
void menu::draw_row(const std::vector<std::string>& row, const SDL_Rect& rect, ROW_TYPE type)
|
||||
{
|
||||
SDL_Rect rect = get_item_rect(item);
|
||||
if(rect.w == 0) {
|
||||
if(rect.w == 0 || rect.h == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
clear_item(item);
|
||||
bg_restore(rect);
|
||||
|
||||
int rgb = 0;
|
||||
double alpha = 0.0;
|
||||
|
||||
switch(type) {
|
||||
case NORMAL_ROW:
|
||||
rgb = 0x000000;
|
||||
alpha = 0.2;
|
||||
break;
|
||||
case SELECTED_ROW:
|
||||
rgb = 0x990000;
|
||||
alpha = 0.6;
|
||||
break;
|
||||
case HEADING_ROW:
|
||||
rgb = 0x333333;
|
||||
alpha = 0.3;
|
||||
break;
|
||||
}
|
||||
|
||||
draw_solid_tinted_rectangle(rect.x, rect.y, rect.w, rect.h,
|
||||
item == selected_ ? 150:0,0,0,
|
||||
item == selected_ ? 0.6 : 0.2,
|
||||
(rgb&0xff0000) >> 16,(rgb&0xff00) >> 8,rgb&0xff,alpha,
|
||||
video().getSurface());
|
||||
|
||||
SDL_Rect const &area = screen_area();
|
||||
//SDL_Rect area = { 0, 0, rect.w, rect.h };
|
||||
SDL_Rect const &loc = inner_location();
|
||||
|
||||
const std::vector<int>& widths = column_widths();
|
||||
|
||||
int xpos = rect.x;
|
||||
for(size_t i = 0; i != items_[item].size(); ++i) {
|
||||
for(size_t i = 0; i != row.size(); ++i) {
|
||||
|
||||
if(type == HEADING_ROW && highlight_heading_ == int(i)) {
|
||||
draw_solid_tinted_rectangle(xpos,rect.y,widths[i],rect.h,255,255,255,0.3,video().getSurface());
|
||||
}
|
||||
|
||||
const int last_x = xpos;
|
||||
std::string str = items_[item][i];
|
||||
std::string str = row[i];
|
||||
std::vector<std::string> img_text_items = utils::split(str, IMG_TEXT_SEPARATOR);
|
||||
for (std::vector<std::string>::const_iterator it = img_text_items.begin();
|
||||
it != img_text_items.end(); it++) {
|
||||
|
@ -448,27 +741,55 @@ void menu::draw_item(int item)
|
|||
|
||||
void menu::draw_contents()
|
||||
{
|
||||
#if 0
|
||||
if (undrawn_items_.empty())
|
||||
return;
|
||||
SDL_Rect heading_rect = inner_location();
|
||||
heading_rect.h = heading_height();
|
||||
draw_row(heading_,heading_rect,HEADING_ROW);
|
||||
|
||||
if (!dirty()) {
|
||||
for(std::set<size_t>::const_iterator i = undrawn_items_.begin(); i != undrawn_items_.end(); ++i) {
|
||||
if(*i < items_.size()) {
|
||||
draw_item(*i);
|
||||
update_rect(get_item_rect(*i));
|
||||
for(size_t i = 0; i != item_pos_.size(); ++i) {
|
||||
draw_row(items_[item_pos_[i]].fields,get_item_rect(i),item_pos_[i] == selected_ ? SELECTED_ROW : NORMAL_ROW);
|
||||
}
|
||||
}
|
||||
|
||||
void menu::draw()
|
||||
{
|
||||
if(hidden()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!dirty()) {
|
||||
|
||||
for(std::set<int>::const_iterator i = invalid_.begin(); i != invalid_.end(); ++i) {
|
||||
if(*i == -1) {
|
||||
SDL_Rect heading_rect = inner_location();
|
||||
heading_rect.h = heading_height();
|
||||
bg_restore(heading_rect);
|
||||
draw_row(heading_,heading_rect,HEADING_ROW);
|
||||
update_rect(heading_rect);
|
||||
} else if(*i >= 0 && *i < int(item_pos_.size())) {
|
||||
const int pos = item_pos_[*i];
|
||||
const SDL_Rect& rect = get_item_rect(*i);
|
||||
bg_restore(rect);
|
||||
draw_row(items_[pos].fields,rect,pos == selected_ ? SELECTED_ROW : NORMAL_ROW);
|
||||
update_rect(rect);
|
||||
}
|
||||
}
|
||||
|
||||
undrawn_items_.clear();
|
||||
invalid_.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
undrawn_items_.clear();
|
||||
#endif
|
||||
invalid_.clear();
|
||||
|
||||
for(size_t i = 0; i != items_.size(); ++i)
|
||||
draw_item(i);
|
||||
bg_restore();
|
||||
|
||||
util::scoped_ptr<clip_rect_setter> clipper(NULL);
|
||||
if(clip_rect())
|
||||
clipper.assign(new clip_rect_setter(video().getSurface(), *clip_rect()));
|
||||
|
||||
draw_contents();
|
||||
|
||||
update_rect(location());
|
||||
set_dirty(false);
|
||||
}
|
||||
|
||||
int menu::hit(int x, int y) const
|
||||
|
@ -485,24 +806,52 @@ int menu::hit(int x, int y) const
|
|||
return -1;
|
||||
}
|
||||
|
||||
std::pair<int,int> menu::hit_cell(int x, int y) const
|
||||
int menu::hit_column(int x, int y) const
|
||||
{
|
||||
int i = hit(x, y);
|
||||
if (i < 0)
|
||||
return std::pair<int,int>(-1, -1);
|
||||
|
||||
std::vector<int> const &widths = column_widths();
|
||||
x -= location().x;
|
||||
for(int j = 0, j_end = widths.size(); j != j_end; ++j) {
|
||||
x -= widths[j];
|
||||
if (x < 0)
|
||||
return std::pair<int,int>(i, j);
|
||||
if (x < 0) {
|
||||
return j;
|
||||
}
|
||||
}
|
||||
|
||||
return std::pair<int,int>(-1, -1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::pair<int,int> menu::hit_cell(int x, int y) const
|
||||
{
|
||||
const int row = hit(x, y);
|
||||
if(row < 0) {
|
||||
return std::pair<int,int>(-1, -1);
|
||||
}
|
||||
|
||||
const int col = hit_column(x,y);
|
||||
if(col < 0) {
|
||||
return std::pair<int,int>(-1, -1);
|
||||
}
|
||||
|
||||
return std::pair<int,int>(x,y);
|
||||
}
|
||||
|
||||
int menu::hit_heading(int x, int y) const
|
||||
{
|
||||
const size_t height = heading_height();
|
||||
const SDL_Rect& loc = inner_location();
|
||||
if(y >= loc.y && y < loc.y + height) {
|
||||
return hit_column(x,y);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Rect menu::get_item_rect(int item) const
|
||||
{
|
||||
return get_item_rect_internal(item_pos_[item]);
|
||||
}
|
||||
|
||||
SDL_Rect menu::get_item_rect_internal(size_t item) const
|
||||
{
|
||||
const SDL_Rect empty_rect = {0,0,0,0};
|
||||
int first_item_on_screen = get_position();
|
||||
|
@ -517,9 +866,9 @@ SDL_Rect menu::get_item_rect(int item) const
|
|||
|
||||
SDL_Rect const &loc = inner_location();
|
||||
|
||||
int y = loc.y;
|
||||
int y = loc.y + heading_height();
|
||||
if (item != first_item_on_screen) {
|
||||
const SDL_Rect& prev = get_item_rect(item-1);
|
||||
const SDL_Rect& prev = get_item_rect_internal(item-1);
|
||||
y = prev.y + prev.h;
|
||||
}
|
||||
|
||||
|
@ -547,17 +896,26 @@ SDL_Rect menu::get_item_rect(int item) const
|
|||
return res;
|
||||
}
|
||||
|
||||
size_t menu::get_item_height_internal(int item) const
|
||||
size_t menu::get_item_height_internal(const std::vector<std::string>& item) const
|
||||
{
|
||||
size_t res = 0;
|
||||
for(size_t n = 0; n != items_[item].size(); ++n) {
|
||||
SDL_Rect rect = item_size(items_[item][n]);
|
||||
for(std::vector<std::string>::const_iterator i = item.begin(); i != item.end(); ++i) {
|
||||
SDL_Rect rect = item_size(*i);
|
||||
res = maximum<int>(rect.h,res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t menu::heading_height() const
|
||||
{
|
||||
if(heading_height_ == -1) {
|
||||
heading_height_ = int(get_item_height_internal(heading_));
|
||||
}
|
||||
|
||||
return minimum<unsigned int>(heading_height_,max_height_);
|
||||
}
|
||||
|
||||
size_t menu::get_item_height(int) const
|
||||
{
|
||||
if(item_height_ != -1)
|
||||
|
@ -565,7 +923,7 @@ size_t menu::get_item_height(int) const
|
|||
|
||||
size_t max_height = 0;
|
||||
for(size_t n = 0; n != items_.size(); ++n) {
|
||||
max_height = maximum<int>(max_height,get_item_height_internal(n));
|
||||
max_height = maximum<int>(max_height,get_item_height_internal(items_[n].fields));
|
||||
}
|
||||
|
||||
return item_height_ = max_height;
|
||||
|
@ -585,8 +943,8 @@ void menu::process_help_string(int mousex, int mousey)
|
|||
help_string_ = -1;
|
||||
}
|
||||
|
||||
if(size_t(loc.first) < help_.size()) {
|
||||
const std::vector<std::string>& row = help_[loc.first];
|
||||
if(size_t(loc.first) < items_.size()) {
|
||||
const std::vector<std::string>& row = items_[loc.first].help;
|
||||
if(size_t(loc.second) < row.size()) {
|
||||
const std::string& help = row[loc.second];
|
||||
if(help.empty() == false) {
|
||||
|
@ -600,4 +958,27 @@ void menu::process_help_string(int mousex, int mousey)
|
|||
cur_help_ = loc;
|
||||
}
|
||||
|
||||
void menu::invalidate_row(size_t id)
|
||||
{
|
||||
if(id >= items_.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
invalid_.insert(int(id));
|
||||
}
|
||||
|
||||
void menu::invalidate_row_pos(size_t pos)
|
||||
{
|
||||
if(pos >= items_.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
invalidate_row(items_[pos].id);
|
||||
}
|
||||
|
||||
void menu::invalidate_heading()
|
||||
{
|
||||
invalid_.insert(-1);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef WIDGET_MENU_HPP_INCLUDED
|
||||
#define WIDGET_MENU_HPP_INCLUDED
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -17,17 +19,66 @@ namespace gui {
|
|||
class menu : public scrollarea
|
||||
{
|
||||
public:
|
||||
|
||||
struct item
|
||||
{
|
||||
item() : id(0)
|
||||
{}
|
||||
|
||||
item(const std::vector<std::string>& fields, size_t id)
|
||||
: fields(fields), id(id)
|
||||
{}
|
||||
|
||||
std::vector<std::string> fields;
|
||||
std::vector<std::string> help;
|
||||
size_t id;
|
||||
};
|
||||
|
||||
class sorter
|
||||
{
|
||||
public:
|
||||
virtual ~sorter() {}
|
||||
virtual bool column_sortable(int column) const = 0;
|
||||
virtual bool less(int column, const item& row1, const item& row2) const = 0;
|
||||
};
|
||||
|
||||
class basic_sorter : public sorter
|
||||
{
|
||||
public:
|
||||
virtual ~basic_sorter() {}
|
||||
|
||||
basic_sorter& set_alpha_sort(int column);
|
||||
basic_sorter& set_numeric_sort(int column);
|
||||
basic_sorter& set_id_sort(int column);
|
||||
basic_sorter& set_redirect_sort(int column, int to);
|
||||
basic_sorter& set_position_sort(int column, const std::vector<int>& pos);
|
||||
protected:
|
||||
virtual bool column_sortable(int column) const;
|
||||
virtual bool less(int column, const item& row1, const item& row2) const;
|
||||
|
||||
private:
|
||||
std::set<int> alpha_sort_, numeric_sort_, id_sort_;
|
||||
std::map<int,int> redirect_sort_;
|
||||
std::map<int,std::vector<int> > pos_sort_;
|
||||
};
|
||||
|
||||
menu(CVideo& video, const std::vector<std::string>& items,
|
||||
bool click_selects=false, int max_height=-1, int max_width=-1);
|
||||
bool click_selects=false, int max_height=-1, int max_width=-1,
|
||||
const sorter* sorter_obj=NULL);
|
||||
|
||||
int selection() const;
|
||||
void move_selection(size_t pos);
|
||||
|
||||
enum SELECTION_MOVE_VIEWPORT { MOVE_VIEWPORT, NO_MOVE_VIEWPORT };
|
||||
void move_selection(size_t id, SELECTION_MOVE_VIEWPORT move_viewport=MOVE_VIEWPORT);
|
||||
void reset_selection();
|
||||
|
||||
// allows user to change_item while running (dangerous)
|
||||
void change_item(int pos1,int pos2,std::string str);
|
||||
void change_item(int pos1,int pos2,const std::string& str);
|
||||
|
||||
void erase_item(size_t index);
|
||||
|
||||
void set_heading(const std::vector<std::string>& heading);
|
||||
|
||||
/// Set new items to show and redraw/recalculate everything. If
|
||||
/// strip_spaces is false, spaces will remain at the item edges. If
|
||||
/// keep_viewport is true, the menu tries to keep the selection at
|
||||
|
@ -47,10 +98,13 @@ public:
|
|||
|
||||
bool double_clicked();
|
||||
|
||||
void set_click_selects(bool value);
|
||||
void set_numeric_keypress_selection(bool value);
|
||||
|
||||
void scroll(int pos);
|
||||
|
||||
void sort_by(int column);
|
||||
|
||||
protected:
|
||||
void handle_event(const SDL_Event& event);
|
||||
void set_inner_location(const SDL_Rect& rect);
|
||||
|
@ -58,13 +112,19 @@ protected:
|
|||
private:
|
||||
size_t max_items_onscreen() const;
|
||||
|
||||
size_t heading_height() const;
|
||||
|
||||
int max_height_, max_width_;
|
||||
mutable int max_items_, item_height_;
|
||||
|
||||
void adjust_viewport_to_selection();
|
||||
void key_press(SDLKey key);
|
||||
|
||||
std::vector<std::vector<std::string> > items_, help_;
|
||||
std::vector<item> items_;
|
||||
std::vector<size_t> item_pos_;
|
||||
|
||||
std::vector<std::string> heading_;
|
||||
mutable int heading_height_;
|
||||
|
||||
void create_help_strings();
|
||||
void process_help_string(int mousex, int mousey);
|
||||
|
@ -84,17 +144,25 @@ private:
|
|||
bool double_clicked_;
|
||||
|
||||
const std::vector<int>& column_widths() const;
|
||||
void draw_item(int item);
|
||||
void column_widths_item(const std::vector<std::string>& row, std::vector<int>& widths) const;
|
||||
|
||||
enum ROW_TYPE { NORMAL_ROW, SELECTED_ROW, HEADING_ROW };
|
||||
void draw_row(const std::vector<std::string>& row, const SDL_Rect& rect, ROW_TYPE type);
|
||||
void clear_item(int item);
|
||||
void draw_contents();
|
||||
void draw();
|
||||
int hit(int x, int y) const;
|
||||
|
||||
std::pair<int,int> hit_cell(int x, int y) const;
|
||||
int hit_column(int x, int y) const;
|
||||
|
||||
int hit_heading(int x, int y) const;
|
||||
|
||||
mutable std::map<int,SDL_Rect> itemRects_;
|
||||
|
||||
SDL_Rect get_item_rect(int item) const;
|
||||
size_t get_item_height_internal(int item) const;
|
||||
SDL_Rect get_item_rect_internal(size_t pos) const;
|
||||
size_t get_item_height_internal(const std::vector<std::string>& item) const;
|
||||
size_t get_item_height(int item) const;
|
||||
int items_start() const;
|
||||
|
||||
|
@ -112,13 +180,29 @@ private:
|
|||
bool ignore_next_doubleclick_;
|
||||
bool last_was_doubleclick_;
|
||||
|
||||
const sorter* sorter_;
|
||||
int sortby_;
|
||||
int highlight_heading_;
|
||||
|
||||
/// Set new items to show. If strip_spaces is false, spaces will
|
||||
/// remain at the item edges.
|
||||
void fill_items(const std::vector<std::string>& items, bool strip_spaces);
|
||||
|
||||
enum SORT_TYPE { NORMAL_SORT, INVERT_SORT };
|
||||
void do_sort(SORT_TYPE type=NORMAL_SORT);
|
||||
void recalculate_pos();
|
||||
void assert_pos();
|
||||
|
||||
void update_size();
|
||||
void set_selection_pos(size_t pos, SELECTION_MOVE_VIEWPORT move_viewport=MOVE_VIEWPORT);
|
||||
void move_selection_up(size_t dep);
|
||||
void move_selection_down(size_t dep);
|
||||
|
||||
void invalidate_row(size_t id);
|
||||
void invalidate_row_pos(size_t pos);
|
||||
void invalidate_heading();
|
||||
|
||||
std::set<int> invalid_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -38,7 +38,10 @@ public:
|
|||
int min_value() const;
|
||||
|
||||
virtual void set_location(SDL_Rect const &rect);
|
||||
using widget::set_location;
|
||||
|
||||
//VC++ doesn't like a 'using scrollarea::set_location' directive here, so we declare
|
||||
//an inline forwarding function instead
|
||||
void set_location(int x, int y) { widget::set_location(x,y); }
|
||||
|
||||
protected:
|
||||
virtual void handle_event(const SDL_Event& event);
|
||||
|
|
|
@ -54,6 +54,11 @@ void widget::update_location(SDL_Rect const &rect)
|
|||
bg_register(rect);
|
||||
}
|
||||
|
||||
const SDL_Rect* widget::clip_rect() const
|
||||
{
|
||||
return clip_ ? &clip_rect_ : NULL;
|
||||
}
|
||||
|
||||
void widget::bg_register(SDL_Rect const &rect)
|
||||
{
|
||||
restorer_.push_back(surface_restorer(&video(), rect));
|
||||
|
|
|
@ -72,6 +72,8 @@ protected:
|
|||
virtual void draw_contents() {};
|
||||
virtual void update_location(SDL_Rect const &rect);
|
||||
|
||||
const SDL_Rect* clip_rect() const;
|
||||
|
||||
private:
|
||||
void volatile_draw();
|
||||
void volatile_undraw();
|
||||
|
|
|
@ -2,6 +2,22 @@
|
|||
#define WIDGET_DEFINES_HPP_INCLUDED
|
||||
|
||||
char const HELP_STRING_SEPARATOR = '|', DEFAULT_ITEM = '*', COLUMN_SEPARATOR = '=',
|
||||
IMAGE_PREFIX = '&', IMG_TEXT_SEPARATOR = 1;
|
||||
IMAGE_PREFIX = '&', IMG_TEXT_SEPARATOR = 1, HEADING_PREFIX = 2;
|
||||
|
||||
inline bool is_wml_separator(char c)
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
case HELP_STRING_SEPARATOR:
|
||||
case DEFAULT_ITEM:
|
||||
case COLUMN_SEPARATOR:
|
||||
case IMAGE_PREFIX:
|
||||
case IMG_TEXT_SEPARATOR:
|
||||
case HEADING_PREFIX:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Reference in a new issue