#ifndef NO_EDITOR

#include <boost/algorithm/string/replace.hpp>
#include <boost/bind.hpp>

#include <algorithm>
#include <iostream>
#include <sstream>
#include <climits>

#include <math.h>

#include "animation_creator.hpp"
#include "animation_widget.hpp"
#include "asserts.hpp"
#include "button.hpp"
#include "code_editor_widget.hpp"
#include "custom_object_dialog.hpp"
#include "draw_scene.hpp"
#include "dropdown_widget.hpp"
#include "file_chooser_dialog.hpp"
#include "font.hpp"
#include "foreach.hpp"
#include "frame.hpp"
#include "grid_widget.hpp"
#include "json_parser.hpp"
#include "graphics.hpp"
#include "label.hpp"
#include "module.hpp"
#include "slider.hpp"
#include "text_editor_widget.hpp"
#include "tree_view_widget.hpp"

const std::string template_directory = "data/object_templates/";

namespace gui {

class item_edit_dialog : public virtual dialog
{
public:
	item_edit_dialog(int x, int y, int w, int h, const std::string&name, variant items) 
		: dialog(x,y,w,h), items_(items), display_name_(name), allow_functions_(false),
		row_count_(0)
	{
		if(items_.is_map() == false) {
			std::map<variant, variant> m;
			items_ = variant(&m);
		}
		init();
	}
	virtual ~item_edit_dialog() 
	{}
	variant get_items() const { return item_grid_->get_tree(); }
	void allow_functions(bool val=true) { allow_functions_ = val; }
protected:
	virtual bool handle_event(const SDL_Event& event, bool claimed);

	void init();
	void on_save();
	bool has_keyboard_focus();

	void editor_select(variant* v, boost::function<void(const variant&)> save_fn);
	void string_entry_save();
	void string_entry_discard();
private:
	std::string display_name_;
	variant items_;
	bool allow_functions_;
	widget_ptr context_menu_;

	tree_editor_widget_ptr item_grid_;
	code_editor_widget_ptr string_entry_;
	button_ptr save_text_button_;
	button_ptr discard_text_button_;
	grid_ptr text_button_grid;
	boost::function<void(const variant&)> save_fn_;
	int row_count_;
	std::string saved_text_;
};

}

namespace {

int slider_transform(double d)
{
	// normalize to [-20.0,20.0] range.
	d = (d - 0.5) * 2.0 * 20;
	double d_abs = abs(d);
	if(d_abs > 10) {
		// Above 10 units we go non-linear.
		return int((d < 0 ? -1.0 : 1.0) * pow(10, d_abs/10));
	}
	return int(d);
}

module::module_file_map& get_template_path()
{
	static module::module_file_map dialog_file_map;
	return dialog_file_map;
}

void load_template_file_paths(const std::string& path)
{
	if(get_template_path().empty()) {
		module::get_unique_filenames_under_dir(path, &get_template_path());
	}
}

void do_draw_scene() {
	draw_scene(level::current(), last_draw_position());
}

void load_default_attributes(std::vector<std::string>& d)
{
	//d.push_back("id");
	//d.push_back("animation");
	//d.push_back("editor_info");
	d.push_back("prototype");
	d.push_back("hitpoints");
	d.push_back("mass");
	d.push_back("vars");
	d.push_back("friction");
	d.push_back("traction");
	d.push_back("traction_in_air");
}

std::vector<std::string>& get_default_attribute_list()
{
	static std::vector<std::string> defaults;
	if(defaults.empty()) {
		load_default_attributes(defaults);
	}
	return defaults;
}

void reset_dialog_paths()
{
	get_template_path().clear();
}

std::string get_dialog_file(const std::string& fname)
{
	load_template_file_paths(template_directory);
	module::module_file_map::const_iterator it = module::find(get_template_path(), fname);
	ASSERT_LOG(it != get_template_path().end(), "OBJECT TEMPLATE FILE NOT FOUND: " << fname);
	return it->second;
}


std::string get_id_from_filemap(std::pair<std::string, std::string> p)
{
	std::string s = module::get_id(p.first);
	if(s.length() > 4 && s.substr(s.length()-4) == ".cfg") {
		return s.substr(0, s.length()-4);
	}
	return s;
}

void do_nothing()
{
}

}

