Add support for fetching lyrics in background for selected songs
[ncmpcpp.git] / src / bindings.cpp
blobb1765fa1f1573d345f4665b314a5df7d9f652b6b
1 /***************************************************************************
2 * Copyright (C) 2008-2016 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include <boost/algorithm/string/trim.hpp>
22 #include <fstream>
23 #include <iostream>
24 #include "global.h"
25 #include "bindings.h"
26 #include "utility/string.h"
27 #include "utility/wide_string.h"
29 BindingsConfiguration Bindings;
31 namespace {
33 void warning(const char *msg)
35 std::cerr << "WARNING: " << msg << "\n";
38 NC::Key::Type stringToKey(const std::string &s);
40 NC::Key::Type stringToSpecialKey(const std::string &s)
42 NC::Key::Type result = NC::Key::None;
43 if (!s.compare(0, 4, "ctrl") && s.length() == 6 && (s[4] == '_' || s[4] == '-'))
45 if (s[4] == '_')
46 warning("prefix 'ctrl_' is deprecated and will be removed in 0.8, use 'ctrl-' instead.");
47 if (s[5] >= 'a' && s[5] <= 'z')
48 result = NC::Key::Ctrl_A + (s[5] - 'a');
49 else if (s[5] == '[')
50 result = NC::Key::Ctrl_LeftBracket;
51 else if (s[5] == '\\')
52 result = NC::Key::Ctrl_Backslash;
53 else if (s[5] == ']')
54 result = NC::Key::Ctrl_RightBracket;
55 else if (s[5] == '^')
56 result = NC::Key::Ctrl_Caret;
57 else if (s[5] == '_')
58 result = NC::Key::Ctrl_Underscore;
60 else if (!s.compare(0, 3, "alt") && s.length() > 3 && (s[3] == '_' || s[3] == '-'))
62 if (s[3] == '_')
63 warning("prefix 'alt_' is deprecated and will be removed in 0.8, use 'alt-' instead.");
64 result = NC::Key::Alt | stringToKey(s.substr(4));
66 else if (!s.compare(0, 4, "ctrl") && s.length() > 4 && (s[4] == '_' || s[4] == '-'))
68 if (s[4] == '_')
69 warning("prefix 'ctrl_' is deprecated and will be removed in 0.8, use 'ctrl-' instead.");
70 result = NC::Key::Ctrl | stringToKey(s.substr(5));
72 else if (!s.compare(0, 5, "shift") && s.length() > 5 && (s[5] == '_' || s[5] == '-'))
74 if (s[5] == '_')
75 warning("prefix 'shift_' is deprecated and will be removed in 0.8, use 'shift-' instead.");
76 result = NC::Key::Shift | stringToKey(s.substr(6));
78 else if (!s.compare("escape"))
79 result = NC::Key::Escape;
80 else if (!s.compare("mouse"))
81 result = NC::Key::Mouse;
82 else if (!s.compare("up"))
83 result = NC::Key::Up;
84 else if (!s.compare("down"))
85 result = NC::Key::Down;
86 else if (!s.compare("page_up"))
87 result = NC::Key::PageUp;
88 else if (!s.compare("page_down"))
89 result = NC::Key::PageDown;
90 else if (!s.compare("home"))
91 result = NC::Key::Home;
92 else if (!s.compare("end"))
93 result = NC::Key::End;
94 else if (!s.compare("space"))
95 result = NC::Key::Space;
96 else if (!s.compare("enter"))
97 result = NC::Key::Enter;
98 else if (!s.compare("insert"))
99 result = NC::Key::Insert;
100 else if (!s.compare("delete"))
101 result = NC::Key::Delete;
102 else if (!s.compare("left"))
103 result = NC::Key::Left;
104 else if (!s.compare("right"))
105 result = NC::Key::Right;
106 else if (!s.compare("tab"))
107 result = NC::Key::Tab;
108 else if ((s.length() == 2 || s.length() == 3) && s[0] == 'f')
110 int n = atoi(s.c_str() + 1);
111 if (n >= 1 && n <= 12)
112 result = NC::Key::F1 + n - 1;
114 else if (!s.compare("backspace"))
115 result = NC::Key::Backspace;
116 else if (!s.compare("backspace_2"))
118 warning("'backspace_2' is deprecated and will be removed in 0.8, use 'backspace' instead.");
119 result = NC::Key::Backspace;
121 return result;
124 NC::Key::Type stringToKey(const std::string &s)
126 NC::Key::Type result = stringToSpecialKey(s);
127 if (result == NC::Key::None)
129 std::wstring ws = ToWString(s);
130 if (ws.length() == 1)
131 result = ws[0];
133 return result;
136 template <typename F>
137 Actions::BaseAction *parseActionLine(const std::string &line, F error)
139 Actions::BaseAction *result = 0;
140 size_t i = 0;
141 for (; i < line.size() && !isspace(line[i]); ++i) { }
142 if (i == line.size()) // only action name
143 result = Actions::get(line);
144 else // there is something else
146 std::string action_name = line.substr(0, i);
147 if (action_name == "push_character")
149 // push single character into input queue
150 std::string arg = getEnclosedString(line, '"', '"', 0);
151 NC::Key::Type k = stringToSpecialKey(arg);
152 auto queue = std::vector<NC::Key::Type>{ k };
153 if (k != NC::Key::None)
154 result = new Actions::PushCharacters(&Global::wFooter, std::move(queue));
155 else
156 error() << "invalid character passed to push_character: '" << arg << "'\n";
158 else if (action_name == "push_characters")
160 // push sequence of characters into input queue
161 std::string arg = getEnclosedString(line, '"', '"', 0);
162 if (!arg.empty())
164 std::vector<NC::Key::Type> queue(arg.begin(), arg.end());
165 // if char is signed, erase 1s from char -> int conversion
166 for (auto it = arg.begin(); it != arg.end(); ++it)
167 *it &= 0xff;
168 result = new Actions::PushCharacters(&Global::wFooter, std::move(queue));
170 else
171 error() << "empty argument passed to push_characters\n";
173 else if (action_name == "require_screen")
175 // require screen of given type
176 std::string arg = getEnclosedString(line, '"', '"', 0);
177 ScreenType screen_type = stringToScreenType(arg);
178 if (screen_type != ScreenType::Unknown)
179 result = new Actions::RequireScreen(screen_type);
180 else
181 error() << "unknown screen passed to require_screen: '" << arg << "'\n";
183 else if (action_name == "require_runnable")
185 // require that given action is runnable
186 std::string arg = getEnclosedString(line, '"', '"', 0);
187 auto action = Actions::get(arg);
188 if (action)
189 result = new Actions::RequireRunnable(action);
190 else
191 error() << "unknown action passed to require_runnable: '" << arg << "'\n";
193 else if (action_name == "run_external_command")
195 std::string command = getEnclosedString(line, '"', '"', 0);
196 if (!command.empty())
197 result = new Actions::RunExternalCommand(std::move(command));
198 else
199 error() << "empty command passed to run_external_command\n";
202 return result;
207 NC::Key::Type readKey(NC::Window &w)
209 NC::Key::Type result = NC::Key::None;
210 std::string tmp;
211 NC::Key::Type input;
212 bool alt_pressed = false;
213 while (true)
215 input = w.readKey();
216 if (input == NC::Key::None)
217 break;
218 if (input & NC::Key::Alt)
220 // Complete the key and reapply the mask at the end.
221 alt_pressed = true;
222 input &= ~NC::Key::Alt;
224 if (input > 255) // NC special character
226 result = input;
227 break;
229 else
231 wchar_t wc;
232 tmp += input;
233 size_t conv_res = mbrtowc(&wc, tmp.c_str(), MB_CUR_MAX, 0);
234 if (conv_res == size_t(-1)) // incomplete multibyte character
235 continue;
236 else if (conv_res == size_t(-2)) // garbage character sequence
237 break;
238 else // character complete
240 result = wc;
241 break;
245 if (alt_pressed)
246 result |= NC::Key::Alt;
247 return result;
250 std::wstring keyToWString(const NC::Key::Type key)
252 std::wstring result;
254 if (key == NC::Key::Tab)
255 result += L"Tab";
256 else if (key == NC::Key::Enter)
257 result += L"Enter";
258 else if (key == NC::Key::Escape)
259 result += L"Escape";
260 else if (key >= NC::Key::Ctrl_A && key <= NC::Key::Ctrl_Z)
262 result += L"Ctrl-";
263 result += 'A' + (key - NC::Key::Ctrl_A);
265 else if (key == NC::Key::Ctrl_LeftBracket)
266 result += L"Ctrl-[";
267 else if (key == NC::Key::Ctrl_Backslash)
268 result += L"Ctrl-\\";
269 else if (key == NC::Key::Ctrl_RightBracket)
270 result += L"Ctrl-]";
271 else if (key == NC::Key::Ctrl_Caret)
272 result += L"Ctrl-^";
273 else if (key == NC::Key::Ctrl_Underscore)
274 result += L"Ctrl-_";
275 else if (key & NC::Key::Alt)
277 result += L"Alt-";
278 result += keyToWString(key & ~NC::Key::Alt);
280 else if (key & NC::Key::Ctrl)
282 result += L"Ctrl-";
283 result += keyToWString(key & ~NC::Key::Ctrl);
285 else if (key & NC::Key::Shift)
287 result += L"Shift-";
288 result += keyToWString(key & ~NC::Key::Shift);
290 else if (key == NC::Key::Space)
291 result += L"Space";
292 else if (key == NC::Key::Backspace)
293 result += L"Backspace";
294 else if (key == NC::Key::Insert)
295 result += L"Insert";
296 else if (key == NC::Key::Delete)
297 result += L"Delete";
298 else if (key == NC::Key::Home)
299 result += L"Home";
300 else if (key == NC::Key::End)
301 result += L"End";
302 else if (key == NC::Key::PageUp)
303 result += L"PageUp";
304 else if (key == NC::Key::PageDown)
305 result += L"PageDown";
306 else if (key == NC::Key::Up)
307 result += L"Up";
308 else if (key == NC::Key::Down)
309 result += L"Down";
310 else if (key == NC::Key::Left)
311 result += L"Left";
312 else if (key == NC::Key::Right)
313 result += L"Right";
314 else if (key >= NC::Key::F1 && key <= NC::Key::F9)
316 result += L"F";
317 result += '1' + (key - NC::Key::F1);
319 else if (key >= NC::Key::F10 && key <= NC::Key::F12)
321 result += L"F1";
322 result += '0' + (key - NC::Key::F10);
324 else
325 result += std::wstring(1, key);
327 return result;
330 bool BindingsConfiguration::read(const std::string &file)
332 enum class InProgress { None, Command, Key };
334 bool result = true;
336 std::ifstream f(file);
337 if (!f.is_open())
338 return result;
340 // shared variables
341 InProgress in_progress = InProgress::None;
342 size_t line_no = 0;
343 std::string line;
344 Binding::ActionChain actions;
346 // def_key specific variables
347 NC::Key::Type key = NC::Key::None;
348 std::string strkey;
350 // def_command specific variables
351 bool cmd_immediate = false;
352 std::string cmd_name;
354 auto error = [&]() -> std::ostream & {
355 std::cerr << file << ":" << line_no << ": error: ";
356 in_progress = InProgress::None;
357 result = false;
358 return std::cerr;
361 auto bind_in_progress = [&]() -> bool {
362 if (in_progress == InProgress::Command)
364 if (!actions.empty())
366 m_commands.insert(std::make_pair(cmd_name, Command(std::move(actions), cmd_immediate)));
367 actions.clear();
368 return true;
370 else
372 error() << "definition of command '" << cmd_name << "' cannot be empty\n";
373 return false;
376 else if (in_progress == InProgress::Key)
378 if (!actions.empty())
380 bind(key, actions);
381 actions.clear();
382 return true;
384 else
386 error() << "definition of key '" << strkey << "' cannot be empty\n";
387 return false;
390 return true;
393 const char def_command[] = "def_command";
394 const char def_key[] = "def_key";
396 while (!f.eof() && ++line_no)
398 getline(f, line);
399 if (line.empty() || line[0] == '#')
400 continue;
402 // beginning of command definition
403 if (!line.compare(0, const_strlen(def_command), def_command))
405 if (!bind_in_progress())
406 break;
407 in_progress = InProgress::Command;
408 cmd_name = getEnclosedString(line, '"', '"', 0);
409 if (cmd_name.empty())
411 error() << "command must have non-empty name\n";
412 break;
414 if (m_commands.find(cmd_name) != m_commands.end())
416 error() << "redefinition of command '" << cmd_name << "'\n";
417 break;
419 std::string cmd_type = getEnclosedString(line, '[', ']', 0);
420 if (cmd_type == "immediate")
421 cmd_immediate = true;
422 else if (cmd_type == "deferred")
423 cmd_immediate = false;
424 else
426 error() << "invalid type of command: '" << cmd_type << "'\n";
427 break;
430 // beginning of key definition
431 else if (!line.compare(0, const_strlen(def_key), def_key))
433 if (!bind_in_progress())
434 break;
435 in_progress = InProgress::Key;
436 strkey = getEnclosedString(line, '"', '"', 0);
437 key = stringToKey(strkey);
438 if (key == NC::Key::None)
440 error() << "invalid key: '" << strkey << "'\n";
441 break;
444 else if (isspace(line[0])) // name of action to be bound
446 boost::trim(line);
447 auto action = parseActionLine(line, error);
448 if (action)
449 actions.push_back(action);
450 else
452 error() << "unknown action: '" << line << "'\n";
453 break;
456 else
458 error() << "invalid line: '" << line << "'\n";
459 break;
462 bind_in_progress();
463 f.close();
464 return result;
467 void BindingsConfiguration::generateDefaults()
469 NC::Key::Type k = NC::Key::None;
470 if (notBound(k = stringToKey("mouse")))
471 bind(k, Actions::Type::MouseEvent);
472 if (notBound(k = stringToKey("up")))
473 bind(k, Actions::Type::ScrollUp);
474 if (notBound(k = stringToKey("shift-up")))
475 bind(k, Binding::ActionChain({ &Actions::get(Actions::Type::SelectItem), &Actions::get(Actions::Type::ScrollUp) }));
476 if (notBound(k = stringToKey("down")))
477 bind(k, Actions::Type::ScrollDown);
478 if (notBound(k = stringToKey("shift-down")))
479 bind(k, Binding::ActionChain({ &Actions::get(Actions::Type::SelectItem), &Actions::get(Actions::Type::ScrollDown) }));
480 if (notBound(k = stringToKey("[")))
481 bind(k, Actions::Type::ScrollUpAlbum);
482 if (notBound(k = stringToKey("]")))
483 bind(k, Actions::Type::ScrollDownAlbum);
484 if (notBound(k = stringToKey("{")))
485 bind(k, Actions::Type::ScrollUpArtist);
486 if (notBound(k = stringToKey("}")))
487 bind(k, Actions::Type::ScrollDownArtist);
488 if (notBound(k = stringToKey("page_up")))
489 bind(k, Actions::Type::PageUp);
490 if (notBound(k = stringToKey("page_down")))
491 bind(k, Actions::Type::PageDown);
492 if (notBound(k = stringToKey("home")))
493 bind(k, Actions::Type::MoveHome);
494 if (notBound(k = stringToKey("end")))
495 bind(k, Actions::Type::MoveEnd);
496 if (notBound(k = stringToKey("insert")))
497 bind(k, Actions::Type::SelectItem);
498 if (notBound(k = stringToKey("enter")))
500 bind(k, Actions::Type::EnterDirectory);
501 bind(k, Actions::Type::ToggleOutput);
502 bind(k, Actions::Type::RunAction);
503 bind(k, Actions::Type::PlayItem);
505 if (notBound(k = stringToKey("space")))
507 bind(k, Actions::Type::AddItemToPlaylist);
508 bind(k, Actions::Type::ToggleLyricsUpdateOnSongChange);
509 bind(k, Actions::Type::ToggleVisualizationType);
511 if (notBound(k = stringToKey("delete")))
513 bind(k, Actions::Type::DeletePlaylistItems);
514 bind(k, Actions::Type::DeleteBrowserItems);
515 bind(k, Actions::Type::DeleteStoredPlaylist);
517 if (notBound(k = stringToKey("right")))
519 bind(k, Actions::Type::NextColumn);
520 bind(k, Actions::Type::SlaveScreen);
521 bind(k, Actions::Type::VolumeUp);
523 if (notBound(k = stringToKey("+")))
524 bind(k, Actions::Type::VolumeUp);
525 if (notBound(k = stringToKey("left")))
527 bind(k, Actions::Type::PreviousColumn);
528 bind(k, Actions::Type::MasterScreen);
529 bind(k, Actions::Type::VolumeDown);
531 if (notBound(k = stringToKey("-")))
532 bind(k, Actions::Type::VolumeDown);
533 if (notBound(k = stringToKey(":")))
534 bind(k, Actions::Type::ExecuteCommand);
535 if (notBound(k = stringToKey("tab")))
536 bind(k, Actions::Type::NextScreen);
537 if (notBound(k = stringToKey("shift-tab")))
538 bind(k, Actions::Type::PreviousScreen);
539 if (notBound(k = stringToKey("f1")))
540 bind(k, Actions::Type::ShowHelp);
541 if (notBound(k = stringToKey("1")))
542 bind(k, Actions::Type::ShowPlaylist);
543 if (notBound(k = stringToKey("2")))
545 bind(k, Actions::Type::ShowBrowser);
546 bind(k, Actions::Type::ChangeBrowseMode);
548 if (notBound(k = stringToKey("3")))
550 bind(k, Actions::Type::ShowSearchEngine);
551 bind(k, Actions::Type::ResetSearchEngine);
553 if (notBound(k = stringToKey("4")))
555 bind(k, Actions::Type::ShowMediaLibrary);
556 bind(k, Actions::Type::ToggleMediaLibraryColumnsMode);
558 if (notBound(k = stringToKey("5")))
559 bind(k, Actions::Type::ShowPlaylistEditor);
560 if (notBound(k = stringToKey("6")))
561 bind(k, Actions::Type::ShowTagEditor);
562 if (notBound(k = stringToKey("7")))
563 bind(k, Actions::Type::ShowOutputs);
564 if (notBound(k = stringToKey("8")))
565 bind(k, Actions::Type::ShowVisualizer);
566 if (notBound(k = stringToKey("=")))
567 bind(k, Actions::Type::ShowClock);
568 if (notBound(k = stringToKey("@")))
569 bind(k, Actions::Type::ShowServerInfo);
570 if (notBound(k = stringToKey("s")))
571 bind(k, Actions::Type::Stop);
572 if (notBound(k = stringToKey("p")))
573 bind(k, Actions::Type::Pause);
574 if (notBound(k = stringToKey(">")))
575 bind(k, Actions::Type::Next);
576 if (notBound(k = stringToKey("<")))
577 bind(k, Actions::Type::Previous);
578 if (notBound(k = stringToKey("ctrl-h")))
580 bind(k, Actions::Type::JumpToParentDirectory);
581 bind(k, Actions::Type::ReplaySong);
583 if (notBound(k = stringToKey("backspace")))
585 bind(k, Actions::Type::JumpToParentDirectory);
586 bind(k, Actions::Type::ReplaySong);
588 if (notBound(k = stringToKey("f")))
589 bind(k, Actions::Type::SeekForward);
590 if (notBound(k = stringToKey("b")))
591 bind(k, Actions::Type::SeekBackward);
592 if (notBound(k = stringToKey("r")))
593 bind(k, Actions::Type::ToggleRepeat);
594 if (notBound(k = stringToKey("z")))
595 bind(k, Actions::Type::ToggleRandom);
596 if (notBound(k = stringToKey("y")))
598 bind(k, Actions::Type::SaveTagChanges);
599 bind(k, Actions::Type::StartSearching);
600 bind(k, Actions::Type::ToggleSingle);
602 if (notBound(k = stringToKey("R")))
603 bind(k, Actions::Type::ToggleConsume);
604 if (notBound(k = stringToKey("Y")))
605 bind(k, Actions::Type::ToggleReplayGainMode);
606 if (notBound(k = stringToKey("T")))
607 bind(k, Actions::Type::ToggleAddMode);
608 if (notBound(k = stringToKey("|")))
609 bind(k, Actions::Type::ToggleMouse);
610 if (notBound(k = stringToKey("#")))
611 bind(k, Actions::Type::ToggleBitrateVisibility);
612 if (notBound(k = stringToKey("Z")))
613 bind(k, Actions::Type::Shuffle);
614 if (notBound(k = stringToKey("x")))
615 bind(k, Actions::Type::ToggleCrossfade);
616 if (notBound(k = stringToKey("X")))
617 bind(k, Actions::Type::SetCrossfade);
618 if (notBound(k = stringToKey("u")))
619 bind(k, Actions::Type::UpdateDatabase);
620 if (notBound(k = stringToKey("ctrl-s")))
622 bind(k, Actions::Type::SortPlaylist);
623 bind(k, Actions::Type::ToggleBrowserSortMode);
624 bind(k, Actions::Type::ToggleMediaLibrarySortMode);
626 if (notBound(k = stringToKey("ctrl-r")))
627 bind(k, Actions::Type::ReversePlaylist);
628 if (notBound(k = stringToKey("ctrl-f")))
629 bind(k, Actions::Type::ApplyFilter);
630 if (notBound(k = stringToKey("ctrl-_")))
631 bind(k, Actions::Type::SelectFoundItems);
632 if (notBound(k = stringToKey("/")))
634 bind(k, Actions::Type::Find);
635 bind(k, Actions::Type::FindItemForward);
637 if (notBound(k = stringToKey("?")))
639 bind(k, Actions::Type::Find);
640 bind(k, Actions::Type::FindItemBackward);
642 if (notBound(k = stringToKey(".")))
643 bind(k, Actions::Type::NextFoundItem);
644 if (notBound(k = stringToKey(",")))
645 bind(k, Actions::Type::PreviousFoundItem);
646 if (notBound(k = stringToKey("w")))
647 bind(k, Actions::Type::ToggleFindMode);
648 if (notBound(k = stringToKey("e")))
650 bind(k, Actions::Type::EditSong);
651 bind(k, Actions::Type::EditLibraryTag);
652 bind(k, Actions::Type::EditLibraryAlbum);
653 bind(k, Actions::Type::EditDirectoryName);
654 bind(k, Actions::Type::EditPlaylistName);
655 bind(k, Actions::Type::EditLyrics);
657 if (notBound(k = stringToKey("i")))
658 bind(k, Actions::Type::ShowSongInfo);
659 if (notBound(k = stringToKey("I")))
660 bind(k, Actions::Type::ShowArtistInfo);
661 if (notBound(k = stringToKey("g")))
662 bind(k, Actions::Type::JumpToPositionInSong);
663 if (notBound(k = stringToKey("l")))
664 bind(k, Actions::Type::ShowLyrics);
665 if (notBound(k = stringToKey("ctrl-v")))
666 bind(k, Actions::Type::SelectRange);
667 if (notBound(k = stringToKey("v")))
668 bind(k, Actions::Type::ReverseSelection);
669 if (notBound(k = stringToKey("V")))
670 bind(k, Actions::Type::RemoveSelection);
671 if (notBound(k = stringToKey("B")))
672 bind(k, Actions::Type::SelectAlbum);
673 if (notBound(k = stringToKey("a")))
674 bind(k, Actions::Type::AddSelectedItems);
675 if (notBound(k = stringToKey("c")))
677 bind(k, Actions::Type::ClearPlaylist);
678 bind(k, Actions::Type::ClearMainPlaylist);
680 if (notBound(k = stringToKey("C")))
682 bind(k, Actions::Type::CropPlaylist);
683 bind(k, Actions::Type::CropMainPlaylist);
685 if (notBound(k = stringToKey("m")))
687 bind(k, Actions::Type::MoveSortOrderUp);
688 bind(k, Actions::Type::MoveSelectedItemsUp);
689 bind(k, Actions::Type::SetVisualizerSampleMultiplier);
691 if (notBound(k = stringToKey("n")))
693 bind(k, Actions::Type::MoveSortOrderDown);
694 bind(k, Actions::Type::MoveSelectedItemsDown);
696 if (notBound(k = stringToKey("M")))
697 bind(k, Actions::Type::MoveSelectedItemsTo);
698 if (notBound(k = stringToKey("A")))
699 bind(k, Actions::Type::Add);
700 if (notBound(k = stringToKey("S")))
701 bind(k, Actions::Type::SavePlaylist);
702 if (notBound(k = stringToKey("o")))
703 bind(k, Actions::Type::JumpToPlayingSong);
704 if (notBound(k = stringToKey("G")))
706 bind(k, Actions::Type::JumpToBrowser);
707 bind(k, Actions::Type::JumpToPlaylistEditor);
709 if (notBound(k = stringToKey("~")))
710 bind(k, Actions::Type::JumpToMediaLibrary);
711 if (notBound(k = stringToKey("E")))
712 bind(k, Actions::Type::JumpToTagEditor);
713 if (notBound(k = stringToKey("U")))
714 bind(k, Actions::Type::TogglePlayingSongCentering);
715 if (notBound(k = stringToKey("P")))
716 bind(k, Actions::Type::ToggleDisplayMode);
717 if (notBound(k = stringToKey("\\")))
718 bind(k, Actions::Type::ToggleInterface);
719 if (notBound(k = stringToKey("!")))
720 bind(k, Actions::Type::ToggleSeparatorsBetweenAlbums);
721 if (notBound(k = stringToKey("L")))
722 bind(k, Actions::Type::ToggleLyricsFetcher);
723 if (notBound(k = stringToKey("F")))
724 bind(k, Actions::Type::FetchLyricsInBackground);
725 if (notBound(k = stringToKey("alt-l")))
726 bind(k, Actions::Type::ToggleFetchingLyricsInBackground);
727 if (notBound(k = stringToKey("ctrl-l")))
728 bind(k, Actions::Type::ToggleScreenLock);
729 if (notBound(k = stringToKey("`")))
731 bind(k, Actions::Type::ToggleLibraryTagType);
732 bind(k, Actions::Type::RefetchLyrics);
733 bind(k, Actions::Type::AddRandomItems);
735 if (notBound(k = stringToKey("ctrl-p")))
736 bind(k, Actions::Type::SetSelectedItemsPriority);
737 if (notBound(k = stringToKey("q")))
738 bind(k, Actions::Type::Quit);
740 if (notBound(k = stringToKey("-")))
741 bind(k, Actions::Type::VolumeDown);
744 const Command *BindingsConfiguration::findCommand(const std::string &name)
746 const Command *ptr = nullptr;
747 auto it = m_commands.find(name);
748 if (it != m_commands.end())
749 ptr = &it->second;
750 return ptr;
753 BindingsConfiguration::BindingIteratorPair BindingsConfiguration::get(const NC::Key::Type &k)
755 std::pair<BindingIterator, BindingIterator> result;
756 auto it = m_bindings.find(k);
757 if (it != m_bindings.end()) {
758 result.first = it->second.begin();
759 result.second = it->second.end();
760 } else {
761 auto list_end = m_bindings.begin()->second.end();
762 result.first = list_end;
763 result.second = list_end;
765 return result;