v0.8.0: fully implement key bindings window, just need saving now

This commit is contained in:
Evan Debenham 2019-12-18 00:32:53 -05:00
parent 93320fb676
commit 56ad068d23
5 changed files with 318 additions and 85 deletions

View File

@ -33,16 +33,33 @@ public class KeyBindings {
public static void addKeyBinding(int keyCode, GameAction action){
bindings.put(keyCode, action);
}
public static boolean isKeyBound(int keyCode ){
return bindings.containsKey( keyCode );
public static void clearKeyBindings(){
bindings.clear();
}
public static LinkedHashMap<Integer, GameAction> getAllBindings(){
return new LinkedHashMap<>(bindings);
}
public static void setAllBindings(LinkedHashMap<Integer, GameAction> newBindings){
bindings = new LinkedHashMap<>(newBindings);
}
public static boolean acceptUnbound = false;
public static boolean isKeyBound(int keyCode){
if (keyCode <= 0 || keyCode > 255){
return false;
}
return acceptUnbound || bindings.containsKey( keyCode );
}
public static GameAction getActionForKey(KeyEvent event ){
public static GameAction getActionForKey(KeyEvent event){
return bindings.get( event.code );
}
public static ArrayList<Integer> getKeysForAction(GameAction action ){
public static ArrayList<Integer> getKeysForAction(GameAction action){
ArrayList<Integer> result = new ArrayList<>();
for( int i : bindings.keySet()){
if (bindings.get(i) == action){
@ -53,12 +70,12 @@ public class KeyBindings {
}
public static String getKeyName( int keyCode ){
String result = Input.Keys.toString(keyCode);
if (result.equals("Plus")){
if (keyCode == Input.Keys.UNKNOWN){
return "None";
} else if (keyCode == Input.Keys.PLUS){
return "+";
} else {
return result;
return Input.Keys.toString(keyCode);
}
}

View File

@ -25,6 +25,8 @@ import com.badlogic.gdx.Input;
import com.watabou.input.GameAction;
import com.watabou.input.KeyBindings;
import java.util.LinkedHashMap;
public class SPDAction extends GameAction {
protected SPDAction( String name ){
@ -68,49 +70,56 @@ public class SPDAction extends GameAction {
public static final GameAction SW = new SPDAction("sw");
public static final GameAction NW = new SPDAction("nw");
public static void initDefaults() {
private static final LinkedHashMap<Integer, GameAction> defaultBindings = new LinkedHashMap<>();
static {
defaultBindings.put( Input.Keys.BACK, SPDAction.BACK );
defaultBindings.put( Input.Keys.MENU, SPDAction.MENU );
//default key bindings
KeyBindings.addKeyBinding( Input.Keys.BACK, SPDAction.BACK );
KeyBindings.addKeyBinding( Input.Keys.MENU, SPDAction.MENU );
defaultBindings.put( Input.Keys.H, SPDAction.HERO_INFO );
defaultBindings.put( Input.Keys.J, SPDAction.JOURNAL );
KeyBindings.addKeyBinding( Input.Keys.H, SPDAction.HERO_INFO );
KeyBindings.addKeyBinding( Input.Keys.J, SPDAction.JOURNAL );
defaultBindings.put( Input.Keys.SPACE, SPDAction.WAIT );
defaultBindings.put( Input.Keys.S, SPDAction.SEARCH );
KeyBindings.addKeyBinding( Input.Keys.SPACE, SPDAction.WAIT );
KeyBindings.addKeyBinding( Input.Keys.S, SPDAction.SEARCH );
defaultBindings.put( Input.Keys.I, SPDAction.INVENTORY );
defaultBindings.put( Input.Keys.Q, SPDAction.QUICKSLOT_1 );
defaultBindings.put( Input.Keys.W, SPDAction.QUICKSLOT_2 );
defaultBindings.put( Input.Keys.E, SPDAction.QUICKSLOT_3 );
defaultBindings.put( Input.Keys.R, SPDAction.QUICKSLOT_4 );
KeyBindings.addKeyBinding( Input.Keys.I, SPDAction.INVENTORY );
KeyBindings.addKeyBinding( Input.Keys.Q, SPDAction.QUICKSLOT_1 );
KeyBindings.addKeyBinding( Input.Keys.W, SPDAction.QUICKSLOT_2 );
KeyBindings.addKeyBinding( Input.Keys.E, SPDAction.QUICKSLOT_3 );
KeyBindings.addKeyBinding( Input.Keys.R, SPDAction.QUICKSLOT_4 );
defaultBindings.put( Input.Keys.A, SPDAction.TAG_ATTACK );
defaultBindings.put( Input.Keys.TAB, SPDAction.TAG_DANGER );
defaultBindings.put( Input.Keys.D, SPDAction.TAG_ACTION );
defaultBindings.put( Input.Keys.ENTER, SPDAction.TAG_LOOT );
defaultBindings.put( Input.Keys.T, SPDAction.TAG_RESUME );
KeyBindings.addKeyBinding( Input.Keys.A, SPDAction.TAG_ATTACK );
KeyBindings.addKeyBinding( Input.Keys.TAB, SPDAction.TAG_DANGER );
KeyBindings.addKeyBinding( Input.Keys.D, SPDAction.TAG_ACTION );
KeyBindings.addKeyBinding( Input.Keys.ENTER, SPDAction.TAG_LOOT );
KeyBindings.addKeyBinding( Input.Keys.T, SPDAction.TAG_RESUME );
defaultBindings.put( Input.Keys.PLUS, SPDAction.ZOOM_IN );
defaultBindings.put( Input.Keys.EQUALS, SPDAction.ZOOM_IN );
defaultBindings.put( Input.Keys.MINUS, SPDAction.ZOOM_OUT );
KeyBindings.addKeyBinding( Input.Keys.PLUS, SPDAction.ZOOM_IN );
KeyBindings.addKeyBinding( Input.Keys.EQUALS, SPDAction.ZOOM_IN );
KeyBindings.addKeyBinding( Input.Keys.MINUS, SPDAction.ZOOM_OUT );
defaultBindings.put( Input.Keys.UP, SPDAction.N );
defaultBindings.put( Input.Keys.RIGHT, SPDAction.E );
defaultBindings.put( Input.Keys.DOWN, SPDAction.S );
defaultBindings.put( Input.Keys.LEFT, SPDAction.W );
KeyBindings.addKeyBinding( Input.Keys.UP, SPDAction.N );
KeyBindings.addKeyBinding( Input.Keys.RIGHT, SPDAction.E );
KeyBindings.addKeyBinding( Input.Keys.DOWN, SPDAction.S );
KeyBindings.addKeyBinding( Input.Keys.LEFT, SPDAction.W );
defaultBindings.put( Input.Keys.NUMPAD_5, SPDAction.WAIT );
defaultBindings.put( Input.Keys.NUMPAD_8, SPDAction.N );
defaultBindings.put( Input.Keys.NUMPAD_9, SPDAction.NE );
defaultBindings.put( Input.Keys.NUMPAD_6, SPDAction.E );
defaultBindings.put( Input.Keys.NUMPAD_3, SPDAction.SE );
defaultBindings.put( Input.Keys.NUMPAD_2, SPDAction.S );
defaultBindings.put( Input.Keys.NUMPAD_1, SPDAction.SW );
defaultBindings.put( Input.Keys.NUMPAD_4, SPDAction.W );
defaultBindings.put( Input.Keys.NUMPAD_7, SPDAction.NW );
}
KeyBindings.addKeyBinding( Input.Keys.NUMPAD_5, SPDAction.WAIT );
KeyBindings.addKeyBinding( Input.Keys.NUMPAD_8, SPDAction.N );
KeyBindings.addKeyBinding( Input.Keys.NUMPAD_9, SPDAction.NE );
KeyBindings.addKeyBinding( Input.Keys.NUMPAD_6, SPDAction.E );
KeyBindings.addKeyBinding( Input.Keys.NUMPAD_3, SPDAction.SE );
KeyBindings.addKeyBinding( Input.Keys.NUMPAD_2, SPDAction.S );
KeyBindings.addKeyBinding( Input.Keys.NUMPAD_1, SPDAction.SW );
KeyBindings.addKeyBinding( Input.Keys.NUMPAD_4, SPDAction.W );
KeyBindings.addKeyBinding( Input.Keys.NUMPAD_7, SPDAction.NW );
public static LinkedHashMap<Integer, GameAction> getDefaults() {
return new LinkedHashMap(defaultBindings);
}
//TODO save functionality for changed keys
public static void initialize(){
KeyBindings.setAllBindings(getDefaults());
}
//file name? perhaps

View File

@ -132,7 +132,7 @@ public class ShatteredPixelDungeon extends Game {
super.create();
updateSystemUI();
SPDAction.initDefaults();
SPDAction.initialize();
Music.INSTANCE.enable( SPDSettings.music() );
Music.INSTANCE.volume( SPDSettings.musicVol()/10f );

View File

@ -21,57 +21,65 @@
package com.shatteredpixel.shatteredpixeldungeon.windows;
import com.shatteredpixel.shatteredpixeldungeon.SPDAction;
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.ui.RedButton;
import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock;
import com.shatteredpixel.shatteredpixeldungeon.ui.ScrollPane;
import com.shatteredpixel.shatteredpixeldungeon.ui.Window;
import com.watabou.input.GameAction;
import com.watabou.input.KeyBindings;
import com.watabou.input.KeyEvent;
import com.watabou.noosa.ColorBlock;
import com.watabou.noosa.ui.Component;
import java.util.ArrayList;
import java.util.LinkedHashMap;
public class WndKeyBindings extends Window {
private static final int WIDTH = 120;
private static final int TTL_HEIGHT = 16;
private static final int BTN_HEIGHT = 16;
private static final int COL1_CENTER = WIDTH/4;
private static final int COL2_CENTER = 5*WIDTH/8;
private static final int COL3_CENTER = 7*WIDTH/8;
private Component bindingsList;
private ArrayList<BindingItem> listItems = new ArrayList<>();
private LinkedHashMap<Integer, GameAction> changedBindings;
public WndKeyBindings() {
resize(WIDTH, Math.min(340, PixelScene.uiCamera.height-20));
changedBindings = KeyBindings.getAllBindings();
RenderedTextBlock ttlAction = PixelScene.renderTextBlock(Messages.get(this, "ttl_action"), 9);
ttlAction.setPos( COL1_CENTER - ttlAction.width()/2, (TTL_HEIGHT - ttlAction.height())/2);
ttlAction.setPos( COL1_CENTER - ttlAction.width()/2, (BTN_HEIGHT - ttlAction.height())/2);
add(ttlAction);
ColorBlock ttlSep1 = new ColorBlock(1, TTL_HEIGHT, 0xFF222222);
ColorBlock ttlSep1 = new ColorBlock(1, BTN_HEIGHT, 0xFF222222);
ttlSep1.x = WIDTH/2;
add(ttlSep1);
RenderedTextBlock ttlKey1 = PixelScene.renderTextBlock(Messages.get(this, "ttl_key1"), 9);
ttlKey1.setPos(COL2_CENTER - ttlKey1.width()/2, (TTL_HEIGHT - ttlKey1.height())/2);
ttlKey1.setPos(COL2_CENTER - ttlKey1.width()/2, (BTN_HEIGHT - ttlKey1.height())/2);
add(ttlKey1);
ColorBlock ttlSep2 = new ColorBlock(1, TTL_HEIGHT, 0xFF222222);
ColorBlock ttlSep2 = new ColorBlock(1, BTN_HEIGHT, 0xFF222222);
ttlSep2.x = 3*WIDTH/4;
add(ttlSep2);
RenderedTextBlock ttlKey2 = PixelScene.renderTextBlock(Messages.get(this, "ttl_key2"), 9);
ttlKey2.setPos(COL3_CENTER - ttlKey2.width()/2, (TTL_HEIGHT - ttlKey2.height())/2);
ttlKey2.setPos(COL3_CENTER - ttlKey2.width()/2, (BTN_HEIGHT - ttlKey2.height())/2);
add(ttlKey2);
ColorBlock ttlSep3 = new ColorBlock(WIDTH, 1, 0xFF222222);
ttlSep3.y = TTL_HEIGHT;
ttlSep3.y = BTN_HEIGHT;
add(ttlSep3);
bindingsList = new Component();
@ -79,7 +87,11 @@ public class WndKeyBindings extends Window {
ScrollPane scrollingList = new ScrollPane(bindingsList){
@Override
public void onClick(float x, float y) {
//TODO
for (BindingItem i : listItems) {
if (i.onClick( x, y )) {
break;
}
}
}
};
@ -87,33 +99,47 @@ public class WndKeyBindings extends Window {
int y = 0;
for (GameAction action : GameAction.allActions()){
//start at 3. No bindings for NONE, BACK, and MENU.
//start at 3. No bindings for NONE, BACK, and MENU atm
if (action.code() < 3) continue;
BindingListItem item = new BindingListItem(action);
item.setRect(0, y, WIDTH, 12);
BindingItem item = new BindingItem(action);
item.setRect(0, y, WIDTH, BindingItem.HEIGHT);
bindingsList.add(item);
listItems.add(item);
y += item.height();
}
bindingsList.setSize(WIDTH, y);
bindingsList.setSize(WIDTH, y+1);
resize(WIDTH, Math.min(BTN_HEIGHT *3 + 3 + BindingItem.HEIGHT*listItems.size(), PixelScene.uiCamera.height-20));
RedButton btnDefaults = new RedButton(Messages.get(this, "default"), 9){
@Override
protected void onClick() {
//TODO reset to default functionality
changedBindings = SPDAction.getDefaults();
for (BindingItem i : listItems){
int key1 = 0;
int key2 = 0;
for( int k : changedBindings.keySet()){
if (changedBindings.get(k) == i.gameAction){
if (key1 == 0) key1 = k;
else key2 = k;
}
}
i.updateBindings(key1, key2);
}
}
};
btnDefaults.setRect(0, height - TTL_HEIGHT*2 -1, WIDTH, TTL_HEIGHT);
btnDefaults.setRect(0, height - BTN_HEIGHT *2 -1, WIDTH, BTN_HEIGHT);
add(btnDefaults);
RedButton btnConfirm = new RedButton(Messages.get(this, "confirm"), 9){
@Override
protected void onClick() {
//TODO save changed bindings
KeyBindings.setAllBindings(changedBindings);
hide();
}
};
btnConfirm.setRect(0, height - TTL_HEIGHT, WIDTH/2 - 1, TTL_HEIGHT);
btnConfirm.setRect(0, height - BTN_HEIGHT, WIDTH/2, BTN_HEIGHT);
add(btnConfirm);
RedButton btnCancel = new RedButton(Messages.get(this, "cancel"), 9){
@ -122,10 +148,10 @@ public class WndKeyBindings extends Window {
hide(); //close and don't save
}
};
btnCancel.setRect(WIDTH/2, height - TTL_HEIGHT, WIDTH/2, TTL_HEIGHT);
btnCancel.setRect(WIDTH/2 + 1, height - BTN_HEIGHT, WIDTH/2 - 1, BTN_HEIGHT);
add(btnCancel);
scrollingList.setRect(0, TTL_HEIGHT+1, WIDTH, btnDefaults.top()-TTL_HEIGHT-1);
scrollingList.setRect(0, BTN_HEIGHT +1, WIDTH, btnDefaults.top()- BTN_HEIGHT - 1);
}
@ -134,16 +160,22 @@ public class WndKeyBindings extends Window {
//do nothing, avoids accidental back presses which would lose progress.
}
private class BindingListItem extends Component {
private class BindingItem extends Component {
private static final int HEIGHT = 12;
private static final int CHANGED = TITLE_COLOR;
private static final int DEFAULT = 0xFFFFFF;
private static final int UNBOUND = 0x888888;
private static final int UNBOUND_CHANGED = 0x888822;
private GameAction gameAction;
private int key1;
private int key2;
private int origKey1;
private int origKey2;
private RenderedTextBlock actionName;
private RenderedTextBlock key1Name;
private RenderedTextBlock key2Name;
@ -152,7 +184,7 @@ public class WndKeyBindings extends Window {
private ColorBlock sep2;
private ColorBlock sep3;
public BindingListItem( GameAction action ){
public BindingItem( GameAction action ){
gameAction = action;
actionName = PixelScene.renderTextBlock(Messages.get(WndKeyBindings.class, action.name()), 6 );
@ -160,25 +192,15 @@ public class WndKeyBindings extends Window {
add(actionName);
ArrayList<Integer> keys = KeyBindings.getKeysForAction(action);
origKey1 = key1 = keys.isEmpty() ? 0 : keys.remove(0);
origKey2 = key2 = keys.isEmpty() ? 0 : keys.remove(0);
if (keys.size() >= 1){
key1Name = PixelScene.renderTextBlock( KeyBindings.getKeyName(keys.get(0)), 6 );
key1 = keys.get(0);
} else {
key1Name = PixelScene.renderTextBlock( Messages.get(WndKeyBindings.class, "unbound"), 6 );
key1Name.hardlight(UNBOUND);
key1 = 0;
}
key1Name = PixelScene.renderTextBlock( KeyBindings.getKeyName(key1), 6 );
if (key1 == 0) key1Name.hardlight(UNBOUND);
add(key1Name);
if (keys.size() >= 2){
key2Name = PixelScene.renderTextBlock( KeyBindings.getKeyName(keys.get(1)), 6 );
key2 = keys.get(1);
} else {
key2Name = PixelScene.renderTextBlock( Messages.get(WndKeyBindings.class, "unbound"), 6 );
key2Name.hardlight(UNBOUND);
key2 = 0;
}
key2Name = PixelScene.renderTextBlock( KeyBindings.getKeyName(key2), 6 );
if (key2 == 0) key2Name.hardlight(UNBOUND);
add(key2Name);
sep1 = new ColorBlock(1, 1, 0xFF222222);
@ -191,6 +213,26 @@ public class WndKeyBindings extends Window {
add(sep3);
}
public void updateBindings(int first, int second){
if (first == 0){
key1 = second;
key2 = first;
} else {
key1 = first;
key2 = second;
}
key1Name.text(KeyBindings.getKeyName(key1));
if (key1 != origKey1) key1Name.hardlight( key1 == 0 ? UNBOUND_CHANGED : CHANGED);
else key1Name.hardlight( key1 == 0 ? UNBOUND : DEFAULT);
key2Name.text(KeyBindings.getKeyName(key2));
if (key2 != origKey2) key2Name.hardlight( key2 == 0 ? UNBOUND_CHANGED : CHANGED);
else key2Name.hardlight( key2 == 0 ? UNBOUND : DEFAULT);
layout();
}
@Override
protected void layout() {
super.layout();
@ -212,8 +254,165 @@ public class WndKeyBindings extends Window {
sep3.y = y;
}
//onclick opens a new window depending on whether you pressed key 1 or 2.
//give you the option to change that binding by pressing a key, then lets your confirm/cancel/unbind
//warns you if a new binding conflicts with an old one
private boolean onClick( float x, float y ){
if (inside(x, y)){
//assigning second key
if (x >= this.x + 3*width()/4 && key1 != 0) {
ShatteredPixelDungeon.scene().addToFront( new WndChangeBinding(gameAction, this, false, key2, key1));
//assigning first key
} else if (x >= this.x + width()/2){
ShatteredPixelDungeon.scene().addToFront( new WndChangeBinding(gameAction, this, true, key1, key2));
}
return true;
} else {
return false;
}
}
}
private class WndChangeBinding extends Window {
private int curKeyCode;
private int otherBoundKey;
private int changedKeyCode = -1;
private BindingItem changedAction;
private RenderedTextBlock changedKey;
private RenderedTextBlock warnErr;
private RedButton btnConfirm;
public WndChangeBinding(GameAction action, BindingItem listItem, boolean firstKey, int curKeyCode, int otherBoundKey ){
this.curKeyCode = curKeyCode;
this.otherBoundKey = otherBoundKey;
RenderedTextBlock desc = PixelScene.renderTextBlock( Messages.get(this, firstKey ? "desc_first" : "desc_second",
Messages.get(WndKeyBindings.class, action.name()),
KeyBindings.getKeyName(curKeyCode)), 6 );
desc.maxWidth(WIDTH);
desc.setRect(0, 0, WIDTH, desc.height());
add(desc);
RenderedTextBlock curBind;
curBind = PixelScene.renderTextBlock(Messages.get(this, "desc_current", KeyBindings.getKeyName(curKeyCode)), 6);
curBind.maxWidth(WIDTH);
curBind.setRect((WIDTH - curBind.width())/2, desc.bottom()+6, WIDTH, curBind.height());
add(curBind);
changedKey = PixelScene.renderTextBlock(6);
changedKey.maxWidth(WIDTH);
changedKey.setRect(0, curBind.bottom()+2, WIDTH, changedKey.height());
add(changedKey);
warnErr = PixelScene.renderTextBlock(6);
warnErr.maxWidth(WIDTH);
warnErr.setRect(0, changedKey.bottom() + 10, WIDTH, warnErr.height());
add(warnErr);
RedButton btnUnbind = new RedButton(Messages.get(this, "unbind"), 9){
@Override
protected void onClick() {
onSignal(new KeyEvent(0, true));
}
};
btnUnbind.setRect(0, warnErr.bottom() + 6, WIDTH, BTN_HEIGHT);
add(btnUnbind);
btnConfirm = new RedButton(Messages.get(this, "confirm"), 9){
@Override
protected void onClick() {
if (changedKeyCode != -1){
changedBindings.remove(changedKeyCode);
changedBindings.remove(listItem.key1);
changedBindings.remove(listItem.key2);
if (firstKey){
if (changedKeyCode != 0) changedBindings.put(changedKeyCode, action);
if (listItem.key2 != 0) changedBindings.put(listItem.key2, action);
listItem.updateBindings(changedKeyCode, listItem.key2);
} else {
if (listItem.key1 != 0) changedBindings.put(listItem.key1, action);
if (changedKeyCode != 0) changedBindings.put(changedKeyCode, action);
listItem.updateBindings(listItem.key1, changedKeyCode);
}
if (changedAction != null){
if (changedAction.key1 != changedKeyCode){
changedAction.updateBindings(changedAction.key1, 0);
} else if (changedAction.key2 != changedKeyCode){
changedAction.updateBindings(changedAction.key2, 0);
} else {
changedAction.updateBindings(0, 0);
}
}
}
hide();
}
};
btnConfirm.setRect(0, btnUnbind.bottom()+1, WIDTH/2, BTN_HEIGHT);
btnConfirm.enable(false);
add(btnConfirm);
RedButton btnCancel = new RedButton(Messages.get(this, "cancel"), 9){
@Override
protected void onClick() {
hide();
}
};
btnCancel.setRect(btnConfirm.right()+1, btnUnbind.bottom()+1, WIDTH/2-1, BTN_HEIGHT);
add(btnCancel);
resize(WIDTH, (int)btnCancel.bottom());
KeyBindings.acceptUnbound = true;
}
@Override
public boolean onSignal(KeyEvent event) {
if (event.pressed){
changedKey.text(Messages.get(this, "changed_bind", KeyBindings.getKeyName(event.code)));
changedKey.setPos((WIDTH - changedKey.width())/2, changedKey.top());
changedKeyCode = event.code;
changedAction = null;
if (event.code != 0 && (event.code == curKeyCode || event.code == otherBoundKey)){
warnErr.text(Messages.get(this, "error"));
warnErr.hardlight(CharSprite.NEGATIVE);
btnConfirm.enable(false);
} else if (event.code != 0 && changedBindings.get(changedKeyCode) != null){
for (BindingItem i : listItems) {
if (i.gameAction == changedBindings.get(changedKeyCode)) {
changedAction = i;
break;
}
}
warnErr.text(Messages.get(this, "warning", Messages.get(WndKeyBindings.class, changedBindings.get(changedKeyCode).name() )));
warnErr.hardlight(CharSprite.WARNING);
btnConfirm.enable(true);
} else {
warnErr.text(" ");
btnConfirm.enable(true);
}
}
return true;
}
@Override
public void destroy() {
super.destroy();
KeyBindings.acceptUnbound = false;
}
}
}

View File

@ -65,7 +65,6 @@ windows.wndkeybindings.ttl_key2=Key 2
windows.wndkeybindings.default=Default Bindings
windows.wndkeybindings.confirm=Confirm
windows.wndkeybindings.cancel=Cancel
windows.wndkeybindings.unbound=Unbound
windows.wndkeybindings.none=None
windows.wndkeybindings.back=Back
windows.wndkeybindings.menu=Menu
@ -93,6 +92,15 @@ windows.wndkeybindings.ne=Go NE
windows.wndkeybindings.se=Go SE
windows.wndkeybindings.sw=Go SW
windows.wndkeybindings.nw=Go NW
windows.wndkeybindings$wndchangebinding.desc_first=Press a key to change the first key binding for: _%s_.
windows.wndkeybindings$wndchangebinding.desc_second=Press a key to change the second key binding for: _%s_.
windows.wndkeybindings$wndchangebinding.desc_current=Current binding: _%s_
windows.wndkeybindings$wndchangebinding.changed_bind=New binding: _%s_
windows.wndkeybindings$wndchangebinding.warning=This key will be unbound from _%s_.
windows.wndkeybindings$wndchangebinding.error=This key is already bound to this action.
windows.wndkeybindings$wndchangebinding.unbind=Unbind Key
windows.wndkeybindings$wndchangebinding.confirm=Confirm
windows.wndkeybindings$wndchangebinding.cancel=Cancel
windows.wndlangs.completed=This language has been fully translated and reviewed.
windows.wndlangs.unreviewed=This language has not yet been reviewed.\n\nIt may contain errors, but all text has been translated.