1 /***************************************************************************
2 * Copyright (C) 2008-2014 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
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. *
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. *
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>
26 #include "utility/string.h"
27 #include "utility/wide_string.h"
29 BindingsConfiguration Bindings
;
33 NC::Key::Type
stringToKey(const std::string
&s
);
35 NC::Key::Type
stringToSpecialKey(const std::string
&s
)
37 NC::Key::Type result
= NC::Key::None
;
38 if (!s
.compare(0, 5, "ctrl_") && s
.length() == 6)
40 if (s
[5] >= 'a' && s
[5] <= 'z')
41 result
= NC::Key::Ctrl_A
+ (s
[5] - 'a');
43 result
= NC::Key::Ctrl_LeftBracket
;
44 else if (s
[5] == '\\')
45 result
= NC::Key::Ctrl_Backslash
;
47 result
= NC::Key::Ctrl_RightBracket
;
49 result
= NC::Key::Ctrl_Caret
;
51 result
= NC::Key::Ctrl_Underscore
;
53 else if (!s
.compare(0, 4, "alt_"))
54 result
= NC::Key::Alt
| stringToKey(s
.substr(4));
55 else if (!s
.compare(0, 5, "ctrl_"))
56 result
= NC::Key::Ctrl
| stringToKey(s
.substr(5));
57 else if (!s
.compare(0, 6, "shift_"))
58 result
= NC::Key::Shift
| stringToKey(s
.substr(6));
59 else if (!s
.compare("escape"))
60 result
= NC::Key::Escape
;
61 else if (!s
.compare("mouse"))
62 result
= NC::Key::Mouse
;
63 else if (!s
.compare("up"))
65 else if (!s
.compare("down"))
66 result
= NC::Key::Down
;
67 else if (!s
.compare("page_up"))
68 result
= NC::Key::PageUp
;
69 else if (!s
.compare("page_down"))
70 result
= NC::Key::PageDown
;
71 else if (!s
.compare("home"))
72 result
= NC::Key::Home
;
73 else if (!s
.compare("end"))
74 result
= NC::Key::End
;
75 else if (!s
.compare("space"))
76 result
= NC::Key::Space
;
77 else if (!s
.compare("enter"))
78 result
= NC::Key::Enter
;
79 else if (!s
.compare("insert"))
80 result
= NC::Key::Insert
;
81 else if (!s
.compare("delete"))
82 result
= NC::Key::Delete
;
83 else if (!s
.compare("left"))
84 result
= NC::Key::Left
;
85 else if (!s
.compare("right"))
86 result
= NC::Key::Right
;
87 else if (!s
.compare("tab"))
88 result
= NC::Key::Tab
;
89 else if ((s
.length() == 2 || s
.length() == 3) && s
[0] == 'f')
91 int n
= atoi(s
.c_str() + 1);
92 if (n
>= 1 && n
<= 12)
93 result
= NC::Key::F1
+ n
- 1;
95 else if (!s
.compare("backspace"))
96 result
= NC::Key::Backspace
;
100 NC::Key::Type
stringToKey(const std::string
&s
)
102 NC::Key::Type result
= stringToSpecialKey(s
);
103 if (result
== NC::Key::None
)
105 std::wstring ws
= ToWString(s
);
106 if (ws
.length() == 1)
112 template <typename F
>
113 Actions::BaseAction
*parseActionLine(const std::string
&line
, F error
)
115 Actions::BaseAction
*result
= 0;
117 for (; i
< line
.size() && !isspace(line
[i
]); ++i
) { }
118 if (i
== line
.size()) // only action name
119 result
= Actions::get(line
);
120 else // there is something else
122 std::string action_name
= line
.substr(0, i
);
123 if (action_name
== "push_character")
125 // push single character into input queue
126 std::string arg
= getEnclosedString(line
, '"', '"', 0);
127 NC::Key::Type k
= stringToSpecialKey(arg
);
128 auto queue
= std::vector
<NC::Key::Type
>{ k
};
129 if (k
!= NC::Key::None
)
130 result
= new Actions::PushCharacters(&Global::wFooter
, std::move(queue
));
132 error() << "invalid character passed to push_character: '" << arg
<< "'\n";
134 else if (action_name
== "push_characters")
136 // push sequence of characters into input queue
137 std::string arg
= getEnclosedString(line
, '"', '"', 0);
140 std::vector
<NC::Key::Type
> queue(arg
.begin(), arg
.end());
141 // if char is signed, erase 1s from char -> int conversion
142 for (auto it
= arg
.begin(); it
!= arg
.end(); ++it
)
144 result
= new Actions::PushCharacters(&Global::wFooter
, std::move(queue
));
147 error() << "empty argument passed to push_characters\n";
149 else if (action_name
== "require_screen")
151 // require screen of given type
152 std::string arg
= getEnclosedString(line
, '"', '"', 0);
153 ScreenType screen_type
= stringToScreenType(arg
);
154 if (screen_type
!= ScreenType::Unknown
)
155 result
= new Actions::RequireScreen(screen_type
);
157 error() << "unknown screen passed to require_screen: '" << arg
<< "'\n";
159 else if (action_name
== "require_runnable")
161 // require that given action is runnable
162 std::string arg
= getEnclosedString(line
, '"', '"', 0);
163 auto action
= Actions::get(arg
);
165 result
= new Actions::RequireRunnable(action
);
167 error() << "unknown action passed to require_runnable: '" << arg
<< "'\n";
169 else if (action_name
== "run_external_command")
171 std::string command
= getEnclosedString(line
, '"', '"', 0);
172 if (!command
.empty())
173 result
= new Actions::RunExternalCommand(std::move(command
));
175 error() << "empty command passed to run_external_command\n";
183 NC::Key::Type
readKey(NC::Window
&w
)
185 NC::Key::Type result
= NC::Key::None
;
188 bool alt_pressed
= false;
192 if (input
== NC::Key::None
)
194 if (input
& NC::Key::Alt
)
196 // Complete the key and reapply the mask at the end.
198 input
&= ~NC::Key::Alt
;
200 if (input
> 255) // NC special character
209 size_t conv_res
= mbrtowc(&wc
, tmp
.c_str(), MB_CUR_MAX
, 0);
210 if (conv_res
== size_t(-1)) // incomplete multibyte character
212 else if (conv_res
== size_t(-2)) // garbage character sequence
214 else // character complete
222 result
|= NC::Key::Alt
;
226 std::wstring
keyToWString(const NC::Key::Type key
)
230 if (key
== NC::Key::Tab
)
232 else if (key
== NC::Key::Enter
)
234 else if (key
== NC::Key::Escape
)
236 else if (key
>= NC::Key::Ctrl_A
&& key
<= NC::Key::Ctrl_Z
)
239 result
+= 'A' + (key
- NC::Key::Ctrl_A
);
241 else if (key
== NC::Key::Ctrl_LeftBracket
)
243 else if (key
== NC::Key::Ctrl_Backslash
)
244 result
+= L
"Ctrl-\\";
245 else if (key
== NC::Key::Ctrl_RightBracket
)
247 else if (key
== NC::Key::Ctrl_Caret
)
249 else if (key
== NC::Key::Ctrl_Underscore
)
251 else if (key
& NC::Key::Alt
)
254 result
+= keyToWString(key
& ~NC::Key::Alt
);
256 else if (key
& NC::Key::Ctrl
)
259 result
+= keyToWString(key
& ~NC::Key::Ctrl
);
261 else if (key
& NC::Key::Shift
)
264 result
+= keyToWString(key
& ~NC::Key::Shift
);
266 else if (key
== NC::Key::Space
)
268 else if (key
== NC::Key::Backspace
)
269 result
+= L
"Backspace";
270 else if (key
== NC::Key::Insert
)
272 else if (key
== NC::Key::Delete
)
274 else if (key
== NC::Key::Home
)
276 else if (key
== NC::Key::End
)
278 else if (key
== NC::Key::PageUp
)
280 else if (key
== NC::Key::PageDown
)
281 result
+= L
"PageDown";
282 else if (key
== NC::Key::Up
)
284 else if (key
== NC::Key::Down
)
286 else if (key
== NC::Key::Left
)
288 else if (key
== NC::Key::Right
)
290 else if (key
>= NC::Key::F1
&& key
<= NC::Key::F9
)
293 result
+= '1' + (key
- NC::Key::F1
);
295 else if (key
>= NC::Key::F10
&& key
<= NC::Key::F12
)
298 result
+= '0' + (key
- NC::Key::F10
);
301 result
+= std::wstring(1, key
);
306 bool BindingsConfiguration::read(const std::string
&file
)
308 enum class InProgress
{ None
, Command
, Key
};
312 std::ifstream
f(file
);
317 InProgress in_progress
= InProgress::None
;
320 Binding::ActionChain actions
;
322 // def_key specific variables
323 NC::Key::Type key
= NC::Key::None
;
326 // def_command specific variables
327 bool cmd_immediate
= false;
328 std::string cmd_name
;
330 auto error
= [&]() -> std::ostream
& {
331 std::cerr
<< file
<< ":" << line_no
<< ": error: ";
332 in_progress
= InProgress::None
;
337 auto bind_in_progress
= [&]() -> bool {
338 if (in_progress
== InProgress::Command
)
340 if (!actions
.empty())
342 m_commands
.insert(std::make_pair(cmd_name
, Command(std::move(actions
), cmd_immediate
)));
348 error() << "definition of command '" << cmd_name
<< "' cannot be empty\n";
352 else if (in_progress
== InProgress::Key
)
354 if (!actions
.empty())
362 error() << "definition of key '" << strkey
<< "' cannot be empty\n";
369 const char def_command
[] = "def_command";
370 const char def_key
[] = "def_key";
372 while (!f
.eof() && ++line_no
)
375 if (line
.empty() || line
[0] == '#')
378 // beginning of command definition
379 if (!line
.compare(0, const_strlen(def_command
), def_command
))
381 if (!bind_in_progress())
383 in_progress
= InProgress::Command
;
384 cmd_name
= getEnclosedString(line
, '"', '"', 0);
385 if (cmd_name
.empty())
387 error() << "command must have non-empty name\n";
390 if (m_commands
.find(cmd_name
) != m_commands
.end())
392 error() << "redefinition of command '" << cmd_name
<< "'\n";
395 std::string cmd_type
= getEnclosedString(line
, '[', ']', 0);
396 if (cmd_type
== "immediate")
397 cmd_immediate
= true;
398 else if (cmd_type
== "deferred")
399 cmd_immediate
= false;
402 error() << "invalid type of command: '" << cmd_type
<< "'\n";
406 // beginning of key definition
407 else if (!line
.compare(0, const_strlen(def_key
), def_key
))
409 if (!bind_in_progress())
411 in_progress
= InProgress::Key
;
412 strkey
= getEnclosedString(line
, '"', '"', 0);
413 key
= stringToKey(strkey
);
414 if (key
== NC::Key::None
)
416 error() << "invalid key: '" << strkey
<< "'\n";
420 else if (isspace(line
[0])) // name of action to be bound
423 auto action
= parseActionLine(line
, error
);
425 actions
.push_back(action
);
428 error() << "unknown action: '" << line
<< "'\n";
434 error() << "invalid line: '" << line
<< "'\n";
443 void BindingsConfiguration::generateDefaults()
445 NC::Key::Type k
= NC::Key::None
;
446 if (notBound(k
= stringToKey("mouse")))
447 bind(k
, Actions::Type::MouseEvent
);
448 if (notBound(k
= stringToKey("up")))
449 bind(k
, Actions::Type::ScrollUp
);
450 if (notBound(k
= stringToKey("down")))
451 bind(k
, Actions::Type::ScrollDown
);
452 if (notBound(k
= stringToKey("[")))
453 bind(k
, Actions::Type::ScrollUpAlbum
);
454 if (notBound(k
= stringToKey("]")))
455 bind(k
, Actions::Type::ScrollDownAlbum
);
456 if (notBound(k
= stringToKey("{")))
457 bind(k
, Actions::Type::ScrollUpArtist
);
458 if (notBound(k
= stringToKey("}")))
459 bind(k
, Actions::Type::ScrollDownArtist
);
460 if (notBound(k
= stringToKey("page_up")))
461 bind(k
, Actions::Type::PageUp
);
462 if (notBound(k
= stringToKey("page_down")))
463 bind(k
, Actions::Type::PageDown
);
464 if (notBound(k
= stringToKey("home")))
465 bind(k
, Actions::Type::MoveHome
);
466 if (notBound(k
= stringToKey("end")))
467 bind(k
, Actions::Type::MoveEnd
);
468 if (notBound(k
= stringToKey("insert")))
469 bind(k
, Actions::Type::SelectItem
);
470 if (notBound(k
= stringToKey("space")))
471 bind(k
, Actions::Type::PressSpace
);
472 if (notBound(k
= stringToKey("enter")))
473 bind(k
, Actions::Type::PressEnter
);
474 if (notBound(k
= stringToKey("delete")))
476 bind(k
, Actions::Type::DeletePlaylistItems
);
477 bind(k
, Actions::Type::DeleteBrowserItems
);
478 bind(k
, Actions::Type::DeleteStoredPlaylist
);
480 if (notBound(k
= stringToKey("right")))
482 bind(k
, Actions::Type::NextColumn
);
483 bind(k
, Actions::Type::SlaveScreen
);
484 bind(k
, Actions::Type::VolumeUp
);
486 if (notBound(k
= stringToKey("+")))
487 bind(k
, Actions::Type::VolumeUp
);
488 if (notBound(k
= stringToKey("left")))
490 bind(k
, Actions::Type::PreviousColumn
);
491 bind(k
, Actions::Type::MasterScreen
);
492 bind(k
, Actions::Type::VolumeDown
);
494 if (notBound(k
= stringToKey("-")))
495 bind(k
, Actions::Type::VolumeDown
);
496 if (notBound(k
= stringToKey(":")))
497 bind(k
, Actions::Type::ExecuteCommand
);
498 if (notBound(k
= stringToKey("tab")))
499 bind(k
, Actions::Type::NextScreen
);
500 if (notBound(k
= stringToKey("shift_tab")))
501 bind(k
, Actions::Type::PreviousScreen
);
502 if (notBound(k
= stringToKey("f1")))
503 bind(k
, Actions::Type::ShowHelp
);
504 if (notBound(k
= stringToKey("1")))
505 bind(k
, Actions::Type::ShowPlaylist
);
506 if (notBound(k
= stringToKey("2")))
508 bind(k
, Actions::Type::ShowBrowser
);
509 bind(k
, Actions::Type::ChangeBrowseMode
);
511 if (notBound(k
= stringToKey("3")))
513 bind(k
, Actions::Type::ShowSearchEngine
);
514 bind(k
, Actions::Type::ResetSearchEngine
);
516 if (notBound(k
= stringToKey("4")))
518 bind(k
, Actions::Type::ShowMediaLibrary
);
519 bind(k
, Actions::Type::ToggleMediaLibraryColumnsMode
);
521 if (notBound(k
= stringToKey("5")))
522 bind(k
, Actions::Type::ShowPlaylistEditor
);
523 if (notBound(k
= stringToKey("6")))
524 bind(k
, Actions::Type::ShowTagEditor
);
525 if (notBound(k
= stringToKey("7")))
526 bind(k
, Actions::Type::ShowOutputs
);
527 if (notBound(k
= stringToKey("8")))
528 bind(k
, Actions::Type::ShowVisualizer
);
529 if (notBound(k
= stringToKey("=")))
530 bind(k
, Actions::Type::ShowClock
);
531 if (notBound(k
= stringToKey("@")))
532 bind(k
, Actions::Type::ShowServerInfo
);
533 if (notBound(k
= stringToKey("s")))
534 bind(k
, Actions::Type::Stop
);
535 if (notBound(k
= stringToKey("p")))
536 bind(k
, Actions::Type::Pause
);
537 if (notBound(k
= stringToKey(">")))
538 bind(k
, Actions::Type::Next
);
539 if (notBound(k
= stringToKey("<")))
540 bind(k
, Actions::Type::Previous
);
541 if (notBound(k
= stringToKey("ctrl_h")))
543 bind(k
, Actions::Type::JumpToParentDirectory
);
544 bind(k
, Actions::Type::ReplaySong
);
546 if (notBound(k
= stringToKey("backspace")))
548 bind(k
, Actions::Type::JumpToParentDirectory
);
549 bind(k
, Actions::Type::ReplaySong
);
551 if (notBound(k
= stringToKey("f")))
552 bind(k
, Actions::Type::SeekForward
);
553 if (notBound(k
= stringToKey("b")))
554 bind(k
, Actions::Type::SeekBackward
);
555 if (notBound(k
= stringToKey("r")))
556 bind(k
, Actions::Type::ToggleRepeat
);
557 if (notBound(k
= stringToKey("z")))
558 bind(k
, Actions::Type::ToggleRandom
);
559 if (notBound(k
= stringToKey("y")))
561 bind(k
, Actions::Type::SaveTagChanges
);
562 bind(k
, Actions::Type::StartSearching
);
563 bind(k
, Actions::Type::ToggleSingle
);
565 if (notBound(k
= stringToKey("R")))
566 bind(k
, Actions::Type::ToggleConsume
);
567 if (notBound(k
= stringToKey("Y")))
568 bind(k
, Actions::Type::ToggleReplayGainMode
);
569 if (notBound(k
= stringToKey("T")))
570 bind(k
, Actions::Type::ToggleAddMode
);
571 if (notBound(k
= stringToKey("|")))
572 bind(k
, Actions::Type::ToggleMouse
);
573 if (notBound(k
= stringToKey("#")))
574 bind(k
, Actions::Type::ToggleBitrateVisibility
);
575 if (notBound(k
= stringToKey("Z")))
576 bind(k
, Actions::Type::Shuffle
);
577 if (notBound(k
= stringToKey("x")))
578 bind(k
, Actions::Type::ToggleCrossfade
);
579 if (notBound(k
= stringToKey("X")))
580 bind(k
, Actions::Type::SetCrossfade
);
581 if (notBound(k
= stringToKey("u")))
582 bind(k
, Actions::Type::UpdateDatabase
);
583 if (notBound(k
= stringToKey("ctrl_v")))
584 bind(k
, Actions::Type::SortPlaylist
);
585 if (notBound(k
= stringToKey("ctrl_r")))
586 bind(k
, Actions::Type::ReversePlaylist
);
587 if (notBound(k
= stringToKey("/")))
589 bind(k
, Actions::Type::Find
);
590 bind(k
, Actions::Type::FindItemForward
);
592 if (notBound(k
= stringToKey("?")))
594 bind(k
, Actions::Type::Find
);
595 bind(k
, Actions::Type::FindItemBackward
);
597 if (notBound(k
= stringToKey(".")))
598 bind(k
, Actions::Type::NextFoundItem
);
599 if (notBound(k
= stringToKey(",")))
600 bind(k
, Actions::Type::PreviousFoundItem
);
601 if (notBound(k
= stringToKey("w")))
602 bind(k
, Actions::Type::ToggleFindMode
);
603 if (notBound(k
= stringToKey("e")))
605 bind(k
, Actions::Type::EditSong
);
606 bind(k
, Actions::Type::EditLibraryTag
);
607 bind(k
, Actions::Type::EditLibraryAlbum
);
608 bind(k
, Actions::Type::EditDirectoryName
);
609 bind(k
, Actions::Type::EditPlaylistName
);
610 bind(k
, Actions::Type::EditLyrics
);
612 if (notBound(k
= stringToKey("i")))
613 bind(k
, Actions::Type::ShowSongInfo
);
614 if (notBound(k
= stringToKey("I")))
615 bind(k
, Actions::Type::ShowArtistInfo
);
616 if (notBound(k
= stringToKey("g")))
617 bind(k
, Actions::Type::JumpToPositionInSong
);
618 if (notBound(k
= stringToKey("l")))
619 bind(k
, Actions::Type::ShowLyrics
);
620 if (notBound(k
= stringToKey("v")))
621 bind(k
, Actions::Type::ReverseSelection
);
622 if (notBound(k
= stringToKey("V")))
623 bind(k
, Actions::Type::RemoveSelection
);
624 if (notBound(k
= stringToKey("B")))
625 bind(k
, Actions::Type::SelectAlbum
);
626 if (notBound(k
= stringToKey("a")))
627 bind(k
, Actions::Type::AddSelectedItems
);
628 if (notBound(k
= stringToKey("c")))
630 bind(k
, Actions::Type::ClearPlaylist
);
631 bind(k
, Actions::Type::ClearMainPlaylist
);
633 if (notBound(k
= stringToKey("C")))
635 bind(k
, Actions::Type::CropPlaylist
);
636 bind(k
, Actions::Type::CropMainPlaylist
);
638 if (notBound(k
= stringToKey("m")))
640 bind(k
, Actions::Type::MoveSortOrderUp
);
641 bind(k
, Actions::Type::MoveSelectedItemsUp
);
642 bind(k
, Actions::Type::ToggleMediaLibrarySortMode
);
643 bind(k
, Actions::Type::SetVisualizerSampleMultiplier
);
645 if (notBound(k
= stringToKey("n")))
647 bind(k
, Actions::Type::MoveSortOrderDown
);
648 bind(k
, Actions::Type::MoveSelectedItemsDown
);
650 if (notBound(k
= stringToKey("M")))
651 bind(k
, Actions::Type::MoveSelectedItemsTo
);
652 if (notBound(k
= stringToKey("A")))
653 bind(k
, Actions::Type::Add
);
654 if (notBound(k
= stringToKey("S")))
655 bind(k
, Actions::Type::SavePlaylist
);
656 if (notBound(k
= stringToKey("o")))
657 bind(k
, Actions::Type::JumpToPlayingSong
);
658 if (notBound(k
= stringToKey("G")))
660 bind(k
, Actions::Type::JumpToBrowser
);
661 bind(k
, Actions::Type::JumpToPlaylistEditor
);
663 if (notBound(k
= stringToKey("~")))
664 bind(k
, Actions::Type::JumpToMediaLibrary
);
665 if (notBound(k
= stringToKey("E")))
666 bind(k
, Actions::Type::JumpToTagEditor
);
667 if (notBound(k
= stringToKey("U")))
668 bind(k
, Actions::Type::TogglePlayingSongCentering
);
669 if (notBound(k
= stringToKey("P")))
670 bind(k
, Actions::Type::ToggleDisplayMode
);
671 if (notBound(k
= stringToKey("\\")))
672 bind(k
, Actions::Type::ToggleInterface
);
673 if (notBound(k
= stringToKey("!")))
674 bind(k
, Actions::Type::ToggleSeparatorsBetweenAlbums
);
675 if (notBound(k
= stringToKey("L")))
676 bind(k
, Actions::Type::ToggleLyricsFetcher
);
677 if (notBound(k
= stringToKey("F")))
678 bind(k
, Actions::Type::ToggleFetchingLyricsInBackground
);
679 if (notBound(k
= stringToKey("ctrl_l")))
680 bind(k
, Actions::Type::ToggleScreenLock
);
681 if (notBound(k
= stringToKey("`")))
683 bind(k
, Actions::Type::ToggleBrowserSortMode
);
684 bind(k
, Actions::Type::ToggleLibraryTagType
);
685 bind(k
, Actions::Type::RefetchLyrics
);
686 bind(k
, Actions::Type::AddRandomItems
);
688 if (notBound(k
= stringToKey("ctrl_p")))
689 bind(k
, Actions::Type::SetSelectedItemsPriority
);
690 if (notBound(k
= stringToKey("q")))
691 bind(k
, Actions::Type::Quit
);
693 if (notBound(k
= stringToKey("-")))
694 bind(k
, Actions::Type::VolumeDown
);
697 const Command
*BindingsConfiguration::findCommand(const std::string
&name
)
699 const Command
*ptr
= nullptr;
700 auto it
= m_commands
.find(name
);
701 if (it
!= m_commands
.end())
706 BindingsConfiguration::BindingIteratorPair
BindingsConfiguration::get(const NC::Key::Type
&k
)
708 std::pair
<BindingIterator
, BindingIterator
> result
;
709 auto it
= m_bindings
.find(k
);
710 if (it
!= m_bindings
.end()) {
711 result
.first
= it
->second
.begin();
712 result
.second
= it
->second
.end();
714 auto list_end
= m_bindings
.begin()->second
.end();
715 result
.first
= list_end
;
716 result
.second
= list_end
;