// Copyright (c) 2013- PPSSPP Project. // 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, version 2.0 or later versions. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "base/logging.h" #include "i18n/i18n.h" #include "input/keycodes.h" #include "input/input_state.h" #include "ui/ui.h" #include "ui/ui_context.h" #include "ui/view.h" #include "ui/viewgroup.h" #include "Core/HLE/sceCtrl.h" #include "Common/KeyMap.h" #include "Core/Config.h" #include "UI/ui_atlas.h" #include "UI/ControlMappingScreen.h" #include "UI/UIShader.h" extern void DrawBackground(float alpha); class ControlMapper : public UI::LinearLayout { public: ControlMapper(int pspKey, std::string keyName, ScreenManager *scrm, UI::LinearLayoutParams *layoutParams = 0); virtual void Update(const InputState &input); private: void Refresh(); UI::EventReturn OnAdd(UI::EventParams ¶ms); UI::EventReturn OnDelete(UI::EventParams ¶ms); UI::EventReturn OnReplace(UI::EventParams ¶ms); UI::EventReturn OnReplaceAll(UI::EventParams ¶ms); void MappedCallback(KeyDef key); enum Action { NONE, REPLACEONE, REPLACEALL, ADD, }; Action action_; int actionIndex_; int pspKey_; std::string keyName_; ScreenManager *scrm_; bool refresh_; }; ControlMapper::ControlMapper(int pspKey, std::string keyName, ScreenManager *scrm, UI::LinearLayoutParams *layoutParams) : UI::LinearLayout(UI::ORIENT_VERTICAL, layoutParams), action_(NONE), pspKey_(pspKey), keyName_(keyName), scrm_(scrm), refresh_(false) { Refresh(); } void ControlMapper::Update(const InputState &input) { if (refresh_) { refresh_ = false; Refresh(); } } void ControlMapper::Refresh() { Clear(); using namespace UI; LinearLayout *root = Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); root->Add(new Choice(keyName_, new LinearLayoutParams(200, WRAP_CONTENT)))->OnClick.Handle(this, &ControlMapper::OnReplaceAll); LinearLayout *rightColumn = root->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f))); std::vector mappings; KeyMap::KeyFromPspButton(pspKey_, &mappings); for (size_t i = 0; i < mappings.size(); i++) { std::string deviceName = GetDeviceName(mappings[i].deviceId); std::string keyName = KeyMap::GetKeyOrAxisName(mappings[i].keyCode); int image = -1; LinearLayout *row = rightColumn->Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); Choice *c = row->Add(new Choice(deviceName + "." + keyName, new LinearLayoutParams(1.0f))); char tagbuf[16]; sprintf(tagbuf, "%i", i); c->SetTag(tagbuf); c->OnClick.Handle(this, &ControlMapper::OnReplace); Choice *d = row->Add(new Choice("X")); d->SetTag(tagbuf); d->OnClick.Handle(this, &ControlMapper::OnDelete); row->Add(new Choice("+"))->OnClick.Handle(this, &ControlMapper::OnAdd); } if (mappings.size() == 0) { // look like an empty line rightColumn->Add(new Choice("", new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT)))->OnClick.Handle(this, &ControlMapper::OnAdd); } } void ControlMapper::MappedCallback(KeyDef kdf) { switch (action_) { case ADD: KeyMap::SetKeyMapping(pspKey_, kdf, false); break; case REPLACEALL: KeyMap::SetKeyMapping(pspKey_, kdf, true); break; case REPLACEONE: KeyMap::g_controllerMap[pspKey_][actionIndex_] = kdf; break; default: ; } refresh_ = true; } UI::EventReturn ControlMapper::OnReplace(UI::EventParams ¶ms) { actionIndex_ = atoi(params.v->Tag().c_str()); action_ = REPLACEONE; scrm_->push(new KeyMappingNewKeyDialog(pspKey_, true, std::bind(&ControlMapper::MappedCallback, this, placeholder::_1))); return UI::EVENT_DONE; } UI::EventReturn ControlMapper::OnReplaceAll(UI::EventParams ¶ms) { action_ = REPLACEALL; scrm_->push(new KeyMappingNewKeyDialog(pspKey_, true, std::bind(&ControlMapper::MappedCallback, this, placeholder::_1))); return UI::EVENT_DONE; } UI::EventReturn ControlMapper::OnAdd(UI::EventParams ¶ms) { action_ = ADD; scrm_->push(new KeyMappingNewKeyDialog(pspKey_, true, std::bind(&ControlMapper::MappedCallback, this, placeholder::_1))); return UI::EVENT_DONE; } UI::EventReturn ControlMapper::OnDelete(UI::EventParams ¶ms) { int index = atoi(params.v->Tag().c_str()); KeyMap::g_controllerMap[pspKey_].erase(KeyMap::g_controllerMap[pspKey_].begin() + index); refresh_ = true; return UI::EVENT_DONE; } void ControlMappingScreen::CreateViews() { using namespace UI; I18NCategory *k = GetI18NCategory("KeyMapping"); I18NCategory *g = GetI18NCategory("General"); root_ = new LinearLayout(ORIENT_HORIZONTAL); LinearLayout *leftColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(200, FILL_PARENT)); leftColumn->Add(new Choice(k->T("Clear All")))->OnClick.Handle(this, &ControlMappingScreen::OnClearMapping); leftColumn->Add(new Choice(k->T("Default All")))->OnClick.Handle(this, &ControlMappingScreen::OnDefaultMapping); leftColumn->Add(new Spacer(new LinearLayoutParams(1.0f))); leftColumn->Add(new Choice(g->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); /* ChoiceStrip *mode = leftColumn->Add(new ChoiceStrip(ORIENT_VERTICAL)); mode->AddChoice("Replace"); mode->AddChoice("Add"); */ ScrollView *rightScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)); LinearLayout *rightColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)); rightScroll->Add(rightColumn); root_->Add(leftColumn); root_->Add(rightScroll); std::vector mappableKeys = KeyMap::GetMappableKeys(); for (size_t i = 0; i < mappableKeys.size(); i++) { rightColumn->Add(new ControlMapper(mappableKeys[i].key, mappableKeys[i].name, screenManager(), new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); } } UI::EventReturn ControlMappingScreen::OnClearMapping(UI::EventParams ¶ms) { KeyMap::g_controllerMap.clear(); RecreateViews(); return UI::EVENT_DONE; } UI::EventReturn ControlMappingScreen::OnDefaultMapping(UI::EventParams ¶ms) { KeyMap::RestoreDefault(); RecreateViews(); return UI::EVENT_DONE; } void KeyMappingNewKeyDialog::CreatePopupContents(UI::ViewGroup *parent) { using namespace UI; I18NCategory *keyI18N = GetI18NCategory("KeyMapping"); I18NCategory *generalI18N = GetI18NCategory("General"); std::string pspButtonName = KeyMap::GetPspButtonName(this->pspBtn_); parent->Add(new TextView(std::string(keyI18N->T("Map a new key for ")) + pspButtonName)); } void KeyMappingNewKeyDialog::key(const KeyInput &key) { if (key.flags & KEY_DOWN) { if (key.keyCode == NKCODE_EXT_MOUSEBUTTON_1) { return; } KeyDef kdf(key.deviceId, key.keyCode); screenManager()->finishDialog(this, DR_OK); if (callback_) callback_(kdf); } } void KeyMappingNewKeyDialog::axis(const AxisInput &axis) { if (axis.value > AXIS_BIND_THRESHOLD) { KeyDef kdf(axis.deviceId, KeyMap::TranslateKeyCodeFromAxis(axis.axisId, 1)); screenManager()->finishDialog(this, DR_OK); if (callback_) callback_(kdf); } if (axis.value < -AXIS_BIND_THRESHOLD) { KeyDef kdf(axis.deviceId, KeyMap::TranslateKeyCodeFromAxis(axis.axisId, -1)); screenManager()->finishDialog(this, DR_OK); if (callback_) callback_(kdf); } }