瀏覽代碼

Merge pull request #5937 from Laserlicht/shortcut

Add key binding menu
Ivan Savenko 2 月之前
父節點
當前提交
d8e6931284

+ 270 - 0
Mods/vcmi/Content/config/english.json

@@ -262,6 +262,276 @@
 	"vcmi.settingsMainWindow.adventureTab.hover" : "Adventure Map",
 	"vcmi.settingsMainWindow.adventureTab.help"  : "Switches to Adventure Map Options tab (adventure map is the section of the game where players can control the movements of their heroes).",
 
+	"vcmi.keyBindings.button.hover" : "Key bindings",
+	"vcmi.keyBindings.button.help"  : "{Key bindings}\n\nShow menu for viewing and adjusting key bindings",
+	"vcmi.keyBindings.editButton.help" : "Edit key binding",
+	"vcmi.keyBindings.input" : "Change key binding for {%s}.\n\nPlease enter a key or key kombination. Click outside to abort.",
+	"vcmi.keyBindings.inputSet" : "Key binding for {%s} will be changed to {%s}.\n\nAppend to existing bindings? Otherwise it will be replaced.",
+	"vcmi.keyBindings.popup" : "For {%s} the following keys are configured:\n\n",
+	"vcmi.keyBindings.reset" : "Reset",
+	"vcmi.keyBindings.reset.help" : "{Reset}\n\nResets key bindings to default",
+	"vcmi.keyBindings.resetConfirm" : "Do you want to reset all key bindings to default?",
+	"vcmi.keyBindings.group.keyboard" : "Keyboard",
+	"vcmi.keyBindings.group.joystickAxes" : "Joystick Axes",
+	"vcmi.keyBindings.group.joystickButtons" : "Joystick Buttons",
+	"vcmi.keyBindings.keyBinding.adventureCastSpell": "Adventure cast spell",
+	"vcmi.keyBindings.keyBinding.adventureDigGrail": "Adventure dig grail",
+	"vcmi.keyBindings.keyBinding.adventureEndTurn": "Adventure end turn",
+	"vcmi.keyBindings.keyBinding.adventureExitWorldView": "Adventure exit world view",
+	"vcmi.keyBindings.keyBinding.adventureFirstHero": "Adventure first hero",
+	"vcmi.keyBindings.keyBinding.adventureFirstTown": "Adventure first town",
+	"vcmi.keyBindings.keyBinding.adventureGameOptions": "Adventure game options",
+	"vcmi.keyBindings.keyBinding.adventureKingdomOverview": "Adventure kingdom overview",
+	"vcmi.keyBindings.keyBinding.adventureLoadGame": "Adventure load game",
+	"vcmi.keyBindings.keyBinding.adventureMainMenu": "Adventure main menu",
+	"vcmi.keyBindings.keyBinding.adventureMarketplace": "Adventure marketplace",
+	"vcmi.keyBindings.keyBinding.adventureMoveHero": "Adventure move hero",
+	"vcmi.keyBindings.keyBinding.adventureMoveHeroEE": "Adventure move hero EE",
+	"vcmi.keyBindings.keyBinding.adventureMoveHeroNE": "Adventure move hero NE",
+	"vcmi.keyBindings.keyBinding.adventureMoveHeroNN": "Adventure move hero NN",
+	"vcmi.keyBindings.keyBinding.adventureMoveHeroNW": "Adventure move hero NW",
+	"vcmi.keyBindings.keyBinding.adventureMoveHeroSE": "Adventure move hero SE",
+	"vcmi.keyBindings.keyBinding.adventureMoveHeroSS": "Adventure move hero SS",
+	"vcmi.keyBindings.keyBinding.adventureMoveHeroSW": "Adventure move hero SW",
+	"vcmi.keyBindings.keyBinding.adventureMoveHeroWW": "Adventure move hero WW",
+	"vcmi.keyBindings.keyBinding.adventureNewGame": "Adventure new game",
+	"vcmi.keyBindings.keyBinding.adventureNextHero": "Adventure next hero",
+	"vcmi.keyBindings.keyBinding.adventureNextObject": "Adventure next object",
+	"vcmi.keyBindings.keyBinding.adventureNextTown": "Adventure next town",
+	"vcmi.keyBindings.keyBinding.adventureQuestLog": "Adventure quest log",
+	"vcmi.keyBindings.keyBinding.adventureQuitGame": "Adventure quit game",
+	"vcmi.keyBindings.keyBinding.adventureReplayTurn": "Adventure replay turn",
+	"vcmi.keyBindings.keyBinding.adventureRestartGame": "Adventure restart game",
+	"vcmi.keyBindings.keyBinding.adventureSaveGame": "Adventure save game",
+	"vcmi.keyBindings.keyBinding.adventureSetHeroAsleep": "Adventure set hero asleep",
+	"vcmi.keyBindings.keyBinding.adventureSetHeroAwake": "Adventure set hero awake",
+	"vcmi.keyBindings.keyBinding.adventureThievesGuild": "Adventure thieves guild",
+	"vcmi.keyBindings.keyBinding.adventureToggleGrid": "Adventure toggle grid",
+	"vcmi.keyBindings.keyBinding.adventureToggleVisitable": "Adventure toggle visitable",
+	"vcmi.keyBindings.keyBinding.adventureToggleBlocked": "Adventure toggle blocked",
+	"vcmi.keyBindings.keyBinding.adventureToggleMapLevel": "Adventure toggle map level",
+	"vcmi.keyBindings.keyBinding.adventureToggleSleep": "Adventure toggle sleep",
+	"vcmi.keyBindings.keyBinding.adventureTrackHero": "Adventure track hero",
+	"vcmi.keyBindings.keyBinding.adventureViewPuzzle": "Adventure view puzzle",
+	"vcmi.keyBindings.keyBinding.adventureViewScenario": "Adventure view scenario",
+	"vcmi.keyBindings.keyBinding.adventureViewSelected": "Adventure view selected",
+	"vcmi.keyBindings.keyBinding.adventureViewWorld": "Adventure view world",
+	"vcmi.keyBindings.keyBinding.adventureViewWorld1": "Adventure view world1",
+	"vcmi.keyBindings.keyBinding.adventureViewWorld2": "Adventure view world2",
+	"vcmi.keyBindings.keyBinding.adventureViewWorld4": "Adventure view world4",
+	"vcmi.keyBindings.keyBinding.adventureVisitObject": "Adventure visit object",
+	"vcmi.keyBindings.keyBinding.adventureZoomIn": "Adventure zoom in",
+	"vcmi.keyBindings.keyBinding.adventureZoomOut": "Adventure zoom out",
+	"vcmi.keyBindings.keyBinding.adventureZoomReset": "Adventure zoom reset",
+	"vcmi.keyBindings.keyBinding.adventureSearch": "Adventure search",
+	"vcmi.keyBindings.keyBinding.adventureSearchContinue": "Adventure search continue",
+	"vcmi.keyBindings.keyBinding.battleAutocombat": "Battle autocombat",
+	"vcmi.keyBindings.keyBinding.battleAutocombatEnd": "Battle autocombat end",
+	"vcmi.keyBindings.keyBinding.battleCastSpell": "Battle cast spell",
+	"vcmi.keyBindings.keyBinding.battleConsoleDown": "Battle console down",
+	"vcmi.keyBindings.keyBinding.battleConsoleUp": "Battle console up",
+	"vcmi.keyBindings.keyBinding.battleDefend": "Battle defend",
+	"vcmi.keyBindings.keyBinding.battleOpenActiveUnit": "Battle open active unit",
+	"vcmi.keyBindings.keyBinding.battleOpenHoveredUnit": "Battle open hovered unit",
+	"vcmi.keyBindings.keyBinding.battleRetreat": "Battle retreat",
+	"vcmi.keyBindings.keyBinding.battleToggleQuickSpell": "Battle toggle quick spell",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut0": "Battle spell shortcut 0",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut1": "Battle spell shortcut 1",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut2": "Battle spell shortcut 2",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut3": "Battle spell shortcut 3",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut4": "Battle spell shortcut 4",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut5": "Battle spell shortcut 5",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut6": "Battle spell shortcut 6",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut7": "Battle spell shortcut 7",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut8": "Battle spell shortcut 8",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut9": "Battle spell shortcut 9",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut10": "Battle spell shortcut 10",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut11": "Battle spell shortcut 11",
+	"vcmi.keyBindings.keyBinding.battleSurrender": "Battle surrender",
+	"vcmi.keyBindings.keyBinding.battleTacticsEnd": "Battle tactics end",
+	"vcmi.keyBindings.keyBinding.battleTacticsNext": "Battle tactics next",
+	"vcmi.keyBindings.keyBinding.battleToggleHeroesStats": "Battle toggle heroes stats",
+	"vcmi.keyBindings.keyBinding.battleToggleQueue": "Battle toggle queue",
+	"vcmi.keyBindings.keyBinding.battleUseCreatureSpell": "Battle use creature spell",
+	"vcmi.keyBindings.keyBinding.battleWait": "Battle wait",
+	"vcmi.keyBindings.keyBinding.exchangeArmySwap": "Exchange army swap",
+	"vcmi.keyBindings.keyBinding.exchangeArmyToLeft": "Exchange army to left",
+	"vcmi.keyBindings.keyBinding.exchangeArmyToRight": "Exchange army to right",
+	"vcmi.keyBindings.keyBinding.exchangeArtifactsSwap": "Exchange artifacts swap",
+	"vcmi.keyBindings.keyBinding.exchangeArtifactsToLeft": "Exchange artifacts to left",
+	"vcmi.keyBindings.keyBinding.exchangeArtifactsToRight": "Exchange artifacts to right",
+	"vcmi.keyBindings.keyBinding.exchangeBackpackLeft": "Exchange backpack left",
+	"vcmi.keyBindings.keyBinding.exchangeBackpackRight": "Exchange backpack right",
+	"vcmi.keyBindings.keyBinding.exchangeBackpackSwap": "Exchange backpack swap",
+	"vcmi.keyBindings.keyBinding.exchangeBackpackToLeft": "Exchange backpack to left",
+	"vcmi.keyBindings.keyBinding.exchangeBackpackToRight": "Exchange backpack to right",
+	"vcmi.keyBindings.keyBinding.exchangeEquippedSwap": "Exchange equipped swap",
+	"vcmi.keyBindings.keyBinding.exchangeEquippedToLeft": "Exchange equipped to left",
+	"vcmi.keyBindings.keyBinding.exchangeEquippedToRight": "Exchange equipped to right",
+	"vcmi.keyBindings.keyBinding.gameActivateConsole": "Game activate console",
+	"vcmi.keyBindings.keyBinding.globalAccept": "Global accept",
+	"vcmi.keyBindings.keyBinding.globalBackspace": "Global backspace",
+	"vcmi.keyBindings.keyBinding.globalCancel": "Global cancel",
+	"vcmi.keyBindings.keyBinding.globalFullscreen": "Global fullscreen",
+	"vcmi.keyBindings.keyBinding.globalMoveFocus": "Global move focus",
+	"vcmi.keyBindings.keyBinding.globalOptions": "Global options",
+	"vcmi.keyBindings.keyBinding.globalReturn": "Global return",
+	"vcmi.keyBindings.keyBinding.heroArmySplit": "Hero army split",
+	"vcmi.keyBindings.keyBinding.heroBackpack": "Hero backpack",
+	"vcmi.keyBindings.keyBinding.heroCommander": "Hero commander",
+	"vcmi.keyBindings.keyBinding.heroCostumeLoad0": "Hero costume load 0",
+	"vcmi.keyBindings.keyBinding.heroCostumeLoad1": "Hero costume load 1",
+	"vcmi.keyBindings.keyBinding.heroCostumeLoad2": "Hero costume load 2",
+	"vcmi.keyBindings.keyBinding.heroCostumeLoad3": "Hero costume load 3",
+	"vcmi.keyBindings.keyBinding.heroCostumeLoad4": "Hero costume load 4",
+	"vcmi.keyBindings.keyBinding.heroCostumeLoad5": "Hero costume load 5",
+	"vcmi.keyBindings.keyBinding.heroCostumeLoad6": "Hero costume load 6",
+	"vcmi.keyBindings.keyBinding.heroCostumeLoad7": "Hero costume load 7",
+	"vcmi.keyBindings.keyBinding.heroCostumeLoad8": "Hero costume load 8",
+	"vcmi.keyBindings.keyBinding.heroCostumeLoad9": "Hero costume load 9",
+	"vcmi.keyBindings.keyBinding.heroCostumeSave0": "Hero costume save 0",
+	"vcmi.keyBindings.keyBinding.heroCostumeSave1": "Hero costume save 1",
+	"vcmi.keyBindings.keyBinding.heroCostumeSave2": "Hero costume save 2",
+	"vcmi.keyBindings.keyBinding.heroCostumeSave3": "Hero costume save 3",
+	"vcmi.keyBindings.keyBinding.heroCostumeSave4": "Hero costume save 4",
+	"vcmi.keyBindings.keyBinding.heroCostumeSave5": "Hero costume save 5",
+	"vcmi.keyBindings.keyBinding.heroCostumeSave6": "Hero costume save 6",
+	"vcmi.keyBindings.keyBinding.heroCostumeSave7": "Hero costume save 7",
+	"vcmi.keyBindings.keyBinding.heroCostumeSave8": "Hero costume save 8",
+	"vcmi.keyBindings.keyBinding.heroCostumeSave9": "Hero costume save 9",
+	"vcmi.keyBindings.keyBinding.heroDismiss": "Hero dismiss",
+	"vcmi.keyBindings.keyBinding.heroLooseFormation": "Hero loose formation",
+	"vcmi.keyBindings.keyBinding.heroTightFormation": "Hero tight formation",
+	"vcmi.keyBindings.keyBinding.heroToggleTactics": "Hero toggle tactics",
+	"vcmi.keyBindings.keyBinding.highScoresCampaigns": "High scores campaigns",
+	"vcmi.keyBindings.keyBinding.highScoresReset": "High scores reset",
+	"vcmi.keyBindings.keyBinding.highScoresStatistics": "High scores statistics",
+	"vcmi.keyBindings.keyBinding.highScoresScenarios": "High scores scenarios",
+	"vcmi.keyBindings.keyBinding.kingdomHeroesTab": "Kingdom heroes tab",
+	"vcmi.keyBindings.keyBinding.kingdomTownsTab": "Kingdom towns tab",
+	"vcmi.keyBindings.keyBinding.lobbyAdditionalOptions": "Lobby additional options",
+	"vcmi.keyBindings.keyBinding.lobbyBeginCampaign": "Lobby begin campaign",
+	"vcmi.keyBindings.keyBinding.lobbyBeginStandardGame": "Lobby begin standard game",
+	"vcmi.keyBindings.keyBinding.lobbyExtraOptions": "Lobby extra options",
+	"vcmi.keyBindings.keyBinding.lobbyFlipCoin": "Lobby flip coin",
+	"vcmi.keyBindings.keyBinding.lobbyInvitePlayers": "Lobby invite players",
+	"vcmi.keyBindings.keyBinding.lobbyLoadGame": "Lobby load game",
+	"vcmi.keyBindings.keyBinding.lobbyRandomMap": "Lobby random map",
+	"vcmi.keyBindings.keyBinding.lobbyRandomTown": "Lobby random town",
+	"vcmi.keyBindings.keyBinding.lobbyRandomTownVs": "Lobby random town vs",
+	"vcmi.keyBindings.keyBinding.lobbyHandicap": "Lobby handicap",
+	"vcmi.keyBindings.keyBinding.lobbyReplayVideo": "Lobby replay video",
+	"vcmi.keyBindings.keyBinding.lobbySaveGame": "Lobby save game",
+	"vcmi.keyBindings.keyBinding.lobbySelectScenario": "Lobby select scenario",
+	"vcmi.keyBindings.keyBinding.lobbyToggleChat": "Lobby toggle chat",
+	"vcmi.keyBindings.keyBinding.lobbyTurnOptions": "Lobby turn options",
+	"vcmi.keyBindings.keyBinding.mainMenuBack": "Main menu back",
+	"vcmi.keyBindings.keyBinding.mainMenuCampaign": "Main menu campaign",
+	"vcmi.keyBindings.keyBinding.mainMenuCampaignAb": "Main menu campaign ab",
+	"vcmi.keyBindings.keyBinding.mainMenuCampaignCustom": "Main menu campaign custom",
+	"vcmi.keyBindings.keyBinding.mainMenuCampaignRoe": "Main menu campaign RoE",
+	"vcmi.keyBindings.keyBinding.mainMenuCampaignSod": "Main menu campaign SoD",
+	"vcmi.keyBindings.keyBinding.mainMenuCampaignChr": "Main menu campaign Chronicles",
+	"vcmi.keyBindings.keyBinding.mainMenuCampaignHota": "Main menu campaign HotA",
+	"vcmi.keyBindings.keyBinding.mainMenuCampaignWog": "Main menu campaign WoG",
+	"vcmi.keyBindings.keyBinding.mainMenuCampaignVCMI": "Main menu campaign VCMI",
+	"vcmi.keyBindings.keyBinding.mainMenuCredits": "Main menu credits",
+	"vcmi.keyBindings.keyBinding.mainMenuHighScores": "Main menu high scores",
+	"vcmi.keyBindings.keyBinding.mainMenuHostGame": "Main menu host game",
+	"vcmi.keyBindings.keyBinding.mainMenuHotseat": "Main menu hotseat",
+	"vcmi.keyBindings.keyBinding.mainMenuJoinGame": "Main menu join game",
+	"vcmi.keyBindings.keyBinding.mainMenuLoadGame": "Main menu load game",
+	"vcmi.keyBindings.keyBinding.mainMenuLobby": "Main menu lobby",
+	"vcmi.keyBindings.keyBinding.mainMenuMultiplayer": "Main menu multiplayer",
+	"vcmi.keyBindings.keyBinding.mainMenuNewGame": "Main menu new game",
+	"vcmi.keyBindings.keyBinding.mainMenuQuit": "Main menu quit",
+	"vcmi.keyBindings.keyBinding.mainMenuSingleplayer": "Main menu singleplayer",
+	"vcmi.keyBindings.keyBinding.mainMenuTutorial": "Main menu tutorial",
+	"vcmi.keyBindings.keyBinding.mapsSizeAll": "Maps size all",
+	"vcmi.keyBindings.keyBinding.mapsSizeL": "Maps size L",
+	"vcmi.keyBindings.keyBinding.mapsSizeM": "Maps size M",
+	"vcmi.keyBindings.keyBinding.mapsSizeS": "Maps size S",
+	"vcmi.keyBindings.keyBinding.mapsSizeXl": "Maps size XL",
+	"vcmi.keyBindings.keyBinding.mapsSortChangedate": "Maps sort changedate",
+	"vcmi.keyBindings.keyBinding.mapsSortDefeat": "Maps sort defeat",
+	"vcmi.keyBindings.keyBinding.mapsSortFormat": "Maps sort format",
+	"vcmi.keyBindings.keyBinding.mapsSortMaps": "Maps sort maps",
+	"vcmi.keyBindings.keyBinding.mapsSortName": "Maps sort name",
+	"vcmi.keyBindings.keyBinding.mapsSortPlayers": "Maps sort players",
+	"vcmi.keyBindings.keyBinding.mapsSortSize": "Maps sort size",
+	"vcmi.keyBindings.keyBinding.mapsSortVictory": "Maps sort victory",
+	"vcmi.keyBindings.keyBinding.marketArtifactExperience": "Market artifact experience",
+	"vcmi.keyBindings.keyBinding.marketArtifactResource": "Market artifact resource",
+	"vcmi.keyBindings.keyBinding.marketCreatureExperience": "Market creature experience",
+	"vcmi.keyBindings.keyBinding.marketCreatureResource": "Market creature resource",
+	"vcmi.keyBindings.keyBinding.marketDeal": "Market deal",
+	"vcmi.keyBindings.keyBinding.marketMaxAmount": "Market max amount",
+	"vcmi.keyBindings.keyBinding.marketResourceArtifact": "Market resource artifact",
+	"vcmi.keyBindings.keyBinding.marketResourcePlayer": "Market resource player",
+	"vcmi.keyBindings.keyBinding.marketResourceResource": "Market resource resource",
+	"vcmi.keyBindings.keyBinding.marketSacrificeAll": "Market sacrifice all",
+	"vcmi.keyBindings.keyBinding.marketSacrificeBackpack": "Market sacrifice backpack",
+	"vcmi.keyBindings.keyBinding.moveDown": "Move down",
+	"vcmi.keyBindings.keyBinding.moveFirst": "Move first",
+	"vcmi.keyBindings.keyBinding.moveLast": "Move last",
+	"vcmi.keyBindings.keyBinding.moveLeft": "Move left",
+	"vcmi.keyBindings.keyBinding.movePageDown": "Move page down",
+	"vcmi.keyBindings.keyBinding.movePageUp": "Move page up",
+	"vcmi.keyBindings.keyBinding.moveRight": "Move right",
+	"vcmi.keyBindings.keyBinding.moveUp": "Move up",
+	"vcmi.keyBindings.keyBinding.recruitmentMax": "Recruitment max",
+	"vcmi.keyBindings.keyBinding.recruitmentMin": "Recruitment min",
+	"vcmi.keyBindings.keyBinding.recruitmentSwitchLevel": "Recruitment switch level",
+	"vcmi.keyBindings.keyBinding.recruitmentUpgrade": "Recruitment upgrade",
+	"vcmi.keyBindings.keyBinding.recruitmentUpgradeAll": "Recruitment upgrade all",
+	"vcmi.keyBindings.keyBinding.selectIndex1": "Select index 1",
+	"vcmi.keyBindings.keyBinding.selectIndex2": "Select index 2",
+	"vcmi.keyBindings.keyBinding.selectIndex3": "Select index 3",
+	"vcmi.keyBindings.keyBinding.selectIndex4": "Select index 4",
+	"vcmi.keyBindings.keyBinding.selectIndex5": "Select index 5",
+	"vcmi.keyBindings.keyBinding.selectIndex6": "Select index 6",
+	"vcmi.keyBindings.keyBinding.selectIndex7": "Select index 7",
+	"vcmi.keyBindings.keyBinding.selectIndex8": "Select index 8",
+	"vcmi.keyBindings.keyBinding.settingsLoadGame": "Settings load game",
+	"vcmi.keyBindings.keyBinding.settingsNewGame": "Settings new game",
+	"vcmi.keyBindings.keyBinding.settingsQuitGame": "Settings quit game",
+	"vcmi.keyBindings.keyBinding.settingsRestartGame": "Settings restart game",
+	"vcmi.keyBindings.keyBinding.settingsSaveGame": "Settings save game",
+	"vcmi.keyBindings.keyBinding.settingsToMainMenu": "Settings to main menu",
+	"vcmi.keyBindings.keyBinding.spectateSkipBattle": "Spectate skip battle",
+	"vcmi.keyBindings.keyBinding.spectateSkipBattleResult": "Spectate skip battle result",
+	"vcmi.keyBindings.keyBinding.spectateTrackHero": "Spectate track hero",
+	"vcmi.keyBindings.keyBinding.spellbookTabAdventure": "Spellbook tab adventure",
+	"vcmi.keyBindings.keyBinding.spellbookTabCombat": "Spellbook tab combat",
+	"vcmi.keyBindings.keyBinding.spellbookSearchFocus": "Spellbook search focus",
+	"vcmi.keyBindings.keyBinding.townOpenFort": "Town open fort",
+	"vcmi.keyBindings.keyBinding.townOpenGarrisonedHero": "Town open garrisoned hero",
+	"vcmi.keyBindings.keyBinding.townOpenHall": "Town open hall",
+	"vcmi.keyBindings.keyBinding.townOpenHero": "Town open hero",
+	"vcmi.keyBindings.keyBinding.townOpenHeroExchange": "Town open hero exchange",
+	"vcmi.keyBindings.keyBinding.townOpenMageGuild": "Town open mage guild",
+	"vcmi.keyBindings.keyBinding.townOpenMarket": "Town open market",
+	"vcmi.keyBindings.keyBinding.townOpenRecruitment": "Town open recruitment",
+	"vcmi.keyBindings.keyBinding.townOpenTavern": "Town open tavern",
+	"vcmi.keyBindings.keyBinding.townOpenThievesGuild": "Town open thieves guild",
+	"vcmi.keyBindings.keyBinding.townOpenVisitingHero": "Town open visiting hero",
+	"vcmi.keyBindings.keyBinding.townSwapArmies": "Town swap armies",
+	"vcmi.keyBindings.keyBinding.listHeroUp": "List hero up",
+	"vcmi.keyBindings.keyBinding.listHeroDown": "List hero down",
+	"vcmi.keyBindings.keyBinding.listHeroTop": "List hero top",
+	"vcmi.keyBindings.keyBinding.listHeroBottom": "List hero bottom",
+	"vcmi.keyBindings.keyBinding.listHeroDismiss": "List hero dismiss",
+	"vcmi.keyBindings.keyBinding.listTownUp": "List town up",
+	"vcmi.keyBindings.keyBinding.listTownDown": "List town down",
+	"vcmi.keyBindings.keyBinding.listTownTop": "List town top",
+	"vcmi.keyBindings.keyBinding.listTownBottom": "List town bottom",
+	"vcmi.keyBindings.keyBinding.mouseCursorX" : "Mouse cursor X",
+	"vcmi.keyBindings.keyBinding.mouseCursorY" : "Mouse cursor Y",
+	"vcmi.keyBindings.keyBinding.mouseSwipeX" : "Mouse swipe X",
+	"vcmi.keyBindings.keyBinding.mouseSwipeY" : "Mouse swipe Y",
+	"vcmi.keyBindings.keyBinding.mouseClickLeft": "Mouse click left",
+	"vcmi.keyBindings.keyBinding.mouseClickRight": "Mouse click right",
+
 	"vcmi.systemOptions.videoGroup" : "Video Settings",
 	"vcmi.systemOptions.audioGroup" : "Audio Settings",
 	"vcmi.systemOptions.otherGroup" : "Other Settings", // unused right now

