Files
oboe/src/dialogxml/dialogs/strchoice.cpp

313 lines
7.9 KiB
C++

/*
* dlogutil.cpp
* BoE
*
* Created by Celtic Minstrel on 11/05/09.
*
*/
#include "strchoice.hpp"
#include <sstream>
#include <algorithm>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include "dialogxml/widgets/field.hpp"
#include "fileio/resmgr/res_dialog.hpp"
#include "sounds.hpp"
#include "gfx/render_shapes.hpp"
const sf::Color HILITE_COLOUR = Colours::LIGHT_GREEN;
cStringChoice::cStringChoice(cDialog* parent, bool editable)
: editable(editable)
, per_page(editable ? 20 : 40)
, dlg(*ResMgr::dialogs.get(editable ? "choose-edit-string" : "choose-string"), parent)
{}
cStringChoice::cStringChoice(const std::vector<std::string>& strs, std::string title, cDialog* parent, bool editable)
: cStringChoice(parent, editable)
{
setTitle(title);
strings = strs;
if(editable) {
if(strings.empty()) strings.resize(per_page);
else strings.resize(per_page * ceil(strings.size() / double(per_page)));
}
attachHandlers();
}
void cStringChoice::attachHandlers() {
using namespace std::placeholders;
dlg["left"].attachClickHandler(std::bind(&cStringChoice::onLeft,this));
dlg["right"].attachClickHandler(std::bind(&cStringChoice::onRight,this));
dlg["done"].attachClickHandler(std::bind(&cStringChoice::onOkay,this,_1));
dlg["cancel"].attachClickHandler(std::bind(&cStringChoice::onCancel,this,_1));
if(!editable) dlg["search"].attachClickHandler(std::bind(&cStringChoice::onSearch,this,_1));
leds = &dynamic_cast<cLedGroup&>(dlg["strings"]);
leds->attachFocusHandler(std::bind(&cStringChoice::onSelect,this,_3));
if(editable) {
for(int i = 1; i <= per_page; i++) {
std::ostringstream sout;
sout << "edit" << i;
dlg[sout.str()].attachFocusHandler(std::bind(&cStringChoice::onFocus,this,_2,_3));
}
}
if(!editable && strings.size() <= per_page) {
dlg["left"].hide();
dlg["right"].hide();
}
}
cDialog* cStringChoice::operator->() {
return &dlg;
}
size_t cStringChoice::show(size_t selectedIndex) {
if(!editable){
// Hide most of the search ui until Ctrl+f or clicking the search button
dlg["search-field"].hide();
dlg["search-label"].hide();
dlg["reverse"].hide();
}
cur = selectedIndex;
if(cur >= strings.size()) {
if(editable) {
strings.resize(per_page * ceil((cur + 1) / double(per_page)));
} else cur = 0;
} else if(cur < 0) {
cur = 0;
}
page = cur / per_page;
dlg.setResult<size_t>(selectedIndex);
dlg.run(std::bind(&cStringChoice::fillPage, this));
return dlg.getResult<size_t>();
}
void cStringChoice::attachSelectHandler(std::function<void(cStringChoice&,int)> f) {
select_handler = f;
}
void cStringChoice::fillPage(){
cLedGroup& group = dynamic_cast<cLedGroup&>(dlg["strings"]);
group.setSelected(""); // unselect all LEDs, since the currently selected one may be on another page
for(unsigned int i = 0; i < per_page; i++){
short string_idx = page * per_page + i;
std::ostringstream sout;
sout << "led" << i + 1;
std::string led_id = sout.str(), text_id;
if(editable) {
clear_sstr(sout);
sout << "edit" << i + 1;
text_id = sout.str();
} else text_id = led_id;
cLed& led = dynamic_cast<cLed&>(dlg[led_id]);
cControl& text = dlg[text_id];
if(string_idx >= strings.size()){
led.hide();
text.hide();
continue;
}else{
text.setText(strings[string_idx]);
if(!editable) led.recalcRect();
led.show();
text.show();
}
if(string_idx == cur) {
group.setSelected(led_id);
if(editable) {
dlg.setFocus(dynamic_cast<cTextField*>(&text));
}
}
}
group.recalcRect();
}
bool cStringChoice::onLeft(){
savePage();
if(editable && page == lastPage()) {
int blanks = 0;
for(int i = strings.size() - 1; i >= 0; i--) {
if(!strings[i].empty()) break;
blanks++;
}
if(blanks >= per_page) {
strings.resize(strings.size() - per_page);
}
}
if(page == 0) page = lastPage();
else page--;
fillPage();
return true;
}
bool cStringChoice::onRight(){
savePage();
if(editable && page == lastPage()) {
strings.resize(strings.size() + per_page);
}
if(page == lastPage()) page = 0;
else page++;
fillPage();
return true;
}
bool cStringChoice::onCancel(cDialog& me){
savePage();
me.toast(false);
return true;
}
bool cStringChoice::onOkay(cDialog& me){
savePage();
dlg.setResult(cur);
me.toast(true);
return true;
}
bool cStringChoice::onSearch(cDialog& me){
if(!search_open){
me["search-field"].show();
me.setFocus(&(dynamic_cast<cTextField&>(me.getControl("search-field"))));
me["search-label"].show();
me["reverse"].show();
me.setDefaultButton("search");
search_open = true;
return true;
}
cLed& reverse_led = dynamic_cast<cLed&>(me["reverse"]);
bool reversed = reverse_led.getState() == led_red;
size_t page_delta = reversed ? -1 : 1;
size_t loop_from_page = reversed ? 0 : lastPage();
size_t loop_to_page = !reversed ? 0 : lastPage();
std::string loop_from_str = reversed ? "Reached the beginning." : "Reached the end.";
std::string loop_to_str = !reversed ? "Starting from the beginning" : "Starting from the end";
size_t start_page = page;
bool looped_once = false;
std::string new_search = me["search-field"].getText();
boost::to_lower(new_search);
bool repeat_search = new_search == search_str;
search_str = new_search;
cControl& output = me["search-label"];
clearHighlights();
if(search_str.empty()) {
output.setText("");
return true;
}
bool found_next = false;
if(!repeat_search){
// First-time search, check the current page before paging forward
if(highlightSearch()) found_next = true;
}
while(!found_next){
if(page == loop_from_page){
// Looped already, found nothing. Avoid infinite loop:
if(looped_once){
break;
}
output.setText(loop_from_str + " " + loop_to_str);
looped_once = true;
page = loop_to_page;
}else{
page += page_delta;
}
found_next = highlightSearch();
}
if(!looped_once){
output.setText("");
}
if(!found_next){
page = start_page;
output.setText("Not found");
}else{
fillPage();
}
return found_next;
}
void cStringChoice::clearHighlights() {
leds->forEach([this](std::string, cControl& led) {
led.setColour(dlg.getDefTextClr());
});
}
bool cStringChoice::highlightSearch() {
bool match_on_page = false;
for(int offset = 0; offset < per_page; ++offset){
std::string led_id = "led" + std::to_string(offset + 1);
int str_idx = page * per_page + offset;
if(str_idx >= strings.size()) break;
// Copy the string
std::string str = strings[str_idx];
// Make case insensitive
boost::to_lower(str);
if(str.find(search_str) != std::string::npos){
match_on_page = true;
leds->getChild(led_id).setColour(HILITE_COLOUR);
}
}
return match_on_page;
}
bool cStringChoice::onSelect(bool losing) {
if(losing) return true;
int i = boost::lexical_cast<int>(leds->getSelected().substr(3));
cur = page * per_page + i - 1;
if(editable) {
std::ostringstream sout;
sout << "edit" << i;
dlg.setFocus(dynamic_cast<cTextField*>(&dlg[sout.str()]));
}
if(select_handler)
select_handler(*this, cur);
return true;
}
bool cStringChoice::onFocus(std::string which, bool losing) {
if(losing || !editable) return true;
if(!dlg[which].getText().empty()) return true;
int i = boost::lexical_cast<int>(which.substr(4));
if(!strings[page * per_page + i - 1].empty()) return true;
std::ostringstream sout;
sout << "led" << i;
if(leds->getSelected() != sout.str()) {
play_sound(34);
leds->setSelected(sout.str());
cur = page * per_page + i - 1;
if(select_handler)
select_handler(*this, cur);
}
return true;
}
void cStringChoice::setTitle(const std::string &title) {
if(!title.empty()) dlg["title"].setText(title);
}
size_t cStringChoice::lastPage() const {
return (strings.size() - 1) / per_page;
}
void cStringChoice::savePage() {
if(!editable) return;
for(unsigned int i = 0; i < per_page; i++){
short string_idx = page * per_page + i;
std::ostringstream sout;
sout << "edit" << i + 1;
std::string text_id = sout.str();
strings[string_idx] = dlg[text_id].getText();
}
}