private void ReportRefreshProgress(int percentComplete, string message)      // percent not implemented for this
 {
     if (!Controller.PendingClose)
     {
         refreshprogressstring      = message;
         toolStripStatusLabel1.Text = ObjectExtensionsStrings.AppendPrePad(syncprogressstring, refreshprogressstring, " | ");
         Update();       // nasty but it works - needed since we are doing UI work here and the UI thread will be blocked
     }
 }
Exemple #2
0
 public static Keys IsAltPrefix(ref string s)
 {
     if (ObjectExtensionsStrings.IsPrefix(ref s, "Alt", StringComparison.InvariantCultureIgnoreCase))
     {
         return(Keys.Menu);
     }
     else if (ObjectExtensionsStrings.IsPrefix(ref s, "RAlt", StringComparison.InvariantCultureIgnoreCase))
     {
         return(Keys.RMenu);
     }
     else
     {
         return(Keys.None);
     }
 }
Exemple #3
0
 public static Keys IsCtrlPrefix(ref string s)
 {
     if (ObjectExtensionsStrings.IsPrefix(ref s, "Ctrl", StringComparison.InvariantCultureIgnoreCase))
     {
         return(Keys.ControlKey);
     }
     else if (ObjectExtensionsStrings.IsPrefix(ref s, "RCtrl", StringComparison.InvariantCultureIgnoreCase))
     {
         return(Keys.RControlKey);
     }
     else
     {
         return(Keys.None);
     }
 }
Exemple #4
0
 public static Keys IsShiftPrefix(ref string s)      // look for prefix, remove, return what the prefix is
 {
     if (ObjectExtensionsStrings.IsPrefix(ref s, "Shift", StringComparison.InvariantCultureIgnoreCase))
     {
         return(Keys.ShiftKey);
     }
     else if (ObjectExtensionsStrings.IsPrefix(ref s, "RShift", StringComparison.InvariantCultureIgnoreCase))
     {
         return(Keys.RShiftKey);
     }
     else
     {
         return(Keys.None);
     }
 }
        private void ReportSyncProgress(int percentComplete, string message)
        {
            if (!Controller.PendingClose)
            {
                if (percentComplete >= 0)
                {
                    toolStripProgressBar1.Visible = true;
                    toolStripProgressBar1.Value   = percentComplete;
                }
                else
                {
                    toolStripProgressBar1.Visible = false;
                }

                syncprogressstring         = message;
                toolStripStatusLabel1.Text = ObjectExtensionsStrings.AppendPrePad(syncprogressstring, refreshprogressstring, " | ");
            }
        }
 public void ObjectExtensions_Strings()
 {
     {
         string s = ObjectExtensionsStrings.RegExWildCardToRegular("*fred");
         System.Diagnostics.Debug.WriteLine($"Pattern is {s}");
         Check.That(System.Text.RegularExpressions.Regex.IsMatch("wwwfred", s, System.Text.RegularExpressions.RegexOptions.IgnoreCase)).IsTrue();
         Check.That(System.Text.RegularExpressions.Regex.IsMatch("wwwfredwww", s, System.Text.RegularExpressions.RegexOptions.IgnoreCase)).IsFalse();
     }
     {
         string s = ObjectExtensionsStrings.RegExWildCardToRegular("*fred*");
         System.Diagnostics.Debug.WriteLine($"Pattern is {s}");
         Check.That(System.Text.RegularExpressions.Regex.IsMatch("fre", s, System.Text.RegularExpressions.RegexOptions.IgnoreCase)).IsFalse();
         Check.That(System.Text.RegularExpressions.Regex.IsMatch("fred", s, System.Text.RegularExpressions.RegexOptions.IgnoreCase)).IsTrue();
         Check.That(System.Text.RegularExpressions.Regex.IsMatch("wwwfred", s, System.Text.RegularExpressions.RegexOptions.IgnoreCase)).IsTrue();
         Check.That(System.Text.RegularExpressions.Regex.IsMatch("wwwfredwww", s, System.Text.RegularExpressions.RegexOptions.IgnoreCase)).IsTrue();
     }
     {
         string s = ObjectExtensionsStrings.RegExWildCardToRegular("*fr?d*");
         System.Diagnostics.Debug.WriteLine($"Pattern is {s}");
         Check.That(System.Text.RegularExpressions.Regex.IsMatch("fre", s, System.Text.RegularExpressions.RegexOptions.IgnoreCase)).IsFalse();
         Check.That(System.Text.RegularExpressions.Regex.IsMatch("fred", s, System.Text.RegularExpressions.RegexOptions.IgnoreCase)).IsTrue();
         Check.That(System.Text.RegularExpressions.Regex.IsMatch("frxd", s, System.Text.RegularExpressions.RegexOptions.IgnoreCase)).IsTrue();
         Check.That(System.Text.RegularExpressions.Regex.IsMatch("wwwfred", s, System.Text.RegularExpressions.RegexOptions.IgnoreCase)).IsTrue();
         Check.That(System.Text.RegularExpressions.Regex.IsMatch("wwwfredwww", s, System.Text.RegularExpressions.RegexOptions.IgnoreCase)).IsTrue();
     }
     {
         string s = ObjectExtensionsStrings.RegExWildCardToRegular("*f()r?d*");
         System.Diagnostics.Debug.WriteLine($"Pattern is {s}");
         Check.That(System.Text.RegularExpressions.Regex.IsMatch("...f()red...", s, System.Text.RegularExpressions.RegexOptions.IgnoreCase)).IsTrue();
     }
     {
         string s = ObjectExtensionsStrings.RegExWildCardToRegular("fr$(^ed");
         System.Diagnostics.Debug.WriteLine($"Pattern is {s}");
         Check.That(System.Text.RegularExpressions.Regex.IsMatch("fr$(^ed", s, System.Text.RegularExpressions.RegexOptions.IgnoreCase)).IsTrue();
         Check.That(System.Text.RegularExpressions.Regex.IsMatch("xfr$(^ed", s, System.Text.RegularExpressions.RegexOptions.IgnoreCase)).IsFalse();
         Check.That(System.Text.RegularExpressions.Regex.IsMatch("fr$(^edx", s, System.Text.RegularExpressions.RegexOptions.IgnoreCase)).IsFalse();
     }
 }