+ 270 - 0
Mods/vcmi/Content/config/german.json

@@ -262,6 +262,276 @@
 	"vcmi.settingsMainWindow.adventureTab.hover" : "Abenteuer-Karte",
 	"vcmi.settingsMainWindow.adventureTab.help"  : "Wechselt zur Registerkarte Abenteuerkartenoptionen - die Abenteuerkarte ist der Teil des Spiels, in dem du deine Helden bewegen kannst.",
 
+	"vcmi.keyBindings.button.hover" : "Tastenbelegung",
+	"vcmi.keyBindings.button.help"  : "{Tastenbelegung}\n\nMenü zum Anzeigen und Anpassen von Tastenbelegungen anzeigen",
+	"vcmi.keyBindings.editButton.help" : "Tastenbelegung bearbeiten",
+	"vcmi.keyBindings.input" : "Tastenbelegung für {%s} ändern.\n\nBitte eine Taste oder Tastenkombination eingeben. Außerhalb klicken zum Abbrechen.",
+	"vcmi.keyBindings.inputSet" : "Tastenbelegung für {%s} wird zu {%s} geändert.\n\nZu den existierenten hinzufügen? Ansonsten wird ersetzt.",
+	"vcmi.keyBindings.popup" : "Für {%s} ist die folgende Tastenbelegung konfiguriert:\n\n",
+	"vcmi.keyBindings.reset" : "Zurücks.",
+	"vcmi.keyBindings.reset.help" : "{Zurücksetzen}\n\nSetzt Tastenbelegungen auf Standardeinstellungen zurück",
+	"vcmi.keyBindings.resetConfirm" : "Alle Tastenbelegungen auf Standardeinstellungen zurücksetzen?",
+	"vcmi.keyBindings.group.keyboard" : "Tastatur",
+	"vcmi.keyBindings.group.joystickAxes" : "Joystick-Achsen",
+	"vcmi.keyBindings.group.joystickButtons" : "Joystick-Tasten",
+	"vcmi.keyBindings.keyBinding.adventureCastSpell": "Abenteuer Zauber wirken",
+	"vcmi.keyBindings.keyBinding.adventureDigGrail": "Abenteuer Gral ausgraben",
+	"vcmi.keyBindings.keyBinding.adventureEndTurn": "Abenteuer Zug beenden",
+	"vcmi.keyBindings.keyBinding.adventureExitWorldView": "Abenteuer Weltansicht verlassen",
+	"vcmi.keyBindings.keyBinding.adventureFirstHero": "Abenteuer erster Held",
+	"vcmi.keyBindings.keyBinding.adventureFirstTown": "Abenteuer erste Stadt",
+	"vcmi.keyBindings.keyBinding.adventureGameOptions": "Abenteuer Spieloptionen",
+	"vcmi.keyBindings.keyBinding.adventureKingdomOverview": "Abenteuer Königreich Übersicht",
+	"vcmi.keyBindings.keyBinding.adventureLoadGame": "Abenteuer Spiel laden",
+	"vcmi.keyBindings.keyBinding.adventureMainMenu": "Abenteuer Hauptmenü",
+	"vcmi.keyBindings.keyBinding.adventureMarketplace": "Abenteuer Marktplatz",
+	"vcmi.keyBindings.keyBinding.adventureMoveHero": "Abenteuer Held bewegen",
+	"vcmi.keyBindings.keyBinding.adventureMoveHeroEE": "Abenteuer Held nach SO bewegen",
+	"vcmi.keyBindings.keyBinding.adventureMoveHeroNE": "Abenteuer Held nach NO bewegen",
+	"vcmi.keyBindings.keyBinding.adventureMoveHeroNN": "Abenteuer Held nach N bewegen",
+	"vcmi.keyBindings.keyBinding.adventureMoveHeroNW": "Abenteuer Held nach NW bewegen",
+	"vcmi.keyBindings.keyBinding.adventureMoveHeroSE": "Abenteuer Held nach SE bewegen",
+	"vcmi.keyBindings.keyBinding.adventureMoveHeroSS": "Abenteuer Held nach S bewegen",
+	"vcmi.keyBindings.keyBinding.adventureMoveHeroSW": "Abenteuer Held nach SW bewegen",
+	"vcmi.keyBindings.keyBinding.adventureMoveHeroWW": "Abenteuer Held nach W bewegen",
+	"vcmi.keyBindings.keyBinding.adventureNewGame": "Abenteuer neues Spiel",
+	"vcmi.keyBindings.keyBinding.adventureNextHero": "Abenteuer nächster Held",
+	"vcmi.keyBindings.keyBinding.adventureNextObject": "Abenteuer nächstes Objekt",
+	"vcmi.keyBindings.keyBinding.adventureNextTown": "Abenteuer nächste Stadt",
+	"vcmi.keyBindings.keyBinding.adventureQuestLog": "Abenteuer Questprotokoll",
+	"vcmi.keyBindings.keyBinding.adventureQuitGame": "Abenteuer Spiel beenden",
+	"vcmi.keyBindings.keyBinding.adventureReplayTurn": "Abenteuer Zug wiederholen",
+	"vcmi.keyBindings.keyBinding.adventureRestartGame": "Abenteuer Spiel neu starten",
+	"vcmi.keyBindings.keyBinding.adventureSaveGame": "Abenteuer Spiel speichern",
+	"vcmi.keyBindings.keyBinding.adventureSetHeroAsleep": "Abenteuer Held schlafen legen",
+	"vcmi.keyBindings.keyBinding.adventureSetHeroAwake": "Abenteuer Held aufwecken",
+	"vcmi.keyBindings.keyBinding.adventureThievesGuild": "Abenteuer Diebesgilde",
+	"vcmi.keyBindings.keyBinding.adventureToggleGrid": "Abenteuer Raster ein-/ausschalten",
+	"vcmi.keyBindings.keyBinding.adventureToggleVisitable": "Abenteuer Besuchbar ein-/ausschalten",
+	"vcmi.keyBindings.keyBinding.adventureToggleBlocked": "Abenteuer Blockiert ein-/ausschalten",
+	"vcmi.keyBindings.keyBinding.adventureToggleMapLevel": "Abenteuer Kartenebene umschalten",
+	"vcmi.keyBindings.keyBinding.adventureToggleSleep": "Abenteuer Schlaf ein-/ausschalten",
+	"vcmi.keyBindings.keyBinding.adventureTrackHero": "Abenteuer Held verfolgen",
+	"vcmi.keyBindings.keyBinding.adventureViewPuzzle": "Abenteuer Rätsel anzeigen",
+	"vcmi.keyBindings.keyBinding.adventureViewScenario": "Abenteuer Szenario anzeigen",
+	"vcmi.keyBindings.keyBinding.adventureViewSelected": "Abenteuer Auswahl anzeigen",
+	"vcmi.keyBindings.keyBinding.adventureViewWorld": "Abenteuer Weltansicht",
+	"vcmi.keyBindings.keyBinding.adventureViewWorld1": "Abenteuer Weltansicht 1",
+	"vcmi.keyBindings.keyBinding.adventureViewWorld2": "Abenteuer Weltansicht 2",
+	"vcmi.keyBindings.keyBinding.adventureViewWorld4": "Abenteuer Weltansicht 4",
+	"vcmi.keyBindings.keyBinding.adventureVisitObject": "Abenteuer Objekt besuchen",
+	"vcmi.keyBindings.keyBinding.adventureZoomIn": "Abenteuer reinzoomen",
+	"vcmi.keyBindings.keyBinding.adventureZoomOut": "Abenteuer rauszoomen",
+	"vcmi.keyBindings.keyBinding.adventureZoomReset": "Abenteuer Zoom zurücksetzen",
+	"vcmi.keyBindings.keyBinding.adventureSearch": "Abenteuer suchen",
+	"vcmi.keyBindings.keyBinding.adventureSearchContinue": "Abenteuer Suche fortsetzen",
+	"vcmi.keyBindings.keyBinding.battleAutocombat": "Kampf Autokampf",
+	"vcmi.keyBindings.keyBinding.battleAutocombatEnd": "Kampf Autokampf beenden",
+	"vcmi.keyBindings.keyBinding.battleCastSpell": "Kampf Zauber wirken",
+	"vcmi.keyBindings.keyBinding.battleConsoleDown": "Kampf Konsole runter",
+	"vcmi.keyBindings.keyBinding.battleConsoleUp": "Kampf Konsole hoch",
+	"vcmi.keyBindings.keyBinding.battleDefend": "Kampf verteidigen",
+	"vcmi.keyBindings.keyBinding.battleOpenActiveUnit": "Kampf aktive Einheit öffnen",
+	"vcmi.keyBindings.keyBinding.battleOpenHoveredUnit": "Kampf Einheit unter Maus öffnen",
+	"vcmi.keyBindings.keyBinding.battleRetreat": "Kampf zurückziehen",
+	"vcmi.keyBindings.keyBinding.battleToggleQuickSpell": "Kampf Schnellzauber umschalten",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut0": "Kampf Zauber-Shortcut 0",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut1": "Kampf Zauber-Shortcut 1",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut2": "Kampf Zauber-Shortcut 2",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut3": "Kampf Zauber-Shortcut 3",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut4": "Kampf Zauber-Shortcut 4",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut5": "Kampf Zauber-Shortcut 5",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut6": "Kampf Zauber-Shortcut 6",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut7": "Kampf Zauber-Shortcut 7",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut8": "Kampf Zauber-Shortcut 8",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut9": "Kampf Zauber-Shortcut 9",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut10": "Kampf Zauber-Shortcut 10",
+	"vcmi.keyBindings.keyBinding.battleSpellShortcut11": "Kampf Zauber-Shortcut 11",
+	"vcmi.keyBindings.keyBinding.battleSurrender": "Kampf aufgeben",
+	"vcmi.keyBindings.keyBinding.battleTacticsEnd": "Kampf Taktik beenden",
+	"vcmi.keyBindings.keyBinding.battleTacticsNext": "Kampf Taktik nächster",
+	"vcmi.keyBindings.keyBinding.battleToggleHeroesStats": "Kampf Heldenstatistiken umschalten",
+	"vcmi.keyBindings.keyBinding.battleToggleQueue": "Kampf Warteschlange umschalten",
+	"vcmi.keyBindings.keyBinding.battleUseCreatureSpell": "Kampf Kreaturenzauber verwenden",
+	"vcmi.keyBindings.keyBinding.battleWait": "Kampf warten",
+	"vcmi.keyBindings.keyBinding.exchangeArmySwap": "Armee tauschen",
+	"vcmi.keyBindings.keyBinding.exchangeArmyToLeft": "Armee nach links",
+	"vcmi.keyBindings.keyBinding.exchangeArmyToRight": "Armee nach rechts",
+	"vcmi.keyBindings.keyBinding.exchangeArtifactsSwap": "Artefakte tauschen",
+	"vcmi.keyBindings.keyBinding.exchangeArtifactsToLeft": "Artefakte nach links",
+	"vcmi.keyBindings.keyBinding.exchangeArtifactsToRight": "Artefakte nach rechts",
+	"vcmi.keyBindings.keyBinding.exchangeBackpackLeft": "Rucksack nach links",
+	"vcmi.keyBindings.keyBinding.exchangeBackpackRight": "Rucksack nach rechts",
+	"vcmi.keyBindings.keyBinding.exchangeBackpackSwap": "Rucksack tauschen",
+	"vcmi.keyBindings.keyBinding.exchangeBackpackToLeft": "Rucksack nach links",
+	"vcmi.keyBindings.keyBinding.exchangeBackpackToRight": "Rucksack nach rechts",
+	"vcmi.keyBindings.keyBinding.exchangeEquippedSwap": "Ausrüstung tauschen",
+	"vcmi.keyBindings.keyBinding.exchangeEquippedToLeft": "Ausrüstung nach links",
+	"vcmi.keyBindings.keyBinding.exchangeEquippedToRight": "Ausrüstung nach rechts",
+	"vcmi.keyBindings.keyBinding.gameActivateConsole": "Spiel Konsole aktivieren",
+	"vcmi.keyBindings.keyBinding.globalAccept": "Global akzeptieren",
+	"vcmi.keyBindings.keyBinding.globalBackspace": "Global Rückschritt",
+	"vcmi.keyBindings.keyBinding.globalCancel": "Global abbrechen",
+	"vcmi.keyBindings.keyBinding.globalFullscreen": "Global Vollbild",
+	"vcmi.keyBindings.keyBinding.globalMoveFocus": "Global Fokus verschieben",
+	"vcmi.keyBindings.keyBinding.globalOptions": "Global Optionen",
+	"vcmi.keyBindings.keyBinding.globalReturn": "Global zurück",
+	"vcmi.keyBindings.keyBinding.heroArmySplit": "Held Armee aufteilen",
+	"vcmi.keyBindings.keyBinding.heroBackpack": "Held Rucksack",
+	"vcmi.keyBindings.keyBinding.heroCommander": "Held Kommandant",
+	"vcmi.keyBindings.keyBinding.heroCostumeLoad0": "Held Kostüm laden 0",
+	"vcmi.keyBindings.keyBinding.heroCostumeLoad1": "Held Kostüm laden 1",
+	"vcmi.keyBindings.keyBinding.heroCostumeLoad2": "Held Kostüm laden 2",
+	"vcmi.keyBindings.keyBinding.heroCostumeLoad3": "Held Kostüm laden 3",
+	"vcmi.keyBindings.keyBinding.heroCostumeLoad4": "Held Kostüm laden 4",
+	"vcmi.keyBindings.keyBinding.heroCostumeLoad5": "Held Kostüm laden 5",
+	"vcmi.keyBindings.keyBinding.heroCostumeLoad6": "Held Kostüm laden 6",
+	"vcmi.keyBindings.keyBinding.heroCostumeLoad7": "Held Kostüm laden 7",
+	"vcmi.keyBindings.keyBinding.heroCostumeLoad8": "Held Kostüm laden 8",
+	"vcmi.keyBindings.keyBinding.heroCostumeLoad9": "Held Kostüm laden 9",
+	"vcmi.keyBindings.keyBinding.heroCostumeSave0": "Held Kostüm speichern 0",
+	"vcmi.keyBindings.keyBinding.heroCostumeSave1": "Held Kostüm speichern 1",
+	"vcmi.keyBindings.keyBinding.heroCostumeSave2": "Held Kostüm speichern 2",
+	"vcmi.keyBindings.keyBinding.heroCostumeSave3": "Held Kostüm speichern 3",
+	"vcmi.keyBindings.keyBinding.heroCostumeSave4": "Held Kostüm speichern 4",
+	"vcmi.keyBindings.keyBinding.heroCostumeSave5": "Held Kostüm speichern 5",
+	"vcmi.keyBindings.keyBinding.heroCostumeSave6": "Held Kostüm speichern 6",
+	"vcmi.keyBindings.keyBinding.heroCostumeSave7": "Held Kostüm speichern 7",
+	"vcmi.keyBindings.keyBinding.heroCostumeSave8": "Held Kostüm speichern 8",
+	"vcmi.keyBindings.keyBinding.heroCostumeSave9": "Held Kostüm speichern 9",
+	"vcmi.keyBindings.keyBinding.heroDismiss": "Held entlassen",
+	"vcmi.keyBindings.keyBinding.heroLooseFormation": "Held lockere Formation",
+	"vcmi.keyBindings.keyBinding.heroTightFormation": "Held enge Formation",
+	"vcmi.keyBindings.keyBinding.heroToggleTactics": "Held Taktiken umschalten",
+	"vcmi.keyBindings.keyBinding.highScoresCampaigns": "Bestenlisten Kampagnen",
+	"vcmi.keyBindings.keyBinding.highScoresReset": "Bestenlisten zurücksetzen",
+	"vcmi.keyBindings.keyBinding.highScoresScenarios": "Bestenlisten Szenarien",
+	"vcmi.keyBindings.keyBinding.highScoresStatistics": "Bestenlisten Statistiken",
+	"vcmi.keyBindings.keyBinding.kingdomHeroesTab": "Königreich Helden-Tab",
+	"vcmi.keyBindings.keyBinding.kingdomTownsTab": "Königreich Städte-Tab",
+	"vcmi.keyBindings.keyBinding.listHeroBottom": "Held unten listen",
+	"vcmi.keyBindings.keyBinding.listHeroDismiss": "Held entlassen",
+	"vcmi.keyBindings.keyBinding.listHeroDown": "Held nach unten",
+	"vcmi.keyBindings.keyBinding.listHeroTop": "Held oben listen",
+	"vcmi.keyBindings.keyBinding.listHeroUp": "Held nach oben",
+	"vcmi.keyBindings.keyBinding.listTownBottom": "Stadt unten listen",
+	"vcmi.keyBindings.keyBinding.listTownDown": "Stadt nach unten",
+	"vcmi.keyBindings.keyBinding.listTownTop": "Stadt oben listen",
+	"vcmi.keyBindings.keyBinding.listTownUp": "Stadt nach oben",
+	"vcmi.keyBindings.keyBinding.lobbyAdditionalOptions": "Lobby Zusatzoptionen",
+	"vcmi.keyBindings.keyBinding.lobbyBeginCampaign": "Lobby Kampagne starten",
+	"vcmi.keyBindings.keyBinding.lobbyBeginStandardGame": "Lobby Standardspiel starten",
+	"vcmi.keyBindings.keyBinding.lobbyExtraOptions": "Lobby Extraoptionen",
+	"vcmi.keyBindings.keyBinding.lobbyFlipCoin": "Lobby Münzwurf",
+	"vcmi.keyBindings.keyBinding.lobbyHandicap": "Lobby Handicap",
+	"vcmi.keyBindings.keyBinding.lobbyInvitePlayers": "Lobby Spieler einladen",
+	"vcmi.keyBindings.keyBinding.lobbyLoadGame": "Lobby Spiel laden",
+	"vcmi.keyBindings.keyBinding.lobbyRandomMap": "Lobby Zufällige Karte",
+	"vcmi.keyBindings.keyBinding.lobbyRandomTown": "Lobby Zufällige Stadt",
+	"vcmi.keyBindings.keyBinding.lobbyRandomTownVs": "Lobby Zufällige Stadt Gegner",
+	"vcmi.keyBindings.keyBinding.lobbyReplayVideo": "Lobby Video wiederholen",
+	"vcmi.keyBindings.keyBinding.lobbySaveGame": "Lobby Spiel speichern",
+	"vcmi.keyBindings.keyBinding.lobbySelectScenario": "Lobby Szenario wählen",
+	"vcmi.keyBindings.keyBinding.lobbyToggleChat": "Lobby Chat umschalten",
+	"vcmi.keyBindings.keyBinding.lobbyTurnOptions": "Lobby Zugoptionen",
+	"vcmi.keyBindings.keyBinding.mainMenuBack": "Hauptmenü zurück",
+	"vcmi.keyBindings.keyBinding.mainMenuCampaign": "Hauptmenü Kampagne",
+	"vcmi.keyBindings.keyBinding.mainMenuCampaignAb": "Hauptmenü Kampagne Ab",
+	"vcmi.keyBindings.keyBinding.mainMenuCampaignChr": "Hauptmenü Kampagne Chronicles",
+	"vcmi.keyBindings.keyBinding.mainMenuCampaignCustom": "Hauptmenü Kampagne Benutzerdefiniert",
+	"vcmi.keyBindings.keyBinding.mainMenuCampaignHota": "Hauptmenü Kampagne HotA",
+	"vcmi.keyBindings.keyBinding.mainMenuCampaignRoe": "Hauptmenü Kampagne RoE",
+	"vcmi.keyBindings.keyBinding.mainMenuCampaignSod": "Hauptmenü Kampagne SoD",
+	"vcmi.keyBindings.keyBinding.mainMenuCampaignVCMI": "Hauptmenü Kampagne VCMI",
+	"vcmi.keyBindings.keyBinding.mainMenuCampaignWog": "Hauptmenü Kampagne WoG",
+	"vcmi.keyBindings.keyBinding.mainMenuCredits": "Hauptmenü Credits",
+	"vcmi.keyBindings.keyBinding.mainMenuHighScores": "Hauptmenü Bestenlisten",
+	"vcmi.keyBindings.keyBinding.mainMenuHostGame": "Hauptmenü Spiel hosten",
+	"vcmi.keyBindings.keyBinding.mainMenuHotseat": "Hauptmenü Hotseat",
+	"vcmi.keyBindings.keyBinding.mainMenuJoinGame": "Hauptmenü Spiel beitreten",
+	"vcmi.keyBindings.keyBinding.mainMenuLoadGame": "Hauptmenü Spiel laden",
+	"vcmi.keyBindings.keyBinding.mainMenuLobby": "Hauptmenü Lobby",
+	"vcmi.keyBindings.keyBinding.mainMenuMultiplayer": "Hauptmenü Mehrspieler",
+	"vcmi.keyBindings.keyBinding.mainMenuNewGame": "Hauptmenü Neues Spiel",
+	"vcmi.keyBindings.keyBinding.mainMenuQuit": "Hauptmenü Beenden",
+	"vcmi.keyBindings.keyBinding.mainMenuSingleplayer": "Hauptmenü Einzelspieler",
+	"vcmi.keyBindings.keyBinding.mainMenuTutorial": "Hauptmenü Tutorial",
+	"vcmi.keyBindings.keyBinding.mapsSizeAll": "Karten Größe Alle",
+	"vcmi.keyBindings.keyBinding.mapsSizeL": "Karten Größe L",
+	"vcmi.keyBindings.keyBinding.mapsSizeM": "Karten Größe M",
+	"vcmi.keyBindings.keyBinding.mapsSizeS": "Karten Größe S",
+	"vcmi.keyBindings.keyBinding.mapsSizeXl": "Karten Größe XL",
+	"vcmi.keyBindings.keyBinding.mapsSortChangedate": "Karten Sortierung Änderungsdatum",
+	"vcmi.keyBindings.keyBinding.mapsSortDefeat": "Karten Sortierung Niederlage",
+	"vcmi.keyBindings.keyBinding.mapsSortFormat": "Karten Sortierung Format",
+	"vcmi.keyBindings.keyBinding.mapsSortMaps": "Karten Sortierung Karten",
+	"vcmi.keyBindings.keyBinding.mapsSortName": "Karten Sortierung Name",
+	"vcmi.keyBindings.keyBinding.mapsSortPlayers": "Karten Sortierung Spieler",
+	"vcmi.keyBindings.keyBinding.mapsSortSize": "Karten Sortierung Größe",
+	"vcmi.keyBindings.keyBinding.mapsSortVictory": "Karten Sortierung Sieg",
+	"vcmi.keyBindings.keyBinding.marketArtifactExperience": "Markt Artefakt Erfahrung",
+	"vcmi.keyBindings.keyBinding.marketArtifactResource": "Markt Artefakt Ressource",
+	"vcmi.keyBindings.keyBinding.marketCreatureExperience": "Markt Kreatur Erfahrung",
+	"vcmi.keyBindings.keyBinding.marketCreatureResource": "Markt Kreatur Ressource",
+	"vcmi.keyBindings.keyBinding.marketDeal": "Markt Deal",
+	"vcmi.keyBindings.keyBinding.marketMaxAmount": "Markt Maximalmenge",
+	"vcmi.keyBindings.keyBinding.marketResourceArtifact": "Markt Ressource Artefakt",
+	"vcmi.keyBindings.keyBinding.marketResourcePlayer": "Markt Ressource Spieler",
+	"vcmi.keyBindings.keyBinding.marketResourceResource": "Markt Ressource Ressource",
+	"vcmi.keyBindings.keyBinding.marketSacrificeAll": "Markt Alles opfern",
+	"vcmi.keyBindings.keyBinding.marketSacrificeBackpack": "Markt Rucksack opfern",
+	"vcmi.keyBindings.keyBinding.mouseClickLeft": "Mausklick links",
+	"vcmi.keyBindings.keyBinding.mouseClickRight": "Mausklick rechts",
+	"vcmi.keyBindings.keyBinding.mouseCursorX": "Mauszeiger X",
+	"vcmi.keyBindings.keyBinding.mouseCursorY": "Mauszeiger Y",
+	"vcmi.keyBindings.keyBinding.mouseSwipeX": "Maus Wisch X",
+	"vcmi.keyBindings.keyBinding.mouseSwipeY": "Maus Wisch Y",
+	"vcmi.keyBindings.keyBinding.moveDown": "Nach unten bewegen",
+	"vcmi.keyBindings.keyBinding.moveFirst": "Zum ersten bewegen",
+	"vcmi.keyBindings.keyBinding.moveLast": "Zum letzten bewegen",
+	"vcmi.keyBindings.keyBinding.moveLeft": "Nach links bewegen",
+	"vcmi.keyBindings.keyBinding.movePageDown": "Seite runter bewegen",
+	"vcmi.keyBindings.keyBinding.movePageUp": "Seite hoch bewegen",
+	"vcmi.keyBindings.keyBinding.moveRight": "Nach rechts bewegen",
+	"vcmi.keyBindings.keyBinding.moveUp": "Nach oben bewegen",
+	"vcmi.keyBindings.keyBinding.recruitmentMax": "Rekrutierung Max",
+	"vcmi.keyBindings.keyBinding.recruitmentMin": "Rekrutierung Min",
+	"vcmi.keyBindings.keyBinding.recruitmentSwitchLevel": "Rekrutierung Level wechseln",
+	"vcmi.keyBindings.keyBinding.recruitmentUpgrade": "Rekrutierung verbessern",
+	"vcmi.keyBindings.keyBinding.recruitmentUpgradeAll": "Rekrutierung alle verbessern",
+	"vcmi.keyBindings.keyBinding.selectIndex1": "Index 1 auswählen",
+	"vcmi.keyBindings.keyBinding.selectIndex2": "Index 2 auswählen",
+	"vcmi.keyBindings.keyBinding.selectIndex3": "Index 3 auswählen",
+	"vcmi.keyBindings.keyBinding.selectIndex4": "Index 4 auswählen",
+	"vcmi.keyBindings.keyBinding.selectIndex5": "Index 5 auswählen",
+	"vcmi.keyBindings.keyBinding.selectIndex6": "Index 6 auswählen",
+	"vcmi.keyBindings.keyBinding.selectIndex7": "Index 7 auswählen",
+	"vcmi.keyBindings.keyBinding.selectIndex8": "Index 8 auswählen",
+	"vcmi.keyBindings.keyBinding.settingsLoadGame": "Einstellungen Spiel laden",
+	"vcmi.keyBindings.keyBinding.settingsNewGame": "Einstellungen neues Spiel",
+	"vcmi.keyBindings.keyBinding.settingsQuitGame": "Einstellungen Spiel beenden",
+	"vcmi.keyBindings.keyBinding.settingsRestartGame": "Einstellungen Spiel neustarten",
+	"vcmi.keyBindings.keyBinding.settingsSaveGame": "Einstellungen Spiel speichern",
+	"vcmi.keyBindings.keyBinding.settingsToMainMenu": "Einstellungen zum Hauptmenü",
+	"vcmi.keyBindings.keyBinding.spectateSkipBattle": "Zuschauen Schlacht überspringen",
+	"vcmi.keyBindings.keyBinding.spectateSkipBattleResult": "Zuschauen Schlacht Ergebnis überspringen",
+	"vcmi.keyBindings.keyBinding.spectateTrackHero": "Zuschauen Held verfolgen",
+	"vcmi.keyBindings.keyBinding.spellbookSearchFocus": "Zauberbuch Suchfokus",
+	"vcmi.keyBindings.keyBinding.spellbookTabAdventure": "Zauberbuch Abenteuer-Tab",
+	"vcmi.keyBindings.keyBinding.spellbookTabCombat": "Zauberbuch Kampf-Tab",
+	"vcmi.keyBindings.keyBinding.townOpenFort": "Stadt Festung öffnen",
+	"vcmi.keyBindings.keyBinding.townOpenGarrisonedHero": "Stadt Garnisonierten Held öffnen",
+	"vcmi.keyBindings.keyBinding.townOpenHall": "Stadt Halle öffnen",
+	"vcmi.keyBindings.keyBinding.townOpenHero": "Stadt Held öffnen",
+	"vcmi.keyBindings.keyBinding.townOpenHeroExchange": "Stadt Held Austausch öffnen",
+	"vcmi.keyBindings.keyBinding.townOpenMageGuild": "Stadt Magiergilde öffnen",
+	"vcmi.keyBindings.keyBinding.townOpenMarket": "Stadt Markt öffnen",
+	"vcmi.keyBindings.keyBinding.townOpenRecruitment": "Stadt Rekrutierung öffnen",
+	"vcmi.keyBindings.keyBinding.townOpenTavern": "Stadt Taverne öffnen",
+	"vcmi.keyBindings.keyBinding.townOpenThievesGuild": "Stadt Diebesgilde öffnen",
+	"vcmi.keyBindings.keyBinding.townOpenVisitingHero": "Stadt Besuchenden Held öffnen",
+	"vcmi.keyBindings.keyBinding.townSwapArmies": "Stadt Armeen tauschen",
+
 	"vcmi.systemOptions.videoGroup" : "Video-Einstellungen",
 	"vcmi.systemOptions.audioGroup" : "Audio-Einstellungen",
 	"vcmi.systemOptions.otherGroup" : "Andere Einstellungen", // unused right now

