GPAL - Generally Positive Automation Library v1.0
GPAL The Fluent Automation LIbrary
Loading...
Searching...
No Matches
GPALClipboard.cs
1// =============================================================================
2// GPAL - Generally Positive Automation Library
3// Copyright © 2026 Software Decisions, Inc. All rights reserved.
4//
5// This file is part of GPAL.
6// Licensed under the Business Source License 1.1
7//
8// Primary development, architecture, and vision by Michael B. Vederman,
9// CEO of Software Decisions, Inc., Texas.
10//
11// Internal development maintained privately.
12// Public releases appear on GitHub: https://github.com/SoftwareDecisionsInc/GPAL.
13//
14// See LICENSE for full terms, including Additional Use Grant.
15// =============================================================================
16
17using System;
18using System.Collections.Generic;
19using System.Collections.Specialized;
20using System.Drawing;
21using System.IO;
22using System.Threading;
23using static GenerallyPositive.Enums;
24
25namespace GenerallyPositive
26{
27 internal class ClipboardSettings
28 {
29 public GPALClipboard Clipboard { get; set; }
30 public ClipboardType ClipboardType { get; set; } = ClipboardType.Text;
31 public string InputText { get; set; }
32 public IGPALGrid<string> InputGrid { get; set; }
33 public GPALFile InputFile { get; set; }
34
35 public ClipboardSettings(GPALClipboard clipboard)
36 {
37 Clipboard = clipboard;
38 }
39 }
40
58 {
59 internal ClipboardSettings ClipboardSettings { get; set; }
60
62 private const int ATTEMPTS = 10;
63
65 private const int BETWEEN_ATTEMPTS = 100;
66
67 internal GPALClipboard()
68 {
69 ClipboardSettings = new ClipboardSettings(this);
70 }
71
72 public IGPALClipboard ToGPALObject()
73 {
74 return this;
75 }
76
82 public IAllowClipboardSet WithInput(string text)
83 {
84 ClipboardSettings.InputText = text;
85 return this;
86 }
87
94 public IAllowClipboardSet WithInput(IGPALGrid<string> grid)
95 {
96 ClipboardSettings.InputGrid = grid;
97 return this;
98 }
99
107 {
108 ClipboardSettings.InputFile = file;
109 return this;
110 }
111
117 public IGPALClipboard SetClipboard(ClipboardType clipboardType = ClipboardType.Text)
118 {
119 ClipboardSettings.ClipboardType = clipboardType;
120
121 string text = InputAsText();
122
123 if (null == text && null == ClipboardSettings.InputFile)
124 GPAL.PublishSimpleEvent(GPALEventType.ERROR, "Nothing said what to put on the clipboard. Use .WithInput first", this, GPALObjectType.Other);
125 else
126 OnStaThread(() => Write(text));
127
128 return this;
129 }
130
137 public IAllowClipboardInto GetClipboard(ClipboardType clipboardType = ClipboardType.Text)
138 {
139 ClipboardSettings.ClipboardType = clipboardType;
140 return this;
141 }
142
149 public IGPALClipboard SaveTo(out string text)
150 {
151 string read = null;
152
153 if (ClipboardType.Image == ClipboardSettings.ClipboardType)
154 OnStaThread(() => read = ReadImageAsBase64());
155 else
156 OnStaThread(() => read = Read());
157
158 text = read;
159 return this;
160 }
161
168 public IGPALClipboard SaveTo(out IGPALGrid<string> grid)
169 {
170 IGPALGrid<string> retVal = GPAL.Grid.ToGPALObject();
171 string read = null;
172
173 OnStaThread(() => read = Read());
174
175 if (false == string.IsNullOrEmpty(read))
176 foreach (string line in read.Replace("\r\n", "\n").TrimEnd('\n').Split('\n'))
177 retVal.AddRow(new List<string>(line.Split('\t')));
178
179 grid = retVal;
180 return this;
181 }
182
189 {
190 if (ClipboardType.Image == ClipboardSettings.ClipboardType)
191 OnStaThread(() => WriteImageFile(file));
192 else
193 {
194 string read = null;
195
196 OnStaThread(() => read = Read());
197
198 if (null == read)
199 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $"The clipboard holds nothing of type [{ClipboardSettings.ClipboardType}], so [{file?.Filename}] was not written", this, GPALObjectType.Other);
200 else
201 File.WriteAllText(file.Filename, read);
202 }
203
204 return this;
205 }
206
212 {
213 OnStaThread(() => Attempt(() => System.Windows.Forms.Clipboard.Clear(), "clear the clipboard"));
214
215 return this;
216 }
217
224 private string InputAsText()
225 {
226 string retVal = ClipboardSettings.InputText;
227
228 if (null == retVal && null != ClipboardSettings.InputGrid)
229 {
230 var lines = new List<string>();
231
232 foreach (List<string> row in ClipboardSettings.InputGrid)
233 lines.Add(string.Join("\t", row));
234
235 retVal = string.Join(Environment.NewLine, lines);
236 }
237
238 if (null == retVal && null != ClipboardSettings.InputFile
239 && ClipboardType.FileList != ClipboardSettings.ClipboardType
240 && ClipboardType.Image != ClipboardSettings.ClipboardType)
241 retVal = File.ReadAllText(ClipboardSettings.InputFile.Filename);
242
243 return retVal;
244 }
245
250 private void Write(string text)
251 {
252 var data = new System.Windows.Forms.DataObject();
253
254 if (ClipboardType.Image == ClipboardSettings.ClipboardType)
255 {
256 // an image goes on from a file or from the base64 a string holds it in, which is the same
257 // string GetClipboard hands back, so what comes off the clipboard goes back on unchanged
258 if (null != ClipboardSettings.InputFile)
259 Attempt(() => System.Windows.Forms.Clipboard.SetImage(Image.FromFile(ClipboardSettings.InputFile.Filename)), "put an image on the clipboard");
260 else if (false == string.IsNullOrWhiteSpace(text))
261 Attempt(() => System.Windows.Forms.Clipboard.SetImage(ImageHelper.Base64ToImage(text)), "put an image on the clipboard");
262 else
263 GPAL.PublishSimpleEvent(GPALEventType.ERROR, "An image goes on the clipboard from a file or from base64. Use .WithInput with a GPALFile or with the encoded string", this, GPALObjectType.Other);
264
265 return;
266 }
267
268 if (ClipboardType.FileList == ClipboardSettings.ClipboardType)
269 {
270 var files = new StringCollection();
271 files.Add(ClipboardSettings.InputFile.Filename);
272 data.SetFileDropList(files);
273 }
274 else
275 data.SetData(FormatFor(ClipboardSettings.ClipboardType), text);
276
277 Attempt(() => System.Windows.Forms.Clipboard.SetDataObject(data, true, ATTEMPTS, BETWEEN_ATTEMPTS), "put data on the clipboard");
278 }
279
284 private string Read()
285 {
286 string retVal = null;
287
288 Attempt(() =>
289 {
290 if (ClipboardType.FileList == ClipboardSettings.ClipboardType)
291 {
292 StringCollection files = System.Windows.Forms.Clipboard.GetFileDropList();
293 var names = new List<string>();
294
295 foreach (string name in files)
296 names.Add(name);
297
298 retVal = 0 == names.Count ? null : string.Join(Environment.NewLine, names);
299 }
300 else
301 {
302 string format = FormatFor(ClipboardSettings.ClipboardType);
303
304 if (true == System.Windows.Forms.Clipboard.ContainsData(format))
305 retVal = System.Windows.Forms.Clipboard.GetData(format) as string;
306 }
307 }, "read the clipboard");
308
309 return retVal;
310 }
323 private string ReadImageAsBase64()
324 {
325 string retVal = null;
326
327 Attempt(() =>
328 {
329 Image image = System.Windows.Forms.Clipboard.GetImage();
330
331 if (null == image)
332 GPAL.PublishSimpleEvent(GPALEventType.WARNING, "The clipboard holds no image, so nothing was encoded", this, GPALObjectType.Other);
333 else
334 {
335 using (var stream = new MemoryStream())
336 {
337 image.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
338 retVal = Convert.ToBase64String(stream.ToArray());
339 }
340 }
341 }, "read an image from the clipboard");
342
343 return retVal;
344 }
345
346 private void WriteImageFile(GPALFile file)
347 {
348 Attempt(() =>
349 {
350 Image image = System.Windows.Forms.Clipboard.GetImage();
351
352 if (null == image)
353 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $"The clipboard holds no image, so [{file?.Filename}] was not written", this, GPALObjectType.Other);
354 else
355 image.Save(file.Filename, ImageHelper.FormatForFile(file.Filename));
356 }, "read an image from the clipboard");
357 }
358
364 private static string FormatFor(ClipboardType clipboardType)
365 {
366 string retVal;
367
368 switch (clipboardType)
369 {
370 case ClipboardType.Html:
371 retVal = System.Windows.Forms.DataFormats.Html;
372 break;
373 case ClipboardType.Rtf:
374 retVal = System.Windows.Forms.DataFormats.Rtf;
375 break;
376 case ClipboardType.Csv:
377 retVal = System.Windows.Forms.DataFormats.CommaSeparatedValue;
378 break;
379 default:
380 retVal = System.Windows.Forms.DataFormats.UnicodeText;
381 break;
382 }
383
384 return retVal;
385 }
386
392 private static void OnStaThread(Action work)
393 {
394 if (ApartmentState.STA == Thread.CurrentThread.GetApartmentState())
395 work();
396 else
397 {
398 Thread staThread = new Thread(() => work());
399
400 staThread.SetApartmentState(ApartmentState.STA);
401 staThread.Start();
402 staThread.Join();
403 }
404 }
405
413 private static void Attempt(Action work, string what)
414 {
415 Exception last = null;
416 bool done = false;
417
418 for (int attempt = 0; attempt < ATTEMPTS && false == done; attempt++)
419 {
420 try
421 {
422 work();
423 done = true;
424 }
425 catch (Exception ex)
426 {
427 last = ex;
428 Thread.Sleep(BETWEEN_ATTEMPTS);
429 }
430 }
431
432 if (false == done)
433 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $"Unable to {what} after [{ATTEMPTS}] attempts. Another process is holding it.", null, GPALObjectType.Other, last);
434 }
435 }
436}
IGPALClipboard SaveTo(GPALFile file)
Writes what is on the clipboard to a file.
IAllowClipboardSet WithInput(string text)
The text to put on the clipboard.
IGPALClipboard Clear()
Empties the clipboard.
IAllowClipboardSet WithInput(GPALFile file)
The file to put on the clipboard. Its text goes on for every type except FileList,...
IAllowClipboardSet WithInput(IGPALGrid< string > grid)
The grid to put on the clipboard. Rows become lines and cells are separated by tabs,...
IGPALClipboard SetClipboard(ClipboardType clipboardType=ClipboardType.Text)
Puts what WithInput named onto the clipboard.
IAllowClipboardInto GetClipboard(ClipboardType clipboardType=ClipboardType.Text)
Reads the clipboard. The type says which of the forms on it to take, and a clipboard usually holds se...
IGPALClipboard SaveTo(out IGPALGrid< string > grid)
What is on the clipboard, split into a grid. Lines become rows and tabs separate the cells,...
IGPALClipboard SaveTo(out string text)
What is on the clipboard, as text. An image comes back base64 encoded, since that is the only form of...
GPAL File object instantied with GPAL.File Used to load tokens into a GPALGrid [rows/columns].
Definition GPALFile.cs:36
string Filename
We have only one file, accessing it.
Definition GPALFile.cs:468
Everything starts here, all the GPAL controls and global settings using fluent syntax are here....
Definition GPAL.cs:49
static IAllowGridActions< string > Grid
New GPALGrid<string></string> (rows/columns).
Definition GPAL.cs:532
static void PublishSimpleEvent(GPALEventType gPALEventType, string msg, dynamic gPALObject=null, Enums.GPALObjectType gPALObjectType=GPALObjectType.None, Exception ex=null)
Publish a message to either the information channel or exception channel (if exception passed in) Pub...
Definition GPAL.cs:2568