Exemple #7
0
        private void Display(HistoryEntry he, HistoryList hl, bool selectedEntry)
        {
            //System.Diagnostics.Debug.WriteLine("SI:Display ");

            if (neverdisplayed)
            {
                UpdateViewOnSelection();  // then turn the right ones on
                neverdisplayed = false;
            }

            last_he = he;

            if (last_he != null)
            {
                SetControlText(he.System.Name);

                HistoryEntry lastfsd = hl.GetLastHistoryEntry(x => x.journalEntry is EliteDangerousCore.JournalEvents.JournalFSDJump, he);

                textBoxSystem.Text      = he.System.Name;
                panelFD.BackgroundImage = (lastfsd != null && (lastfsd.journalEntry as EliteDangerousCore.JournalEvents.JournalFSDJump).EDSMFirstDiscover) ? EDDiscovery.Icons.Controls.firstdiscover : EDDiscovery.Icons.Controls.notfirstdiscover;

                discoveryform.history.FillEDSM(he); // Fill in any EDSM info we have

                //textBoxBody.Text = he.WhereAmI + ((he.IsInHyperSpace) ? " (HS)": "");
                textBoxBody.Text = he.WhereAmI + " (" + he.BodyType + ")";

                if (he.System.HasCoordinate)         // cursystem has them?
                {
                    string SingleCoordinateFormat = "0.##";

                    string separ = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator + " ";

                    textBoxPosition.Text = he.System.X.ToString(SingleCoordinateFormat) + separ + he.System.Y.ToString(SingleCoordinateFormat) + separ + he.System.Z.ToString(SingleCoordinateFormat);

                    ISystem homesys = EDCommander.Current.HomeSystemIOrSol;

                    textBoxHomeDist.Text = he.System.Distance(homesys).ToString(SingleCoordinateFormat);
                    textBoxSolDist.Text  = he.System.Distance(0, 0, 0).ToString(SingleCoordinateFormat);
                }
                else
                {
                    textBoxPosition.Text = "?";
                    textBoxHomeDist.Text = "";
                    textBoxSolDist.Text  = "";
                }

                int count = discoveryform.history.GetVisitsCount(he.System.Name);
                textBoxVisits.Text = count.ToString();

                bool enableedddross = (he.System.EDDBID > 0);  // Only enable eddb/ross for system that it knows about

                buttonRoss.Enabled = buttonEDDB.Enabled = enableedddross;

                string allegiance, economy, gov, faction, factionstate, security;
                hl.ReturnSystemInfo(he, out allegiance, out economy, out gov, out faction, out factionstate, out security);

                textBoxAllegiance.Text = allegiance;
                textBoxEconomy.Text    = economy;
                textBoxGovernment.Text = gov;
                textBoxState.Text      = factionstate;

                List <MissionState> mcurrent = (from MissionState ms in he.MissionList.Missions.Values where ms.InProgressDateTime(last_he.EventTimeUTC) orderby ms.Mission.EventTimeUTC descending select ms).ToList();

                if (mcurrent == null || mcurrent.Count == 0)
                {
                    richTextBoxScrollMissions.Text = "No Missions".T(EDTx.UserControlSysInfo_NoMissions);
                }
                else
                {
                    string t = "";
                    foreach (MissionState ms in mcurrent)
                    {
                        t = ObjectExtensionsStrings.AppendPrePad(t,
                                                                 JournalFieldNaming.ShortenMissionName(ms.Mission.Name)
                                                                 + " Exp:" + (EDDiscoveryForm.EDDConfig.DisplayUTC ? ms.Mission.Expiry : ms.Mission.Expiry.ToLocalTime())
                                                                 + " @ " + ms.DestinationSystemStation(),
                                                                 Environment.NewLine);
                    }

                    richTextBoxScrollMissions.Text = t;
                }

                SetNote(he.snc != null ? he.snc.Note : "");
                textBoxGameMode.Text = he.GameModeGroup;
                if (he.isTravelling)
                {
                    textBoxTravelDist.Text  = he.TravelledDistance.ToStringInvariant("0.0") + "ly";
                    textBoxTravelTime.Text  = he.TravelledSeconds.ToString();
                    textBoxTravelJumps.Text = he.TravelledJumpsAndMisses;
                }
                else
                {
                    textBoxTravelDist.Text = textBoxTravelTime.Text = textBoxTravelJumps.Text = "";
                }

                int cc = (he.ShipInformation) != null?he.ShipInformation.CargoCapacity() : 0;

                if (cc > 0)
                {
                    textBoxCargo.Text = he.MaterialCommodity.CargoCount.ToStringInvariant() + "/" + cc.ToStringInvariant();
                }
                else
                {
                    textBoxCargo.Text = he.MaterialCommodity.CargoCount.ToStringInvariant();
                }

                textBoxMaterials.Text = he.MaterialCommodity.MaterialsCount.ToStringInvariant();
                textBoxData.Text      = he.MaterialCommodity.DataCount.ToStringInvariant();
                textBoxCredits.Text   = he.Credits.ToString("N0");

                textBoxJumpRange.Text = "";

                if (he.ShipInformation != null)
                {
                    ShipInformation si = he.ShipInformation;

                    textBoxShip.Text = si.ShipFullInfo(cargo: false, fuel: false);
                    if (si.FuelCapacity > 0 && si.FuelLevel > 0)
                    {
                        textBoxFuel.Text = si.FuelLevel.ToStringInvariant("0.#") + "/" + si.FuelCapacity.ToStringInvariant("0.#");
                    }
                    else if (si.FuelCapacity > 0)
                    {
                        textBoxFuel.Text = si.FuelCapacity.ToStringInvariant("0.#");
                    }
                    else
                    {
                        textBoxFuel.Text = "N/A".T(EDTx.UserControlSysInfo_NA);
                    }

                    EliteDangerousCalculations.FSDSpec fsd = si.GetFSDSpec();
                    if (fsd != null)
                    {
                        EliteDangerousCalculations.FSDSpec.JumpInfo ji = fsd.GetJumpInfo(he.MaterialCommodity.CargoCount,
                                                                                         si.ModuleMass() + si.HullMass(), si.FuelLevel, si.FuelCapacity / 2);

                        //System.Diagnostics.Debug.WriteLine("Jump range " + si.FuelLevel + " " + si.FuelCapacity + " " + ji.cursinglejump);
                        textBoxJumpRange.Text = ji.cursinglejump.ToString("N2") + "ly";
                    }
                }
                else
                {
                    textBoxShip.Text = textBoxFuel.Text = "";
                }

                RefreshTargetDisplay(this);
            }
            else
            {
                SetControlText("");
                textBoxSystem.Text                = textBoxBody.Text = textBoxPosition.Text =
                    textBoxAllegiance.Text        = textBoxEconomy.Text = textBoxGovernment.Text =
                        textBoxVisits.Text        = textBoxState.Text = textBoxHomeDist.Text = textBoxSolDist.Text =
                            textBoxGameMode.Text  = textBoxTravelDist.Text = textBoxTravelTime.Text = textBoxTravelJumps.Text =
                                textBoxCargo.Text = textBoxMaterials.Text = textBoxData.Text = textBoxShip.Text = textBoxFuel.Text =
                                    "";

                buttonRoss.Enabled = buttonEDDB.Enabled = false;
                SetNote("");
            }
        }