namespace editor_dialogs {

custom_object_dialog::custom_object_dialog(editor& e, int x, int y, int w, int h)
	: gui::dialog(x,y,w,h), dragging_slider_(false), selected_template_(0)
{
	load_template_file_paths(template_directory);
	set_clear_bg_amount(255);
	std::map<variant, variant> m;
	object_template_ = variant(&m);
	current_object_save_path_ = module::get_module_path() + "data/objects/";
	std::transform(prototype_file_paths().begin(), prototype_file_paths().end(), std::back_inserter(prototypes_), get_id_from_filemap);
	std::sort(prototypes_.begin(), prototypes_.end());
	init();
}


void custom_object_dialog::init()
{
	const int border_offset = 30;
	using namespace gui;
	clear();

	add_widget(widget_ptr(new label("Object Properties", graphics::color_white(), 20)), border_offset, border_offset);

	grid_ptr container(new grid(1));
	container->set_col_width(0, width() - border_offset);

	// Get choices for dropdown list.
	std::vector<std::string> template_choices;
	std::transform(get_template_path().begin(), get_template_path().end(), std::back_inserter(template_choices),
		boost::bind(&module::module_file_map::value_type::first,_1));
	std::sort(template_choices.begin(), template_choices.end());
	template_choices.insert(template_choices.begin(), "Blank");

	dropdown_widget_ptr template_dropdown(new dropdown_widget(template_choices, 200, 30, dropdown_widget::DROPDOWN_LIST));
	template_dropdown->set_dropdown_height(100);
	template_dropdown->set_on_select_handler(boost::bind(&custom_object_dialog::change_template, this, _1, _2));
	template_dropdown->set_selection(selected_template_);

	grid_ptr g(new grid(4));
	g->set_hpad(20);
	g->set_zorder(1);
	g->add_col(widget_ptr(new label("Template  ", graphics::color_white(), 14)))
		.add_col(template_dropdown);
	text_editor_widget_ptr change_entry(new text_editor_widget(200, 28));
	change_entry->set_font_size(14);
	if(object_template_.has_key("id")) {
		change_entry->set_text(object_template_["id"].as_string());
	}
	change_entry->set_on_change_handler(boost::bind(&custom_object_dialog::change_text_attribute, this, change_entry, "id"));
	change_entry->set_on_enter_handler(boost::bind(&custom_object_dialog::init, this));
	change_entry->set_on_tab_handler(boost::bind(&custom_object_dialog::init, this));
	change_entry->set_on_esc_handler(boost::bind(&custom_object_dialog::init, this));
	change_entry->set_on_change_focus_handler(boost::bind(&custom_object_dialog::id_change_focus, this, _1));
	g->add_col(widget_ptr(new label("id: ", graphics::color_white(), 14)))
		.add_col(widget_ptr(change_entry));
	container->add_col(g);

	g.reset(new grid(4));
	g->add_col(widget_ptr(new button(new label("Animations", graphics::color_white(), 20), boost::bind(&custom_object_dialog::on_edit_animations, this))));
	g->add_col(widget_ptr(new button(new label("Variables", graphics::color_white(), 20), boost::bind(&custom_object_dialog::on_edit_items, this, "Variables Editor", "vars", false))));
	g->add_col(widget_ptr(new button(new label("Properties", graphics::color_white(), 20), boost::bind(&custom_object_dialog::on_edit_items, this, "Properties Editor", "properties", true))));
	g->add_col(widget_ptr(new button(new label("Editor Info", graphics::color_white(), 20), boost::bind(&custom_object_dialog::on_edit_items, this, "Editor Info", "editor_info", false))));
	container->add_col(g);

	if(template_file_.first.empty()) {
		foreach(const std::string& attr, get_default_attribute_list()) {
			std::vector<widget_ptr> widget_list = get_widget_for_attribute(attr);
			foreach(const widget_ptr& w, widget_list) {
				if(w) {
					container->add_col(w);
				}
			}
		}
	} else {
		std::vector<variant> keys = object_template_.get_keys().as_list();
		foreach(const variant& v, keys) {
			std::vector<widget_ptr> widget_list = get_widget_for_attribute(v.as_string());
			foreach(const widget_ptr& w, widget_list) {
				if(w) {
					container->add_col(w);
				}
			}
		}
	}

	error_text_.clear();
	assert_recover_scope recover_from_assert;
	try {
		object_ = custom_object_type_ptr(new custom_object_type(object_template_, NULL, NULL));

		animation_widget_ptr preview(new animation_widget(128, 128, object_template_));
		add_widget(preview, width() - border_offset - 128, border_offset + 200);
	} catch(validation_failure_exception& e) {
		error_text_ = e.msg;
		std::cerr << "error parsing formula: " << e.msg << std::endl;
	} catch(type_error& e) {
		error_text_ = e.message;
		std::cerr << "error executing formula: " << e.message << std::endl;
	}

	std::string err_text = error_text_;
	boost::replace_all(err_text, "\n", "\\n");
	int max_chars = (width() - border_offset*2)/font::char_width(14);
	if(err_text.length() > max_chars && max_chars > 3) {
		err_text = err_text.substr(0, max_chars-3) + "...";
	}
	label_ptr error_text(new label(err_text, graphics::color_red(), 14));
	add_widget(error_text, border_offset, height() - g->height() - border_offset - error_text->height() - 5);

	g.reset(new grid(3));
	g->set_hpad(20);
	g->add_col(button_ptr(new button(new label("Create", graphics::color_white(), 20), boost::bind(&custom_object_dialog::on_create, this))));
	g->add_col(button_ptr(new button(new label("Set Path...", graphics::color_white(), 20), boost::bind(&custom_object_dialog::on_set_path, this))));
	std::string path = current_object_save_path_;
	if(object_template_.has_key("id")) {
		path += object_template_["id"].as_string() + ".cfg";
	} else {
		path += "<no id>.cfg";
	}
	g->add_col(label_ptr(new label(path, graphics::color_green())));
	add_widget(g, border_offset, height() - g->height() - border_offset);

	container->set_max_height(height() - g->height() - border_offset - error_text->height() - 10);
	add_widget(container, border_offset, border_offset*2);
}

void custom_object_dialog::on_set_path()
{
	gui::file_chooser_dialog dir_dlg(
		int(preferences::virtual_screen_width()*0.2), 
		int(preferences::virtual_screen_height()*0.2), 
		int(preferences::virtual_screen_width()*0.6), 
		int(preferences::virtual_screen_height()*0.6),
		gui::filter_list(), 
		true, current_object_save_path_);
	dir_dlg.set_background_frame("empty_window");
	dir_dlg.set_draw_background_fn(do_draw_scene);
	dir_dlg.use_relative_paths(true);
	dir_dlg.show_modal();

	if(dir_dlg.cancelled() == false) {
		current_object_save_path_ = dir_dlg.get_path() + "/";
	}
	init();
}

void custom_object_dialog::id_change_focus(bool focus)
{
	if(focus == false) {
		init();
	}
}

std::vector<gui::widget_ptr> custom_object_dialog::get_widget_for_attribute(const std::string& attr)
{
	using namespace gui;
	if(attr == "id") {
		//grid_ptr g(new grid(2));
		//text_editor_widget_ptr change_entry(new text_editor_widget(200, 28));
		//change_entry->set_font_size(14);
		//if(object_template_.has_key(attr)) {
		//	change_entry->set_text(object_template_[attr].as_string());
		//}
		//change_entry->set_on_change_handler(boost::bind(&custom_object_dialog::change_text_attribute, this, change_entry, attr));
		//change_entry->set_on_enter_handler(do_nothing);
		//g->add_col(widget_ptr(new label(attr + ": ", graphics::color_white(), 14))).add_col(widget_ptr(change_entry));
		//return g;
	} else if(attr == "hitpoints" || attr == "mass" || attr == "friction" 
		|| attr == "traction" || attr == "traction_in_air") {
		grid_ptr g(new grid(3));
		int value = 0;
		text_editor_widget_ptr change_entry(new text_editor_widget(100, 28));
		change_entry->set_font_size(14);
		if(object_template_.has_key(attr)) {
			std::stringstream ss;
			value = object_template_[attr].as_int();
			ss << object_template_[attr].as_int();
			change_entry->set_text(ss.str());
		} else {
			change_entry->set_text("0");
		}
		slider_offset_[attr] = object_template_.has_key(attr) ? object_template_[attr].as_int() : 0;

		slider_ptr slide(new slider(200, 
			boost::bind((&custom_object_dialog::change_int_attribute_slider), this, change_entry, attr, _1), 
			value));
		slide->set_position(0.5);
		slide->set_drag_end(boost::bind(&custom_object_dialog::slider_drag_end, this, change_entry, attr, slide, _1));
		change_entry->set_on_change_handler(boost::bind(&custom_object_dialog::change_int_attribute_text, this, change_entry, attr, slide));
		change_entry->set_on_enter_handler(do_nothing);
		label_ptr attr_label(new label(attr + ": ", graphics::color_white(), 14));
		attr_label->set_dim(200, attr_label->height());
		change_entry->set_dim(100, change_entry->height());
		slide->set_dim(200, slide->height());
		g->add_col(attr_label).add_col(widget_ptr(change_entry)).add_col(slide);

		g->set_col_width(0, 200);
		g->set_col_width(1, 100);
		g->set_col_width(2, 200);

		return std::vector<gui::widget_ptr>(1, g);
	} else if(attr == "animation") {
		//button_ptr bb(new button(new label("Edit Animations", graphics::color_white(), 20), boost::bind(&custom_object_dialog::on_edit_animations, this)));
		//return bb;
	} else if(attr == "vars") {
		//grid_ptr g(new grid(1));
		//g->add_col(widget_ptr(new label(attr + ": ", graphics::color_white(), 14)));
		//return std::vector<gui::widget_ptr>(1, g);
	} else if(attr == "editor_info") {
		//grid_ptr g(new grid(1));
		//g->add_col(widget_ptr(new label(attr + ": ", graphics::color_white(), 14)));
		//return std::vector<gui::widget_ptr>(1, g);
	} else if(attr == "prototype") {
		//int count = 0;
		// To make this nicer. Create the buttons before adding them to the grid.
		// Estimate the maximum number of columns needed (take the minimum size button 
		// divided into screen width being used.  Then we start adding buttons to the 
		// grid, if we are about to add a button that would go over the maximum
		// width then we do a .finish_row() (if needed) and start continue
		// adding the column to the next row (with .add_col()).
		std::vector<button_ptr> buttons;
		int min_size_button = INT_MAX;
		if(object_template_.has_key("prototype")) {
			foreach(const std::string& s, object_template_["prototype"].as_list_string()) {
				buttons.push_back(new button(widget_ptr(new label(s, graphics::color_white())), 
					boost::bind(&custom_object_dialog::remove_prototype, this, s)));
				if(min_size_button > buttons.back()->width()) {
					min_size_button = buttons.back()->width();
				}
			}
		}
		std::vector<gui::widget_ptr> rows;
		// conservative
		int column_estimate = (width() - 100) / min_size_button + 2;
		grid_ptr g(new grid(column_estimate));
		label_ptr attr_label  = new label(attr + ": ", graphics::color_white(), 14);
		button_ptr add_button = new button(widget_ptr(new label("Add...", graphics::color_white())), 
			boost::bind(&custom_object_dialog::change_prototype, this));
		g->add_col(attr_label).add_col(add_button);

		int current_row_size = attr_label->width() + add_button->width();
		int buttons_on_current_row = 2;
		foreach(const button_ptr& b, buttons) {
			if(b->width() + current_row_size >= width()-100 ) {
				if(buttons_on_current_row < column_estimate) {
					g->finish_row();
				}
				rows.push_back(g);
				g.reset(new grid(column_estimate));
				
				current_row_size = 0;
				buttons_on_current_row = 0;
			}
			g->add_col(b);
			current_row_size += b->width();
			buttons_on_current_row++;
		}
		if(buttons_on_current_row != 0) {
			if(buttons_on_current_row < column_estimate) {
				g->finish_row();
			}
			rows.push_back(g);
		}
		return rows;
	}
	std::cerr << "Unhandled attribute " << attr << std::endl;
	return std::vector<gui::widget_ptr>();
}

void custom_object_dialog::slider_drag_end(const gui::text_editor_widget_ptr editor, const std::string& s, gui::slider_ptr slide, double d)
{
	int i = slider_transform(d) + slider_offset_[s];
	slider_offset_[s] = i;
	slide->set_position(0.5);
	dragging_slider_ = false;
}

void custom_object_dialog::change_int_attribute_slider(const gui::text_editor_widget_ptr editor, const std::string& s, double d)
{	
	dragging_slider_ = true;
	std::ostringstream ss;
	int i = slider_transform(d) + slider_offset_[s];
	ss << i;
	editor->set_text(ss.str(), false);
	object_template_.add_attr(variant(s), variant(i));
}

void custom_object_dialog::change_text_attribute(const gui::text_editor_widget_ptr editor, const std::string& s)
{
	object_template_.add_attr(variant(s), variant(editor->text()));
}

void custom_object_dialog::change_int_attribute_text(const gui::text_editor_widget_ptr editor, const std::string& s, gui::slider_ptr slide)
{
	if(!dragging_slider_) {
		int i;
		std::istringstream(editor->text()) >> i;
		slider_offset_[s] = i;
		slide->set_position(0.5);
		object_template_.add_attr(variant(s), variant(i));
	}
}

void custom_object_dialog::change_template(int selection, const std::string& s)
{
	selected_template_ = selection;
	if(selection == 0) {
		template_file_ = std::pair<std::string, std::string>();
	} else {
		template_file_.first = get_id_from_filemap(std::pair<std::string, std::string>(s,""));
		template_file_.second = get_dialog_file(s);
	}
	if(template_file_.first.empty() == false) {
		object_template_ = json::parse_from_file(template_file_.second);
		ASSERT_LOG(object_template_.is_map(), 
			"OBJECT TEMPLATE READ FROM FILE IS NOT MAP: " << template_file_.second)
		// ignorning these exceptions till we're finished
		//assert_recover_scope recover_from_assert;
		//try {
		//	object_ = custom_object_type_ptr(new custom_object_type(object_template_, NULL, NULL));
		//} catch(validation_failure_exception& e) {
		//	std::cerr << "error parsing formula: " << e.msg << std::endl;
		//} catch(type_error& e) {
		//	std::cerr << "error executing formula: " << e.message << std::endl;
		//}
	} else {
		std::map<variant, variant> m;
		object_template_ = variant(&m);
	}
	init();
}

void custom_object_dialog::change_prototype()
{
	using namespace gui;
	std::vector<std::string> choices;
	if(object_template_.has_key("prototype")) {
		std::vector<std::string> v = object_template_["prototype"].as_list_string();
		std::sort(v.begin(), v.end());
		std::set_difference(prototypes_.begin(), prototypes_.end(), v.begin(), v.end(), 
			std::inserter(choices, choices.end()));
	} else {
		choices = prototypes_;
	}

	int mousex, mousey, my;
	SDL_GetMouseState(&mousex, &my);
	mousex -= this->x();
	mousey = my - this->y();

	gui::grid* grid = new gui::grid(1);
	grid->set_max_height(height() - my);
	grid->set_hpad(10);
	grid->set_show_background(true);
	grid->allow_selection();
	grid->swallow_clicks();
	foreach(const std::string& s, choices) {
		grid->add_col(widget_ptr(new label(s, graphics::color_white())));
	}
	grid->register_selection_callback(boost::bind(&custom_object_dialog::execute_change_prototype, this, choices, _1));

	remove_widget(context_menu_);
	context_menu_.reset(grid);
	add_widget(context_menu_, mousex, mousey);
}

void custom_object_dialog::remove_prototype(const std::string& s)
{
	if(object_template_.has_key("prototype")) {
		std::vector<variant> v = object_template_["prototype"].as_list();
		v.erase(std::remove(v.begin(), v.end(), variant(s)), v.end());
		object_template_.add_attr(variant("prototype"), variant(&v));
	}
	init();
}

void custom_object_dialog::execute_change_prototype(const std::vector<std::string>& choices, size_t index)
{
	if(context_menu_) {
		remove_widget(context_menu_);
		context_menu_.reset();
	}
	if(index >= choices.size()) {
		return;
	}

	std::vector<variant> v;
	if(object_template_.has_key("prototype")) {
		v = object_template_["prototype"].as_list();
	}
	v.push_back(variant(choices[index]));
	object_template_.add_attr(variant("prototype"), variant(&v));

	init();
}


void custom_object_dialog::on_create()
{
	// write out the file
	sys::write_file(current_object_save_path_ + "/" + object_template_[variant("id")].as_string() + ".cfg", object_template_.write_json());
	close();
}

void custom_object_dialog::on_edit_animations()
{
	gui::animation_creator_dialog d(0, 0, preferences::virtual_screen_width(), 
		preferences::virtual_screen_height(),
		object_template_.has_key("animation") ? object_template_["animation"] : variant());
	d.set_background_frame("empty_window");
	d.set_draw_background_fn(do_draw_scene);

	d.show_modal();
	if(d.cancelled() == false) {
		object_template_.add_attr(variant("animation"), d.get_animations());
	}
}

void custom_object_dialog::on_edit_items(const std::string& name, const std::string& attr, bool allow_functions)
{
	gui::item_edit_dialog d(0, 0, preferences::virtual_screen_width(), 
		preferences::virtual_screen_height(),
		name,
		object_template_.has_key(attr) ? object_template_[attr] : variant());
	d.set_background_frame("empty_window");
	d.set_draw_background_fn(do_draw_scene);
	d.allow_functions(allow_functions);

	d.show_modal();
	if(d.cancelled() == false) {
		object_template_.add_attr(variant(attr), d.get_items());
	}
}

}

