1use super::*;
16
17fn str_to_key(cmd: &str) -> Option<Key> {
19 match cmd.trim().to_lowercase().as_str() {
20 "tab" => Some(Key::Tab),
21 "enter" => Some(Key::Enter),
22 "backspace" => Some(Key::Backspace),
23 "left" => Some(Key::Left),
24 "right" => Some(Key::Right),
25 "up" => Some(Key::Up),
26 "down" => Some(Key::Down),
27 "ins" => Some(Key::Ins),
28 "del" => Some(Key::Del),
29 "home" => Some(Key::Home),
30 "end" => Some(Key::End),
31 "page_up" => Some(Key::PageUp),
32 "page_down" => Some(Key::PageDown),
33 "pause_break" => Some(Key::PauseBreak),
34 "esc" => Some(Key::Esc),
35 _ => None,
36 }
37}
38
39fn key_to_str(key: &Key) -> &'static str {
40 match key {
41 Key::Tab => "Tab",
42 Key::Enter => "Enter",
43 Key::Backspace => "Backspace",
44 Key::Left => "Left",
45 Key::Right => "Right",
46 Key::Up => "Up",
47 Key::Down => "Down",
48 Key::Ins => "Ins",
49 Key::Del => "Del",
50 Key::Home => "Home",
51 Key::End => "End",
52 Key::PageUp => "PageUp",
53 Key::PageDown => "PageDown",
54 Key::PauseBreak => "PauseBreak",
55 Key::Esc => "Esc",
56 _ => "Unknown",
57 }
58}
59
60pub fn str_to_event(cmd: &str) -> Option<Event> {
64 if cmd.len() == 1 {
65 return Some(Event::Char(
66 cmd.chars()
67 .next()
68 .expect("Failed to parse first char from command"),
69 ));
70 }
71
72 let cmd_vec = cmd.split('-').collect::<Vec<&str>>();
73 match cmd_vec.len() {
74 1 => str_to_key(cmd_vec[0]).map(Event::Key),
75 2 => match cmd_vec[0].trim().to_lowercase().as_str() {
76 "ctrl" if cmd_vec[1].len() == 1 => Some(Event::CtrlChar(
77 cmd_vec[1]
78 .chars()
79 .next()
80 .expect("Failed to parse first char from ctrl-command"),
81 )),
82 "alt" if cmd_vec[1].len() == 1 => Some(Event::AltChar(
83 cmd_vec[1]
84 .chars()
85 .next()
86 .expect("Failed to parse first char from alt-command"),
87 )),
88 "shift" => str_to_key(cmd_vec[1]).map(Event::Shift),
89 "alt" => str_to_key(cmd_vec[1]).map(Event::Alt),
90 "altshift" => str_to_key(cmd_vec[1]).map(Event::AltShift),
91 "ctrl" => str_to_key(cmd_vec[1]).map(Event::Ctrl),
92 "ctrlshift" => str_to_key(cmd_vec[1]).map(Event::CtrlShift),
93 "ctrlalt" => str_to_key(cmd_vec[1]).map(Event::CtrlAlt),
94 _ => None,
95 },
96 _ => None,
97 }
98}
99
100pub fn event_to_string(event: &Event) -> String {
101 match event {
102 Event::Char(c) => format!("'{}'", c),
103 Event::CtrlChar(c) => format!("<Ctrl> '{}'", c),
104 Event::AltChar(c) => format!("<Alt> '{}'", c),
105 Event::Key(key) => format!("<{}>", key_to_str(key)),
106 Event::Shift(key) => format!("<Shift><{}>", key_to_str(key)),
107 Event::Alt(key) => format!("<Alt><{}>", key_to_str(key)),
108 Event::AltShift(key) => format!("<Alt><Shift><{}>", key_to_str(key)),
109 Event::Ctrl(key) => format!("<Ctrl><{}>", key_to_str(key)),
110 Event::CtrlShift(key) => format!("<Ctrl><Shift><{}>", key_to_str(key)),
111 Event::CtrlAlt(key) => format!("<Ctrl><Alt><{}>", key_to_str(key)),
112 _ => "Unknown".into(),
113 }
114}
115
116pub trait EventController {
119 fn command() -> &'static str;
121
122 fn cmd_shortcut() -> &'static str;
124
125 fn default_events() -> Vec<Event>;
127
128 fn handle<T: 'static + ViewBridge>(_view: &mut StatsView<T>, _cmd_vec: &[&str]) {}
130
131 fn callback<T: 'static + ViewBridge>(_c: &mut Cursive, _cmd_vec: &[&str]) {}
133}
134
135macro_rules! make_event_controller {
144 ($name:ident, $cmd:expr, $cmd_short:expr, $event:expr, $handle:expr) => {
145 pub struct $name;
146
147 impl EventController for $name {
148 fn command() -> &'static str {
149 $cmd
150 }
151
152 fn cmd_shortcut() -> &'static str {
153 $cmd_short
154 }
155
156 fn default_events() -> Vec<Event> {
157 $event
158 }
159
160 fn handle<T: 'static + ViewBridge>(view: &mut StatsView<T>, cmd_vec: &[&str]) {
161 $handle(view, cmd_vec)
162 }
163 }
164 };
165
166 ($name:ident, $cmd:expr, $cmd_short:expr, $event:expr, $handle:expr, $callback:expr) => {
167 pub struct $name;
168
169 impl EventController for $name {
170 fn command() -> &'static str {
171 $cmd
172 }
173
174 fn cmd_shortcut() -> &'static str {
175 $cmd_short
176 }
177
178 fn default_events() -> Vec<Event> {
179 $event
180 }
181
182 fn handle<T: 'static + ViewBridge>(view: &mut StatsView<T>, cmd_vec: &[&str]) {
183 $handle(view, cmd_vec)
184 }
185
186 fn callback<T: 'static + ViewBridge>(c: &mut Cursive, cmd_vec: &[&str]) {
187 $callback(c, cmd_vec)
188 }
189 }
190 };
191}
192
193macro_rules! make_controllers {
197 ($($(#[$attr:meta])* $enum_item:ident: $struct_item:ident,)*) => {
198 #[derive(Clone, PartialEq, Debug, Hash, Eq)]
199 pub enum Controllers {
200 Unknown,
201 $(
202 $(#[$attr])*
203 $enum_item,
204 )*
205 }
206
207 impl Controllers {
208 pub fn command(&self) -> &'static str {
209 match self {
210 Controllers::Unknown => "",
211 $(
212 $(#[$attr])*
213 Controllers::$enum_item => $struct_item::command(),
214 )*
215 }
216 }
217
218 pub fn cmd_shortcut(&self) -> &'static str {
219 match self {
220 Controllers::Unknown => "",
221 $(
222 $(#[$attr])*
223 Controllers::$enum_item => $struct_item::cmd_shortcut(),
224 )*
225 }
226 }
227
228 pub fn default_events(&self) -> Vec<Event> {
229 match self {
230 Controllers::Unknown => vec![Event::Unknown(vec![])],
231 $(
232 $(#[$attr])*
233 Controllers::$enum_item => $struct_item::default_events(),
234 )*
235 }
236 }
237
238 pub fn handle<T: 'static + ViewBridge>(&self, view: &mut StatsView<T>, cmd_vec: &[&str]) {
239 match self {
240 Controllers::Unknown => (),
241 $(
242 $(#[$attr])*
243 Controllers::$enum_item => $struct_item::handle(view, cmd_vec),
244 )*
245 }
246 }
247
248 pub fn callback<T: 'static + ViewBridge>(&self, c: &mut Cursive, cmd_vec: &[&str]) {
249 match self {
250 Controllers::Unknown => (),
251 $(
252 $(#[$attr])*
253 Controllers::$enum_item => $struct_item::callback::<T>(c, cmd_vec),
254 )*
255 }
256 }
257 }
258
259 fn insert_event_string(c: &mut Cursive, res: &mut HashMap<Event, Controllers>, table: &toml::value::Table,
260 event_str: &str, controller: &Controllers) {
261 match (str_to_event(event_str)) {
262 Some(event) => {
263 match res.get(&event) {
264 Some(existing_controller) if !table.contains_key(existing_controller.command()) => {
267 view_warn!(
268 c,
269 "Event {} has been used by: {}",
270 event_str,
271 existing_controller.command()
272 );
273 }
274 _ => {
275 res.insert(event, controller.clone());
276 }
277 }
278 },
279 None => {
280 view_warn!(c, "Fail to parse command from cmdrc: {} --> {}", controller.command(), event_str);
281 }
282 }
283 }
284
285 pub fn make_event_controller_map(c: &mut Cursive, cmdrc: &Option<Value>) -> HashMap<Event, Controllers> {
287 let mut res: HashMap<Event, Controllers> = HashMap::new();
288
289 $(
291 for event in $struct_item::default_events() {
292 $(#[$attr])*
293 res.insert(
294 event,
295 Controllers::$enum_item
296 );
297 }
298 )*
299
300 cmdrc.as_ref().map(|value| {
302 let cmd_controllers = c
303 .user_data::<crate::ViewState>()
304 .expect("No user data set")
305 .cmd_controllers
306 .clone();
307
308 value.as_table().map(|table| table.iter().for_each(|(k, v)| {
309 match (cmd_controllers.borrow().get::<str>(k), v.as_array()) {
310 (Some(controller), Some(key_array)) => {
311 for event_item in key_array.iter() {
312 match event_item.as_str() {
313 Some(event_str) => insert_event_string(
314 c,
315 &mut
316 res,
317 table,
318 event_str,
319 controller
320 ),
321 None => view_warn!(c, "Unknown entry in event list {}", event_item),
322 }
323 }
324 },
325 (Some(controller), None) => {
326 match (v.as_str()) {
328 Some(event_str) => insert_event_string(c, &mut res, table, event_str, controller),
329 None => {
330 view_warn!(c, "Could not parse key binding {} for command {}", k, v);
331 }
332 }
333 },
334 (None, _) => {
335 view_warn!(c, "Unrecogonized command: {}", k);
336 }
337 }
338 }));
339 });
340
341 res
342 }
343
344 pub fn make_cmd_controller_map() -> HashMap<&'static str, Controllers> {
346 let mut res = HashMap::new();
347 $(
348 $(#[$attr])*
349 res.insert(
350 $struct_item::command(),
351 Controllers::$enum_item
352 );
353
354 $(#[$attr])*
355 if !$struct_item::cmd_shortcut().is_empty() {
356 res.insert(
357 $struct_item::cmd_shortcut(),
358 Controllers::$enum_item
359 );
360 }
361 )*
362 res
363 }
364 }
365}