Exemple #8
0
        public static string ParseKeys(Queue <SKEvent> events, string s, int defdelay, int defshiftdelay, int defupdelay, IAdditionalKeyParser additionalkeyparser = null)
        {
            //debugevents = null;
            s = s.Trim();
            IntPtr hwnd = (IntPtr)0;

            while (s.Length > 0)
            {
                if (additionalkeyparser != null)                                  // See if key needs translating out - moved to here to allow for control sequences before this key
                {
                    Tuple <string, int, string> t = additionalkeyparser.Parse(s); // Allow the parser to sniff the string

                    if (t.Item3 != null)                                          // error condition here, such as no matching key binding
                    {
                        return(t.Item3);
                    }

                    if (t.Item1 != null)                                      // if replace.. (and the parser can return multiple keys)
                    {
                        s = t.Item1 + " " + s.Substring(t.Item2);             // its the replace string, followed by the cut out current string
                    }
                }

                int d1 = -1, d2 = -1, d3 = -1;

                if (s.Length >= 1 && s[0] == '[')
                {
                    if (!s.Contains(']'))
                    {
                        return("Missing closing ] in delay");
                    }

                    s = s.Substring(1);
                    string word = ObjectExtensionsStrings.FirstWord(ref s, new char[] { ']', ',' });
                    if (!word.InvariantParse(out d1))
                    {
                        return("Delay not properly given");
                    }

                    if (s.Length >= 1 && s[0] == ',')
                    {
                        s    = s.Substring(1);
                        word = ObjectExtensionsStrings.FirstWord(ref s, new char[] { ']', ',' });
                        if (!word.InvariantParse(out d2))
                        {
                            return("Second Delay not properly given");
                        }
                    }

                    if (s.Length >= 1 && s[0] == ',')
                    {
                        s    = s.Substring(1);
                        word = ObjectExtensionsStrings.FirstWord(ref s, new char[] { ']' });
                        if (!word.InvariantParse(out d3))
                        {
                            return("Third Delay not properly given");
                        }
                    }

                    if (s.Length >= 1 && s[0] == ']')
                    {
                        s = s.Substring(1);
                    }
                    else
                    {
                        return("Missing closing ] in delay");
                    }
                }

                KMode kmd = KMode.press;

                if (s.Length == 0)
                {
                    return("Invalid no characters after delay");
                }

                if (s[0] == '^' || s[0] == '<')
                {
                    kmd = KMode.up;
                    s   = s.Substring(1);
                }
                else if (s[0] == '!' || s[0] == '>')
                {
                    kmd = KMode.down;
                    s   = s.Substring(1);
                }

                if (additionalkeyparser != null)                                  // Also see here if key needs translating out - 9.0.3.0
                {
                    Tuple <string, int, string> t = additionalkeyparser.Parse(s); // Allow the parser to sniff the string

                    if (t.Item3 != null)                                          // error condition here, such as no matching key binding
                    {
                        return(t.Item3);
                    }

                    if (t.Item1 != null)                                      // if replace.. (and the parser can return multiple keys)
                    {
                        s = t.Item1 + " " + s.Substring(t.Item2);             // its the replace string, followed by the cut out current string
                    }
                }

                Keys shift = KeyObjectExtensions.IsShiftPrefix(ref s);
                Keys ctrl  = Keys.None;
                Keys alt   = Keys.None;
                if (shift == Keys.None || s.StartsWith("+"))
                {
                    s = s.Skip("+");

                    alt = KeyObjectExtensions.IsAltPrefix(ref s);

                    if (alt == Keys.None || s.StartsWith("+"))
                    {
                        s = s.Skip("+");

                        ctrl = KeyObjectExtensions.IsCtrlPrefix(ref s);

                        if (ctrl != Keys.None)
                        {
                            s = s.Skip("+");
                        }
                    }
                }

                bool mainpart = s.Length > 0 && s[0] != ' ';

                // keydown is d1 or def
                int keydowndelay = (d1 != -1) ? d1 : defdelay;
                // if mainpart present, its d2 or defshift.  If no main part, its d1 or def shift
                int shiftdelay = (mainpart) ? (d2 != -1 ? d2 : defshiftdelay) : (d1 != -1 ? d1 : defshiftdelay);
                // if in up/down mode, its d1 or def up.   If its got a main part, its d3/defup.  else its d2/defup
                int keyupdelay = (kmd == KMode.up || kmd == KMode.down) ? (d1 != -1 ? d1 : defupdelay) : (mainpart ? (d3 != -1 ? d3 : defupdelay) : (d2 != -1 ? d2 : defupdelay));

                //System.Diagnostics.Debug.WriteLine(string.Format("{0} {1} {2} {3} {4} {5} ", d1, d2, d3, keydowndelay, shiftdelay, keyupdelay));

                if (shift != Keys.None)         // we already run shift keys here. If we are doing UP, we send a up, else we are doing down/press
                {
                    events.Enqueue(new SKEvent(kmd == KMode.up ? BaseUtils.Win32Constants.WM.KEYUP : BaseUtils.Win32Constants.WM.KEYDOWN, shift, shiftdelay));
                }

                if (ctrl != Keys.None)
                {
                    events.Enqueue(new SKEvent(kmd == KMode.up ? BaseUtils.Win32Constants.WM.KEYUP : BaseUtils.Win32Constants.WM.KEYDOWN, ctrl, shiftdelay));
                }

                if (alt != Keys.None)
                {
                    events.Enqueue(new SKEvent(kmd == KMode.up ? BaseUtils.Win32Constants.WM.SYSKEYUP : BaseUtils.Win32Constants.WM.SYSKEYDOWN, alt, shiftdelay));
                }

                if (mainpart)
                {
                    if (s.Length == 0)
                    {
                        return("Invalid no characters after shifters");
                    }

                    bool brackets = ObjectExtensionsStrings.IsPrefix(ref s, "(");

                    while (s.Length > 0)
                    {
                        string word = ObjectExtensionsStrings.FirstWord(ref s, new char[] { ' ', ')' });

                        Keys key = word.ToVkey();

                        if (key != Keys.None)
                        {
                            AddMsgsForVK(events, key, alt != Keys.None && ctrl == Keys.None, keydowndelay, keyupdelay, kmd);
                            //System.Diagnostics.Debug.WriteLine(shift + " " + alt + " " + ctrl + "  press " + key.VKeyToString());
                        }
                        else
                        {
                            while (word.Length > 0)
                            {
                                string ch = new string(word[0], 1);
                                key = ch.ToVkey();

                                if (key.IsSingleCharName())
                                {
                                    AddMsgsForVK(events, key, alt != Keys.None && ctrl == Keys.None, keydowndelay, keyupdelay, kmd);
                                    //System.Diagnostics.Debug.WriteLine(shift + " " + alt + " " + ctrl + "  press " + key.VKeyToString());
                                    word = word.Substring(1);
                                }
                                else
                                {
                                    return("Invalid key " + word);
                                }
                            }
                        }

                        if (!brackets)
                        {
                            break;
                        }
                        else if (s.Length > 0 && s[0] == ')')
                        {
                            s = s.Substring(1);
                            break;
                        }
                    }
                }

                if (kmd == KMode.press)     // only on a press do we release here
                {
                    if (alt != Keys.None)
                    {
                        events.Enqueue(new SKEvent(BaseUtils.Win32Constants.WM.SYSKEYUP, alt, keyupdelay));
                    }

                    if (ctrl != Keys.None)
                    {
                        events.Enqueue(new SKEvent(BaseUtils.Win32Constants.WM.KEYUP, ctrl, keyupdelay));
                    }

                    if (shift != Keys.None)
                    {
                        events.Enqueue(new SKEvent(BaseUtils.Win32Constants.WM.KEYUP, shift, keyupdelay));
                    }
                }

                s = s.Trim();
                if (s.Length > 0 && s[0] == ',')        // comma can be used between key groups
                {
                    s = s.Substring(1).TrimStart();
                }
            }

            return("");
        }