namespace gui {

void item_edit_dialog::init()
{
	clear();

	const int border_offset = 35;
	const int hpad = 20;
	int current_height = border_offset;
	label_ptr title(new label(display_name_.empty() ? "Edit" : display_name_, graphics::color_white(), 20));
	add_widget(title, border_offset, current_height);
	current_height += title->height() + hpad;

	grid_ptr g(new grid(2));
	g->set_hpad(100);
	button_ptr mod_button(new button(new label("Save&Close", graphics::color_white(), 16), boost::bind(&item_edit_dialog::on_save, this)));
	button_ptr del_button(new button(new label("Cancel", graphics::color_white(), 16), boost::bind(&item_edit_dialog::cancel, this)));
	g->add_col(mod_button).add_col(del_button);
	add_widget(g, (width() - g->width())/2, current_height);
	current_height += g->height() + hpad;


	text_button_grid.reset(new grid(2));
	text_button_grid->set_hpad(30);
	save_text_button_.reset(new button(new label("Save Text", graphics::color_white(), 14), boost::bind(&item_edit_dialog::string_entry_save, this)));
	discard_text_button_.reset(new button(new label("Discard Text", graphics::color_white(), 14), boost::bind(&item_edit_dialog::string_entry_discard, this)));
	text_button_grid->add_col(save_text_button_).add_col(discard_text_button_);
	text_button_grid->set_visible(false);

	const int string_entry_height = height() - current_height - border_offset - text_button_grid->height() - 5;
	const int string_entry_width = 2*width()/3 - 2*border_offset;

	add_widget(text_button_grid, width()/3 + border_offset + (string_entry_width - text_button_grid->width())/2, string_entry_height + current_height + 5);

	string_entry_.reset(new code_editor_widget(string_entry_width, string_entry_height));
	string_entry_->set_font_size(12);
	string_entry_->set_on_esc_handler(boost::bind(&item_edit_dialog::string_entry_discard, this));
	string_entry_->set_loc(width()/3 + border_offset, current_height);
	if(allow_functions_) {
		string_entry_->set_formula();
	}

	item_grid_.reset(new tree_editor_widget(width()/3 - border_offset, height() - current_height - border_offset, items_));
	item_grid_->allow_selection();
	item_grid_->allow_persistent_highlight();
	item_grid_->set_editor_handler(variant::VARIANT_TYPE_STRING, string_entry_, boost::bind(&item_edit_dialog::editor_select, this, _1, _2));
	add_widget(item_grid_, border_offset, current_height);

	current_height += item_grid_->height() + hpad;
}

void item_edit_dialog::editor_select(variant* v, boost::function<void(const variant&)> save_fn)
{
	text_button_grid->set_visible(true);
	saved_text_ = v->as_string();
	string_entry_->set_text(saved_text_);
	string_entry_->set_focus(true);
	save_fn_ = save_fn;
}

void item_edit_dialog::on_save()
{
	close();
}

bool item_edit_dialog::has_keyboard_focus()
{
	return string_entry_->has_focus();
}

bool item_edit_dialog::handle_event(const SDL_Event& event, bool claimed)
{
	if(dialog::handle_event(event, claimed)) {
		return true;
	}

	if(has_keyboard_focus()) {
		if(event.type == SDL_KEYDOWN) {
			if(event.key.keysym.sym == SDLK_s && (event.key.keysym.mod&KMOD_CTRL)) {
				string_entry_save();
				return true;
			}
		}
	}
	return claimed;
}

void item_edit_dialog::string_entry_save()
{
	if(save_fn_) {
		text_button_grid->set_visible(false);
		save_fn_(variant(string_entry_->text()));
	}
}

void item_edit_dialog::string_entry_discard()
{
	if(save_fn_) {
		text_button_grid->set_visible(false);
		save_fn_(variant(saved_text_));
	}
}

}

#endif // !NO_EDITOR
