New hotkey preferences dialog.

Have a look in the players_changelog for details.
This commit is contained in:
Fabian Müller 2012-09-23 18:33:07 +00:00
parent b56df83d08
commit 36fc26b8fd
8 changed files with 633 additions and 236 deletions

View file

@ -36,6 +36,7 @@ Version 1.11.0+svn:
* Miscellaneous and bug fixes:
* Fix invalid memory access crash resulting from deleting all saved games
in the Load Game dialog
* Redesigned the hotkey preferences dialog
* Removed two Khalifate leftovers (Hakim portrait and KHALIFATE_NAMES macro)
* Hex field size and default terrain are wml configurable
* Fix OOS when dismissing a recall in a multiplayer campaign (bug #19924).

View file

@ -18,6 +18,15 @@ Version 1.11.0+svn:
* User interface:
* Healing animations are now played when poison is cured.
* Preferences
* "Hotkey Settings" dialog redisigned.
* Features a tab for each hotkey scope.
* Truncates hotkey descriptions to keep the dialog functional,
but shows their full description as help tooltips.
* Allows reasigning hotkeys
* Cancel button to discard any changes
* Assigning of more than one hotkey per action possible
* Support for assigning mouse buttons to actions
* The recruit and recall commands are restored when right-clicking on a
leader, but with new semantics -- only that leader's recruits/recalls will
be presented as options.

View file

@ -735,6 +735,7 @@ set(wesnoth-main_SRC
gui/dialogs/wml_message.cpp
halo.cpp
help.cpp
hotkey_preferences_display.cpp
intro.cpp
leader_list.cpp
map.cpp

View file

@ -491,6 +491,7 @@ wesnoth_sources = Split("""
""")
wesnoth_sources.extend(client_env.Object("game_preferences_display.cpp", EXTRA_DEFINE = env["PLATFORM"] != "win32" and "WESNOTH_PREFIX='\"$prefix\"'" or None))
wesnoth_sources.extend(client_env.Object("hotkey_preferences_display.cpp", EXTRA_DEFINE = env["PLATFORM"] != "win32" and "WESNOTH_PREFIX='\"$prefix\"'" or None))
libwesnoth_extras = client_env.Library("wesnoth_extras", wesnoth_sources)

View file

@ -243,8 +243,7 @@ preferences_dialog::preferences_dialog(display& disp, const config& game_cfg)
{
sort_advanced_preferences();
// FIXME: this box should be vertically centered on the screen, but is not
set_measurements(465, 400);
set_measurements(preferences::width, preferences::height);
sound_button_.set_check(sound_on());
@ -748,7 +747,7 @@ void preferences_dialog::process_event()
if (hide_whiteboard_button_.pressed())
set_hide_whiteboard(hide_whiteboard_button_.checked());
if (hotkeys_button_.pressed()) {
show_hotkeys_dialog(disp_);
show_hotkeys_preferences_dialog(disp_);
parent->clear_buttons();
}

View file

@ -0,0 +1,614 @@
/* $Id$ */
/*
Copyright (C) 2012 by Fabian Mueler <fabianmueller5@gmx.de>
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.
*/
#define GETTEXT_DOMAIN "wesnoth-lib"
/**
* @file
* Manages the hotkey bindings.
*/
#include "preferences_display.hpp"
#include "construct_dialog.hpp"
#include "display.hpp"
#include "formatter.hpp"
#include "formula_string_utils.hpp"
#include "game_preferences.hpp"
#include "gettext.hpp"
#include "gui/dialogs/message.hpp"
#include "gui/dialogs/simple_item_selector.hpp"
#include "gui/dialogs/transient_message.hpp"
#include "gui/widgets/window.hpp"
#include "hotkeys.hpp"
#include "log.hpp"
#include "marked-up_text.hpp"
#include "wml_separators.hpp"
#include <boost/assign/list_of.hpp>
#include <boost/foreach.hpp>
namespace preferences {
class hotkey_preferences_parent_dialog: public gui::dialog {
public:
hotkey_preferences_parent_dialog(display &disp) :
dialog(disp, _("Hotkey Settings"), "", gui::OK_CANCEL),
clear_buttons_(false),
hotkey_cfg_()
{
// keep the old config in case the user cancels the dialog
hotkey::save_hotkeys(hotkey_cfg_);
}
~hotkey_preferences_parent_dialog() {
ERR_GUI_D << "resulte ist" << result() << "\n";
// save or restore depending on the exit result
if (result() >= 0)
save_hotkeys();
else hotkey::load_hotkeys(hotkey_cfg_, false);
}
void action(gui::dialog_process_info &info) {
if (clear_buttons_) {
info.clear_buttons();
clear_buttons_ = false;
}
}
void clear_buttons() {
clear_buttons_ = true;
}
private:
bool clear_buttons_;
config hotkey_cfg_;
};
class hotkey_preferences_dialog: public gui::preview_pane {
private :
/* nice little magic number, the size of the truncated hotkey command */
static const int truncate_at = 25;
public:
hotkey_preferences_dialog(display& disp);
private:
// overrides events::handler::process_event
void process_event();
// overrides gui::widget::update_location
void update_location(SDL_Rect const &rect);
// overrides gui::preview_pane::handler_members
virtual handler_vector handler_members();
// implements gui::preview_pane::set_selection
void set_selection(int index);
// implements gui::preview_pane::left_side
bool left_side() const {
return false;
}
/*
* Populates, sorts and redraws the hotkey menu
* specified by tab_.
* @keep_viewport feeds the keep_viewport param of menu::set_menu_items()
*/
void set_hotkey_menu(bool keep_viewport);
/* Subdialog, recognizing the hotkey sequence
* @command add the result of the dialog to this item
*/
void show_binding_dialog(const std::string& command);
/*
* Buttons to trigger the tools involved in hotkey assignment.
* The buttons are shared by all scope tabs.
*/
gui::button add_button_, clear_button_, reset_button_;
/* The dialog features a tab for each hotkey scope (except the SCOPE_COUNTER)*/
hotkey::scope tab_;
/* These are to map the menu selection to the corresponding command
* example: std::string selected_general_command = general_commands_[general_hotkey_.get_selected()];
*/
std::vector<std::string> general_commands_;
std::vector<std::string> game_commands_;
std::vector<std::string> editor_commands_;
/* The header of all the scope menus */
const std::string heading_;
/* Every scope gets its own menu and sorter to allow keeping the viewport
* while switching through the tabs.
*/
int selected_command_;
gui::menu::basic_sorter general_sorter_, game_sorter_, editor_sorter_;
gui::menu general_hotkeys_, game_hotkeys_, editor_hotkeys_;
/* The display, for usage by child dialogs */
display &disp_;
public:
/*
* TODO Okay, we have here a public member in a class.
* Like in game_preferences_dialog.
*/
util::scoped_ptr<hotkey_preferences_parent_dialog> parent;
};
void show_hotkeys_preferences_dialog(display& disp) {
std::vector<std::string> items;
const std::string pre = IMAGE_PREFIX + std::string("icons/icon-");
char const sep = COLUMN_SEPARATOR;
// tab names and icons
items.push_back(pre + "general.png" + sep + sgettext("Prefs section^General"));
items.push_back(pre + "game.png" + sep + sgettext("Prefs section^Game"));
items.push_back(pre + "editor.png" + sep + sgettext("Prefs section^Editor"));
// determine the current scope, but not general
int scope;
for (scope = 1; scope != hotkey::SCOPE_COUNT; scope++)
if (hotkey::is_scope_active((hotkey::scope)scope))
break;
// The restorer will change the scope back to where we came from
// when it runs out of the function's scope
hotkey::scope_changer scope_restorer;
hotkey_preferences_dialog dialog(disp);
dialog.parent.assign(new hotkey_preferences_parent_dialog(disp));
dialog.parent->set_menu(items);
dialog.parent->add_pane(&dialog);
// select the tab corresponding to the current scope.
dialog.parent->get_menu().move_selection(scope);
dialog.parent->show();
return;
}
/************ hotkey_preferences_dialog members ************************/
hotkey_preferences_dialog::hotkey_preferences_dialog(display& disp) :
gui::preview_pane(disp.video()),
add_button_(disp.video(), _("Add Hotkey")),
clear_button_(disp.video(), _("Clear Hotkey")),
reset_button_(disp.video(), _("Reset All")),
tab_(hotkey::SCOPE_GENERAL),
general_commands_(),
game_commands_(),
editor_commands_(),
heading_( (formatter() << HEADING_PREFIX << _("Action")
<< COLUMN_SEPARATOR << _("Binding")).str()),
selected_command_(0),
general_sorter_(),
game_sorter_(),
editor_sorter_(),
// Note: If you don't instantiate the menus with heading_,
// the header can't be enabled later, seems to be a bug in gui::menu
general_hotkeys_(disp.video(), boost::assign::list_of(heading_), false, -1, -1,
&general_sorter_, &gui::menu::bluebg_style),
game_hotkeys_(disp.video(), boost::assign::list_of(heading_), false, -1, -1,
&game_sorter_, &gui::menu::bluebg_style),
editor_hotkeys_(disp.video(), boost::assign::list_of(heading_), false, -1, -1,
&editor_sorter_, &gui::menu::bluebg_style),
disp_(disp),
parent(NULL)
{
set_measurements(preferences::width, preferences::height);
// Populate the command vectors, this needs to happen only once.
const hotkey::hotkey_command* list = hotkey::get_hotkey_commands();
for (size_t i = 0; list[i].id != hotkey::HOTKEY_NULL; ++i) {
if (list[i].hidden)
continue;
switch (list[i].scope) {
case hotkey::SCOPE_GAME:
game_commands_.push_back(list[i].command);
break;
case hotkey::SCOPE_EDITOR:
editor_commands_.push_back(list[i].command);
break;
case hotkey::SCOPE_GENERAL:
general_commands_.push_back(list[i].command);
break;
case hotkey::SCOPE_COUNT:
break;
}
}
//TODO move to the caller?
disp_.video().clear_all_help_strings();
// Add help tooltips to the buttons
//TODO adjust them corresponding to the selected item
clear_button_.set_help_string(_("Clears the selected actions's bindings"));
add_button_.set_help_string(_("Add additional binding to the selected action"));
reset_button_.set_help_string(_("Reset all bindings to the default values"));
// Initialize sorters.
general_sorter_.set_alpha_sort(0).set_alpha_sort(1);
game_sorter_.set_alpha_sort(0).set_alpha_sort(1);
editor_sorter_.set_alpha_sort(0).set_alpha_sort(1);
// Populate every menu_
for (int scope = 0; scope != hotkey::SCOPE_COUNT; scope++) {
tab_ = hotkey::scope(scope);
set_hotkey_menu(false);
}
}
void hotkey_preferences_dialog::set_hotkey_menu(bool keep_viewport) {
/*
* First hide all elements of all tabs,
* we might have redrawing glitches otherwise.
*/
add_button_.hide(true);
reset_button_.hide(true);
clear_button_.hide(true);
game_hotkeys_.hide(true);
editor_hotkeys_.hide(true);
general_hotkeys_.hide(true);
// Helpers to keep the switch statement small.
gui::menu* active_hotkeys = NULL;
std::vector<std::string>* commands = NULL;
// Determine the menu corresponding to the selected tab.
switch (tab_) {
case hotkey::SCOPE_GAME:
active_hotkeys = &game_hotkeys_;
commands = &game_commands_;
break;
case hotkey::SCOPE_EDITOR:
active_hotkeys = &editor_hotkeys_;
commands = &editor_commands_;
break;
case hotkey::SCOPE_GENERAL:
active_hotkeys = &general_hotkeys_;
commands = &general_commands_;
break;
case hotkey::SCOPE_COUNT:
assert(false); // should not happen.
break;
}
// Fill the menu rows
std::vector<std::string> menu_items;
BOOST_FOREACH(const std::string& command, *commands) {
const std::string& description = hotkey::get_description(command);
std::string truncated_description = description;
if (truncated_description.size() >= (truncate_at + 2) )
utils::ellipsis_truncate(truncated_description, truncate_at);
const std::string& name = hotkey::get_names(hotkey::get_id(command));
menu_items.push_back(
(formatter() << truncated_description << HELP_STRING_SEPARATOR << description << COLUMN_SEPARATOR
<< font::NULL_MARKUP << name << HELP_STRING_SEPARATOR << name).str() );
}
// No effect if the vector is used in set_items instead of the menu's constructor.
// menu_items.push_back(heading_);
// Re-populate the items.
// workaround for set_items not keeping the selected item.
selected_command_ = active_hotkeys->selection();
active_hotkeys->set_items(menu_items, true, keep_viewport);
// This shall happen only once at the start of the dialog.
if (!keep_viewport) {
active_hotkeys->sort_by(0);
active_hotkeys->reset_selection();
} else {
active_hotkeys->move_selection_keeping_viewport(selected_command_);
// !hide and thus redraw only the current tab_'s items
active_hotkeys->hide(false);
add_button_.hide(false);
reset_button_.hide(false);
clear_button_.hide(false);
}
}
handler_vector hotkey_preferences_dialog::handler_members() {
handler_vector h;
h.push_back(&add_button_);
h.push_back(&clear_button_);
h.push_back(&reset_button_);
h.push_back(&general_hotkeys_);
h.push_back(&game_hotkeys_);
h.push_back(&editor_hotkeys_);
return h;
}
void hotkey_preferences_dialog::set_selection(int index) {
tab_ = hotkey::scope(index);
set_dirty();
bg_restore();
hotkey::deactivate_all_scopes();
switch (tab_) {
case hotkey::SCOPE_GENERAL:
hotkey::set_scope_active(hotkey::SCOPE_GENERAL);
hotkey::set_scope_active(hotkey::SCOPE_GAME);
hotkey::set_scope_active(hotkey::SCOPE_EDITOR);
break;
case hotkey::SCOPE_GAME:
hotkey::set_scope_active(hotkey::SCOPE_GENERAL);
hotkey::set_scope_active(hotkey::SCOPE_GAME);
break;
case hotkey::SCOPE_EDITOR:
hotkey::set_scope_active(hotkey::SCOPE_GENERAL);
hotkey::set_scope_active(hotkey::SCOPE_EDITOR);
break;
case hotkey::SCOPE_COUNT:
break;
}
set_hotkey_menu(true);
}
void hotkey_preferences_dialog::process_event() {
std::string id;
gui::menu* active_menu_ = NULL;
switch (tab_) {
case hotkey::SCOPE_GAME:
id = game_commands_[game_hotkeys_.selection()];
active_menu_ = &game_hotkeys_;
break;
case hotkey::SCOPE_EDITOR:
id = editor_commands_[editor_hotkeys_.selection()];
active_menu_ = &editor_hotkeys_;
break;
case hotkey::SCOPE_GENERAL:
id = general_commands_[general_hotkeys_.selection()];
active_menu_ = &general_hotkeys_;
break;
case hotkey::SCOPE_COUNT:
break;
}
if ( selected_command_ != active_menu_->selection()) {
selected_command_ = active_menu_->selection();
}
if (add_button_.pressed() || active_menu_->double_clicked()) {
show_binding_dialog(id);
}
if (clear_button_.pressed()) {
// clear hotkey
hotkey::clear_hotkeys(id);
set_hotkey_menu(true);
}
if (reset_button_.pressed()) {
// reset all bindings to default
const int res =
gui2::show_message(disp_.video(), _("Reset Hotkeys"),
_("This will reset all hotkeys to their default values. Do you wish to continue?"),
gui2::tmessage::yes_no_buttons);
if (res != gui2::twindow::CANCEL) {
clear_hotkeys();
gui2::show_transient_message(disp_.video(), _("Hotkeys Reset"),
_("All hotkeys have been reset to their default values."));
set_hotkey_menu(true);
}
}
}
void hotkey_preferences_dialog::update_location(SDL_Rect const &rect) {
bg_register(rect);
// some magic numbers :-P
// TODO they match the ones in game_preferences_dialog.
const int top_border = 10;
const int right_border = font::relative_size(10);
const int h = height() - 75; // well, this one not.
const int w = rect.w - right_border;
int xpos = rect.x;
int ypos = rect.y + top_border;
general_hotkeys_.set_location(xpos, ypos);
general_hotkeys_.set_max_height(h);
general_hotkeys_.set_height(h);
general_hotkeys_.set_max_width(w);
general_hotkeys_.set_width(w);
game_hotkeys_.set_location(xpos, ypos);
game_hotkeys_.set_max_height(h);
game_hotkeys_.set_height(h);
game_hotkeys_.set_max_width(w);
game_hotkeys_.set_width(w);
editor_hotkeys_.set_location(xpos, ypos);
editor_hotkeys_.set_max_height(h);
editor_hotkeys_.set_height(h);
editor_hotkeys_.set_max_width(w);
editor_hotkeys_.set_width(w);
ypos += h + font::relative_size(14);
add_button_.set_location(xpos, ypos);
xpos += add_button_.width() + font::relative_size(14);
reset_button_.set_location(xpos, ypos);
xpos += reset_button_.width() + font::relative_size(14);
clear_button_.set_location(xpos, ypos);
}
void hotkey_preferences_dialog::show_binding_dialog(const std::string& id) {
// Lets change this hotkey......
SDL_Rect clip_rect = create_rect(0, 0, disp_.w(), disp_.h());
SDL_Rect text_size = font::draw_text(NULL, clip_rect, font::SIZE_LARGE,
font::NORMAL_COLOR, _("Press desired hotkey (Esc cancels)"), 0,
0);
const int centerx = disp_.w() / 2;
const int centery = disp_.h() / 2;
SDL_Rect dlgr = create_rect(centerx - text_size.w / 2 - 30,
centery - text_size.h / 2 - 16, text_size.w + 60,
text_size.h + 32);
surface_restorer restorer(&disp_.video(), dlgr);
gui::dialog_frame mini_frame(disp_.video());
mini_frame.layout(centerx - text_size.w / 2 - 20,
centery - text_size.h / 2 - 6, text_size.w + 40,
text_size.h + 12);
mini_frame.draw_background();
mini_frame.draw_border();
font::draw_text(&disp_.video(), clip_rect, font::SIZE_LARGE,
font::NORMAL_COLOR, _("Press desired hotkey (Esc cancels)"),
centerx - text_size.w / 2, centery - text_size.h / 2);
disp_.update_display();
SDL_Event event;
event.type = 0;
int character = 0, keycode = 0, mod = 0; // Just to avoid warning
int device = 0, button = 0, hat = 0, value = 0;
const int any_mod = KMOD_CTRL | KMOD_ALT | KMOD_LMETA;
while (event.type != SDL_KEYDOWN && event.type != SDL_JOYBUTTONDOWN
&& event.type != SDL_JOYHATMOTION
&& (event.type != SDL_MOUSEBUTTONDOWN || event.button.button < 8))
SDL_PollEvent(&event);
do {
switch (event.type) {
case SDL_KEYDOWN:
keycode = event.key.keysym.sym;
character = event.key.keysym.unicode;
mod = event.key.keysym.mod;
break;
case SDL_JOYBUTTONDOWN:
device = event.jbutton.which;
button = event.jbutton.button;
break;
case SDL_JOYHATMOTION:
device = event.jhat.which;
hat = event.jhat.hat;
value = event.jhat.value;
break;
case SDL_MOUSEBUTTONDOWN:
device = event.button.which;
button = event.button.button;
break;
}
SDL_PollEvent(&event);
disp_.flip();
disp_.delay(10);
} while (event.type != SDL_KEYUP && event.type != SDL_JOYBUTTONUP
&& event.type != SDL_JOYHATMOTION
&& event.type != SDL_MOUSEBUTTONUP);
restorer.restore();
disp_.update_display();
// only if not canceled.
if (!(keycode == SDLK_ESCAPE && (mod & any_mod) == 0)) {
hotkey::hotkey_item newhk(id);
hotkey::hotkey_item* oldhk = NULL;
Uint8 *keystate = SDL_GetKeyState(NULL);
bool alt = keystate[SDLK_RALT] || keystate[SDLK_LALT];
bool ctrl = keystate[SDLK_RCTRL] || keystate[SDLK_LCTRL];
bool shift = keystate[SDLK_RSHIFT] || keystate[SDLK_LSHIFT];
bool cmd = keystate[SDLK_RMETA] || keystate[SDLK_LMETA];
switch (event.type) {
case SDL_JOYHATMOTION:
oldhk = &hotkey::get_hotkey(hotkey::hotkey_item::JHAT, device,
hat, value, shift, ctrl, alt, cmd);
newhk.set_jhat(device, hat, value, shift, ctrl, alt, cmd);
break;
case SDL_JOYBUTTONUP:
oldhk = &hotkey::get_hotkey(hotkey::hotkey_item::JBUTTON,
device, button, 0, shift, ctrl, alt, cmd);
newhk.set_jbutton(device, button, shift, ctrl, alt, cmd);
break;
case SDL_MOUSEBUTTONUP:
oldhk = &hotkey::get_hotkey(hotkey::hotkey_item::MBUTTON,
device, button, 0, shift, ctrl, alt, cmd);
newhk.set_mbutton(device, button, shift, ctrl, alt, cmd);
break;
case SDL_KEYUP:
oldhk =
&hotkey::get_hotkey(character, keycode,
(mod & KMOD_SHIFT)!= 0,
(mod & KMOD_CTRL) != 0, (mod & KMOD_ALT) != 0, (mod & KMOD_LMETA) != 0);newhk.set_key(character, keycode, (mod & KMOD_SHIFT) != 0,
(mod & KMOD_CTRL) != 0, (mod & KMOD_ALT) != 0, (mod & KMOD_LMETA) != 0);
if ( (newhk.get_id() == hotkey::HOTKEY_SCREENSHOT
|| newhk.get_id() == hotkey::HOTKEY_MAP_SCREENSHOT)
&& (mod & any_mod) == 0 ) {
gui2::show_transient_message(disp_.video(), _("Warning"),
_("Screenshot hotkeys should be combined with the Control, Alt or Meta modifiers to avoid problems."));
}
break;
}
if ((oldhk && (!(oldhk->null())))) {
if (oldhk->get_command() != id) {
utils::string_map symbols;
symbols["hotkey_sequence"] = oldhk->get_name();
symbols["old_hotkey_action"] = oldhk->get_description();
symbols["new_hotkey_action"] = newhk.get_description();
std::string text =
vgettext("\"$hotkey_sequence|\" is in use by \"$old_hotkey_action|\".\nDo you wish to reassign it to \"$new_hotkey_action|\"?",
symbols);
text += "\n\n";
const int res = gui2::show_message(disp_.video(),
_("Hotkey is already in use."), text,
gui2::tmessage::yes_no_buttons);
if (res == gui2::twindow::OK) {
oldhk->set_command(id);
set_hotkey_menu(true);
}
}
} else {
hotkey::add_hotkey(newhk);
set_hotkey_menu(true);
}
}
}
} // end namespace preferences

View file

@ -281,238 +281,6 @@ void repopulate_hotkeys_menu(std::vector<std::string>& menu_items,
} // end anonymous namespace
#ifdef _MSC_VER
#pragma warning (push)
#pragma warning (disable:4701)
#endif
void show_hotkeys_dialog(display & disp)
{
log_scope ("show_hotkeys_dialog");
const events::event_context dialog_events_context;
const int centerx = disp.w()/2;
const int centery = disp.h()/2;
const int width = 750;
const int height = disp.video().gety() < 600 ? 380 : 500;
const int xpos = centerx - width/2;
const int ypos = centery - height/2;
gui::button close_button (disp.video(), _("Close"));
std::vector<gui::button*> buttons;
buttons.push_back(&close_button);
gui::dialog_frame f(disp.video(),_("Hotkey Settings"),gui::dialog_frame::default_style,true,&buttons);
f.layout(xpos,ypos,width,height);
f.draw();
SDL_Rect clip_rect = create_rect(0, 0, disp.w (), disp.h ());
SDL_Rect text_size = font::draw_text(NULL, clip_rect, font::SIZE_LARGE,
font::NORMAL_COLOR,_("Press desired hotkey (Esc cancels)"),
0, 0);
gui::menu::basic_sorter sorter;
sorter.set_alpha_sort(0).set_alpha_sort(1);
std::vector<std::string> menu_items;
std::vector<std::string> item_commands;
repopulate_hotkeys_menu(menu_items, item_commands);
gui::menu menu_(disp.video(), menu_items, false, height - font::relative_size(10), -1, &sorter, &gui::menu::bluebg_style);
menu_.sort_by(0);
menu_.reset_selection();
menu_.set_width(font::relative_size(550));
menu_.set_location(xpos + font::relative_size(10), ypos + font::relative_size(10));
gui::button change_button(disp.video(), _("Add Hotkey"));
change_button.set_location(xpos + width - change_button.width () - font::relative_size(30), ypos + font::relative_size(30));
gui::button clear_button(disp.video(), _("Clear Hotkey"));
clear_button.set_location(xpos + width - clear_button.width () - font::relative_size(30), ypos + font::relative_size(60));
gui::button reset_button(disp.video(), _("Reset All"));
reset_button.set_location(xpos + width - reset_button.width() - font::relative_size(30), ypos + font::relative_size(90));
escape_handler esc_hand;
for(;;) {
if (close_button.pressed() || esc_hand.escape_pressed())
{
save_hotkeys();
break;
}
bool repopulate = false;
const int selected = menu_.selection();
const std::string id = item_commands[selected];
if (change_button.pressed () || menu_.double_clicked()) {
// Lets change this hotkey......
SDL_Rect dlgr = create_rect(centerx - text_size.w / 2 - 30
, centery - text_size.h / 2 - 16
, text_size.w + 60
, text_size.h + 32);
surface_restorer restorer(&disp.video(),dlgr);
gui::dialog_frame mini_frame(disp.video());
mini_frame.layout(centerx-text_size.w/2 - 20,
centery-text_size.h/2 - 6,
text_size.w+40,
text_size.h+12);
mini_frame.draw_background();
mini_frame.draw_border();
font::draw_text (&disp.video(), clip_rect, font::SIZE_LARGE,font::NORMAL_COLOR,
_("Press desired hotkey (Esc cancels)"),centerx-text_size.w/2,
centery-text_size.h/2);
disp.update_display();
SDL_Event event;
event.type = 0;
int character = 0, keycode = 0, mod = 0; // Just to avoid warning
int device = 0, button = 0, hat = 0, value = 0;
const int any_mod = KMOD_CTRL | KMOD_ALT | KMOD_LMETA;
while (event.type!=SDL_KEYDOWN
&& event.type!=SDL_JOYBUTTONDOWN && event.type!= SDL_JOYHATMOTION
&& (event.type!=SDL_MOUSEBUTTONDOWN || event.button.button < 8) )
SDL_PollEvent(&event);
do {
switch (event.type) {
case SDL_KEYDOWN:
keycode=event.key.keysym.sym;
character=event.key.keysym.unicode;
mod=event.key.keysym.mod;
break;
case SDL_JOYBUTTONDOWN:
device = event.jbutton.which;
button = event.jbutton.button;
break;
case SDL_JOYHATMOTION:
device = event.jhat.which;
hat = event.jhat.hat;
value = event.jhat.value;
break;
case SDL_MOUSEBUTTONDOWN:
device = event.button.which;
button = event.button.button;
break;
}
SDL_PollEvent(&event);
disp.flip();
disp.delay(10);
} while (event.type!=SDL_KEYUP
&& event.type!=SDL_JOYBUTTONUP && event.type!=SDL_JOYHATMOTION
&& event.type!=SDL_MOUSEBUTTONUP);
restorer.restore();
disp.update_display();
// only if not canceled.
if (!(keycode == SDLK_ESCAPE && (mod & any_mod) == 0)) {
hotkey::hotkey_item newhk(id);
hotkey::hotkey_item* oldhk = NULL;
Uint8 *keystate = SDL_GetKeyState(NULL);
bool alt = keystate[SDLK_RALT] || keystate[SDLK_LALT];
bool ctrl = keystate[SDLK_RCTRL] || keystate[SDLK_LCTRL];
bool shift = keystate[SDLK_RSHIFT] || keystate[SDLK_LSHIFT];
bool cmd = keystate[SDLK_RMETA] || keystate[SDLK_LMETA];
switch (event.type) {
case SDL_JOYHATMOTION:
oldhk = &hotkey::get_hotkey(hotkey::hotkey_item::JHAT, device, hat, value, shift, ctrl, alt, cmd);
newhk.set_jhat(device, hat, value, shift, ctrl, alt, cmd);
break;
case SDL_JOYBUTTONUP:
oldhk = &hotkey::get_hotkey(hotkey::hotkey_item::JBUTTON, device, button, 0, shift, ctrl, alt, cmd);
newhk.set_jbutton(device, button, shift, ctrl, alt, cmd);
break;
case SDL_MOUSEBUTTONUP:
oldhk = &hotkey::get_hotkey(hotkey::hotkey_item::MBUTTON, device, button, 0, shift, ctrl, alt, cmd);
newhk.set_mbutton(device, button, shift, ctrl, alt, cmd);
break;
case SDL_KEYUP:
oldhk = &hotkey::get_hotkey(character, keycode, (mod & KMOD_SHIFT) != 0,
(mod & KMOD_CTRL) != 0, (mod & KMOD_ALT) != 0, (mod & KMOD_LMETA) != 0);
newhk.set_key(character, keycode, (mod & KMOD_SHIFT) != 0,
(mod & KMOD_CTRL) != 0, (mod & KMOD_ALT) != 0, (mod & KMOD_LMETA) != 0);
if ( (newhk.get_id() == hotkey::HOTKEY_SCREENSHOT
|| newhk.get_id() == hotkey::HOTKEY_MAP_SCREENSHOT)
&& (mod & any_mod) == 0 ) {
gui2::show_transient_message(disp.video(), _("Warning"),
_("Screenshot hotkeys should be combined with the Control, Alt or Meta modifiers to avoid problems."));
}
break;
}
if ((oldhk && (!(oldhk->null())) )) {
if (oldhk->get_command() != id) {
utils::string_map symbols;
symbols["hotkey_sequence"] = oldhk->get_name();
symbols["old_hotkey_action"] = oldhk->get_description();
symbols["new_hotkey_action"] = newhk.get_description();
std::string text = vgettext("\"$hotkey_sequence|\" is in use by \"$old_hotkey_action|\".\nDo you wish to reassign it to \"$new_hotkey_action|\"?"
, symbols);
text += "\n\n";
const int res = gui2::show_message(disp.video(), _("Hotkey is already in use.")
, text, gui2::tmessage::yes_no_buttons);
if(res == gui2::twindow::OK) {
oldhk->set_command(id);
repopulate = true;
}
}
} else {
hotkey::add_hotkey(newhk);
repopulate = true;
}
}
}
if (clear_button.pressed()) {
// clear hotkey
hotkey::clear_hotkeys(id);
repopulate = true;
}
if (reset_button.pressed()) {
const int res = gui2::show_message(
disp.video(), _("Reset Hotkeys"),
_("This will reset all hotkeys to their default values. Do you wish to continue?"), gui2::tmessage::yes_no_buttons);
if(res != gui2::twindow::CANCEL) {
clear_hotkeys();
repopulate = true;
gui2::show_transient_message(disp.video(), _("Hotkeys Reset"), _("All hotkeys have been reset to their default values."));
}
}
if (repopulate) {
repopulate_hotkeys_menu(menu_items, item_commands);
menu_.set_items(menu_items,true,true);
menu_.move_selection_keeping_viewport(selected);
}
menu_.process();
events::pump();
events::raise_process_event();
events::raise_draw_event();
disp.update_display();
disp.delay(10);
}
}
#ifdef _MSC_VER
#pragma warning (pop)
#endif
bool compare_resolutions(const std::pair<int,int>& lhs, const std::pair<int,int>& rhs)
{

View file

@ -24,6 +24,10 @@ class display;
namespace preferences {
// FIXME: this box should be vertically centered on the screen, but is not
static const int height = 400;
static const int width = 465;
struct display_manager
{
display_manager(display* disp);
@ -77,7 +81,7 @@ namespace preferences {
// If prefs is non-null, save the hotkeys in that config
// instead of the default.
void show_hotkeys_dialog(display & disp);
void show_hotkeys_preferences_dialog(display & disp);
} // end namespace preferences
#endif