Exemple #9
0
        public static string ParseKeys(string s, int defdelay, int defshiftdelay, int defupdelay)
        {
            //debugevents = null;
            s = s.Trim();
            IntPtr hwnd = (IntPtr)0;

            while (s.Length > 0)
            {
                KMode kmd = KMode.press;

                int d1 = -1, d2 = -1, d3 = -1;

                if (s[0] == '[')
                {
                    s = s.Substring(1);
                    string word = ObjectExtensionsStrings.FirstWord(ref s, new char[] { ']', ',' });
                    if (!word.InvariantParse(out d1))
                    {
                        return("Delay not properly given");
                    }

                    if (s[0] == ',')
                    {
                        s    = s.Substring(1);
                        word = ObjectExtensionsStrings.FirstWord(ref s, new char[] { ']', ',' });
                        if (!word.InvariantParse(out d2))
                        {
                            return("Second Delay not properly given");
                        }
                    }

                    if (s[0] == ',')
                    {
                        s    = s.Substring(1);
                        word = ObjectExtensionsStrings.FirstWord(ref s, new char[] { ']' });
                        if (!word.InvariantParse(out d3))
                        {
                            return("Third Delay not properly given");
                        }
                    }

                    if (s[0] == ']')
                    {
                        s = s.Substring(1);
                    }
                    else
                    {
                        return("Missing closing ] in delay");
                    }
                }

                if (s[0] == '^' || s[0] == '<')
                {
                    kmd = KMode.up;
                    s   = s.Substring(1);
                }
                else if (s[0] == '!' || s[0] == '>')
                {
                    kmd = KMode.down;
                    s   = s.Substring(1);
                }

                Keys shift = KeyObjectExtensions.IsShiftPrefix(ref s);
                Keys ctrl  = Keys.None;
                Keys alt   = Keys.None;
                if (shift == Keys.None || s.StartsWith("+"))
                {
                    s = s.Skip("+");

                    alt = KeyObjectExtensions.IsAltPrefix(ref s);

                    if (alt == Keys.None || s.StartsWith("+"))
                    {
                        s = s.Skip("+");

                        ctrl = KeyObjectExtensions.IsCtrlPrefix(ref s);

                        if (ctrl != Keys.None)
                        {
                            s = s.Skip("+");
                        }
                    }
                }

                bool mainpart = s.Length > 0 && s[0] != ' ';

                // keydown is d1 or def
                int keydowndelay = (d1 != -1) ? d1 : defdelay;
                // if mainpart present, its d2 or defshift.  If no main part, its d1 or def shift
                int shiftdelay = (mainpart) ? (d2 != -1 ? d2 : defshiftdelay) : (d1 != -1 ? d1 : defshiftdelay);
                // if in up/down mode, its d1 or def up.   If its got a main part, its d3/defup.  else its d2/defup
                int keyupdelay = (kmd == KMode.up || kmd == KMode.down) ? (d1 != -1 ? d1 : defupdelay) : (mainpart ? (d3 != -1 ? d3: defupdelay) : (d2 != -1 ? d2 : defupdelay));

                System.Diagnostics.Debug.WriteLine(string.Format("{0} {1} {2} {3} {4} {5} ", d1, d2, d3, keydowndelay, shiftdelay, keyupdelay));

                if (shift != Keys.None)         // we already run shift keys here. If we are doing UP, we send a up, else we are doing down/press
                {
                    AddEvent(new SKEvent(kmd == KMode.up ? BaseUtils.Win32Constants.WM.KEYUP : BaseUtils.Win32Constants.WM.KEYDOWN, shift, shiftdelay));
                }

                if (ctrl != Keys.None)
                {
                    AddEvent(new SKEvent(kmd == KMode.up ? BaseUtils.Win32Constants.WM.KEYUP : BaseUtils.Win32Constants.WM.KEYDOWN, ctrl, shiftdelay));
                }

                if (alt != Keys.None)
                {
                    AddEvent(new SKEvent(kmd == KMode.up ? BaseUtils.Win32Constants.WM.SYSKEYUP: BaseUtils.Win32Constants.WM.SYSKEYDOWN, alt, shiftdelay));
                }

                if (mainpart)
                {
                    if (s.Length == 0)
                    {
                        return("Invalid no characters after shifters");
                    }

                    bool brackets = ObjectExtensionsStrings.IsPrefix(ref s, "(");

                    while (s.Length > 0)
                    {
                        string word = ObjectExtensionsStrings.FirstWord(ref s, new char[] { ' ', ')' });

                        Keys key = word.ToVkey();

                        if (key != Keys.None)
                        {
                            AddMsgsForVK(key, alt != Keys.None && ctrl == Keys.None, keydowndelay, keyupdelay, kmd);
                            //System.Diagnostics.Debug.WriteLine(shift + " " + alt + " " + ctrl + "  press " + key.VKeyToString());
                        }
                        else
                        {
                            while (word.Length > 0)
                            {
                                string ch = new string(word[0], 1);
                                key = ch.ToVkey();

                                if (key.IsSingleCharName())
                                {
                                    AddMsgsForVK(key, alt != Keys.None && ctrl == Keys.None, keydowndelay, keyupdelay, kmd);
                                    //System.Diagnostics.Debug.WriteLine(shift + " " + alt + " " + ctrl + "  press " + key.VKeyToString());
                                    word = word.Substring(1);
                                }
                                else
                                {
                                    return("Invalid key " + word);
                                }
                            }
                        }

                        if (!brackets)
                        {
                            break;
                        }
                        else if (s.Length > 0 && s[0] == ')')
                        {
                            s = s.Substring(1);
                            break;
                        }
                    }
                }

                if (kmd == KMode.press)     // only on a press do we release here
                {
                    if (alt != Keys.None)
                    {
                        AddEvent(new SKEvent(BaseUtils.Win32Constants.WM.SYSKEYUP, alt, keyupdelay));
                    }

                    if (ctrl != Keys.None)
                    {
                        AddEvent(new SKEvent(BaseUtils.Win32Constants.WM.KEYUP, ctrl, keyupdelay));
                    }

                    if (shift != Keys.None)
                    {
                        AddEvent(new SKEvent(BaseUtils.Win32Constants.WM.KEYUP, shift, keyupdelay));
                    }
                }

                s = s.Trim();
                if (s.Length > 0 && s[0] == ',')        // comma can be used between key groups
                {
                    s = s.Substring(1).TrimStart();
                }
            }

            return("");
        }
        // understand keycore - see above
        public static string KeyCore(Queue <SKEvent> events, ref string s, int defdelay, int defshiftdelay, int defupdelay, int d1, int d2, int d3, KeyPressMode kmd)
        {
            Keys shift = KeyObjectExtensions.IsShiftPrefix(ref s);
            Keys ctrl  = Keys.None;
            Keys alt   = Keys.None;

            if (shift == Keys.None || s.StartsWith("+"))
            {
                s = s.Skip("+");

                alt = KeyObjectExtensions.IsAltPrefix(ref s);

                if (alt == Keys.None || s.StartsWith("+"))
                {
                    s = s.Skip("+");

                    ctrl = KeyObjectExtensions.IsCtrlPrefix(ref s);

                    if (ctrl != Keys.None)
                    {
                        s = s.Skip("+");
                    }
                }
            }

            bool vkeyspresent = s.Length > 0 && s[0] != ' ';
            bool shiftpresent = shift != Keys.None || ctrl != Keys.None || alt != Keys.None;

            // keydown is d1 or def
            int keydowndelay = (d1 != -1) ? d1 : defdelay;

            // if mainpart present, its d2 or defshift.  If no main part, its d1 or def shift
            int shiftdelay = (vkeyspresent) ? (d2 != -1 ? d2 : defshiftdelay) : (d1 != -1 ? d1 : defshiftdelay);

            // if in up/down mode, its d1 or def up.   If its got shift and vkeys, its d3/defup.  else its d2/defup
            int keyupdelay = (kmd == KeyPressMode.Up || kmd == KeyPressMode.Down) ? (d1 != -1 ? d1 : defupdelay) : ((shiftpresent && vkeyspresent) ? (d3 != -1 ? d3 : defupdelay) : (d2 != -1 ? d2 : defupdelay));

            if (shift != Keys.None)         // we already run shift keys here. If we are doing UP, we send a up, else we are doing down/press
            {
                events.Enqueue(new SKEvent(kmd == KeyPressMode.Up ? BaseUtils.Win32Constants.WM.KEYUP : BaseUtils.Win32Constants.WM.KEYDOWN, shift, shiftdelay));
            }

            if (ctrl != Keys.None)
            {
                events.Enqueue(new SKEvent(kmd == KeyPressMode.Up ? BaseUtils.Win32Constants.WM.KEYUP : BaseUtils.Win32Constants.WM.KEYDOWN, ctrl, shiftdelay));
            }

            if (alt != Keys.None)
            {
                events.Enqueue(new SKEvent(kmd == KeyPressMode.Up ? BaseUtils.Win32Constants.WM.SYSKEYUP : BaseUtils.Win32Constants.WM.SYSKEYDOWN, alt, shiftdelay));
            }

            if (vkeyspresent)
            {
                if (s.Length == 0)
                {
                    return("Invalid no characters after shifters");
                }

                bool brackets = ObjectExtensionsStrings.IsPrefixRemove(ref s, "(");

                while (s.Length > 0)
                {
                    string word = ObjectExtensionsStrings.FirstWord(ref s, new char[] { ' ', ')' });

                    Keys key = word.ToVkey();

                    if (key != Keys.None)
                    {
                        AddMsgsForVK(events, key, alt != Keys.None && ctrl == Keys.None, keydowndelay, keyupdelay, kmd);
                        //System.Diagnostics.Debug.WriteLine(shift + " " + alt + " " + ctrl + "  press " + key.VKeyToString());
                    }
                    else
                    {
                        while (word.Length > 0)
                        {
                            string ch = new string(word[0], 1);
                            key = ch.ToVkey();

                            if (key.IsSingleCharName())
                            {
                                AddMsgsForVK(events, key, alt != Keys.None && ctrl == Keys.None, keydowndelay, keyupdelay, kmd);
                                //System.Diagnostics.Debug.WriteLine(shift + " " + alt + " " + ctrl + "  press " + key.VKeyToString());
                                word = word.Substring(1);
                            }
                            else
                            {
                                return("Invalid key " + word);
                            }
                        }
                    }

                    if (!brackets)
                    {
                        break;
                    }
                    else if (s.Length > 0 && s[0] == ')')
                    {
                        s = s.Substring(1);
                        break;
                    }
                }
            }

            if (kmd == KeyPressMode.Press)     // only on a press do we release here
            {
                if (alt != Keys.None)
                {
                    events.Enqueue(new SKEvent(BaseUtils.Win32Constants.WM.SYSKEYUP, alt, keyupdelay));
                }

                if (ctrl != Keys.None)
                {
                    events.Enqueue(new SKEvent(BaseUtils.Win32Constants.WM.KEYUP, ctrl, keyupdelay));
                }

                if (shift != Keys.None)
                {
                    events.Enqueue(new SKEvent(BaseUtils.Win32Constants.WM.KEYUP, shift, keyupdelay));
                }
            }

            s = s.Trim();
            if (s.Length > 0 && s[0] == ',')        // comma can be used between key groups
            {
                s = s.Substring(1).TrimStart();
            }

            return("");
        }
        // understand parsekeys above

        public static string ParseKeys(Queue <SKEvent> events, string s, int defdelay, int defshiftdelay, int defupdelay,
                                       IAdditionalKeyParser additionalkeyparser = null,
                                       int d1def = -1, int d2def = -1, int d3def = -1, KeyPressMode kmddef = KeyPressMode.Press)
        {
            s = s.Trim();

            while (s.Length > 0)
            {
                int d1 = d1def;             // reset dx's to their default values
                int d2 = d2def;
                int d3 = d3def;

                if (s.Length >= 1 && s[0] == '[')
                {
                    if (!s.Contains(']'))
                    {
                        return("Missing closing ] in delay");
                    }

                    s = s.Substring(1);
                    string word = ObjectExtensionsStrings.FirstWord(ref s, new char[] { ']', ',' });
                    if (!word.InvariantParse(out d1))
                    {
                        return("Delay not properly given");
                    }

                    if (s.Length >= 1 && s[0] == ',')
                    {
                        s    = s.Substring(1);
                        word = ObjectExtensionsStrings.FirstWord(ref s, new char[] { ']', ',' });
                        if (!word.InvariantParse(out d2))
                        {
                            return("Second Delay not properly given");
                        }
                    }

                    if (s.Length >= 1 && s[0] == ',')
                    {
                        s    = s.Substring(1);
                        word = ObjectExtensionsStrings.FirstWord(ref s, new char[] { ']' });
                        if (!word.InvariantParse(out d3))
                        {
                            return("Third Delay not properly given");
                        }
                    }

                    if (s.Length >= 1 && s[0] == ']')
                    {
                        s = s.Substring(1);
                    }
                    else
                    {
                        return("Missing closing ] in delay");
                    }
                }

                int repeat = 1;         // repeat is always 1 - it does not carry down the tree
                if (s.Length >= 1 && s[0] == '#')
                {
                    s = s.Substring(1);
                    var r = ObjectExtensionsNumbersBool.ReadDecimalInt(ref s);
                    if (r == null || r < 1)
                    {
                        return("Missing/Invalid decimal count after #");
                    }
                    s      = s.TrimStart();
                    repeat = r.Value;
                }

                KeyPressMode kmd = kmddef;      // set keymode to its default.

                if (s[0] == '^' || s[0] == '<')
                {
                    kmd = KeyPressMode.Up;
                    s   = s.Substring(1);
                }
                else if (s[0] == '!' || s[0] == '>')
                {
                    kmd = KeyPressMode.Down;
                    s   = s.Substring(1);
                }
                else if (s[0] == '&')
                {
                    kmd = KeyPressMode.Press;
                    s   = s.Substring(1);
                }

                string repeatarea = s;                 // keep it pristine for repeats

                for (int rpc = 0; rpc < repeat; rpc++) // and repeat..
                {
                    s = repeatarea;

                    var replace = additionalkeyparser?.Parse(ref s) ?? null; // try a replace

                    if (replace != null)                                     // if found
                    {
                        if (replace.Item2.HasChars())                        // report errors
                        {
                            return(replace.Item2);
                        }

                        // recurse in. note no additionalparser so no recursion below, and pass in the collected dx and kmd values to the level down
                        string res = ParseKeys(events, replace.Item1, defdelay, defshiftdelay, defupdelay, null, d1, d2, d3, kmd);
                        if (res.HasChars())
                        {
                            return(res);
                        }
                    }
                    else
                    {
                        string res = KeyCore(events, ref s, defdelay, defshiftdelay, defupdelay, d1, d2, d3, kmd);  // key core processing with these values
                        if (res.HasChars())
                        {
                            return(res);
                        }
                    }
                }
            }

            //foreach (BaseUtils.EnhancedSendKeysParser.SKEvent x in events) System.Diagnostics.Debug.WriteLine($"Event {x.wm} {x.sc} {x.vkey} {x.delay}");

            return("");
        }