+ 2 - 0
client/CMakeLists.txt

@@ -181,6 +181,7 @@ set(vcmiclientcommon_SRCS
 	windows/settings/SettingsMainWindow.cpp
 	windows/settings/BattleOptionsTab.cpp
 	windows/settings/AdventureOptionsTab.cpp
+	windows/settings/KeyBindingsWindow.cpp
 
 	xBRZ/xbrz.cpp
 
@@ -403,6 +404,7 @@ set(vcmiclientcommon_HEADERS
 	windows/settings/SettingsMainWindow.h
 	windows/settings/BattleOptionsTab.h
 	windows/settings/AdventureOptionsTab.h
+	windows/settings/KeyBindingsWindow.h
 
 	xBRZ/xbrz.h
 	xBRZ/xbrz_tools.h

+ 1 - 3
client/battle/QuickSpellPanel.cpp

@@ -91,8 +91,6 @@ void QuickSpellPanel::create()
 {
 	OBJECT_CONSTRUCTION;
 
-	const JsonNode config = JsonUtils::assembleFromFiles("config/shortcutsConfig");
-
 	labels.clear();
 	buttons.clear();
 	buttonsDisabled.clear();
@@ -132,7 +130,7 @@ void QuickSpellPanel::create()
 			buttonsDisabled.push_back(std::make_shared<TransparentFilledRectangle>(Rect(2, 7 + 50 * i, 48, 36), ColorRGBA(0, 0, 0, 172)));
 		}
 		if(ENGINE->input().getCurrentInputMode() == InputMode::KEYBOARD_AND_MOUSE)
-			labels.push_back(std::make_shared<CLabel>(7, 10 + 50 * i, EFonts::FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, config["keyboard"]["battleSpellShortcut" + std::to_string(i)].String()));
+			labels.push_back(std::make_shared<CLabel>(7, 10 + 50 * i, EFonts::FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, keyBindingsConfig["keyboard"]["battleSpellShortcut" + std::to_string(i)].String()));
 
 		buttons.push_back(button);
 	}

+ 7 - 2
client/eventsSDL/InputSourceGameController.cpp

@@ -140,12 +140,13 @@ double InputSourceGameController::getRealAxisValue(int value) const
 	return clampedValue;
 }
 
