|
| 1 | +using Bespoke.Osc; |
| 2 | +using System; |
| 3 | +using System.Collections.Generic; |
| 4 | +using System.ComponentModel; |
| 5 | +using System.Data; |
| 6 | +using System.Drawing; |
| 7 | +using System.Linq; |
| 8 | +using System.Net; |
| 9 | +using System.Text; |
| 10 | +using System.Threading.Tasks; |
| 11 | +using System.Windows.Forms; |
| 12 | + |
| 13 | +namespace osc_macro_panel |
| 14 | +{ |
| 15 | + |
| 16 | + public partial class MainForm : Form |
| 17 | + { |
| 18 | + |
| 19 | + private string configPath; |
| 20 | + |
| 21 | + public MainForm(string configPath) |
| 22 | + { |
| 23 | + this.configPath = configPath; |
| 24 | + InitializeComponent(); |
| 25 | + } |
| 26 | + |
| 27 | + private void MainForm_Load(object sender, EventArgs e) |
| 28 | + { |
| 29 | + try |
| 30 | + { |
| 31 | + loadConfig(); |
| 32 | + } |
| 33 | + catch (Exception ex) |
| 34 | + { |
| 35 | + MessageBox.Show(ex.Message, "Configuration error", MessageBoxButtons.OK, MessageBoxIcon.Error); |
| 36 | + Application.Exit(); |
| 37 | + } |
| 38 | + CenterToScreen(); |
| 39 | + } |
| 40 | + |
| 41 | + private const string KEYWORD_IP = "ip"; |
| 42 | + private const string KEYWORD_TITLE = "title"; |
| 43 | + private const string KEYWORD_SIZE = "size"; |
| 44 | + private const string KEYWORD_MARGIN = "margin"; |
| 45 | + private const string KEYWORD_FONT = "font"; |
| 46 | + private const string KEYWORD_BUTTON = "button"; |
| 47 | + private const string KEYWORD_EMPTY = "empty"; |
| 48 | + private const string KEYWORD_NEWLINE = "newline"; |
| 49 | + |
| 50 | + private int buttonWidth = 120; |
| 51 | + private int buttonHeight = 120; |
| 52 | + private int buttonMargin = 10; |
| 53 | + private const string BUTTON_FONT_NAME = "Montserrat"; |
| 54 | + private string buttonFontName = BUTTON_FONT_NAME; |
| 55 | + private const int BUTTON_FONT_SIZE = 14; |
| 56 | + private int buttonFontSize = BUTTON_FONT_SIZE; |
| 57 | + private bool buttonFontBold = true; |
| 58 | + private bool buttonFontItalic = false; |
| 59 | + private Font buttonFont = new Font(BUTTON_FONT_NAME, BUTTON_FONT_SIZE, FontStyle.Bold); |
| 60 | + |
| 61 | + private void loadConfig() |
| 62 | + { |
| 63 | + string[] lines; |
| 64 | + try |
| 65 | + { |
| 66 | + lines = System.IO.File.ReadAllLines(configPath); |
| 67 | + } |
| 68 | + catch (Exception ex) |
| 69 | + { |
| 70 | + throw new Exception($"Couldn't read configuration file '{configPath}':\r\n{ex.Message}"); |
| 71 | + } |
| 72 | + int lineNr = 1; |
| 73 | + int currentRow = 0; |
| 74 | + int currentColumn = 0; |
| 75 | + foreach (string line in lines) |
| 76 | + { |
| 77 | + string[] linePieces = line.Split(':'); |
| 78 | + switch (linePieces[0]) |
| 79 | + { |
| 80 | + case KEYWORD_IP: |
| 81 | + if (linePieces.Length != 3) |
| 82 | + throw new ConfigException(lineNr, "Row with keyword 'ip' should contain 2 data columns."); |
| 83 | + IPAddress ip; |
| 84 | + try |
| 85 | + { |
| 86 | + ip = IPAddress.Parse(linePieces[1]); |
| 87 | + } |
| 88 | + catch |
| 89 | + { |
| 90 | + throw new ConfigException(lineNr, "Invalid IP address."); |
| 91 | + } |
| 92 | + if (!int.TryParse(linePieces[2], out int port) || (port < 1) || (port > 65535)) |
| 93 | + throw new ConfigException(lineNr, "Invalid port."); |
| 94 | + destinationEndPoint = new IPEndPoint(ip, port); |
| 95 | + break; |
| 96 | + case KEYWORD_TITLE: |
| 97 | + if (linePieces.Length != 2) |
| 98 | + throw new ConfigException(lineNr, "Row with keyword 'title' should contain 1 data column."); |
| 99 | + Text = linePieces[1]; |
| 100 | + break; |
| 101 | + case KEYWORD_SIZE: |
| 102 | + if (linePieces.Length != 3) |
| 103 | + throw new ConfigException(lineNr, "Row with keyword 'size' should contain 2 data columns."); |
| 104 | + if (!int.TryParse(linePieces[1], out buttonWidth) || (buttonWidth < 1) || (buttonWidth > 500)) |
| 105 | + throw new ConfigException(lineNr, "Invalid width."); |
| 106 | + if (!int.TryParse(linePieces[2], out buttonHeight) || (buttonHeight < 1) || (buttonHeight > 500)) |
| 107 | + throw new ConfigException(lineNr, "Invalid height."); |
| 108 | + break; |
| 109 | + case KEYWORD_MARGIN: |
| 110 | + if (linePieces.Length != 2) |
| 111 | + throw new ConfigException(lineNr, "Row with keyword 'margin' should contain 1 data column."); |
| 112 | + if (!int.TryParse(linePieces[1], out buttonMargin) || (buttonMargin < 1) || (buttonMargin > 500)) |
| 113 | + throw new ConfigException(lineNr, "Invalid margin."); |
| 114 | + break; |
| 115 | + case KEYWORD_FONT: |
| 116 | + if (linePieces.Length < 3) |
| 117 | + throw new ConfigException(lineNr, "Row with keyword 'font' should contain at least 2 data columns."); |
| 118 | + string fontName = linePieces[1]; |
| 119 | + if (!int.TryParse(linePieces[2], out int fontSize) || (fontSize < 1) || (fontSize > 120)) |
| 120 | + throw new ConfigException(lineNr, "Invalid font size."); |
| 121 | + buttonFontBold = false; |
| 122 | + buttonFontItalic = false; |
| 123 | + for (int i = 3; i < linePieces.Length; i++) |
| 124 | + { |
| 125 | + if (linePieces[i] == "bold") |
| 126 | + buttonFontBold = true; |
| 127 | + if (linePieces[i] == "italic") |
| 128 | + buttonFontItalic = true; |
| 129 | + } |
| 130 | + FontStyle buttonFontStyle = FontStyle.Regular; |
| 131 | + if (buttonFontBold) |
| 132 | + buttonFontStyle |= FontStyle.Bold; |
| 133 | + if (buttonFontItalic) |
| 134 | + buttonFontStyle |= FontStyle.Italic; |
| 135 | + buttonFont = new Font(buttonFontName, buttonFontSize, buttonFontStyle); |
| 136 | + break; |
| 137 | + case KEYWORD_BUTTON: |
| 138 | + if (linePieces.Length != 5) |
| 139 | + throw new ConfigException(lineNr, "Row with keyword 'button' should contain 4 data columns."); |
| 140 | + string buttonText = linePieces[1]; |
| 141 | + if (!int.TryParse(linePieces[2], out int macroIndex) || (macroIndex < 0)) |
| 142 | + throw new ConfigException(lineNr, "Invalid macro index."); |
| 143 | + Color buttonBackgroundColor, buttonForegroundColor; |
| 144 | + try |
| 145 | + { |
| 146 | + buttonBackgroundColor = ColorTranslator.FromHtml(linePieces[3]); |
| 147 | + } |
| 148 | + catch |
| 149 | + { |
| 150 | + throw new ConfigException(lineNr, "Invalid background color."); |
| 151 | + } |
| 152 | + try |
| 153 | + { |
| 154 | + buttonForegroundColor = ColorTranslator.FromHtml(linePieces[4]); |
| 155 | + } |
| 156 | + catch |
| 157 | + { |
| 158 | + throw new ConfigException(lineNr, "Invalid foreground color."); |
| 159 | + } |
| 160 | + Button newButton = new Button(); |
| 161 | + newButton.Text = buttonText; |
| 162 | + newButton.BackColor = buttonBackgroundColor; |
| 163 | + newButton.ForeColor = buttonForegroundColor; |
| 164 | + newButton.Size = new Size(buttonWidth, buttonHeight); |
| 165 | + newButton.Margin = new Padding(buttonMargin); |
| 166 | + newButton.FlatStyle = FlatStyle.Flat; |
| 167 | + newButton.Font = buttonFont; |
| 168 | + newButton.Tag = macroIndex; |
| 169 | + newButton.Click += buttonClickHandler; |
| 170 | + if (tableLayout.ColumnCount <= currentColumn) |
| 171 | + { |
| 172 | + tableLayout.ColumnCount++; |
| 173 | + tableLayout.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize)); |
| 174 | + } |
| 175 | + tableLayout.Controls.Add(newButton, currentColumn, currentRow); |
| 176 | + currentColumn++; |
| 177 | + break; |
| 178 | + case KEYWORD_EMPTY: |
| 179 | + currentColumn++; |
| 180 | + break; |
| 181 | + case KEYWORD_NEWLINE: |
| 182 | + currentRow++; |
| 183 | + tableLayout.RowCount++; |
| 184 | + tableLayout.RowStyles.Add(new RowStyle(SizeType.AutoSize)); |
| 185 | + currentColumn = 0; |
| 186 | + break; |
| 187 | + default: |
| 188 | + throw new ConfigException(lineNr, "Invalid keyword."); |
| 189 | + } |
| 190 | + lineNr++; |
| 191 | + } |
| 192 | + if (destinationEndPoint == null) |
| 193 | + throw new Exception("No IP endpoint provided."); |
| 194 | + } |
| 195 | + |
| 196 | + private IPEndPoint destinationEndPoint = null; |
| 197 | + |
| 198 | + private void buttonClickHandler(object sender, EventArgs e) |
| 199 | + { |
| 200 | + int macroIndex = (int)(((Button)sender).Tag); |
| 201 | + IPEndPoint sourceEndPoint = new IPEndPoint(IPAddress.Any, 9700); |
| 202 | + OscMessage message = new OscMessage(sourceEndPoint, $"/macros/exec/{macroIndex}"); |
| 203 | + message.AppendNil(); |
| 204 | + OscBundle bundle = new OscBundle(sourceEndPoint); |
| 205 | + bundle.Append(message); |
| 206 | + bundle.Send(destinationEndPoint); |
| 207 | + } |
| 208 | + |
| 209 | + private class ConfigException : Exception |
| 210 | + { |
| 211 | + public ConfigException(int lineNr, string message) : base($"Line {lineNr}: {message}") |
| 212 | + { } |
| 213 | + } |
| 214 | + |
| 215 | + } |
| 216 | + |
| 217 | +} |
0 commit comments