Exemplo n.º 1
0
        /// <summary>
        /// Construct the screens
        /// </summary>
        /// <param name="game">Game from which to get repos</param>
        /// <param name="reps">Collection of Repository objects</param>
        /// <param name="initName">Initial value of the Name field</param>
        /// <param name="initUrl">Iniital value of the URL field</param>
        protected RepoScreen(IGame game, SortedDictionary <string, Repository> reps, string initName, string initUrl) : base()
        {
            editList     = reps;
            defaultRepos = RepositoryList.DefaultRepositories(game);

            name = new ConsoleField(labelWidth, nameRow, -1, initName)
            {
                GhostText = () => "<Enter the name to use for this repository>"
            };
            url = new ConsoleField(labelWidth, urlRow, -1, initUrl)
            {
                GhostText = () => "<Enter the URL of this repository>"
            };

            AddObject(new ConsoleLabel(1, nameRow, labelWidth, () => "Name:"));
            AddObject(name);
            AddObject(new ConsoleLabel(1, urlRow, labelWidth, () => "URL:"));
            AddObject(url);

            AddTip("F2", "Accept");
            AddBinding(Keys.F2, (object sender, ConsoleTheme theme) => {
                if (Valid())
                {
                    Save();
                    return(false);
                }
                else
                {
                    return(true);
                }
            });

            AddTip("Esc", "Cancel");
            AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => {
                return(false);
            });

            // mainMenu = list of default options
            if (defaultRepos.repositories != null && defaultRepos.repositories.Length > 0)
            {
                List <ConsoleMenuOption> opts = new List <ConsoleMenuOption>();
                foreach (Repository r in defaultRepos.repositories)
                {
                    // This variable will be remembered correctly in our lambdas later
                    Repository repo = r;
                    opts.Add(new ConsoleMenuOption(
                                 repo.name, "", $"Import values from default mod list source {repo.name}",
                                 true, (ConsoleTheme theme) => {
                        name.Value    = repo.name;
                        name.Position = name.Value.Length;
                        url.Value     = repo.uri.ToString();
                        url.Position  = url.Value.Length;
                        return(true);
                    }
                                 ));
                }
                mainMenu = new ConsolePopupMenu(opts);
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Construct the screens
        /// </summary>
        /// <param name="reps">Collection of Repository objects</param>
        /// <param name="initName">Initial value of the Name field</param>
        /// <param name="initUrl">Iniital value of the URL field</param>
        protected RepoScreen(SortedDictionary <string, Repository> reps, string initName, string initUrl) : base()
        {
            editList = reps;

            name = new ConsoleField(labelWidth, nameRow, -1, initName);
            url  = new ConsoleField(labelWidth, urlRow, -1, initUrl);

            AddObject(new ConsoleLabel(1, nameRow, labelWidth, () => "Name:"));
            AddObject(name);
            AddObject(new ConsoleLabel(1, urlRow, labelWidth, () => "URL:"));
            AddObject(url);

            AddTip("F2", "Accept");
            AddBinding(Keys.F2, (object sender) => {
                if (Valid())
                {
                    Save();
                    return(false);
                }
                else
                {
                    return(true);
                }
            });

            AddTip("Esc", "Cancel");
            AddBinding(Keys.Escape, (object sender) => {
                return(false);
            });

            // mainMenu = list of default options
            if (defaultRepos.repositories != null && defaultRepos.repositories.Length > 0)
            {
                List <ConsoleMenuOption> opts = new List <ConsoleMenuOption>();
                foreach (Repository r in defaultRepos.repositories)
                {
                    // This variable will be remembered correctly in our lambdas later
                    Repository repo = r;
                    opts.Add(new ConsoleMenuOption(
                                 repo.name, "", $"Import values from default mod list source {repo.name}",
                                 true, () => {
                        name.Value    = repo.name;
                        name.Position = name.Value.Length;
                        url.Value     = repo.uri.ToString();
                        url.Position  = url.Value.Length;
                        return(true);
                    }
                                 ));
                }
                mainMenu = new ConsolePopupMenu(opts);
            }

            LeftHeader   = () => $"CKAN {Meta.GetVersion()}";
            CenterHeader = () => "Edit Mod List Source";
        }
Exemplo n.º 3
0
        /// <summary>
        /// Initialize the screen
        /// </summary>
        /// <param name="mgr">Game instance manager object containing the current instance</param>
        /// <param name="dbg">True if debug options should be available, false otherwise</param>
        public ModListScreen(GameInstanceManager mgr, bool dbg)
        {
            debug    = dbg;
            manager  = mgr;
            registry = RegistryManager.Instance(manager.CurrentInstance).registry;

            moduleList = new ConsoleListBox <CkanModule>(
                1, 4, -1, -2,
                GetAllMods(),
                new List <ConsoleListBoxColumn <CkanModule> >()
            {
                new ConsoleListBoxColumn <CkanModule>()
                {
                    Header   = "",
                    Width    = 1,
                    Renderer = StatusSymbol
                }, new ConsoleListBoxColumn <CkanModule>()
                {
                    Header   = "Name",
                    Width    = 44,
                    Renderer = m => m.name ?? ""
                }, new ConsoleListBoxColumn <CkanModule>()
                {
                    Header   = "Version",
                    Width    = 10,
                    Renderer = m => ModuleInstaller.StripEpoch(m.version?.ToString() ?? ""),
                    Comparer = (a, b) => a.version.CompareTo(b.version)
                }, new ConsoleListBoxColumn <CkanModule>()
                {
                    Header   = "Max game version",
                    Width    = 17,
                    Renderer = m => registry.LatestCompatibleKSP(m.identifier)?.ToString() ?? "",
                    Comparer = (a, b) => registry.LatestCompatibleKSP(a.identifier).CompareTo(registry.LatestCompatibleKSP(b.identifier))
                }
            },
                1, 0, ListSortDirection.Descending,
                (CkanModule m, string filter) => {
                // Search for author
                if (filter.StartsWith("@"))
                {
                    string authorFilt = filter.Substring(1);
                    if (string.IsNullOrEmpty(authorFilt))
                    {
                        return(true);
                    }
                    else
                    {
                        // Remove special characters from search term
                        authorFilt = CkanModule.nonAlphaNums.Replace(authorFilt, "");
                        return(m.SearchableAuthors.Any((author) => author.IndexOf(authorFilt, StringComparison.CurrentCultureIgnoreCase) == 0));
                    }
                    // Other special search params: installed, updatable, new, conflicting and dependends
                }
                else if (filter.StartsWith("~"))
                {
                    if (filter.Length <= 1)
                    {
                        // Don't blank the list for just "~" by itself
                        return(true);
                    }
                    else
                    {
                        switch (filter.Substring(1, 1))
                        {
                        case "i":
                            return(registry.IsInstalled(m.identifier, false));

                        case "u":
                            return(registry.HasUpdate(m.identifier, manager.CurrentInstance.VersionCriteria()));

                        case "n":
                            // Filter new
                            return(recent.Contains(m.identifier));

                        case "c":
                            if (m.conflicts != null)
                            {
                                string conflictsWith = filter.Substring(2);
                                // Search for mods depending on a given mod
                                foreach (var rel in m.conflicts)
                                {
                                    if (rel.StartsWith(conflictsWith))
                                    {
                                        return(true);
                                    }
                                }
                            }
                            return(false);

                        case "d":
                            if (m.depends != null)
                            {
                                string dependsOn = filter.Substring(2);
                                // Search for mods depending on a given mod
                                foreach (var rel in m.depends)
                                {
                                    if (rel.StartsWith(dependsOn))
                                    {
                                        return(true);
                                    }
                                }
                            }
                            return(false);
                        }
                    }
                    return(false);
                }
                else
                {
                    filter = CkanModule.nonAlphaNums.Replace(filter, "");

                    return(m.SearchableIdentifier.IndexOf(filter, StringComparison.CurrentCultureIgnoreCase) >= 0 ||
                           m.SearchableName.IndexOf(filter, StringComparison.CurrentCultureIgnoreCase) >= 0 ||
                           m.SearchableAbstract.IndexOf(filter, StringComparison.CurrentCultureIgnoreCase) >= 0 ||
                           m.SearchableDescription.IndexOf(filter, StringComparison.CurrentCultureIgnoreCase) >= 0);
                }
            }
                );

            searchBox = new ConsoleField(-searchWidth, 2, -1)
            {
                GhostText = () => Focused() == searchBox
                    ? "<Type to search>"
                    : "<Ctrl+F to search>"
            };
            searchBox.OnChange += (ConsoleField sender, string newValue) => {
                moduleList.FilterString = newValue;
            };

            AddObject(new ConsoleLabel(
                          1, 2, -searchWidth - 2,
                          () => $"{moduleList.VisibleRowCount()} mods"
                          ));
            AddObject(searchBox);
            AddObject(moduleList);

            AddBinding(Keys.CtrlQ, (object sender, ConsoleTheme theme) => false);
            AddBinding(Keys.AltX, (object sender, ConsoleTheme theme) => false);
            AddBinding(Keys.F1, (object sender, ConsoleTheme theme) => Help(theme));
            AddBinding(Keys.AltH, (object sender, ConsoleTheme theme) => Help(theme));
            AddBinding(Keys.F5, (object sender, ConsoleTheme theme) => UpdateRegistry(theme));
            AddBinding(Keys.CtrlR, (object sender, ConsoleTheme theme) => UpdateRegistry(theme));
            AddBinding(Keys.CtrlU, (object sender, ConsoleTheme theme) => UpgradeAll(theme));

            // Now a bunch of convenience shortcuts so you don't get stuck in the search box
            searchBox.AddBinding(Keys.PageUp, (object sender, ConsoleTheme theme) => {
                SetFocus(moduleList);
                return(true);
            });
            searchBox.AddBinding(Keys.PageDown, (object sender, ConsoleTheme theme) => {
                SetFocus(moduleList);
                return(true);
            });
            searchBox.AddBinding(Keys.Enter, (object sender, ConsoleTheme theme) => {
                SetFocus(moduleList);
                return(true);
            });

            moduleList.AddBinding(Keys.CtrlF, (object sender, ConsoleTheme theme) => {
                SetFocus(searchBox);
                return(true);
            });
            moduleList.AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => {
                searchBox.Clear();
                return(true);
            });

            moduleList.AddTip("Enter", "Details",
                              () => moduleList.Selection != null
                              );
            moduleList.AddBinding(Keys.Enter, (object sender, ConsoleTheme theme) => {
                if (moduleList.Selection != null)
                {
                    LaunchSubScreen(theme, new ModInfoScreen(manager, plan, moduleList.Selection, debug));
                }
                return(true);
            });

            // Conditionally show only one of these based on selected mod status

            moduleList.AddTip("+", "Install",
                              () => moduleList.Selection != null && !moduleList.Selection.IsDLC &&
                              !registry.IsInstalled(moduleList.Selection.identifier, false)
                              );
            moduleList.AddTip("+", "Upgrade",
                              () => moduleList.Selection != null && !moduleList.Selection.IsDLC &&
                              registry.HasUpdate(moduleList.Selection.identifier, manager.CurrentInstance.VersionCriteria())
                              );
            moduleList.AddTip("+", "Replace",
                              () => moduleList.Selection != null &&
                              registry.GetReplacement(moduleList.Selection.identifier, manager.CurrentInstance.VersionCriteria()) != null
                              );
            moduleList.AddBinding(Keys.Plus, (object sender, ConsoleTheme theme) => {
                if (moduleList.Selection != null && !moduleList.Selection.IsDLC)
                {
                    if (!registry.IsInstalled(moduleList.Selection.identifier, false))
                    {
                        plan.ToggleInstall(moduleList.Selection);
                    }
                    else if (registry.IsInstalled(moduleList.Selection.identifier, false) &&
                             registry.HasUpdate(moduleList.Selection.identifier, manager.CurrentInstance.VersionCriteria()))
                    {
                        plan.ToggleUpgrade(moduleList.Selection);
                    }
                    else if (registry.GetReplacement(moduleList.Selection.identifier, manager.CurrentInstance.VersionCriteria()) != null)
                    {
                        plan.ToggleReplace(moduleList.Selection.identifier);
                    }
                }
                return(true);
            });

            moduleList.AddTip("-", "Remove",
                              () => moduleList.Selection != null && !moduleList.Selection.IsDLC &&
                              registry.IsInstalled(moduleList.Selection.identifier, false) &&
                              !registry.IsAutodetected(moduleList.Selection.identifier)
                              );
            moduleList.AddBinding(Keys.Minus, (object sender, ConsoleTheme theme) => {
                if (moduleList.Selection != null && !moduleList.Selection.IsDLC &&
                    registry.IsInstalled(moduleList.Selection.identifier, false) &&
                    !registry.IsAutodetected(moduleList.Selection.identifier))
                {
                    plan.ToggleRemove(moduleList.Selection);
                }
                return(true);
            });

            moduleList.AddTip("F8", "Mark auto-installed",
                              () => moduleList.Selection != null && !moduleList.Selection.IsDLC &&
                              (!registry.InstalledModule(moduleList.Selection.identifier)?.AutoInstalled ?? false)
                              );
            moduleList.AddTip("F8", "Mark user-selected",
                              () => moduleList.Selection != null && !moduleList.Selection.IsDLC &&
                              (registry.InstalledModule(moduleList.Selection.identifier)?.AutoInstalled ?? false)
                              );
            moduleList.AddBinding(Keys.F8, (object sender, ConsoleTheme theme) => {
                InstalledModule im = registry.InstalledModule(moduleList.Selection.identifier);
                if (im != null && !moduleList.Selection.IsDLC)
                {
                    im.AutoInstalled = !im.AutoInstalled;
                    RegistryManager.Instance(manager.CurrentInstance).Save(false);
                }
                return(true);
            });

            AddTip("F9", "Apply changes", plan.NonEmpty);
            AddBinding(Keys.F9, (object sender, ConsoleTheme theme) => {
                ApplyChanges(theme);
                return(true);
            });

            // Show total download size of all installed mods
            AddObject(new ConsoleLabel(
                          1, -1, searchWidth,
                          () => $"{CkanModule.FmtSize(totalInstalledDownloadSize())} installed",
                          null,
                          th => th.DimLabelFg
                          ));

            AddObject(new ConsoleLabel(
                          -searchWidth, -1, -2,
                          () => {
                int days = daysSinceUpdated(registryFilePath());
                return(days < 1 ? ""
                        :  days == 1 ? $"Updated at least {days} day ago"
                        :              $"Updated at least {days} days ago");
            },
                          null,
                          (ConsoleTheme th) => {
                int daysSince = daysSinceUpdated(registryFilePath());
                if (daysSince < daysTillStale)
                {
                    return(th.RegistryUpToDate);
                }
                else if (daysSince < daystillVeryStale)
                {
                    return(th.RegistryStale);
                }
                else
                {
                    return(th.RegistryVeryStale);
                }
            }
                          ));

            List <ConsoleMenuOption> opts = new List <ConsoleMenuOption>()
            {
                new ConsoleMenuOption("Sort...", "",
                                      "Change the sorting of the list of mods",
                                      true, null, null, moduleList.SortMenu()),
                null,
                new ConsoleMenuOption("Refresh mod list", "F5, Ctrl+R",
                                      "Refresh the list of mods",
                                      true, UpdateRegistry),
                new ConsoleMenuOption("Upgrade all", "Ctrl+U",
                                      "Mark all available updates for installation",
                                      true, UpgradeAll, null, null, HasAnyUpgradeable()),
                new ConsoleMenuOption("Audit recommendations", "",
                                      "List mods suggested and recommended by installed mods",
                                      true, ViewSuggestions),
                new ConsoleMenuOption("Import downloads...", "",
                                      "Select manually downloaded mods to import into CKAN",
                                      true, ImportDownloads),
                new ConsoleMenuOption("Export installed...", "",
                                      "Save your mod list",
                                      true, ExportInstalled),
                null,
                new ConsoleMenuOption("Select game instance...", "",
                                      "Switch to a different game instance",
                                      true, SelectInstall),
                new ConsoleMenuOption("Authentication tokens...", "",
                                      "Edit authentication tokens sent to download servers",
                                      true, EditAuthTokens),
                null,
                new ConsoleMenuOption("Help", helpKey,
                                      "Tips & tricks",
                                      true, Help),
                null,
                new ConsoleMenuOption("Quit", "Ctrl+Q",
                                      "Exit to DOS",
                                      true, (ConsoleTheme th) => false)
            };

            if (debug)
            {
                opts.Add(null);
                opts.Add(new ConsoleMenuOption("DEBUG: Capture key...", "",
                                               "Print details of how your system reports a keystroke for debugging",
                                               true, CaptureKey));
            }
            mainMenu = new ConsolePopupMenu(opts);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Initialize the screen.
        /// </summary>
        public AuthTokenScreen() : base()
        {
            mainMenu = new ConsolePopupMenu(new List <ConsoleMenuOption>()
            {
                new ConsoleMenuOption("Make a GitHub API token", "",
                                      "Open the web page for creating GitHub API authentication tokens",
                                      true, openGitHubURL)
            });

            AddObject(new ConsoleLabel(
                          1, 2, -1,
                          () => "Authentication tokens for downloads:"
                          ));

            tokenList = new ConsoleListBox <string>(
                1, 4, -1, -2,
                new List <string>(Win32Registry.GetAuthTokenHosts()),
                new List <ConsoleListBoxColumn <string> >()
            {
                new ConsoleListBoxColumn <string>()
                {
                    Header   = "Host",
                    Width    = 20,
                    Renderer = (string s) => s
                },
                new ConsoleListBoxColumn <string>()
                {
                    Header   = "Token",
                    Width    = 50,
                    Renderer = (string s) => {
                        string token;
                        return(Win32Registry.TryGetAuthToken(s, out token)
                                ? token
                                : missingTokenValue);
                    }
                }
            },
                0, 0, ListSortDirection.Descending
                );
            AddObject(tokenList);

            AddObject(new ConsoleLabel(
                          3, -1, -1,
                          () => "NOTE: These values are private! Do not share screenshots of this screen!",
                          null,
                          () => ConsoleTheme.Current.AlertFrameFg
                          ));

            AddTip("Esc", "Back");
            AddBinding(Keys.Escape, (object sender) => false);

            tokenList.AddTip("A", "Add");
            tokenList.AddBinding(Keys.A, (object sender) => {
                AuthTokenAddDialog ad = new AuthTokenAddDialog();
                ad.Run();
                DrawBackground();
                tokenList.SetData(new List <string>(Win32Registry.GetAuthTokenHosts()));
                return(true);
            });

            tokenList.AddTip("R", "Remove", () => tokenList.Selection != null);
            tokenList.AddBinding(Keys.R, (object sender) => {
                if (tokenList.Selection != null)
                {
                    Win32Registry.SetAuthToken(tokenList.Selection, null);
                    tokenList.SetData(new List <string>(Win32Registry.GetAuthTokenHosts()));
                }
                return(true);
            });
        }
Exemplo n.º 5
0
        /// <summary>
        /// Initialize the Screen
        /// </summary>
        /// <param name="mgr">KSP manager containing game instances</param>
        /// <param name="cp">Plan of other mods to be added or removed</param>
        /// <param name="m">The module to display</param>
        /// <param name="dbg">True if debug options should be available, false otherwise</param>
        public ModInfoScreen(KSPManager mgr, ChangePlan cp, CkanModule m, bool dbg)
        {
            debug    = dbg;
            mod      = m;
            manager  = mgr;
            plan     = cp;
            registry = RegistryManager.Instance(manager.CurrentInstance).registry;

            int midL = Console.WindowWidth / 2 - 1;

            AddObject(new ConsoleLabel(
                          1, 1, -1,
                          () => mod.name == mod.identifier ? mod.name : $"{mod.name} ({mod.identifier})",
                          null,
                          () => ConsoleTheme.Current.ActiveFrameFg
                          ));
            AddObject(new ConsoleLabel(
                          1, 2, -1,
                          () => $"By {string.Join(", ", mod.author)}"
                          ));

            AddObject(new ConsoleFrame(
                          1, 3, midL, 7,
                          () => "",
                          () => ConsoleTheme.Current.NormalFrameFg,
                          false
                          ));
            AddObject(new ConsoleLabel(
                          3, 4, 11,
                          () => "License:",
                          null,
                          () => ConsoleTheme.Current.DimLabelFg
                          ));
            AddObject(new ConsoleLabel(
                          13, 4, midL - 2,
                          () => string.Join(", ", Array.ConvertAll <License, string>(
                                                mod.license.ToArray(), (l => l.ToString())))
                          ));
            AddObject(new ConsoleLabel(
                          3, 5, 12,
                          () => "Download:",
                          null,
                          () => ConsoleTheme.Current.DimLabelFg
                          ));
            AddObject(new ConsoleLabel(
                          13, 5, midL - 2,
                          () => CkanModule.FmtSize(mod.download_size)
                          ));
            AddObject(new ConsoleLabel(
                          3, 6, midL - 2,
                          HostedOn
                          ));

            int depsBot = addDependencies();
            int versBot = addVersionDisplay();

            AddObject(new ConsoleFrame(
                          1, Math.Max(depsBot, versBot) + 1, -1, -1,
                          () => "Description",
                          () => ConsoleTheme.Current.NormalFrameFg,
                          false
                          ));
            ConsoleTextBox tb = new ConsoleTextBox(
                3, Math.Max(depsBot, versBot) + 2, -3, -2, false,
                TextAlign.Left,
                () => ConsoleTheme.Current.MainBg,
                () => ConsoleTheme.Current.LabelFg
                );

            tb.AddLine(mod.@abstract);
            if (!string.IsNullOrEmpty(mod.description) &&
                mod.description != mod.@abstract)
            {
                tb.AddLine(mod.description);
            }
            AddObject(tb);
            if (!ChangePlan.IsAnyAvailable(registry, mod.identifier))
            {
                tb.AddLine("\r\nNOTE: This mod is installed but no longer available.");
                tb.AddLine("If you uninstall it, CKAN will not be able to re-install it.");
            }
            tb.AddScrollBindings(this);

            AddTip("Esc", "Back");
            AddBinding(Keys.Escape, (object sender) => false);

            AddTip("Ctrl+D", "Download",
                   () => !manager.Cache.IsMaybeCachedZip(mod)
                   );
            AddBinding(Keys.CtrlD, (object sender) => {
                Download();
                return(true);
            });

            if (mod.resources != null)
            {
                List <ConsoleMenuOption> opts = new List <ConsoleMenuOption>();

                if (mod.resources.homepage != null)
                {
                    opts.Add(new ConsoleMenuOption(
                                 "Home page", "", "Open the home page URL in a browser",
                                 true,
                                 () => LaunchURL(mod.resources.homepage)
                                 ));
                }
                if (mod.resources.repository != null)
                {
                    opts.Add(new ConsoleMenuOption(
                                 "Repository", "", "Open the repository URL in a browser",
                                 true,
                                 () => LaunchURL(mod.resources.repository)
                                 ));
                }
                if (mod.resources.bugtracker != null)
                {
                    opts.Add(new ConsoleMenuOption(
                                 "Bugtracker", "", "Open the bug tracker URL in a browser",
                                 true,
                                 () => LaunchURL(mod.resources.bugtracker)
                                 ));
                }
                if (mod.resources.spacedock != null)
                {
                    opts.Add(new ConsoleMenuOption(
                                 "SpaceDock", "", "Open the SpaceDock URL in a browser",
                                 true,
                                 () => LaunchURL(mod.resources.spacedock)
                                 ));
                }
                if (mod.resources.curse != null)
                {
                    opts.Add(new ConsoleMenuOption(
                                 "Curse", "", "Open the Curse URL in a browser",
                                 true,
                                 () => LaunchURL(mod.resources.curse)
                                 ));
                }
                if (debug)
                {
                    opts.Add(null);
                    opts.Add(new ConsoleMenuOption(
                                 "DEBUG: View metadata", "", "Display the full registry data for this mod",
                                 true,
                                 ViewMetadata
                                 ));
                }

                if (opts.Count > 0)
                {
                    mainMenu = new ConsolePopupMenu(opts);
                }
            }
        }
Exemplo n.º 6
0
        public InstallFiltersScreen(IConfiguration globalConfig, GameInstance instance)
        {
            this.globalConfig = globalConfig;
            this.instance     = instance;
            globalFilters     = globalConfig.GlobalInstallFilters.ToList();
            instanceFilters   = instance.InstallFilters.ToList();

            AddTip("F2", "Accept");
            AddBinding(Keys.F2, (object sender, ConsoleTheme theme) => {
                Save();
                // Close screen
                return(false);
            });
            AddTip("Esc", "Cancel");
            AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => {
                // Discard changes
                return(false);
            });

            mainMenu = new ConsolePopupMenu(new List <ConsoleMenuOption>()
            {
                new ConsoleMenuOption("Add MiniAVC", "",
                                      "Prevent MiniAVC from being installed",
                                      true, AddMiniAVC),
            });

            int vMid = Console.WindowHeight / 2;

            globalList = new ConsoleListBox <string>(
                2, 2, -2, vMid - 1,
                globalFilters,
                new List <ConsoleListBoxColumn <string> >()
            {
                new ConsoleListBoxColumn <string>()
                {
                    Header   = "Global Filters",
                    Width    = 40,
                    Renderer = f => f,
                }
            },
                0
                );
            AddObject(globalList);
            globalList.AddTip("A", "Add");
            globalList.AddBinding(Keys.A, (object sender, ConsoleTheme theme) => {
                AddFilter(theme, globalList, globalFilters);
                return(true);
            });
            globalList.AddTip("R", "Remove");
            globalList.AddBinding(Keys.R, (object sender, ConsoleTheme theme) => {
                RemoveFilter(globalList, globalFilters);
                return(true);
            });
            instanceList = new ConsoleListBox <string>(
                2, vMid + 1, -2, -2,
                instanceFilters,
                new List <ConsoleListBoxColumn <string> >()
            {
                new ConsoleListBoxColumn <string>()
                {
                    Header   = "Instance Filters",
                    Width    = 40,
                    Renderer = f => f,
                }
            },
                0
                );
            AddObject(instanceList);
            instanceList.AddTip("A", "Add");
            instanceList.AddBinding(Keys.A, (object sender, ConsoleTheme theme) => {
                AddFilter(theme, instanceList, instanceFilters);
                return(true);
            });
            instanceList.AddTip("R", "Remove");
            instanceList.AddBinding(Keys.R, (object sender, ConsoleTheme theme) => {
                RemoveFilter(instanceList, instanceFilters);
                return(true);
            });
        }