using System.Diagnostics.Contracts; using System.IO.Pipes; using Microsoft.VisualBasic; using Mindmagma.Curses; using Newtonsoft.Json.Linq; namespace SCI.CursesWrapper; public class Helper { /// /// Create a centered NCurses window on the specified screen /// /// Parent screen to attach window to /// Width of columns of window /// Height of rows of window /// If true, shows default borders around window /// nint - Pointer of new window /// Throws exception if window is outside of screen bounds public static nint CreateCenteredWindow (nint screen, int width, int height, bool borders = true) { NCurses.GetMaxYX (screen, out int screenHeight, out int screenWidth); int originY = (screenHeight / 2) - (height / 2); int originX = (screenWidth / 2) - (width / 2); if (originX + width > screenWidth) throw new ArgumentOutOfRangeException ("width"); if (originY + height > screenHeight) throw new ArgumentOutOfRangeException ("height"); if (originX < 0) throw new ArgumentOutOfRangeException ("originX"); if (originY < 0) throw new ArgumentOutOfRangeException ("originY"); nint window = NCurses.NewWindow (height, width, originY, originX); if (borders) NCurses.Box (window, (char) 0, (char) 0); return window; } /// /// Prints text horizontally centered to window (on a specific line); /// /// Parent window to print into /// Text to display /// Y posititon of the text public static void PrintHCenteredText (nint window, string text, int y = 0) { NCurses.GetMaxYX (window, out int _, out int windowWidth); var x = windowWidth / 2 - text.Length / 2; NCurses.MoveWindowAddString (window, y, x, text); } } public class MessageBox { public enum MessageBoxButtons { OK, YesNo, RetryCancel, RetryIgnoreCancel } /// /// Shows a message box with specified text and returns the selected button from zero index /// /// Parent screen to show message box on /// Text to show /// Type of buttons to show /// Button pressed by user, zero indexed public static int Show (nint screen, string text, MessageBoxButtons buttons = MessageBoxButtons.OK) { NCurses.GetMaxYX (screen, out int screenHeight, out int screenWidth); // Set static box width, if width is larger than screen, reduce to screen size, // then calculate the message box and inner pad origin X coords var boxWidth = 50; if (boxWidth + 3 >= screenWidth) boxWidth = screenWidth - 3; int originX = (screenWidth / 2) - (boxWidth / 2); var padOriginX = originX + 2; // Add one for the border and for a single character padding // Calculate sizes for the inner pad element var padWidth = boxWidth - 4; // Add four to the total box width due to the two character padding on both sides var totalTextLines = (int) Math.Ceiling ((double) text.Length / padWidth); // Get total amount of lines required to hold all text limited by box width var padHeight = totalTextLines; if (padHeight + 8 > screenHeight) { // If height required to show all text lines plus padding is larger than screen, reduce size of message box padHeight = screenHeight - 8; } // Now calculate the height and Y origin of the text box and inner pad var boxHeight = padHeight + 6; // Add five to the pad height due to the title bar and two character padding top and bottom if (boxHeight + 2 > screenHeight) boxHeight = screenHeight - 2; int originY = (screenHeight / 2) - (boxHeight / 2); var padOriginY = originY + 3; // Add one for the border, one for the title and one for a single character padding //if (text.Length > (totalTextRows * textCols)) text = text.Substring (0, (totalTextRows * textCols) - 1) + "$"; // Create message box and inner Pad var msgBoxWindow = Helper.CreateCenteredWindow (screen, boxWidth, boxHeight, true); var msgTextPad = NCurses.NewPad (padHeight, padWidth); NCurses.Refresh (); // Title, currently not in use Helper.PrintHCenteredText (msgBoxWindow, "Info", 1); NCurses.WindowRefresh (msgBoxWindow); NCurses.WindowAddString (msgTextPad, text); NCurses.PadRefresh (msgTextPad, 0, 0, padOriginY, padOriginX, padOriginY + padHeight, padOriginX + padWidth); return InputHandler (msgBoxWindow, buttons); } private static List GetStringsFromButtonEnum (MessageBoxButtons buttons) { List buttonStrings; switch (buttons) { case MessageBoxButtons.OK: buttonStrings = new List () { "OK" }; break; case MessageBoxButtons.YesNo: buttonStrings = new List () { "Yes", "No" }; break; case MessageBoxButtons.RetryCancel: buttonStrings = new List () { "Retry", "Cancel" }; break; case MessageBoxButtons.RetryIgnoreCancel: buttonStrings = new List () { "Retry", "Ignore", "Cancel" }; break; default: throw new Exception ("Unknown button layout for message box"); } return buttonStrings; } /// /// Draws buttons specified by the buttons type /// /// Window to draw buttons on /// Buttons to display private static void DrawButtons (nint window, MessageBoxButtons buttons, int active = 0) { NCurses.GetMaxYX (window, out int windowHeight, out int windowWidth); var buttonStrings = GetStringsFromButtonEnum (buttons); var totalStringLength = buttonStrings.Count - 1; foreach (var str in buttonStrings) totalStringLength += str.Length + 2; NCurses.WindowMove (window, windowHeight - 2, (windowWidth / 2) - (totalStringLength / 2)); var index = 0; foreach (var str in buttonStrings) { if (index == active) { NCurses.WindowAddString (window, $"[{str}]"); } else { NCurses.WindowAddString (window, $" {str} "); } NCurses.WindowAddChar (window, ' '); index ++; } NCurses.WindowRefresh (window); } /// /// Handle the user selection of the buttons /// /// Window to draw buttons on /// Buttons to display /// User choice of button, zero indexed private static int InputHandler (nint window, MessageBoxButtons buttons) { DrawButtons (window, buttons); int index = 0; int maxIndex = GetStringsFromButtonEnum (buttons).Count - 1; int chr = 0; while ((chr = NCurses.GetChar ()) != 10) { if (chr == 260) { // Left index ++; if (index > maxIndex) index = 0; DrawButtons (window, buttons, index); } else if (chr == 261) { // Right index --; if (index < 0) index = maxIndex; DrawButtons (window, buttons, index); } } return index; } }