From d3578fdf9b8410e21ed8b30babfb3488a8ca8e47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=BCseyin=20ASLIT=C3=9CRK?= Date: Tue, 2 Jun 2020 20:59:30 +0300 Subject: [PATCH] Applications: Add new KeyboardMapper application New editing app for keymap files. --- Applications/CMakeLists.txt | 1 + Applications/KeyboardMapper/CMakeLists.txt | 8 + Applications/KeyboardMapper/KeyButton.cpp | 68 +++++ Applications/KeyboardMapper/KeyButton.h | 47 +++ Applications/KeyboardMapper/KeyPositions.h | 115 +++++++ .../KeyboardMapper/KeyboardMapperWidget.cpp | 289 ++++++++++++++++++ .../KeyboardMapper/KeyboardMapperWidget.h | 60 ++++ Applications/KeyboardMapper/main.cpp | 114 +++++++ 8 files changed, 702 insertions(+) create mode 100644 Applications/KeyboardMapper/CMakeLists.txt create mode 100644 Applications/KeyboardMapper/KeyButton.cpp create mode 100644 Applications/KeyboardMapper/KeyButton.h create mode 100644 Applications/KeyboardMapper/KeyPositions.h create mode 100644 Applications/KeyboardMapper/KeyboardMapperWidget.cpp create mode 100644 Applications/KeyboardMapper/KeyboardMapperWidget.h create mode 100644 Applications/KeyboardMapper/main.cpp diff --git a/Applications/CMakeLists.txt b/Applications/CMakeLists.txt index ad5c7bdaf9..ecdf208ebf 100644 --- a/Applications/CMakeLists.txt +++ b/Applications/CMakeLists.txt @@ -9,6 +9,7 @@ add_subdirectory(FontEditor) add_subdirectory(Help) add_subdirectory(HexEditor) add_subdirectory(IRCClient) +add_subdirectory(KeyboardMapper) add_subdirectory(Piano) add_subdirectory(PixelPaint) add_subdirectory(QuickShow) diff --git a/Applications/KeyboardMapper/CMakeLists.txt b/Applications/KeyboardMapper/CMakeLists.txt new file mode 100644 index 0000000000..fe24376eb1 --- /dev/null +++ b/Applications/KeyboardMapper/CMakeLists.txt @@ -0,0 +1,8 @@ +set(SOURCES + KeyboardMapperWidget.cpp + KeyButton.cpp + main.cpp +) + +serenity_bin(KeyboardMapper) +target_link_libraries(KeyboardMapper LibGUI LibKeyboard) diff --git a/Applications/KeyboardMapper/KeyButton.cpp b/Applications/KeyboardMapper/KeyButton.cpp new file mode 100644 index 0000000000..78e8195cca --- /dev/null +++ b/Applications/KeyboardMapper/KeyButton.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2020, Hüseyin Aslıtürk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "KeyButton.h" +#include +#include +#include +#include + +KeyButton::~KeyButton() +{ +} + +void KeyButton::paint_event(GUI::PaintEvent& event) +{ + GUI::Painter painter(*this); + painter.add_clip_rect(event.rect()); + + auto content_rect = rect().shrunken(5, 5); + auto& font = this->font(); + + Gfx::StylePainter::paint_button(painter, rect(), palette(), Gfx::ButtonStyle::Normal, is_being_pressed(), is_hovered(), is_checked(), true); + + if (m_pressed) + painter.fill_rect(content_rect, Color::Cyan); + else if (!is_enabled()) + painter.fill_rect(content_rect, Color::from_rgb(0x8C7272)); + + if (!text().is_empty()) { + Gfx::Rect text_rect { 0, 0, font.width(text()), font.glyph_height() }; + text_rect.align_within(content_rect, Gfx::TextAlignment::Center); + + auto clipped_rect = rect().intersected(this->rect()); + + painter.draw_text(clipped_rect, text(), font, Gfx::TextAlignment::Center, palette().button_text(), Gfx::TextElision::Right); + if (is_focused()) + painter.draw_rect(clipped_rect.inflated(6, 4), palette().focus_outline()); + } +} + +void KeyButton::click(unsigned) +{ + if (on_click) + on_click(); +} diff --git a/Applications/KeyboardMapper/KeyButton.h b/Applications/KeyboardMapper/KeyButton.h new file mode 100644 index 0000000000..7f1c20f122 --- /dev/null +++ b/Applications/KeyboardMapper/KeyButton.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020, Hüseyin Aslıtürk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +class KeyButton : public GUI::AbstractButton { + C_OBJECT(KeyButton) + +public: + virtual ~KeyButton() override; + + void set_pressed(bool value) { m_pressed = value; } + + Function on_click; + +protected: + virtual void click(unsigned modifiers = 0) override; + virtual void paint_event(GUI::PaintEvent&) override; + +private: + bool m_pressed { false }; +}; diff --git a/Applications/KeyboardMapper/KeyPositions.h b/Applications/KeyboardMapper/KeyPositions.h new file mode 100644 index 0000000000..d69ead8192 --- /dev/null +++ b/Applications/KeyboardMapper/KeyPositions.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2020, Hüseyin Aslıtürk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +struct KeyPosition { + u32 scancode; + int x; + int y; + int width; + int height; + bool enabled; + int map_index; + AK::String name; +}; + +#define KEY_COUNT 63 + +struct KeyPosition keys[KEY_COUNT] = { + [ 0] = { 0, 0, 0, 0, 0, false, 0, ""}, + + [ 1] = { 0x29, 0, 0, 50, 50, true, 41, "`"}, + [ 2] = { 0x02, 51, 0, 50, 50, true, 2, "1"}, + [ 3] = { 0x03, 102, 0, 50, 50, true, 3, "2"}, + [ 4] = { 0x04, 153, 0, 50, 50, true, 4, "3"}, + [ 5] = { 0x05, 204, 0, 50, 50, true, 5, "4"}, + [ 6] = { 0x06, 255, 0, 50, 50, true, 6, "5"}, + [ 7] = { 0x07, 306, 0, 50, 50, true, 7, "6"}, + [ 8] = { 0x08, 357, 0, 50, 50, true, 8, "7"}, + [ 9] = { 0x09, 408, 0, 50, 50, true, 9, "8"}, + [10] = { 0x0A, 459, 0, 50, 50, true, 10, "9"}, + [11] = { 0x0B, 510, 0, 50, 50, true, 11, "0"}, + [12] = { 0x0C, 561, 0, 50, 50, true, 12, "-"}, + [13] = { 0x0D, 612, 0, 50, 50, true, 13, "="}, + [14] = { 0x0E, 663, 0, 100, 50, false, 0, "back space"}, + + + [15] = { 0x0F, 0, 52, 76, 50, false, 0, "tab"}, + [16] = { 0x10, 77, 52, 50, 50, true, 16, "q"}, + [17] = { 0x11, 128, 52, 50, 50, true, 17, "w"}, + [18] = { 0x12, 179, 52, 50, 50, true, 18, "e"}, + [19] = { 0x13, 230, 52, 50, 50, true, 19, "r"}, + [20] = { 0x14, 281, 52, 50, 50, true, 20, "t"}, + [21] = { 0x15, 332, 52, 50, 50, true, 21, "y"}, + [22] = { 0x16, 383, 52, 50, 50, true, 22, "u"}, + [23] = { 0x17, 434, 52, 50, 50, true, 23, "ı"}, + [24] = { 0x18, 485, 52, 50, 50, true, 24, "o"}, + [25] = { 0x19, 536, 52, 50, 50, true, 25, "p"}, + [26] = { 0x1A, 587, 52, 50, 50, true, 26, "["}, + [27] = { 0x1B, 638, 52, 50, 50, true, 27, "]"}, + [28] = { 0x1C, 689, 52, 74, 50, false, 0, "enter"}, + + + [29] = { 0x3A, 0, 104, 101, 50, false, 0, "caps lock"}, + [30] = { 0x1E, 103, 104, 50, 50, true, 30, "a"}, + [31] = { 0x1F, 154, 104, 50, 50, true, 31, "s"}, + [32] = { 0x20, 205, 104, 50, 50, true, 32, "d"}, + [33] = { 0x21, 256, 104, 50, 50, true, 33, "f"}, + [34] = { 0x22, 307, 104, 50, 50, true, 34, "g"}, + [35] = { 0x23, 358, 104, 50, 50, true, 35, "h"}, + [36] = { 0x24, 409, 104, 50, 50, true, 36, "j"}, + [37] = { 0x25, 460, 104, 50, 50, true, 37, "k"}, + [38] = { 0x26, 511, 104, 50, 50, true, 38, "l"}, + [39] = { 0x27, 562, 104, 50, 50, true, 39, ";"}, + [40] = { 0x28, 614, 104, 50, 50, true, 40, "\""}, + [41] = { 0x2B, 665, 104, 50, 50, true, 43, "\\"}, + + + + [42] = { 0x2A, 0, 156, 76, 50, false, 0, "left shift"}, + [43] = { 0x56, 77, 156, 50, 50, true, 86, "\\"}, + [44] = { 0x2C, 128, 156, 50, 50, true, 44, "z"}, + [45] = { 0x2D, 179, 156, 50, 50, true, 45, "x"}, + [46] = { 0x2E, 230, 156, 50, 50, true, 46, "c"}, + [47] = { 0x2F, 281, 156, 50, 50, true, 47, "v"}, + [48] = { 0x30, 332, 156, 50, 50, true, 48, "b"}, + [49] = { 0x31, 383, 156, 50, 50, true, 49, "n"}, + [50] = { 0x32, 434, 156, 50, 50, true, 50, "m"}, + [51] = { 0x33, 485, 156, 50, 50, true, 51, ","}, + [52] = { 0x34, 536, 156, 50, 50, true, 52, "."}, + [53] = { 0x35, 587, 156, 50, 50, true, 53, "/"}, + [54] = { 0x36, 638, 156, 125, 50, false, 0, "right shift"}, + + [55] = { 0x1D, 0, 208, 76, 50, false, 0, "left ctrl"}, + [56] = {0xE05B, 77, 208, 50, 50, false, 0, "left\nsuper"}, + [57] = { 0x38, 128, 208, 50, 50, false, 0, "alt"}, + [58] = { 0x39, 179, 208, 356, 50, false, 0, "space"}, + [59] = {0xE038, 536, 208, 50, 50, false, 0, "alt gr"}, + [60] = {0xE05C, 587, 208, 50, 50, false, 0, "right\nsuper"}, + [61] = {0xE05D, 638, 208, 50, 50, false, 0, "menu"}, + [62] = {0xE01D, 689, 208, 74, 50, false, 0, "right ctrl"} +}; diff --git a/Applications/KeyboardMapper/KeyboardMapperWidget.cpp b/Applications/KeyboardMapper/KeyboardMapperWidget.cpp new file mode 100644 index 0000000000..ad3876d2fd --- /dev/null +++ b/Applications/KeyboardMapper/KeyboardMapperWidget.cpp @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2020, Hüseyin Aslıtürk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "KeyboardMapperWidget.h" +#include "KeyPositions.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +KeyboardMapperWidget::KeyboardMapperWidget() +{ + create_frame(); +} + +KeyboardMapperWidget::~KeyboardMapperWidget() +{ +} + +void KeyboardMapperWidget::create_frame() +{ + set_fill_with_background_color(true); + set_layout(); + set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fill); + layout()->set_margins({ 4, 4, 4, 4 }); + + auto& main_widget = add(); + main_widget.set_relative_rect(0, 0, 200, 200); + + m_keys.resize(KEY_COUNT); + + for (unsigned i = 0; i < KEY_COUNT; i++) { + Gfx::Rect rect = { keys[i].x, keys[i].y, keys[i].width, keys[i].height }; + + auto& tmp_button = main_widget.add(); + tmp_button.set_relative_rect(rect); + tmp_button.set_text(keys[i].name); + tmp_button.set_enabled(keys[i].enabled); + + tmp_button.on_click = [&]() { + auto input_box = GUI::InputBox::construct("New Character:", "Select Character", window()); + if (input_box->exec() == GUI::InputBox::ExecOK) { + auto value = input_box->text_value(); + + int i = m_keys.find_first_index(&tmp_button).value_or(0); + ASSERT(i > 0); + + auto index = keys[i].map_index; + ASSERT(index > 0); + + tmp_button.set_text(value); + char* map; + + 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; + } else { + ASSERT_NOT_REACHED(); + } + + 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(); + bottom_widget.set_layout(); + bottom_widget.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); + bottom_widget.set_preferred_size(0, 40); + + // Map Selection + m_map_group = bottom_widget.add(); + m_map_group->set_layout(); + m_map_group->set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fill); + m_map_group->set_preferred_size(250, 0); + + auto& radio_map = m_map_group->add("Default"); + radio_map.set_name("map"); + radio_map.on_checked = [&](bool) { + set_current_map("map"); + }; + auto& radio_shift = m_map_group->add("Shift"); + radio_shift.set_name("shift_map"); + radio_shift.on_checked = [&](bool) { + set_current_map("shift_map"); + }; + auto& radio_altgr = m_map_group->add("AltGr"); + radio_altgr.set_name("altgr_map"); + radio_altgr.on_checked = [&](bool) { + set_current_map("altgr_map"); + }; + auto& radio_alt = m_map_group->add("Alt"); + radio_alt.set_name("alt_map"); + radio_alt.on_checked = [&](bool) { + set_current_map("alt_map"); + }; + + bottom_widget.layout()->add_spacer(); + + auto& ok_button = bottom_widget.add(); + ok_button.set_text("Save"); + ok_button.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fill); + ok_button.set_preferred_size(80, 0); + ok_button.on_click = [this](auto) { + save(); + }; +} + +void KeyboardMapperWidget::load_from_file(String file_name) +{ + auto result = Keyboard::CharacterMapFile::load_from_file(file_name); + if (!result.has_value()) { + ASSERT_NOT_REACHED(); + } + + m_file_name = file_name; + m_character_map = result.value(); + set_current_map("map"); + + for (Widget* widget : m_map_group->child_widgets()) { + auto radio_button = (GUI::RadioButton*)widget; + radio_button->set_checked(radio_button->name() == "map"); + } + + update_window_title(); +} + +void KeyboardMapperWidget::save() +{ + save_to_file(m_file_name); +} + +void KeyboardMapperWidget::save_to_file(const StringView& file_name) +{ + JsonObject map_json; + + auto add_array = [&](String name, char* values) { + JsonArray items; + for (int i = 0; i < 90; i++) { + AK::StringBuilder sb; + sb.append(values[i]); + + 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); + + // Write to file. + String file_content = map_json.to_string(); + + auto file = Core::File::construct(file_name); + file->open(Core::IODevice::WriteOnly); + if (!file->is_open()) { + StringBuilder sb; + sb.append("Failed to open "); + sb.append(file_name); + sb.append(" for write. Error: "); + sb.append(file->error_string()); + + GUI::MessageBox::show(sb.to_string(), "Error", GUI::MessageBox::Type::Error, GUI::MessageBox::InputType::OK, window()); + 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)); + + GUI::MessageBox::show(sb.to_string(), "Error", GUI::MessageBox::Type::Error, GUI::MessageBox::InputType::OK, window()); + return; + } + + m_modified = false; + m_file_name = file_name; + 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; + char* map; + + 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; + } else { + ASSERT_NOT_REACHED(); + } + + for (unsigned k = 0; k < KEY_COUNT; k++) { + auto index = keys[k].map_index; + if (index == 0) + continue; + + AK::StringBuilder sb; + sb.append(map[index]); + + m_keys.at(k)->set_text(sb.to_string()); + } + + this->update(); +} + +void KeyboardMapperWidget::update_window_title() +{ + StringBuilder sb; + sb.append(m_file_name); + if (m_modified) + sb.append(" (*)"); + sb.append(" - KeyboardMapper"); + + window()->set_title(sb.to_string()); +} diff --git a/Applications/KeyboardMapper/KeyboardMapperWidget.h b/Applications/KeyboardMapper/KeyboardMapperWidget.h new file mode 100644 index 0000000000..e0df34ed78 --- /dev/null +++ b/Applications/KeyboardMapper/KeyboardMapperWidget.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2020, Hüseyin Aslıtürk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "KeyButton.h" +#include +#include + +class KeyboardMapperWidget : public GUI::Widget { + C_OBJECT(KeyboardMapperWidget) + +public: + KeyboardMapperWidget(); + virtual ~KeyboardMapperWidget() override; + + void create_frame(); + void load_from_file(const String); + void save(); + void save_to_file(const StringView&); + +protected: + virtual void keydown_event(GUI::KeyEvent&) override; + virtual void keyup_event(GUI::KeyEvent&) override; + + void set_current_map(const String); + void update_window_title(); + +private: + Vector m_keys; + RefPtr m_map_group; + + String m_file_name; + Keyboard::CharacterMapData m_character_map; + String m_current_map_name; + bool m_modified { false }; +}; diff --git a/Applications/KeyboardMapper/main.cpp b/Applications/KeyboardMapper/main.cpp new file mode 100644 index 0000000000..d88b908430 --- /dev/null +++ b/Applications/KeyboardMapper/main.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2020, Hüseyin Aslıtürk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "KeyboardMapperWidget.h" +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + const char* path = nullptr; + Core::ArgsParser args_parser; + args_parser.add_positional_argument(path, "Keyboard character mapping file.", "file", Core::ArgsParser::Required::No); + args_parser.parse(argc, argv); + + GUI::Application app(argc, argv); + + auto app_icon = GUI::Icon::default_icon("app-keyboard-mapper"); + + auto window = GUI::Window::construct(); + window->set_title("KeyboardMapper"); + window->set_icon(app_icon.bitmap_for_size(16)); + window->set_main_widget(); + window->resize(775, 315); + window->move_to(50, 50); + window->set_resizable(false); + window->show(); + + auto keyboard_mapper_widget = (KeyboardMapperWidget*)window->main_widget(); + if (path != nullptr) { + keyboard_mapper_widget->load_from_file(path); + } else { + keyboard_mapper_widget->load_from_file("/res/keymaps/en.json"); + } + + // Actions + auto open_action = GUI::CommonActions::make_open_action( + [&](auto&) { + Optional path = GUI::FilePicker::get_open_filepath("Open"); + if (path.has_value()) { + keyboard_mapper_widget->load_from_file(path.value()); + } + }); + + auto save_action = GUI::CommonActions::make_save_action( + [&](auto&) { + keyboard_mapper_widget->save(); + }); + + auto save_as_action = GUI::Action::create("Save as...", { Mod_Ctrl | Mod_Shift, Key_S }, Gfx::Bitmap::load_from_file("/res/icons/16x16/save.png"), + [&](auto&) { + String m_name = "Unnamed"; + Optional save_path = GUI::FilePicker::get_save_filepath(m_name, "json"); + if (!save_path.has_value()) + return; + + keyboard_mapper_widget->save_to_file(save_path.value()); + }); + + auto quit_action = GUI::CommonActions::make_quit_action( + [&](auto&) { + app.quit(); + }); + + auto about_action = GUI::Action::create("About", + [&](auto&) { + GUI::AboutDialog::show("KeyboardMapper", app_icon.bitmap_for_size(32), window); + }); + + // Menu + auto menubar = GUI::MenuBar::construct(); + + auto& app_menu = menubar->add_menu("KeyboardMapper"); + app_menu.add_action(open_action); + app_menu.add_action(save_action); + app_menu.add_action(save_as_action); + app_menu.add_separator(); + app_menu.add_action(quit_action); + + auto& help_menu = menubar->add_menu("Help"); + help_menu.add_action(about_action); + + app.set_menubar(move(menubar)); + + return app.exec(); +}