-void InputSourceGameController::dispatchAxisShortcuts(const std::vector<EShortcut> & shortcutsVector, SDL_GameControllerAxis axisID, int axisValue)
+void InputSourceGameController::dispatchAxisShortcuts(const std::vector<EShortcut> & shortcutsVector, SDL_GameControllerAxis axisID, int axisValue, std::string axisName)
 {
 	if(getRealAxisValue(axisValue) > configTriggerThreshold)
 	{
 		if(!pressedAxes.count(axisID))
 		{
+			ENGINE->events().dispatchKeyPressed(axisName);
 			ENGINE->events().dispatchShortcutPressed(shortcutsVector);
 			pressedAxes.insert(axisID);
 		}
@@ -154,6 +155,7 @@ void InputSourceGameController::dispatchAxisShortcuts(const std::vector<EShortcu
 	{
 		if(pressedAxes.count(axisID))
 		{
+			ENGINE->events().dispatchKeyReleased(axisName);
 			ENGINE->events().dispatchShortcutReleased(shortcutsVector);
 			pressedAxes.erase(axisID);
 		}
@@ -189,7 +191,7 @@ void InputSourceGameController::handleEventAxisMotion(const SDL_ControllerAxisEv
 		}
 	}
 
-	dispatchAxisShortcuts(buttonActions, axisID, axis.value);
+	dispatchAxisShortcuts(buttonActions, axisID, axis.value, axisName);
 }
 
 void InputSourceGameController::tryToConvertCursor()
@@ -208,6 +210,8 @@ void InputSourceGameController::handleEventButtonDown(const SDL_ControllerButton
 {
 	std::string buttonName = SDL_GameControllerGetStringForButton(static_cast<SDL_GameControllerButton>(button.button));
 	const auto & shortcutsVector = ENGINE->shortcuts().translateJoystickButton(buttonName);
+	
+	ENGINE->events().dispatchKeyPressed(buttonName);
 	ENGINE->events().dispatchShortcutPressed(shortcutsVector);
 }
 
@@ -215,6 +219,7 @@ void InputSourceGameController::handleEventButtonUp(const SDL_ControllerButtonEv
 {
 	std::string buttonName = SDL_GameControllerGetStringForButton(static_cast<SDL_GameControllerButton>(button.button));
 	const auto & shortcutsVector = ENGINE->shortcuts().translateJoystickButton(buttonName);
+	ENGINE->events().dispatchKeyReleased(buttonName);
 	ENGINE->events().dispatchShortcutReleased(shortcutsVector);
 }
 

+ 1 - 1
client/eventsSDL/InputSourceGameController.h

@@ -48,7 +48,7 @@ class InputSourceGameController
 	void openGameController(int index);
 	int getJoystickIndex(SDL_GameController * controller);
 	double getRealAxisValue(int value) const;
-	void dispatchAxisShortcuts(const std::vector<EShortcut> & shortcutsVector, SDL_GameControllerAxis axisID, int axisValue);
+	void dispatchAxisShortcuts(const std::vector<EShortcut> & shortcutsVector, SDL_GameControllerAxis axisID, int axisValue, std::string axisName);
 	void tryToConvertCursor();
 	void doCursorMove(int deltaX, int deltaY);
 	int getMoveDis(float planDis);

+ 4 - 0
client/eventsSDL/InputSourceKeyboard.cpp

@@ -88,6 +88,8 @@ void InputSourceKeyboard::handleEventKeyDown(const SDL_KeyboardEvent & key)
 
 	auto shortcutsVector = ENGINE->shortcuts().translateKeycode(keyName);
 
+	ENGINE->events().dispatchKeyPressed(keyName);
+
 	if (vstd::contains(shortcutsVector, EShortcut::MAIN_MENU_LOBBY))
 		ENGINE->user().onGlobalLobbyInterfaceActivated();
 
@@ -142,6 +144,8 @@ void InputSourceKeyboard::handleEventKeyUp(const SDL_KeyboardEvent & key)
 	assert(key.state == SDL_RELEASED);
 
 	auto shortcutsVector = ENGINE->shortcuts().translateKeycode(keyName);
+	
+	ENGINE->events().dispatchKeyReleased(keyName);
 
 	ENGINE->events().dispatchShortcutReleased(shortcutsVector);
 }

+ 18 - 1
client/gui/EventDispatcher.cpp

@@ -43,6 +43,7 @@ void EventDispatcher::processLists(ui16 activityFlag, const Functor & cb)
 	processList(AEventsReceiver::TEXTINPUT, textInterested);
 	processList(AEventsReceiver::GESTURE, panningInterested);
 	processList(AEventsReceiver::INPUT_MODE_CHANGE, inputModeChangeInterested);
+	processList(AEventsReceiver::KEY_NAME, keyNameInterested);
 }
 
 void EventDispatcher::activateElement(AEventsReceiver * elem, ui16 activityFlag)
@@ -133,6 +134,22 @@ void EventDispatcher::dispatchShortcutReleased(const std::vector<EShortcut> & sh
 	}
 }
 
+void EventDispatcher::dispatchKeyPressed(const std::string & keyName)
+{
+	EventReceiversList miCopy = keyNameInterested;
+
+	for(auto & i : miCopy)
+		i->keyPressed(keyName);
+}
+
+void EventDispatcher::dispatchKeyReleased(const std::string & keyName)
+{
+	EventReceiversList miCopy = keyNameInterested;
+
+	for(auto & i : miCopy)
+		i->keyReleased(keyName);
+}
+
 void EventDispatcher::dispatchMouseDoubleClick(const Point & position, int tolerance)
 {
 	handleDoubleButtonClick(position, tolerance);
@@ -183,7 +200,7 @@ AEventsReceiver * EventDispatcher::findElementInToleranceRange(const EventReceiv
 
 void EventDispatcher::dispatchShowPopup(const Point & position, int tolerance)
 {
-	AEventsReceiver * nearestElement = findElementInToleranceRange(rclickable, position, AEventsReceiver::LCLICK, tolerance);
+	AEventsReceiver * nearestElement = findElementInToleranceRange(rclickable, position, AEventsReceiver::SHOW_POPUP, tolerance);
 
 	auto hlp = rclickable;
 

+ 5 - 0
client/gui/EventDispatcher.h

@@ -37,6 +37,7 @@ class EventDispatcher
 	EventReceiversList textInterested;
 	EventReceiversList panningInterested;
 	EventReceiversList inputModeChangeInterested;
+	EventReceiversList keyNameInterested;
 
 	void handleLeftButtonClick(const Point & position, int tolerance, bool isPressed);
 	void handleDoubleButtonClick(const Point & position, int tolerance);
@@ -59,6 +60,10 @@ public:
 	void dispatchShortcutPressed(const std::vector<EShortcut> & shortcuts);
 	void dispatchShortcutReleased(const std::vector<EShortcut> & shortcuts);
 
+	/// Key events (to get keyname of pressed key)
+	void dispatchKeyPressed(const std::string & keyName);
+	void dispatchKeyReleased(const std::string & keyName);
+
 	/// Mouse events
 	void dispatchMouseLeftButtonPressed(const Point & position, int tolerance);
 	void dispatchMouseLeftButtonReleased(const Point & position, int tolerance);

+ 5 - 1
client/gui/EventsReceiver.h

@@ -77,6 +77,9 @@ public:
 	virtual void keyPressed(EShortcut key) {}
 	virtual void keyReleased(EShortcut key) {}
 
+	virtual void keyPressed(const std::string & keyName) {}
+	virtual void keyReleased(const std::string & keyName) {}
+
 	virtual void tick(uint32_t msPassed) {}
 
 	virtual void inputModeChanged(InputMode modi) {}
@@ -101,7 +104,8 @@ public:
 		GESTURE = 1024,
 		DRAG = 2048,
 		INPUT_MODE_CHANGE = 4096,
-		DRAG_POPUP = 8192
+		DRAG_POPUP = 8192,
+		KEY_NAME = 16384
 	};
 
 	/// Returns true if element is currently hovered by mouse

+ 5 - 6
client/gui/ShortcutHandler.cpp

@@ -13,21 +13,20 @@
 #include "ShortcutHandler.h"
 #include "Shortcut.h"
 
+#include "../../lib/CConfigHandler.h"
 #include "../../lib/json/JsonUtils.h"
 
 ShortcutHandler::ShortcutHandler()
 {
-	const JsonNode config = JsonUtils::assembleFromFiles("config/shortcutsConfig");
-
-	mappedKeyboardShortcuts = loadShortcuts(config["keyboard"]);
-	mappedJoystickShortcuts = loadShortcuts(config["joystickButtons"]);
-	mappedJoystickAxes = loadShortcuts(config["joystickAxes"]);
+	mappedKeyboardShortcuts = loadShortcuts(keyBindingsConfig["keyboard"]);
+	mappedJoystickShortcuts = loadShortcuts(keyBindingsConfig["joystickButtons"]);
+	mappedJoystickAxes = loadShortcuts(keyBindingsConfig["joystickAxes"]);
 
 #ifndef ENABLE_GOLDMASTER
 	std::vector<EShortcut> assignedShortcuts;
 	std::vector<EShortcut> missingShortcuts;
 
-	for (auto const & entry : config["keyboard"].Struct())
+	for (auto const & entry : keyBindingsConfig["keyboard"].Struct())
 	{
 		EShortcut shortcutID = findShortcut(entry.first);
 		assert(!vstd::contains(assignedShortcuts, shortcutID));

+ 5 - 0
client/windows/settings/AdventureOptionsTab.cpp

@@ -10,6 +10,7 @@
 #include "StdInc.h"
 
 #include "AdventureOptionsTab.h"
+#include "KeyBindingsWindow.h"
 
 #include "../../GameEngine.h"
 #include "../../GameInstance.h"
@@ -163,6 +164,10 @@ AdventureOptionsTab::AdventureOptionsTab()
 			GAME->interface()->localState->erasePath(GAME->interface()->localState->getCurrentHero());
 		ENGINE->windows().totalRedraw();
 	});
+	addCallback("openShortcutMenu", [](int dummyValue)
+	{
+		ENGINE->windows().createAndPushWindow<KeyBindingsWindow>();
+	});
 	build(config);
 
 	std::shared_ptr<CToggleGroup> playerHeroSpeedToggle = widget<CToggleGroup>("heroMovementSpeedPicker");

+ 243 - 0
client/windows/settings/KeyBindingsWindow.cpp

@@ -0,0 +1,243 @@
+/*
+ * KeyBindingsWindow.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#include "StdInc.h"
+#include "KeyBindingsWindow.h"
+
+#include "../../CPlayerInterface.h"
+#include "../../GameEngine.h"
+#include "../../GameInstance.h"
+#include "../../gui/Shortcut.h"
+#include "../../gui/WindowHandler.h"
+#include "../../widgets/Buttons.h"
+#include "../../widgets/GraphicalPrimitiveCanvas.h"
+#include "../../widgets/Images.h"
+#include "../../widgets/TextControls.h"
+#include "../../widgets/Slider.h"
+#include "../../windows/InfoWindows.h"
+
+#include "../../../lib/CConfigHandler.h"
+#include "../../../lib/texts/MetaString.h"
+#include "../../../lib/json/JsonNode.h"
+#include "../../../lib/json/JsonUtils.h"
+
+KeyBindingsWindow::KeyBindingsWindow()
+	: CWindowObject(BORDERED)
+{
+	OBJECT_CONSTRUCTION;
+	pos.w = 500;
+	pos.h = 450;
+
+	updateShadow();
+	center();
+
+	backgroundTexture = std::make_shared<CFilledTexture>(ImagePath::builtin("DiBoxBck"), Rect(0, 0, pos.w, pos.h));
+	buttonOk = std::make_shared<CButton>(Point(218, 404), AnimationPath::builtin("IOKAY"), CButton::tooltip(), [this](){ close(); }, EShortcut::GLOBAL_ACCEPT);
+	labelTitle = std::make_shared<CLabel>(
+		pos.w / 2, 20, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, MetaString::createFromTextID("vcmi.keyBindings.button.hover").toString()
+	);
+	backgroundRect = std::make_shared<TransparentFilledRectangle>(Rect(8, 48, pos.w - 16, 348), ColorRGBA(0, 0, 0, 64), ColorRGBA(128, 100, 75), 1);
+
+	int count = 0;
+	for(auto & group : keyBindingsConfig.toJsonNode().Struct())
+	{
+		count++;
+		count += group.second.Struct().size();
+	}
+
+	slider = std::make_shared<CSlider>(Point(backgroundRect->pos.x - pos.x + backgroundRect->pos.w - 18, backgroundRect->pos.y - pos.y + 1), backgroundRect->pos.h - 3, [this](int pos){ fillList(pos); redraw(); }, MAX_LINES, count, 0, Orientation::VERTICAL, CSlider::BROWN);
+	slider->setPanningStep(LINE_HEIGHT);
+	slider->setScrollBounds(Rect(-backgroundRect->pos.w + slider->pos.w, 0, slider->pos.x - pos.x + slider->pos.w, slider->pos.h));
+
+	buttonReset = std::make_shared<CButton>(Point(411, 403), AnimationPath::builtin("settingsWindow/button80"), std::make_pair("", MetaString::createFromTextID("vcmi.keyBindings.reset.help").toString()));
+	buttonReset->setOverlay(std::make_shared<CLabel>(0, 0, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, MetaString::createFromTextID("vcmi.keyBindings.reset").toString()));
+	buttonReset->addCallback([this](){
+		GAME->interface()->showYesNoDialog(MetaString::createFromTextID("vcmi.keyBindings.resetConfirm").toString(), [this](){
+			resetKeyBinding();
+		}, nullptr);
+	});
+
+	fillList(0);
+}
+
+void KeyBindingsWindow::fillList(int start)
+{
+	OBJECT_CONSTRUCTION;
+
+	listElements.clear();
+	int i = 0;
+	[&]{
+		for(auto group = keyBindingsConfig.toJsonNode().Struct().rbegin(); group != keyBindingsConfig.toJsonNode().Struct().rend(); ++group)
+		{
+			if(i >= start)
+				listElements.push_back(std::make_shared<KeyBindingElement>(group->first, listElements.size()));
+			i++;
+			if(listElements.size() == MAX_LINES)
+				return;
+			for(auto & elem : group->second.Struct())
+			{
+				if(i >= start)
+					listElements.push_back(std::make_shared<KeyBindingElement>(elem.first, elem.second, listElements.size(), [this, group](const std::string & id, const std::string & keyName){
+						auto str = MetaString::createFromTextID("vcmi.keyBindings.inputSet");
+						str.replaceTextID("vcmi.keyBindings.keyBinding." + id);
+						str.replaceRawString(keyName);
+
+						GAME->interface()->showYesNoDialog(str.toString(), [this, group, id, keyName](){
+							setKeyBinding(id, group->first, keyName, true);
+						}, [this, group, id, keyName](){
+							setKeyBinding(id, group->first, keyName, false);
+						});
+					}));
+				i++;
+				if(listElements.size() == MAX_LINES)
+					return;
+			}
+		}
+	}();
+}
+
+void KeyBindingsWindow::setKeyBinding(const std::string & id, const std::string & group, const std::string & keyName, bool append)
+{
+	auto existing = keyBindingsConfig[group][id];
+	Settings existingWrite = keyBindingsConfig.write[group][id];
+	if((existing.isVector() || (existing.isString() && !existing.String().empty())) && append)
+	{
+		JsonVector tmp;
+		if(existing.isVector())
+			tmp = existing.Vector();
+		if(existing.isString())
+			tmp.push_back(existing);
+		if(!vstd::contains(tmp, JsonNode(keyName)))
+			tmp.push_back(JsonNode(keyName));
+		existingWrite->Vector() = tmp;
+	}
+	else
+		existingWrite->String() = keyName;
+
+	fillList(slider->getValue());
+}
+
+void KeyBindingsWindow::resetKeyBinding()
+{
+	{
+		Settings write = keyBindingsConfig.write;
+		write->clear();
+	}
+	{
+		Settings write = keyBindingsConfig.write;
+		write->Struct() = JsonUtils::assembleFromFiles("config/keyBindingsConfig.json").Struct();
+	}
+
+	fillList(slider->getValue());
+}
+
+KeyBindingElement::KeyBindingElement(std::string id, JsonNode keys, int elem, std::function<void(const std::string & id, const std::string & keyName)> func)
+	: func(func)
+{
+	OBJECT_CONSTRUCTION;
+
+	pos.x += 14;
+	pos.y += 56;
+	pos.y += elem * LINE_HEIGHT;
+
+	pos.w = 455;
+	pos.h = LINE_HEIGHT;
+
+	addUsedEvents(SHOW_POPUP);
+
+	popupText = MetaString::createFromTextID("vcmi.keyBindings.popup");
+	popupText.replaceTextID("vcmi.keyBindings.keyBinding." + id);
+
+	std::string keyBinding = "";
+	if(keys.isString())
+	{
+		keyBinding = keys.String();
+		popupText.appendRawString(keyBinding);
+	}
+	else if(keys.isVector())
+	{
+		std::vector<std::string> strings;
+		std::transform(keys.Vector().begin(), keys.Vector().end(), std::back_inserter(strings), [](const auto& k) { return k.String(); });
+		keyBinding = boost::join(strings, " | ");
+		popupText.appendRawString(boost::join(strings, "\n"));
+	}
+
+	labelName = std::make_shared<CLabel>(
+		0, LINE_HEIGHT / 2, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::WHITE, MetaString::createFromTextID("vcmi.keyBindings.keyBinding." + id).toString(), 245
+	);
+	labelKeys = std::make_shared<CLabel>(
+		250, LINE_HEIGHT / 2, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::WHITE, keyBinding, 170
+	);
+	buttonEdit = std::make_shared<CButton>(Point(422, 3), AnimationPath::builtin("settingsWindow/button32"), std::make_pair("", MetaString::createFromTextID("vcmi.keyBindings.editButton.help").toString()));
+	buttonEdit->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("settingsWindow/gear")));
+	buttonEdit->addCallback([id, func](){
+		ENGINE->windows().createAndPushWindow<KeyBindingsEditWindow>(id, [func](const std::string & id, const std::string & keyName){
+			if(func)
+				func(id, keyName);
+		});
+	});
+	if(elem < MAX_LINES - 1)
+		seperationLine = std::make_shared<TransparentFilledRectangle>(Rect(0, LINE_HEIGHT, 456, 1), ColorRGBA(0, 0, 0, 64), ColorRGBA(128, 100, 75), 1);
+}
+
+KeyBindingElement::KeyBindingElement(std::string group, int elem)
+	: func(nullptr)
+{
+	OBJECT_CONSTRUCTION;
+
+	pos.x += 14;
+	pos.y += 56;
+	pos.y += elem * LINE_HEIGHT;
+
+	labelName = std::make_shared<CLabel>(
+		0, LINE_HEIGHT / 2, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::YELLOW, MetaString::createFromTextID("vcmi.keyBindings.group." + group).toString(), 300
+	);
+	if(elem < MAX_LINES - 1)
+		seperationLine = std::make_shared<TransparentFilledRectangle>(Rect(0, LINE_HEIGHT, 456, 1), ColorRGBA(0, 0, 0, 64), ColorRGBA(128, 100, 75), 1);
+}
+
+void KeyBindingElement::showPopupWindow(const Point & cursorPosition)
+{
+	CRClickPopup::createAndPush(popupText.toString());
+}
+
+KeyBindingsEditWindow::KeyBindingsEditWindow(const std::string & id, std::function<void(const std::string & id, const std::string & keyName)> func)
+	: CWindowObject(BORDERED)
+	, id(id)
+	, func(func)
+{
+	OBJECT_CONSTRUCTION;
+	pos.w = 250;
+	pos.h = 150;
+
+	auto str = MetaString::createFromTextID("vcmi.keyBindings.input");
+	str.replaceTextID("vcmi.keyBindings.keyBinding." + id);
+
+	backgroundTexture = std::make_shared<CFilledTexture>(ImagePath::builtin("DiBoxBck"), Rect(0, 0, pos.w, pos.h));
+	text = std::make_shared<CTextBox>(str.toString(), Rect(0, 0, 250, 150), 0, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE);
+
+	updateShadow();
+	center();
+
+	addUsedEvents(LCLICK | KEY_NAME);
+}
+
+void KeyBindingsEditWindow::keyReleased(const std::string & keyName)
+{
+	if(boost::algorithm::ends_with(keyName, "Ctrl") || boost::algorithm::ends_with(keyName, "Shift") || boost::algorithm::ends_with(keyName, "Alt")) // skip if only control key pressed
+		return;
+	close();
+	func(id, keyName);
+}
+
+void KeyBindingsEditWindow::notFocusedClick()
+{
+	close(); // possibility to close without setting key (e.g. on touch screens)
+}

+ 78 - 0
client/windows/settings/KeyBindingsWindow.h

@@ -0,0 +1,78 @@
+/*
+ * KeyBindingsWindow.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+#include "../CWindowObject.h"
+#include "../../../lib/json/JsonNode.h"
+#include "../../../lib/texts/MetaString.h"
+
+class CFilledTexture;
+class CButton;
+class CLabel;
+class TransparentFilledRectangle;
+class CSlider;
+class CTextBox;
+
+const int MAX_LINES = 11;
+const int LINE_HEIGHT = 30;
+
+class KeyBindingElement : public CIntObject
+{
+private:
+	std::shared_ptr<CButton> buttonEdit;
+	std::shared_ptr<CLabel> labelName;
+	std::shared_ptr<CLabel> labelKeys;
+	std::shared_ptr<TransparentFilledRectangle> seperationLine; // rectangle is cleaner than line...
+
+	MetaString popupText;
+
+	std::function<void(const std::string & id, const std::string & keyName)> func;
+
+	void showPopupWindow(const Point & cursorPosition) override;
+public:
+	KeyBindingElement(std::string id, JsonNode keys, int elem, std::function<void(const std::string & id, const std::string & keyName)> func);
+	KeyBindingElement(std::string group, int elem);
+};
+
+class KeyBindingsWindow : public CWindowObject
+{
+private:
+	std::shared_ptr<CFilledTexture> backgroundTexture;
+	std::shared_ptr<CButton> buttonOk;
+	std::shared_ptr<CLabel> labelTitle;
+	std::shared_ptr<TransparentFilledRectangle> backgroundRect;
+	std::shared_ptr<CSlider> slider;
+	std::vector<std::shared_ptr<KeyBindingElement>> listElements;
+	std::shared_ptr<CButton> buttonReset;
+
+	void fillList(int start);
+	void setKeyBinding(const std::string & id, const std::string & group, const std::string & keyName, bool append);
+	void resetKeyBinding();
+
+public:
+	KeyBindingsWindow();
+};
+
+class KeyBindingsEditWindow : public CWindowObject
+{
+private:
+	std::shared_ptr<CFilledTexture> backgroundTexture;
+	std::shared_ptr<CTextBox> text;
+
+	std::string id;
+	std::function<void(const std::string & id, const std::string & keyName)> func;
+
+	void keyReleased(const std::string & keyName) override;
+	void notFocusedClick() override;
+public:
+	KeyBindingsEditWindow(const std::string & id, std::function<void(const std::string & id, const std::string & keyName)> func);
+};
+

+ 0 - 4
config/shortcutsConfig.json → config/keyBindingsConfig.json

@@ -1,9 +1,5 @@
 // This file defines all shortcuts used by VCMI
-// For modders: create file with same name (Content/config/shortcutsConfig.json) to modify this set in your mod
-// For players (Windows): create file Documents/My Games/vcmi/config/shortcutsConfig.json to modify this set
-// For players (Linux): create file ~/.config/vcmi/shortcutsConfig.json (or ~/.var/app/eu.vcmi.VCMI/config for Flatpak) to modify this set
 //
-// When creating your own config, you can remove all hotkeys that you have not changed and game will read them from this file
 // It is possible to add modifiers to keys: Ctrl, Shift, or Alt. For example, "Ctrl+Tab" hotkey will only activate if Ctrl is pressed
 {
 	"keyboard" : {

+ 9 - 0
config/widgets/settings/adventureOptionsTab.json

@@ -305,6 +305,9 @@
 				},
 				{
 					"text": "vcmi.adventureOptions.minimapShowHeroes.hover"
+				},
+				{
+					"text": "vcmi.keyBindings.button.hover"
 				}
 			]
 		},
@@ -343,6 +346,12 @@
 					"name": "minimapShowHeroesCheckbox",
 					"help": "vcmi.adventureOptions.minimapShowHeroes",
 					"callback": "minimapShowHeroesChanged"
+				},
+				{
+					"name": "shortcutButton",
+					"type": "buttonGear",
+					"help": "vcmi.keyBindings.button",
+					"callback": "openShortcutMenu"
 				}
 			]
 		},

+ 1 - 0
launcher/settingsView/configeditordialog_moc.cpp

@@ -37,6 +37,7 @@ ConfigEditorDialog::ConfigEditorDialog(QWidget *parent):
 		"settings.json",
 		"persistentStorage.json",
 		"modSettings.json",
+		"keyBindingsConfig.json",
 	};
 	ui->comboBox->addItems(files);
 

+ 1 - 0
lib/CConfigHandler.cpp

@@ -19,6 +19,7 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 SettingsStorage settings;
 SettingsStorage persistentStorage;
+SettingsStorage keyBindingsConfig;
 
 template<typename Accessor>
 SettingsStorage::NodeAccessor<Accessor>::NodeAccessor(SettingsStorage & _parent, std::vector<std::string> _path):

+ 1 - 0
lib/CConfigHandler.h

@@ -122,5 +122,6 @@ public:
 
 extern DLL_LINKAGE SettingsStorage settings;
 extern DLL_LINKAGE SettingsStorage persistentStorage;
+extern DLL_LINKAGE SettingsStorage keyBindingsConfig;
 
 VCMI_LIB_NAMESPACE_END

+ 1 - 0
lib/GameLibrary.cpp

@@ -160,6 +160,7 @@ void GameLibrary::initializeFilesystem(bool extractArchives)
 	loadFilesystem(extractArchives);
 	settings.init("config/settings.json", "vcmi:settings");
 	persistentStorage.init("config/persistentStorage.json", "");
+	keyBindingsConfig.init("config/keyBindingsConfig.json", "");
 	loadModFilesystem();
 }