2020-06-02 14:59:30 -03:00
|
|
|
|
/*
|
|
|
|
|
|
* Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com>
|
|
|
|
|
|
*
|
2021-04-22 05:24:48 -03:00
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-06-02 14:59:30 -03:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "KeyboardMapperWidget.h"
|
|
|
|
|
|
#include "KeyPositions.h"
|
|
|
|
|
|
#include <LibCore/File.h>
|
|
|
|
|
|
#include <LibGUI/BoxLayout.h>
|
|
|
|
|
|
#include <LibGUI/InputBox.h>
|
|
|
|
|
|
#include <LibGUI/MessageBox.h>
|
|
|
|
|
|
#include <LibGUI/RadioButton.h>
|
2021-01-30 18:30:46 -03:00
|
|
|
|
#include <LibKeyboard/CharacterMap.h>
|
2020-06-02 14:59:30 -03:00
|
|
|
|
#include <LibKeyboard/CharacterMapFile.h>
|
2020-09-27 07:23:52 -03:00
|
|
|
|
#include <string.h>
|
2020-06-02 14:59:30 -03:00
|
|
|
|
|
|
|
|
|
|
KeyboardMapperWidget::KeyboardMapperWidget()
|
|
|
|
|
|
{
|
|
|
|
|
|
create_frame();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
KeyboardMapperWidget::~KeyboardMapperWidget()
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void KeyboardMapperWidget::create_frame()
|
|
|
|
|
|
{
|
|
|
|
|
|
set_fill_with_background_color(true);
|
|
|
|
|
|
set_layout<GUI::VerticalBoxLayout>();
|
Userland+LibGUI: Add shorthand versions of the Margins constructor
This allows for typing [8] instead of [8, 8, 8, 8] to specify the same
margin on all edges, for example. The constructors follow CSS' style of
specifying margins. The added constructors are:
- Margins(int all): Sets the same margin on all edges.
- Margins(int vertical, int horizontal): Sets the first argument to top
and bottom margins, and the second argument to left and right margins.
- Margins(int top, int vertical, int bottom): Sets the first argument to
the top margin, the second argument to the left and right margins,
and the third argument to the bottom margin.
2021-08-16 21:11:38 -03:00
|
|
|
|
layout()->set_margins(4);
|
2020-06-02 14:59:30 -03:00
|
|
|
|
|
|
|
|
|
|
auto& main_widget = add<GUI::Widget>();
|
|
|
|
|
|
main_widget.set_relative_rect(0, 0, 200, 200);
|
|
|
|
|
|
|
|
|
|
|
|
m_keys.resize(KEY_COUNT);
|
|
|
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < KEY_COUNT; i++) {
|
2020-06-10 05:57:59 -03:00
|
|
|
|
Gfx::IntRect rect = { keys[i].x, keys[i].y, keys[i].width, keys[i].height };
|
2020-06-02 14:59:30 -03:00
|
|
|
|
|
|
|
|
|
|
auto& tmp_button = main_widget.add<KeyButton>();
|
|
|
|
|
|
tmp_button.set_relative_rect(rect);
|
|
|
|
|
|
tmp_button.set_text(keys[i].name);
|
|
|
|
|
|
tmp_button.set_enabled(keys[i].enabled);
|
|
|
|
|
|
|
2020-09-13 16:17:21 -03:00
|
|
|
|
tmp_button.on_click = [this, &tmp_button]() {
|
2020-07-16 10:54:42 -03:00
|
|
|
|
String value;
|
2021-02-20 08:03:28 -03:00
|
|
|
|
if (GUI::InputBox::show(window(), value, "New Character:", "Select Character") == GUI::InputBox::ExecOK) {
|
2020-06-02 14:59:30 -03:00
|
|
|
|
int i = m_keys.find_first_index(&tmp_button).value_or(0);
|
2021-02-23 16:42:32 -03:00
|
|
|
|
VERIFY(i > 0);
|
2020-06-02 14:59:30 -03:00
|
|
|
|
|
|
|
|
|
|
auto index = keys[i].map_index;
|
2021-02-23 16:42:32 -03:00
|
|
|
|
VERIFY(index > 0);
|
2020-06-02 14:59:30 -03:00
|
|
|
|
|
|
|
|
|
|
tmp_button.set_text(value);
|
2020-06-13 07:52:56 -03:00
|
|
|
|
u32* map;
|
2020-06-02 14:59:30 -03:00
|
|
|
|
|
|
|
|
|
|
if (m_current_map_name == "map") {
|
|
|
|
|
|
map = m_character_map.map;
|
|
|
|
|
|
} else if (m_current_map_name == "shift_map") {
|
|
|
|
|
|
map = m_character_map.shift_map;
|
|
|
|
|
|
} else if (m_current_map_name == "alt_map") {
|
|
|
|
|
|
map = m_character_map.alt_map;
|
|
|
|
|
|
} else if (m_current_map_name == "altgr_map") {
|
|
|
|
|
|
map = m_character_map.altgr_map;
|
2021-01-05 05:46:36 -03:00
|
|
|
|
} else if (m_current_map_name == "shift_altgr_map") {
|
|
|
|
|
|
map = m_character_map.shift_altgr_map;
|
2020-06-02 14:59:30 -03:00
|
|
|
|
} else {
|
2021-02-23 16:42:32 -03:00
|
|
|
|
VERIFY_NOT_REACHED();
|
2020-06-02 14:59:30 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (value.length() == 0)
|
|
|
|
|
|
map[index] = '\0'; // Empty string
|
|
|
|
|
|
else
|
|
|
|
|
|
map[index] = value[0];
|
|
|
|
|
|
|
|
|
|
|
|
m_modified = true;
|
|
|
|
|
|
update_window_title();
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
m_keys.insert(i, &tmp_button);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Action Buttons
|
|
|
|
|
|
auto& bottom_widget = add<GUI::Widget>();
|
|
|
|
|
|
bottom_widget.set_layout<GUI::HorizontalBoxLayout>();
|
2020-12-29 21:23:32 -03:00
|
|
|
|
bottom_widget.set_fixed_height(40);
|
2020-06-02 14:59:30 -03:00
|
|
|
|
|
|
|
|
|
|
// Map Selection
|
|
|
|
|
|
m_map_group = bottom_widget.add<GUI::Widget>();
|
|
|
|
|
|
m_map_group->set_layout<GUI::HorizontalBoxLayout>();
|
2021-01-30 02:56:24 -03:00
|
|
|
|
m_map_group->set_fixed_width(450);
|
2020-06-02 14:59:30 -03:00
|
|
|
|
|
|
|
|
|
|
auto& radio_map = m_map_group->add<GUI::RadioButton>("Default");
|
|
|
|
|
|
radio_map.set_name("map");
|
|
|
|
|
|
radio_map.on_checked = [&](bool) {
|
|
|
|
|
|
set_current_map("map");
|
|
|
|
|
|
};
|
|
|
|
|
|
auto& radio_shift = m_map_group->add<GUI::RadioButton>("Shift");
|
|
|
|
|
|
radio_shift.set_name("shift_map");
|
2020-09-13 16:17:21 -03:00
|
|
|
|
radio_shift.on_checked = [this](bool) {
|
2020-06-02 14:59:30 -03:00
|
|
|
|
set_current_map("shift_map");
|
|
|
|
|
|
};
|
|
|
|
|
|
auto& radio_altgr = m_map_group->add<GUI::RadioButton>("AltGr");
|
|
|
|
|
|
radio_altgr.set_name("altgr_map");
|
2020-09-13 16:17:21 -03:00
|
|
|
|
radio_altgr.on_checked = [this](bool) {
|
2020-06-02 14:59:30 -03:00
|
|
|
|
set_current_map("altgr_map");
|
|
|
|
|
|
};
|
|
|
|
|
|
auto& radio_alt = m_map_group->add<GUI::RadioButton>("Alt");
|
|
|
|
|
|
radio_alt.set_name("alt_map");
|
2020-09-13 16:17:21 -03:00
|
|
|
|
radio_alt.on_checked = [this](bool) {
|
2020-06-02 14:59:30 -03:00
|
|
|
|
set_current_map("alt_map");
|
|
|
|
|
|
};
|
2021-01-05 05:46:36 -03:00
|
|
|
|
auto& radio_shift_altgr = m_map_group->add<GUI::RadioButton>("Shift+AltGr");
|
|
|
|
|
|
radio_shift_altgr.set_name("shift_altgr_map");
|
|
|
|
|
|
radio_shift_altgr.on_checked = [this](bool) {
|
|
|
|
|
|
set_current_map("shift_altgr_map");
|
|
|
|
|
|
};
|
2020-06-02 14:59:30 -03:00
|
|
|
|
|
|
|
|
|
|
bottom_widget.layout()->add_spacer();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-04-29 16:46:15 -03:00
|
|
|
|
void KeyboardMapperWidget::load_from_file(String filename)
|
2020-06-02 14:59:30 -03:00
|
|
|
|
{
|
2021-04-29 16:46:15 -03:00
|
|
|
|
auto result = Keyboard::CharacterMapFile::load_from_file(filename);
|
2021-06-03 12:19:07 -03:00
|
|
|
|
if (!result.has_value()) {
|
2021-06-04 15:19:01 -03:00
|
|
|
|
auto error_message = String::formatted("Failed to load character map from file {}", filename);
|
|
|
|
|
|
GUI::MessageBox::show(window(), error_message, "Error", GUI::MessageBox::Type::Error);
|
2021-06-03 12:19:07 -03:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2020-06-02 14:59:30 -03:00
|
|
|
|
|
2021-04-29 16:46:15 -03:00
|
|
|
|
m_filename = filename;
|
2020-06-02 14:59:30 -03:00
|
|
|
|
m_character_map = result.value();
|
|
|
|
|
|
set_current_map("map");
|
|
|
|
|
|
|
2021-06-08 12:06:27 -03:00
|
|
|
|
for (auto& widget : m_map_group->child_widgets()) {
|
|
|
|
|
|
auto& radio_button = static_cast<GUI::RadioButton&>(widget);
|
|
|
|
|
|
radio_button.set_checked(radio_button.name() == "map");
|
2020-06-02 14:59:30 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
update_window_title();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-30 18:30:46 -03:00
|
|
|
|
void KeyboardMapperWidget::load_from_system()
|
|
|
|
|
|
{
|
|
|
|
|
|
auto result = Keyboard::CharacterMap::fetch_system_map();
|
2021-02-23 16:42:32 -03:00
|
|
|
|
VERIFY(!result.is_error());
|
2021-01-30 18:30:46 -03:00
|
|
|
|
|
2021-04-29 16:46:15 -03:00
|
|
|
|
m_filename = String::formatted("/res/keymaps/{}.json", result.value().character_map_name());
|
2021-01-30 18:30:46 -03:00
|
|
|
|
m_character_map = result.value().character_map_data();
|
|
|
|
|
|
set_current_map("map");
|
|
|
|
|
|
|
2021-06-08 12:06:27 -03:00
|
|
|
|
for (auto& widget : m_map_group->child_widgets()) {
|
|
|
|
|
|
auto& radio_button = static_cast<GUI::RadioButton&>(widget);
|
|
|
|
|
|
radio_button.set_checked(radio_button.name() == "map");
|
2021-01-30 18:30:46 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
update_window_title();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-02 14:59:30 -03:00
|
|
|
|
void KeyboardMapperWidget::save()
|
|
|
|
|
|
{
|
2021-04-29 16:46:15 -03:00
|
|
|
|
save_to_file(m_filename);
|
2020-06-02 14:59:30 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-04-29 16:46:15 -03:00
|
|
|
|
void KeyboardMapperWidget::save_to_file(const StringView& filename)
|
2020-06-02 14:59:30 -03:00
|
|
|
|
{
|
|
|
|
|
|
JsonObject map_json;
|
|
|
|
|
|
|
2020-06-13 07:52:56 -03:00
|
|
|
|
auto add_array = [&](String name, u32* values) {
|
2020-06-02 14:59:30 -03:00
|
|
|
|
JsonArray items;
|
|
|
|
|
|
for (int i = 0; i < 90; i++) {
|
2021-02-25 17:10:47 -03:00
|
|
|
|
StringBuilder sb;
|
2021-01-30 16:13:55 -03:00
|
|
|
|
if (values[i])
|
|
|
|
|
|
sb.append_code_point(values[i]);
|
2020-06-02 14:59:30 -03:00
|
|
|
|
|
|
|
|
|
|
JsonValue val(sb.to_string());
|
|
|
|
|
|
items.append(move(val));
|
|
|
|
|
|
}
|
|
|
|
|
|
map_json.set(name, move(items));
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
add_array("map", m_character_map.map);
|
|
|
|
|
|
add_array("shift_map", m_character_map.shift_map);
|
|
|
|
|
|
add_array("alt_map", m_character_map.alt_map);
|
|
|
|
|
|
add_array("altgr_map", m_character_map.altgr_map);
|
2021-01-05 05:46:36 -03:00
|
|
|
|
add_array("shift_altgr_map", m_character_map.shift_altgr_map);
|
2020-06-02 14:59:30 -03:00
|
|
|
|
|
|
|
|
|
|
// Write to file.
|
|
|
|
|
|
String file_content = map_json.to_string();
|
|
|
|
|
|
|
2021-04-29 16:46:15 -03:00
|
|
|
|
auto file = Core::File::construct(filename);
|
2021-05-12 06:26:43 -03:00
|
|
|
|
file->open(Core::OpenMode::WriteOnly);
|
2020-06-02 14:59:30 -03:00
|
|
|
|
if (!file->is_open()) {
|
|
|
|
|
|
StringBuilder sb;
|
|
|
|
|
|
sb.append("Failed to open ");
|
2021-04-29 16:46:15 -03:00
|
|
|
|
sb.append(filename);
|
2020-06-02 14:59:30 -03:00
|
|
|
|
sb.append(" for write. Error: ");
|
|
|
|
|
|
sb.append(file->error_string());
|
|
|
|
|
|
|
2020-07-15 23:45:11 -03:00
|
|
|
|
GUI::MessageBox::show(window(), sb.to_string(), "Error", GUI::MessageBox::Type::Error);
|
2020-06-02 14:59:30 -03:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool result = file->write(file_content);
|
|
|
|
|
|
if (!result) {
|
|
|
|
|
|
int error_number = errno;
|
|
|
|
|
|
StringBuilder sb;
|
|
|
|
|
|
sb.append("Unable to save file. Error: ");
|
|
|
|
|
|
sb.append(strerror(error_number));
|
|
|
|
|
|
|
2020-07-15 23:45:11 -03:00
|
|
|
|
GUI::MessageBox::show(window(), sb.to_string(), "Error", GUI::MessageBox::Type::Error);
|
2020-06-02 14:59:30 -03:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m_modified = false;
|
2021-04-29 16:46:15 -03:00
|
|
|
|
m_filename = filename;
|
2020-06-02 14:59:30 -03:00
|
|
|
|
update_window_title();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void KeyboardMapperWidget::keydown_event(GUI::KeyEvent& event)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < KEY_COUNT; i++) {
|
|
|
|
|
|
auto& tmp_button = m_keys.at(i);
|
|
|
|
|
|
tmp_button->set_pressed(keys[i].scancode == event.scancode());
|
|
|
|
|
|
tmp_button->update();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void KeyboardMapperWidget::keyup_event(GUI::KeyEvent& event)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < KEY_COUNT; i++) {
|
|
|
|
|
|
if (keys[i].scancode == event.scancode()) {
|
|
|
|
|
|
auto& tmp_button = m_keys.at(i);
|
|
|
|
|
|
tmp_button->set_pressed(false);
|
|
|
|
|
|
tmp_button->update();
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void KeyboardMapperWidget::set_current_map(const String current_map)
|
|
|
|
|
|
{
|
|
|
|
|
|
m_current_map_name = current_map;
|
2020-06-13 07:52:56 -03:00
|
|
|
|
u32* map;
|
2020-06-02 14:59:30 -03:00
|
|
|
|
|
|
|
|
|
|
if (m_current_map_name == "map") {
|
|
|
|
|
|
map = m_character_map.map;
|
|
|
|
|
|
} else if (m_current_map_name == "shift_map") {
|
|
|
|
|
|
map = m_character_map.shift_map;
|
|
|
|
|
|
} else if (m_current_map_name == "alt_map") {
|
|
|
|
|
|
map = m_character_map.alt_map;
|
|
|
|
|
|
} else if (m_current_map_name == "altgr_map") {
|
|
|
|
|
|
map = m_character_map.altgr_map;
|
2021-01-05 05:46:36 -03:00
|
|
|
|
} else if (m_current_map_name == "shift_altgr_map") {
|
|
|
|
|
|
map = m_character_map.shift_altgr_map;
|
2020-06-02 14:59:30 -03:00
|
|
|
|
} else {
|
2021-02-23 16:42:32 -03:00
|
|
|
|
VERIFY_NOT_REACHED();
|
2020-06-02 14:59:30 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (unsigned k = 0; k < KEY_COUNT; k++) {
|
|
|
|
|
|
auto index = keys[k].map_index;
|
|
|
|
|
|
if (index == 0)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
2021-02-25 17:10:47 -03:00
|
|
|
|
StringBuilder sb;
|
2020-08-05 17:31:20 -03:00
|
|
|
|
sb.append_code_point(map[index]);
|
2020-06-02 14:59:30 -03:00
|
|
|
|
|
|
|
|
|
|
m_keys.at(k)->set_text(sb.to_string());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this->update();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void KeyboardMapperWidget::update_window_title()
|
|
|
|
|
|
{
|
|
|
|
|
|
StringBuilder sb;
|
2021-04-29 16:46:15 -03:00
|
|
|
|
sb.append(m_filename);
|
2020-06-02 14:59:30 -03:00
|
|
|
|
if (m_modified)
|
|
|
|
|
|
sb.append(" (*)");
|
2021-05-12 19:06:06 -03:00
|
|
|
|
sb.append(" - Keyboard Mapper");
|
2020-06-02 14:59:30 -03:00
|
|
|
|
|
|
|
|
|
|
window()->set_title(sb.to_string());
|
|
|
|
|
|
}
|