use std::cell::Cell;
use std::rc::Rc;
use gdk::{EventKey, ModifierType};
use gdk::keys::constants::{Escape, Tab, ISO_Left_Tab};
use gtk::{Inhibit, traits::LabelExt};
use mg_settings::{self, EnumFromStr, EnumMetaData, SettingCompletion, SpecialCommand};
use mg_settings::key::Key::{self, Char};
use app::{
Mg,
Mode,
Msg,
BLOCKING_INPUT_MODE,
COMMAND_MODE,
INPUT_MODE,
};
use app::ShortcutCommand::{Complete, Incomplete};
use key_converter::gdk_key_to_key;
pub fn shortcut_to_string(keys: &[Key], show_count: bool) -> String {
if show_count {
let strings: Vec<_> = keys.iter().map(ToString::to_string).collect();
strings.join("")
}
else {
String::new()
}
}
impl<COMM, SETT> Mg<COMM, SETT>
where COMM: Clone + EnumFromStr + EnumMetaData + SpecialCommand + 'static,
SETT: Default + EnumMetaData + mg_settings::settings::Settings + SettingCompletion + 'static,
{
pub fn add_to_shortcut(&mut self, key: Key) {
self.model.current_shortcut.push(key);
self.update_shortcut_label();
}
pub fn clear_shortcut(&mut self) {
self.model.current_shortcut.clear();
self.update_shortcut_label();
}
pub fn handle_input_shortcut(&mut self, key: &EventKey) -> bool {
if let Some(key) = gdk_key_to_key(key) {
if self.model.shortcuts.contains_key(&key) {
let answer = &self.model.shortcuts[&key].clone();
self.model.shortcut_pressed = true;
self.set_dialog_answer(answer);
return true;
}
}
false
}
pub fn inhibit_handle_shortcut(current_mode: &Rc<Cell<Mode>>, key: &EventKey) -> Inhibit {
let keyval = key.keyval();
let alt_pressed = key.state().contains(ModifierType::MOD1_MASK);
let control_pressed = key.state().contains(ModifierType::CONTROL_MASK);
let shift_pressed = key.state().contains(ModifierType::SHIFT_MASK);
let current_mode = current_mode.get();
let is_char = keyval.to_unicode().is_some();
let should_inhibit =
current_mode == Mode::Normal || keyval == Escape ||
((current_mode == Mode::Command || current_mode == Mode::Input || current_mode == Mode::BlockingInput) &&
(alt_pressed || control_pressed || (!is_char && shift_pressed) || keyval == Tab ||
keyval == ISO_Left_Tab));
Inhibit(should_inhibit)
}
pub fn handle_shortcut(&mut self, key: &EventKey) -> Option<Msg<COMM, SETT>> {
let keyval = key.keyval();
let alt_pressed = key.state().contains(ModifierType::MOD1_MASK);
let control_pressed = key.state().contains(ModifierType::CONTROL_MASK);
let shift_pressed = key.state().contains(ModifierType::SHIFT_MASK);
if !self.model.entry_shown || alt_pressed || control_pressed || shift_pressed || keyval == Tab ||
keyval == ISO_Left_Tab
{
if let Some(key) = gdk_key_to_key(key) {
self.add_to_shortcut(key);
let action = {
let mut current_mode = self.model.mode_string.clone();
if current_mode == INPUT_MODE || current_mode == BLOCKING_INPUT_MODE {
current_mode = COMMAND_MODE.to_string();
}
self.model.mappings.get(¤t_mode.as_ref())
.and_then(|mappings| mappings.get(self.shortcut_without_prefix()).cloned())
};
if let Some(action) = action {
let prefix = self.shortcut_prefix();
if !self.model.entry_shown {
self.reset();
}
self.clear_shortcut();
match self.action_to_command(&action) {
Complete(command) => {
return self.handle_command(command, false, prefix);
},
Incomplete(command) => {
self.input_command(command);
self.show_completion();
},
}
}
else if self.no_possible_shortcut() {
let current_mode = self.model.current_mode.get();
if current_mode != Mode::Input && !self.model.entry_shown {
self.reset();
}
self.clear_shortcut();
}
}
}
None
}
fn no_possible_shortcut(&self) -> bool {
if let Some(mappings) = self.model.mappings.get(&self.model.mode_string.as_ref()) {
let shortcut = self.shortcut_without_prefix();
for key in mappings.keys() {
if key.starts_with(shortcut) {
return false;
}
}
}
true
}
fn shortcut_prefix(&self) -> Option<u32> {
let mut digits = self.model.current_shortcut.iter()
.take_while(is_digit)
.peekable();
if digits.peek().is_none() {
None
}
else {
let num = digits
.fold(0, |num, key| {
if let Char(c) = *key {
if let Some(digit) = c.to_digit(10) {
return num * 10 + digit;
}
}
num
});
Some(num)
}
}
fn shortcut_without_prefix(&self) -> &[Key] {
let first = self.model.current_shortcut.first().cloned().unwrap_or(Char('0'));
let start =
if first == Char('0') {
0
}
else {
self.model.current_shortcut.iter()
.position(is_not_digit)
.unwrap_or_else(|| self.model.current_shortcut.len())
};
&self.model.current_shortcut[start..]
}
fn update_shortcut_label(&self) {
self.widgets.shortcut.set_text(&shortcut_to_string(&self.model.current_shortcut, self.model.show_count));
}
}
fn is_digit(key: &&Key) -> bool {
!is_not_digit(key)
}
fn is_not_digit(key: &Key) -> bool {
if let Char(c) = *key {
!c.to_digit(10).is_some()
}
else {
true
}
}