public static bool IsValidSequence(KeySequence sequence, bool ignore_case) => m_sequences.IsValidSequence(sequence, ignore_case);
public static string GetSequenceResult(KeySequence sequence, bool ignore_case) => m_sequences.GetSequenceResult(sequence, ignore_case);
public static bool IsFavorite(KeySequence sequence, string result) => m_dict.TryGet(sequence, result)?.Favorite ?? false;
public SequenceIdentifier(KeySequence sequence, string result) { Sequence = sequence; Result = result; }
/// <summary> /// Add a key to the sequence currently being built. If necessary, output /// the finished sequence or trigger actions for invalid sequences. /// </summary> private static bool AddToSequence(Key key) { KeySequence old_sequence = new KeySequence(m_sequence); m_sequence.Add(key); // We try the following, in this order: // 1. if m_sequence + key is a valid sequence, we don't go further, // we append key to m_sequence and output the result. // 2. if m_sequence + key is a valid prefix, it means the user // could type other characters to build a longer sequence, // so just append key to m_sequence. // 3. if m_sequence + key is a valid generic prefix, continue as well. // 4. if m_sequence + key is a valid sequence, send it. // 5. (optionally) try again 1. and 2. ignoring case. // 6. none of the characters make sense, output all of them as if // the user didn't press Compose. foreach (bool ignore_case in Settings.CaseInsensitive.Value ? new bool[] { false, true } : new bool[] { false }) { if (Settings.IsValidSequence(m_sequence, ignore_case)) { string tosend = Settings.GetSequenceResult(m_sequence, ignore_case); SendString(tosend); Log.Debug("Valid sequence! Sent “{0}”", tosend); Stats.AddSequence(m_sequence); ResetSequence(); return(true); } if (Settings.IsValidPrefix(m_sequence, ignore_case)) { // Still a valid prefix, continue building sequence return(true); } if (!ignore_case) { if (Settings.IsValidGenericPrefix(m_sequence)) { return(true); } if (Settings.GetGenericSequenceResult(m_sequence, out var tosend)) { SendString(tosend); Log.Debug("Valid generic sequence! Sent “{0}”", tosend); Stats.AddSequence(m_sequence); ResetSequence(); return(true); } } // Try to swap characters if the corresponding option is set if (m_sequence.Count == 2 && Settings.SwapOnInvalid.Value) { var other_sequence = new KeySequence() { m_sequence[1], m_sequence[0] }; if (Settings.IsValidSequence(other_sequence, ignore_case)) { string tosend = Settings.GetSequenceResult(other_sequence, ignore_case); SendString(tosend); Log.Debug("Found swapped sequence! Sent “{0}”", tosend); Stats.AddSequence(other_sequence); ResetSequence(); return(true); } } } // FIXME: At this point we know the key can’t be part of a valid // sequence, but this should not be a hard error if the key is a // modifier key. // Unknown characters for sequence, print them if necessary if (!Settings.DiscardOnInvalid.Value) { string tosend = ""; foreach (Key k in m_sequence) { if (k.IsPrintable) // FIXME: what if the key is e.g. left arrow? { tosend += k.PrintableResult; } } if (!string.IsNullOrEmpty(tosend)) { SendString(tosend); Log.Debug("Invalid sequence! Sent “{0}”", tosend); } } if (Settings.BeepOnInvalid.Value) { SystemSounds.Beep.Play(); } ResetSequence(); return(true); }
public static void IncrementUsage(KeySequence sequence, string result) { var data = m_dict.GetOrAdd(sequence, result); ++data.UsageCount; }
/// <summary> /// Add a key to the sequence currently being built. If necessary, output /// the finished sequence or trigger actions for invalid sequences. /// </summary> private static bool AddToSequence(Key key) { KeySequence old_sequence = new KeySequence(m_sequence); m_sequence.Add(key); // We try the following, in this order: // 1. if m_sequence + key is a valid prefix, it means the user // could type other characters to build a longer sequence, // so just append key to m_sequence. // 2. if m_sequence + key is a valid sequence, we can't go further, // we append key to m_sequence and output the result. // 3. if m_sequence is a valid sequence, the user didn't type a // valid key, so output the m_sequence result _and_ process key. // 4. (optionally) try again 1. 2. and 3. ignoring case. // 5. none of the characters make sense, output all of them as if // the user didn't press Compose. foreach (bool ignore_case in Settings.CaseInsensitive.Value ? new bool[] { false, true } : new bool[] { false }) { if (Settings.IsValidPrefix(m_sequence, ignore_case)) { // Still a valid prefix, continue building sequence return(true); } if (Settings.IsValidSequence(m_sequence, ignore_case)) { string tosend = Settings.GetSequenceResult(m_sequence, ignore_case); Stats.AddSequence(m_sequence); Log.Debug("Valid sequence! Sending {0}", tosend); ResetSequence(); SendString(tosend); return(true); } // Some code duplication with the above block, but this way // what we are doing is more clear. if (Settings.IsValidSequence(old_sequence, ignore_case)) { string tosend = Settings.GetSequenceResult(old_sequence, ignore_case); Stats.AddSequence(old_sequence); Log.Debug("Sending previously valid sequence {0}", tosend); ResetSequence(); SendString(tosend); return(false); } } // Unknown characters for sequence, print them if necessary if (!Settings.DiscardOnInvalid.Value) { foreach (Key k in m_sequence) { // FIXME: what if the key is e.g. left arrow? if (k.IsPrintable()) { SendString(k.ToString()); } } } if (Settings.BeepOnInvalid.Value) { SystemSounds.Beep.Play(); } ResetSequence(); return(true); }
private void ParseRule(string line) { // If this is an include directive, use LoadFile() again Match m0 = m_r0.Match(line); if (m0.Success) { string file = m0.Groups[1].Captures[0].Value; // We support %H (user directory) but not %L (locale-specific dir) if (file.Contains("%L")) { return; } file = file.Replace("%H", Utils.UserDir); // Also if path is not absolute, prepend user directory if (!Path.IsPathRooted(file)) { file = Path.Combine(Utils.UserDir, file); } // Prevent against include recursion if (!m_loaded_files.Contains(file)) { LoadFile(file); } return; } var m1 = m_r1.Match(line); if (!m1.Success) { return; } KeySequence seq = new KeySequence(); var keysyms = m_r2.Split(m1.Groups[1].Captures[0].Value); for (int i = 1; i < keysyms.Length - 1; ++i) { Key k = Key.FromKeySymOrChar(keysyms[i]); if (k == null) { if (m_invalid_keys.Add(keysyms[i])) { Logger.Warn($"Unknown key name <{keysyms[i]}>, ignoring sequence"); } return; // Unknown key name! Better bail out } seq.Add(k); } // Only bother with sequences of length >= 2 that start with <Multi_key> if (seq.Count < 2 || seq[0].VirtualKey != VK.COMPOSE) { return; } string result = m1.Groups[2].Captures[0].Value; if (result[0] == '"') { result = result.Trim('"'); // Unescape \n \\ \" and more in the string output result = Regex.Replace(result, @"\\([0-7]{3}|0x[0-7a-fA-F]{4}|.)", m => { var s = m.Value; if (s.Length == 4) // Octal escape { return(((char)Convert.ToInt32(s.Substring(1), 8)).ToString()); } if (s.Length == 7) // Hex escape { return(((char)Convert.ToInt32(s.Substring(3), 16)).ToString()); } switch (s[1]) { // These sequences are converted to their known value case 'n': return("\n"); case 'r': return("\r"); case 't': return("\t"); case '"': return("\""); case '\\': return("\\"); default: return(s); } }); } else { var result_key = Key.FromKeySymOrChar(result); if (result_key == null) { Logger.Warn($"Unknown key name {result}, ignoring sequence"); return; } result = result_key.PrintableResult; } string description = m1.Groups.Count >= 5 ? m1.Groups[4].Captures[0].Value : ""; // Try to translate the description if appropriate int utf32 = StringToCodepoint(result); if (utf32 >= 0) { string key = $"U{utf32:X04}"; string alt_desc = unicode.Char.ResourceManager.GetString(key); if (!string.IsNullOrEmpty(alt_desc)) { description = alt_desc; } } // HACK: remove the first key (Multi_key) for now, because the // rest of the code cannot handle it. seq.RemoveAt(0); InsertSequence(seq, result, utf32, description); ++Count; }
private static void ParseRule(string line) { // If this is an include directive, use LoadSequenceFile() again Match m0 = m_r0.Match(line); if (m0.Success) { string file = m0.Groups[1].Captures[0].Value; // We support %H (user directory) but not %L (locale-specific dir) if (file.Contains("%L")) { return; } file = file.Replace("%H", GetUserDir()); // Also if path is not absolute, prepend user directory if (!Path.IsPathRooted(file)) { file = Path.Combine(GetUserDir(), file); } LoadSequenceFile(file); return; } // Only bother with sequences that start with <Multi_key> var m1 = m_r1.Match(line); if (!m1.Success) { return; } var keysyms = m_r2.Split(m1.Groups[1].Captures[0].Value); if (keysyms.Length < 4) // We need 2 empty strings + at least 2 keysyms { return; } KeySequence seq = new KeySequence(); for (int i = 1; i < keysyms.Length; ++i) { if (keysyms[i] == String.Empty) { continue; } Key k = Key.FromKeySym(keysyms[i]); if (k == null) { //Console.WriteLine("Unknown key name <{0}>, ignoring sequence", keysyms[i]); return; // Unknown key name! Better bail out } seq.Add(k); } string result = m1.Groups[2].Captures[0].Value; string description = m1.Groups.Count >= 5 ? m1.Groups[4].Captures[0].Value : ""; // Unescape \n \\ \" and more in the string output result = Regex.Replace(result, @"\\.", m => { switch (m.Value) { // These sequences are converted to their known value case @"\n": return("\n"); case @"\r": return("\r"); case @"\t": return("\t"); // For all other sequences, just strip the leading \ default: return(m.Value.Substring(1).ToString()); } }); // Try to translate the description if appropriate int utf32 = StringToCodepoint(result); if (utf32 >= 0) { string key = string.Format("U{0:X04}", utf32); string alt_desc = unicode.Char.ResourceManager.GetString(key); if (!string.IsNullOrEmpty(alt_desc)) { description = alt_desc; } } m_sequences.Add(seq, result, utf32, description); ++m_sequence_count; }
public static string GetSequenceResult(KeySequence sequence, bool ignore_case) { return(m_sequences.GetSequenceResult(sequence, ignore_case)); }
public static bool IsValidSequence(KeySequence sequence, bool ignore_case) { return(m_sequences.IsValidSequence(sequence, ignore_case)); }