Merge branch 'clangen/hotkey_viewer'

This commit is contained in:
casey langen 2018-06-03 20:54:43 -07:00
commit 1753a09862
19 changed files with 827 additions and 27 deletions

View File

@ -318,4 +318,18 @@ namespace musik { namespace core {
#endif
}
bool CopyFile(const std::string& from, const std::string& to) {
if (from.size() && to.size() && from != to) {
std::ifstream in(from);
if (in.is_open()) {
std::ofstream out(to);
if (out.is_open()) {
out << in.rdbuf();
return true;
}
}
}
return false;
}
} }

View File

@ -46,6 +46,7 @@ namespace musik { namespace core {
std::string GetPluginDirectory();
std::string NormalizeDir(std::string path);
void OpenFile(const std::string& path);
bool CopyFile(const std::string& from, const std::string& to);
int64_t Checksum(char *data,unsigned int bytes);
size_t CopyString(const std::string& src, char* dst, size_t size);
void ReplaceAll(std::string& input, const std::string& find, const std::string& replace);

View File

@ -0,0 +1,244 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2017 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * 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.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// 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 OWNER 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 "stdafx.h"
#include "HotkeysLayout.h"
#include <core/support/Common.h>
#include <cursespp/App.h>
#include <cursespp/Colors.h>
#include <cursespp/DialogOverlay.h>
#include <app/util/Hotkeys.h>
#include <app/model/HotkeysAdapter.h>
#include <app/overlay/ReassignHotkeyOverlay.h>
#include <app/util/Messages.h>
#include <ctime>
#include <time.h>
using namespace cursespp;
using namespace musik::cube;
using namespace musik::core;
using Entry = IScrollAdapter::EntryPtr;
static std::string formattedTime() {
char buffer[128];
time_t rawtime;
struct tm* timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
strftime(buffer, sizeof(buffer), "%F-%H-%M-%S", timeinfo);
return std::string(buffer);
}
static void confirmResetHotkeys() {
std::shared_ptr<DialogOverlay> dialog(new DialogOverlay());
(*dialog)
.SetTitle(_TSTR("hotkeys_reset_all_title"))
.SetMessage(_TSTR("hotkeys_reset_all_message"))
.AddButton("^[", "ESC", _TSTR("button_no"))
.AddButton(
"KEY_ENTER",
"ENTER",
_TSTR("button_yes"),
[](const std::string& str) {
Hotkeys::Reset();
});
App::Overlays().Push(dialog);
}
static void checkConflictAndSave(Hotkeys::Id id, const std::string& key, std::function<void()> cb) {
const std::string existing = Hotkeys::Existing(key);
if (existing == Hotkeys::Name(id)) {
return;
}
if (existing.size()) {
std::shared_ptr<DialogOverlay> dialog(new DialogOverlay());
std::string message = _TSTR("hotkeys_conflict_message");
ReplaceAll(message, "{{hotkey}}", key);
ReplaceAll(message, "{{existing}}", existing);
(*dialog)
.SetTitle(_TSTR("hotkeys_conflict_title"))
.SetMessage(message)
.AddButton("^[", "ESC", _TSTR("button_no"))
.AddButton(
"KEY_ENTER",
"ENTER",
_TSTR("button_yes"),
[id, key, cb](const std::string& str) {
Hotkeys::Set(id, key);
if (cb) {
cb();
}
});
App::Overlays().Push(dialog);
}
else {
Hotkeys::Set(id, key);
if (cb) {
cb();
}
}
}
static void backupAndShowDialog() {
std::string dir = NormalizeDir(GetDataDirectory());
std::string in = dir + "hotkeys.json";
std::string out = dir + "hotkeys-" + formattedTime() + ".json";
if (CopyFile(in, out)) {
std::shared_ptr<DialogOverlay> dialog(new DialogOverlay());
std::string message = _TSTR("hotkeys_backup_success_message");
ReplaceAll(message, "{{path}}", out);
(*dialog)
.SetTitle(_TSTR("hotkeys_backup_success_title"))
.SetMessage(message)
.AddButton("KEY_ENTER", "ENTER", _TSTR("button_ok"));
App::Overlays().Push(dialog);
}
else {
std::shared_ptr<DialogOverlay> dialog(new DialogOverlay());
std::string message = _TSTR("hotkeys_backup_failure_message");
ReplaceAll(message, "{{path}}", dir);
(*dialog)
.SetTitle(_TSTR("hotkeys_backup_failure_title"))
.SetMessage(message)
.AddButton("KEY_ENTER", "ENTER", _TSTR("button_ok"));
App::Overlays().Push(dialog);
}
}
HotkeysLayout::HotkeysLayout() {
auto adapter = std::make_shared<HotkeysAdapter>();
adapter->SetItemDecorator([this](ScrollableWindow*, size_t index, size_t, Entry) -> int64_t {
if (this->listWindow->GetSelectedIndex() == index) {
return COLOR_PAIR(CURSESPP_HIGHLIGHTED_LIST_ITEM);
}
return -1LL;
});
this->listWindow = std::make_shared<ListWindow>();
this->listWindow->SetFrameVisible(true);
this->listWindow->SetAdapter(adapter);
this->listWindow->SetFrameTitle(_TSTR("hotkeys_title"));
this->listWindow->EntryActivated.connect(this, &HotkeysLayout::OnEntryActivated);
this->AddWindow(this->listWindow);
this->listWindow->SetFocusOrder(0);
}
HotkeysLayout::~HotkeysLayout() {
}
void HotkeysLayout::OnEntryActivated(cursespp::ListWindow* list, size_t index) {
Hotkeys::Id id = static_cast<Hotkeys::Id>(index);
auto shortcuts = this->shortcuts;
ReassignHotkeyOverlay::Show(id, [this, list, id](std::string key) {
checkConflictAndSave(id, key, [this, list]() {
list->OnAdapterChanged();
this->SetShortcutsWindow(this->shortcuts);
});
});
}
void HotkeysLayout::SetShortcutsWindow(ShortcutsWindow* shortcuts) {
this->shortcuts = shortcuts;
if (shortcuts) {
shortcuts->RemoveAll();
shortcuts->AddShortcut(
Hotkeys::Get(Hotkeys::HotkeysResetToDefault),
_TSTR("hotkeys_reset_defaults"));
shortcuts->AddShortcut(
Hotkeys::Get(Hotkeys::HotkeysBackup),
_TSTR("hotkeys_backup"));
shortcuts->AddShortcut(
Hotkeys::Get(Hotkeys::NavigateLibrary),
_TSTR("shortcuts_library"));
shortcuts->AddShortcut(
Hotkeys::Get(Hotkeys::NavigateSettings),
_TSTR("shortcuts_settings"));
shortcuts->AddShortcut("^D", _TSTR("shortcuts_quit"));
shortcuts->SetChangedCallback([this](std::string key) {
this->KeyPress(key);
});
}
}
bool HotkeysLayout::KeyPress(const std::string& kn) {
if (Hotkeys::Is(Hotkeys::HotkeysResetToDefault, kn)) {
confirmResetHotkeys();
this->listWindow->OnAdapterChanged();
return true;
}
else if (Hotkeys::Is(Hotkeys::HotkeysBackup, kn)) {
backupAndShowDialog();
return true;
}
else if (Hotkeys::Is(Hotkeys::NavigateSettings, kn)) {
this->BroadcastMessage(message::JumpToSettings);
return true;
}
else if (Hotkeys::Is(Hotkeys::NavigateLibrary, kn)) {
this->BroadcastMessage(message::JumpToLibrary);
return true;
}
else {
return LayoutBase::KeyPress(kn);
}
}
void HotkeysLayout::OnLayout() {
this->listWindow->MoveAndResize(
0, 0, this->GetWidth(), this->GetHeight());
}

View File

@ -0,0 +1,74 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2017 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * 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.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// 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 OWNER 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 <cursespp/LayoutBase.h>
#include <cursespp/ListWindow.h>
#include <cursespp/ShortcutsWindow.h>
#include <vector>
#include "ITopLevelLayout.h"
namespace musik {
namespace cube {
class HotkeysLayout :
public cursespp::LayoutBase,
#if (__clang_major__ == 7 && __clang_minor__ == 3)
public std::enable_shared_from_this<HotkeysLayout>,
#endif
public ITopLevelLayout,
public sigslot::has_slots<>
{
public:
HotkeysLayout();
~HotkeysLayout();
virtual void SetShortcutsWindow(
cursespp::ShortcutsWindow* shortcuts);
virtual bool KeyPress(const std::string& kn);
protected:
virtual void OnLayout();
private:
void OnEntryActivated(cursespp::ListWindow* w, size_t index);
std::shared_ptr<cursespp::ListWindow> listWindow;
cursespp::ShortcutsWindow* shortcuts;
};
}
}

View File

@ -45,6 +45,7 @@
#include <app/layout/ConsoleLayout.h>
#include <app/layout/LibraryLayout.h>
#include <app/layout/SettingsLayout.h>
#include <app/layout/HotkeysLayout.h>
#include <app/util/Hotkeys.h>
#include <map>
@ -112,9 +113,10 @@ MainLayout::MainLayout(
library->Indexer()->Finished.connect(this, &MainLayout::OnIndexerFinished);
library->Indexer()->Progress.connect(this, &MainLayout::OnIndexerProgress);
this->libraryLayout.reset(new LibraryLayout(playback, library));
this->consoleLayout.reset(new ConsoleLayout(playback.GetTransport(), library));
this->settingsLayout.reset(new SettingsLayout(app, library, playback));
this->libraryLayout = std::make_shared<LibraryLayout>(playback, library);
this->consoleLayout = std::make_shared<ConsoleLayout>(playback.GetTransport(), library);
this->settingsLayout = std::make_shared<SettingsLayout>(app, library, playback);
this->hotkeysLayout = std::make_shared<HotkeysLayout>();
/* take user to settings if they don't have a valid configuration. otherwise,
switch to the library view immediately */
@ -286,6 +288,10 @@ bool MainLayout::KeyPress(const std::string& key) {
this->SetMainLayout(consoleLayout);
return true;
}
else if (Hotkeys::Is(Hotkeys::NavigateHotkeys, key)) {
this->SetMainLayout(hotkeysLayout);
return true;
}
else if (Hotkeys::Is(Hotkeys::NavigateLibrary, key)) {
this->SetMainLayout(libraryLayout);
return true;

View File

@ -100,7 +100,10 @@ namespace musik {
std::shared_ptr<cursespp::ShortcutsWindow> shortcuts;
std::shared_ptr<cursespp::LayoutBase> layout;
std::shared_ptr<cursespp::TextLabel> syncing;
std::shared_ptr<cursespp::LayoutBase> consoleLayout, libraryLayout, settingsLayout;
std::shared_ptr<cursespp::LayoutBase> consoleLayout;
std::shared_ptr<cursespp::LayoutBase> libraryLayout;
std::shared_ptr<cursespp::LayoutBase> settingsLayout;
std::shared_ptr<cursespp::LayoutBase> hotkeysLayout;
std::shared_ptr<cursespp::TextLabel> hotkey;
musik::core::ILibraryPtr library;
cursespp::IWindowPtr lastFocus;

View File

@ -0,0 +1,68 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2017 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * 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.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// 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 OWNER 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 "stdafx.h"
#include "HotkeysAdapter.h"
#include <app/util/Hotkeys.h>
#include <cursespp/Colors.h>
#include <cursespp/SingleLineEntry.h>
#include <cursespp/Text.h>
using namespace cursespp;
using namespace musik::cube;
using Entry = IScrollAdapter::EntryPtr;
HotkeysAdapter::HotkeysAdapter() {
}
HotkeysAdapter::~HotkeysAdapter() {
}
size_t HotkeysAdapter::GetEntryCount() {
return Hotkeys::COUNT;
}
Entry HotkeysAdapter::GetEntry(ScrollableWindow* window, size_t index) {
auto id = static_cast<Hotkeys::Id>(index);
auto name = Hotkeys::Name(id);
auto key = Hotkeys::Get(id);
int width = window->GetContentWidth();
int avail = std::max(0, width - int(u8cols(name)) - 1 - 1);
auto value = " " + name + " " + text::Align(key + " ", text::AlignRight, avail);
return IScrollAdapter::EntryPtr(new SingleLineEntry(value));
}

View File

@ -0,0 +1,53 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2017 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * 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.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// 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 OWNER 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 <cursespp/ScrollAdapterBase.h>
#include <cursespp/ScrollableWindow.h>
namespace musik {
namespace cube {
class HotkeysAdapter : public cursespp::ScrollAdapterBase {
public:
static const size_t NO_INDEX = (size_t)-1;
HotkeysAdapter();
virtual ~HotkeysAdapter();
virtual size_t GetEntryCount();
virtual EntryPtr GetEntry(cursespp::ScrollableWindow* window, size_t index);
};
}
}

View File

@ -0,0 +1,142 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2017 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * 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.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// 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 OWNER 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 "stdafx.h"
#include "ReassignHotkeyOverlay.h"
#include <cursespp/App.h>
#include <cursespp/Screen.h>
#include <algorithm>
using namespace cursespp;
using namespace musik::cube;
#define VERTICAL_PADDING 2
#define DEFAULT_HEIGHT 10
#define DEFAULT_WIDTH 35
static void applyLabelOverlayStyle(TextLabel& label) {
label.SetContentColor(CURSESPP_OVERLAY_CONTENT);
label.SetFocusedContentColor(CURSESPP_OVERLAY_TEXT_FOCUSED);
}
void ReassignHotkeyOverlay::Show(Hotkeys::Id id, Callback callback) {
using T = ReassignHotkeyOverlay;
auto overlay = std::shared_ptr<T>(new T(id, callback));
App::Overlays().Push(overlay);
}
void ReassignHotkeyOverlay::Layout() {
this->RecalculateSize();
this->MoveAndResize(this->x, this->y, this->width, this->height);
auto clientHeight = this->height - 2;
auto clientWidth = this->width - 2;
auto name = Hotkeys::Name(this->id);
this->hotkeyLabel->SetText(name, text::AlignCenter);
this->titleLabel->MoveAndResize(0, 0, clientWidth, 1);
this->hotkeyLabel->MoveAndResize(1, 2, clientWidth - 2, 3);
this->hotkeyInput->MoveAndResize(1, 3, clientWidth - 2, 3);
this->shortcuts->MoveAndResize(0, clientHeight - 1, clientWidth, 1);
}
bool ReassignHotkeyOverlay::KeyPress(const std::string& key) {
if (key == "^[" || key == "ESC") { /* esc closes */
this->Dismiss();
return true;
}
else if (key == "KEY_ENTER") {
auto current = this->hotkeyInput->GetText();
if (u8len(current)) {
callback(current);
this->Dismiss();
return true;
}
}
return LayoutBase::KeyPress(key);
}
ReassignHotkeyOverlay::ReassignHotkeyOverlay(Hotkeys::Id id, Callback callback)
: id(id), callback(callback) {
LayoutBase();
this->InitViews();
}
void ReassignHotkeyOverlay::RecalculateSize() {
auto preferredWidth = _DIMEN("reassign_hotkey_overlay_width", DEFAULT_WIDTH);
auto name = Hotkeys::Name(this->id);
this->width = std::max(int(u8cols(name) + 4), preferredWidth);
this->width = std::max(0, std::min(Screen::GetWidth(), this->width));
this->height = std::max(0, std::min(Screen::GetHeight() - 2, DEFAULT_HEIGHT));
this->y = VERTICAL_PADDING;
this->x = (Screen::GetWidth() / 2) - (this->width / 2);
}
void ReassignHotkeyOverlay::InitViews() {
this->SetFrameVisible(true);
this->SetFrameColor(CURSESPP_OVERLAY_FRAME);
this->SetContentColor(CURSESPP_OVERLAY_CONTENT);
this->titleLabel = std::make_shared<TextLabel>();
applyLabelOverlayStyle(*this->titleLabel);
this->titleLabel->SetText(_TSTR("hotkeys_reassign_overlay_title"), text::AlignCenter);
this->titleLabel->SetBold(true);
this->hotkeyLabel = std::make_shared<TextLabel>();
applyLabelOverlayStyle(*this->hotkeyLabel);
this->hotkeyInput = std::make_shared<TextInput>(
TextInput::Style::StyleBox, TextInput::InputRaw);
this->hotkeyInput->SetRawKeyBlacklist({ "KEY_ENTER", "^[" });
this->hotkeyInput->SetFocusOrder(0);
this->hotkeyInput->SetFocusedFrameColor(CURSESPP_OVERLAY_INPUT_FRAME);
this->hotkeyInput->SetText(Hotkeys::Get(this->id));
this->hotkeyInput->SetFocusedContentColor(CURSESPP_OVERLAY_CONTENT);
this->shortcuts = std::make_shared<ShortcutsWindow>();
this->shortcuts->SetAlignment(text::AlignRight);
this->shortcuts->AddShortcut("ESC", _TSTR("button_cancel"));
this->shortcuts->AddShortcut("ENTER", _TSTR("button_save"));
this->AddWindow(this->titleLabel);
this->AddWindow(this->hotkeyLabel);
this->AddWindow(this->hotkeyInput);
this->AddWindow(this->shortcuts);
this->Layout();
}

View File

@ -0,0 +1,74 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2017 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * 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.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// 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 OWNER 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 <functional>
#include <cursespp/TextInput.h>
#include <cursespp/TextLabel.h>
#include <cursespp/OverlayBase.h>
#include <cursespp/ShortcutsWindow.h>
#include <app/util/Hotkeys.h>
namespace musik {
namespace cube {
class ReassignHotkeyOverlay : public cursespp::OverlayBase, public sigslot::has_slots<>
#if (__clang_major__ == 7 && __clang_minor__ == 3)
, public std::enable_shared_from_this<ReassignHotkeyOverlay>
#endif
{
public:
using Callback = std::function<void(std::string)>;
static void Show(Hotkeys::Id id, Callback callback);
virtual void Layout();
virtual bool KeyPress(const std::string& key);
private:
ReassignHotkeyOverlay(Hotkeys::Id id, Callback callback);
void RecalculateSize();
void InitViews();
Hotkeys::Id id;
Callback callback;
int width, height, x, y;
std::shared_ptr<cursespp::TextLabel> titleLabel, hotkeyLabel;
std::shared_ptr<cursespp::TextInput> hotkeyInput;
std::shared_ptr<cursespp::ShortcutsWindow> shortcuts;
};
}
}

View File

@ -42,6 +42,8 @@
using namespace musik::cube;
using namespace musik::core;
#define ENSURE_LOADED() { if (!prefs) { loadPreferences(); } }
using Id = Hotkeys::Id;
/* sigh: http://stackoverflow.com/a/24847480 */
@ -77,6 +79,7 @@ static std::unordered_map<std::string, Id> NAME_TO_ID = {
{ "navigate_library_play_queue", Id::NavigateLibraryPlayQueue },
{ "navigate_settings", Id::NavigateSettings },
{ "navigate_console", Id::NavigateConsole },
{ "navigate_hotkeys", Id::NavigateHotkeys},
{ "navigate_jump_to_playing", Id::NavigateJumpToPlaying },
{ "play_queue_move_up", Id::PlayQueueMoveUp },
@ -113,6 +116,9 @@ static std::unordered_map<std::string, Id> NAME_TO_ID = {
{ "metadata_rescan", Id::RescanMetadata },
{ "hotkeys_reset_to_default", Id::HotkeysResetToDefault },
{ "hotkeys_backup", Id::HotkeysBackup },
{ "context_menu", Id::ContextMenu }
};
@ -142,6 +148,7 @@ static std::unordered_map<Id, std::string, EnumHasher> ID_TO_DEFAULT = {
{ Id::NavigateLibraryPlayQueue, "n" },
{ Id::NavigateSettings, "s" },
{ Id::NavigateConsole, "`" },
{ Id::NavigateHotkeys, "?" },
{ Id::NavigateJumpToPlaying, "x" },
#ifdef __APPLE__
@ -190,18 +197,20 @@ static std::unordered_map<Id, std::string, EnumHasher> ID_TO_DEFAULT = {
{ Id::RescanMetadata, "^R"},
{ Id::HotkeysResetToDefault, "M-r" },
{ Id::HotkeysBackup, "M-b" },
{ Id::ContextMenu, "M-enter" }
};
/* custom keys */
static std::unordered_set<std::string> customKeys;
static std::unordered_map<Id, std::string, EnumHasher> customIdToKey;
/* preferences file */
static std::shared_ptr<Preferences> prefs;
static void savePreferences() {
for (const std::pair<std::string, Id>& pair : NAME_TO_ID) {
for (const auto& pair : NAME_TO_ID) {
prefs->SetString(
pair.first.c_str(),
Hotkeys::Get(pair.second).c_str());
@ -214,16 +223,12 @@ static void loadPreferences() {
prefs = Preferences::ForComponent("hotkeys", Preferences::ModeReadWrite);
try {
/* load all of the custom key mappings into customKeys and
customIdToKey structures for quick lookup. */
if (prefs) {
customKeys.clear();
std::vector<std::string> names;
prefs->GetKeys(names);
for (auto n : names) {
auto it = NAME_TO_ID.find(n);
if (it != NAME_TO_ID.end()) {
customKeys.insert(prefs->GetString(n));
customIdToKey[it->second] = prefs->GetString(n);
}
}
@ -235,7 +240,6 @@ static void loadPreferences() {
}
catch (...) {
std::cerr << "failed to load hotkeys.json! default hotkeys selected.";
customKeys.clear();
customIdToKey.clear();
}
}
@ -264,24 +268,74 @@ bool Hotkeys::Is(Id id, const std::string& kn) {
return false;
}
std::string Hotkeys::Get(Id id) {
if (!prefs) {
loadPreferences();
}
auto custom = customIdToKey.find(id);
if (custom != customIdToKey.end()) {
return custom->second;
}
auto it = ID_TO_DEFAULT.find(id);
if (it != ID_TO_DEFAULT.end()) {
template <typename T>
std::string find(Id id, T& map) {
auto it = map.find(id);
if (it != map.end()) {
return it->second;
}
return "";
}
template <typename T>
Hotkeys::Id find(const std::string& kn, T& map) {
for (auto it : map) {
if (it.second == kn) {
return it.first;
}
}
return Hotkeys::COUNT;
}
std::string Hotkeys::Default(Id id) {
ENSURE_LOADED()
return find(id, ID_TO_DEFAULT);
}
std::string Hotkeys::Custom(Id id) {
ENSURE_LOADED()
return find(id, customIdToKey);
}
std::string Hotkeys::Get(Id id) {
auto kn = Custom(id);
return kn.size() ? kn : Default(id);
}
void Hotkeys::Set(Id id, const std::string& kn) {
customIdToKey[id] = kn;
savePreferences();
}
void Hotkeys::Reset() {
customIdToKey.clear();
savePreferences();
loadPreferences();
}
std::string Hotkeys::Existing(const std::string& kn) {
auto id = find(kn, customIdToKey);
if (id == Hotkeys::COUNT) {
id = find(kn, ID_TO_DEFAULT);
if (customIdToKey.find(id) != customIdToKey.end()) {
/* we found a default key for this one, but that default
binding has already been overridden! ensure we return
that it's available. */
id = Hotkeys::COUNT;
}
}
return id != Hotkeys::COUNT ? Name(id) : "";
}
std::string Hotkeys::Name(Id id) {
for (auto entry : NAME_TO_ID) {
if (entry.second == id) {
return entry.first;
}
}
return "<error>";
}
class NavigationKeysImpl : public cursespp::INavigationKeys {
public:
virtual bool Up(const std::string& key) override { return Up() == key; }

View File

@ -44,7 +44,7 @@ namespace musik {
public:
enum Id {
/* selection */
Up,
Up = 0,
Down,
Left,
Right,
@ -68,6 +68,7 @@ namespace musik {
NavigateLibraryPlayQueue,
NavigateSettings,
NavigateConsole,
NavigateHotkeys,
NavigateJumpToPlaying,
/* views */
@ -108,12 +109,25 @@ namespace musik {
/* indexer */
RescanMetadata,
/* hotkeys */
HotkeysResetToDefault,
HotkeysBackup,
/* general */
ContextMenu
ContextMenu,
/* :3 */
COUNT
};
static bool Is(Id id, const std::string& kn);
static std::string Get(Id id);
static void Set(Id id, const std::string& kn);
static void Reset();
static std::string Existing(const std::string& kn);
static std::string Name(Id id);
static std::string Default(Id id);
static std::string Custom(Id id);
static std::shared_ptr<cursespp::INavigationKeys> NavigationKeys();
private:

View File

@ -69,6 +69,7 @@ void ShortcutsWindow::AddShortcut(
void ShortcutsWindow::RemoveAll() {
this->entries.clear();
this->activeKey = this->originalKey = "";
this->Redraw();
}

View File

@ -143,6 +143,10 @@ bool TextInput::Write(const std::string& key) {
int len = u8len(key);
if (len == 1 || (len > 1 && this->inputMode == InputRaw)) {
if (this->inputMode == InputRaw) {
auto& bl = this->rawBlacklist;
if (std::find(bl.begin(), bl.end(), key) != bl.end()) {
return false;
}
this->buffer = key;
this->bufferLength = len;
this->position = len;
@ -180,7 +184,15 @@ void TextInput::SetEnterEnabled(bool enabled) {
this->enterEnabled = enabled;
}
void TextInput::SetRawKeyBlacklist(const std::vector<std::string>&& blacklist) {
this->rawBlacklist = blacklist;
}
bool TextInput::KeyPress(const std::string& key) {
if (this->inputMode == InputMode::InputRaw) {
return false;
}
if (key == "M-KEY_BACKSPACE") {
this->SetText("");
return true;

View File

@ -77,6 +77,7 @@ namespace cursespp {
virtual void SetText(const std::string& value);
virtual std::string GetText() { return this->buffer; }
void SetRawKeyBlacklist(const std::vector<std::string>&& blacklist);
void SetTruncate(bool truncate);
void SetHint(const std::string& hint);
void SetEnterEnabled(bool enabled);
@ -85,6 +86,7 @@ namespace cursespp {
private:
bool OffsetPosition(int delta);
std::vector<std::string> rawBlacklist;
std::string buffer, hintText;
int position;
bool enterEnabled;

View File

@ -233,7 +233,7 @@ namespace cursespp {
}
bool AlreadyRunning() {
return /*!IsDebuggerPresent() &&*/ (runningMutexLastError == ERROR_ALREADY_EXISTS);
return !IsDebuggerPresent() && (runningMutexLastError == ERROR_ALREADY_EXISTS);
}
void ShowOtherInstance(const std::string& title) {

View File

@ -37,6 +37,19 @@
"console_debug_logs_title": "debug logs",
"console_command_title": "command",
"hotkeys_title": "hotkey configuration",
"hotkeys_reset_defaults": "reset all",
"hotkeys_backup": "backup",
"hotkeys_reassign_overlay_title": "reassign hotkey",
"hotkeys_reset_all_title": "reset hotkeys",
"hotkeys_reset_all_message": "are you sure you want to reset all hotkeys to their default values?\n\nexisting custom hotkeys will be forgotten!",
"hotkeys_conflict_title": "hotkey conflict",
"hotkeys_conflict_message": "the hotkey '{{hotkey}}' *may* conflict with '{{existing}}', depending on context.\n\nare you sure you want to assign this binding?",
"hotkeys_backup_success_title": "backup succeeded",
"hotkeys_backup_success_message": "hotkeys were backed up to the following location:\n\n{{path}}",
"hotkeys_backup_failure_title": "backup failed",
"hotkeys_backup_failure_message": "hotkey backup was *NOT* successful! please make sure you have write permission to the following directory:\n\n{{path}}",
"settings_space_to_add": "browse (SPACE to add)",
"settings_backspace_to_remove": "indexed paths (BACKSPACE to remove)",
"settings_enable_disable_plugins": "configure plugins",
@ -183,6 +196,7 @@
"playqueue_playlist_name_overlay_width": 35,
"browse_categories_overlay_width": 35,
"indexer_overlay_width": 28,
"reassign_hotkey_overlay_width": 35,
"server_overlay_width": 45,
"preamp_overlay_width": 40
}

View File

@ -146,6 +146,7 @@ xcopy "$(SolutionDir)src\3rdparty\bin\win32\font\*.ttf" "$(TargetDir)fonts\" /Y
<ItemGroup>
<ClCompile Include="app\layout\BrowseLayout.cpp" />
<ClCompile Include="app\layout\DirectoryLayout.cpp" />
<ClCompile Include="app\layout\HotkeysLayout.cpp" />
<ClCompile Include="app\layout\LibraryLayout.cpp" />
<ClCompile Include="app\layout\ConsoleLayout.cpp" />
<ClCompile Include="app\layout\NowPlayingLayout.cpp" />
@ -154,6 +155,7 @@ xcopy "$(SolutionDir)src\3rdparty\bin\win32\font\*.ttf" "$(TargetDir)fonts\" /Y
<ClCompile Include="app\layout\SettingsLayout.cpp" />
<ClCompile Include="app\layout\TrackSearchLayout.cpp" />
<ClCompile Include="app\model\DirectoryAdapter.cpp" />
<ClCompile Include="app\model\HotkeysAdapter.cpp" />
<ClCompile Include="app\overlay\BrowseOverlays.cpp" />
<ClCompile Include="app\overlay\ColorThemeOverlay.cpp" />
<ClCompile Include="app\overlay\LastFmOverlay.cpp" />
@ -162,6 +164,7 @@ xcopy "$(SolutionDir)src\3rdparty\bin\win32\font\*.ttf" "$(TargetDir)fonts\" /Y
<ClCompile Include="app\overlay\PlayQueueOverlays.cpp" />
<ClCompile Include="app\overlay\PluginOverlay.cpp" />
<ClCompile Include="app\overlay\PreampOverlay.cpp" />
<ClCompile Include="app\overlay\ReassignHotkeyOverlay.cpp" />
<ClCompile Include="app\overlay\ServerOverlay.cpp" />
<ClCompile Include="app\overlay\VisualizerOverlay.cpp" />
<ClCompile Include="app\util\GlobalHotkeys.cpp" />
@ -206,6 +209,7 @@ xcopy "$(SolutionDir)src\3rdparty\bin\win32\font\*.ttf" "$(TargetDir)fonts\" /Y
<ItemGroup>
<ClInclude Include="app\layout\BrowseLayout.h" />
<ClInclude Include="app\layout\DirectoryLayout.h" />
<ClInclude Include="app\layout\HotkeysLayout.h" />
<ClInclude Include="app\layout\ITopLevelLayout.h" />
<ClInclude Include="app\layout\LibraryLayout.h" />
<ClInclude Include="app\layout\ConsoleLayout.h" />
@ -215,6 +219,7 @@ xcopy "$(SolutionDir)src\3rdparty\bin\win32\font\*.ttf" "$(TargetDir)fonts\" /Y
<ClInclude Include="app\layout\SettingsLayout.h" />
<ClInclude Include="app\layout\TrackSearchLayout.h" />
<ClInclude Include="app\model\DirectoryAdapter.h" />
<ClInclude Include="app\model\HotkeysAdapter.h" />
<ClInclude Include="app\overlay\BrowseOverlays.h" />
<ClInclude Include="app\overlay\ColorThemeOverlay.h" />
<ClInclude Include="app\overlay\LastFmOverlay.h" />
@ -223,6 +228,7 @@ xcopy "$(SolutionDir)src\3rdparty\bin\win32\font\*.ttf" "$(TargetDir)fonts\" /Y
<ClInclude Include="app\overlay\PlayQueueOverlays.h" />
<ClInclude Include="app\overlay\PluginOverlay.h" />
<ClInclude Include="app\overlay\PreampOverlay.h" />
<ClInclude Include="app\overlay\ReassignHotkeyOverlay.h" />
<ClInclude Include="app\overlay\ServerOverlay.h" />
<ClInclude Include="app\overlay\VisualizerOverlay.h" />
<ClInclude Include="app\util\GlobalHotkeys.h" />

View File

@ -162,6 +162,15 @@
<ClCompile Include="cursespp\IMouseHandler.cpp">
<Filter>cursespp</Filter>
</ClCompile>
<ClCompile Include="app\layout\HotkeysLayout.cpp">
<Filter>app\layout</Filter>
</ClCompile>
<ClCompile Include="app\model\HotkeysAdapter.cpp">
<Filter>app\model</Filter>
</ClCompile>
<ClCompile Include="app\overlay\ReassignHotkeyOverlay.cpp">
<Filter>app\overlay</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h" />
@ -373,6 +382,15 @@
<ClInclude Include="cursespp\IMouseHandler.h">
<Filter>cursespp</Filter>
</ClInclude>
<ClInclude Include="app\layout\HotkeysLayout.h">
<Filter>app\layout</Filter>
</ClInclude>
<ClInclude Include="app\model\HotkeysAdapter.h">
<Filter>app\model</Filter>
</ClInclude>
<ClInclude Include="app\overlay\ReassignHotkeyOverlay.h">
<Filter>app\overlay</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="cursespp">