Toasts – Blame information for rev 55
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
41 | office | 1 | using System; |
2 | using System.Collections.Concurrent; |
||
44 | office | 3 | using System.ComponentModel; |
53 | office | 4 | using System.Diagnostics; |
41 | office | 5 | using System.Drawing; |
55 | office | 6 | using System.Linq; |
44 | office | 7 | using System.Runtime.InteropServices; |
41 | office | 8 | using System.Threading; |
9 | using System.Threading.Tasks; |
||
10 | using System.Threading.Tasks.Dataflow; |
||
11 | using System.Windows.Forms; |
||
12 | using Markdig; |
||
13 | using Markdig.Renderers; |
||
55 | office | 14 | using Toasts.Utilities; |
15 | |||
41 | office | 16 | namespace Toasts |
17 | { |
||
44 | office | 18 | public class ToastDisplay : IDisposable |
19 | { |
||
20 | private readonly CancellationToken _cancellationToken; |
||
21 | private readonly BufferBlock<ToastDisplayData> _toastFormBufferBlock; |
||
22 | private ConcurrentBag<IDisposable> tplDataflowLinks; |
||
23 | private readonly TransformBlock<ToastDisplayData, ToastDisplayData> _toastBodyTransformBlock; |
||
24 | private readonly ActionBlock<ToastDisplayData> _toastFormActionBlock; |
||
55 | office | 25 | private readonly ConcurrentDictionary<IntPtr, ToastForm> _toastForms; |
44 | office | 26 | private ToastDisplay() |
27 | { |
||
55 | office | 28 | _toastForms = new ConcurrentDictionary<IntPtr, ToastForm>(); |
44 | office | 29 | tplDataflowLinks = new ConcurrentBag<IDisposable>(); |
30 | } |
||
31 | |||
32 | public ToastDisplay(CancellationToken cancellationToken) : this() |
||
33 | { |
||
34 | _cancellationToken = cancellationToken; |
||
35 | |||
36 | _toastFormBufferBlock = new BufferBlock<ToastDisplayData>(new DataflowBlockOptions { CancellationToken = _cancellationToken }); |
||
37 | _toastBodyTransformBlock = new TransformBlock<ToastDisplayData, ToastDisplayData>(ToastBodyToHTML, |
||
38 | new ExecutionDataflowBlockOptions { CancellationToken = _cancellationToken }); |
||
39 | _toastFormActionBlock = new ActionBlock<ToastDisplayData>(DisplayForm, new ExecutionDataflowBlockOptions { CancellationToken = _cancellationToken }); |
||
40 | |||
41 | tplDataflowLinks.Add(_toastFormBufferBlock.LinkTo(_toastBodyTransformBlock, new DataflowLinkOptions { PropagateCompletion = true })); |
||
42 | tplDataflowLinks.Add(_toastBodyTransformBlock.LinkTo(DataflowBlock.NullTarget<ToastDisplayData>(), new DataflowLinkOptions { PropagateCompletion = true }, result => result == null)); |
||
43 | tplDataflowLinks.Add(_toastBodyTransformBlock.LinkTo(_toastFormActionBlock, new DataflowLinkOptions { PropagateCompletion = true })); |
||
44 | } |
||
45 | |||
46 | private ToastDisplayData ToastBodyToHTML(ToastDisplayData toastDisplayData) |
||
47 | { |
||
48 | switch (toastDisplayData.Content) |
||
49 | { |
||
50 | case "text/markdown": |
||
46 | office | 51 | var pipeline = new MarkdownPipelineBuilder().UsePipeTables().Build(); |
52 | var html = Markdown.ToHtml(toastDisplayData.Body, pipeline); |
||
53 | toastDisplayData.Body = html; |
||
44 | office | 54 | break; |
55 | } |
||
56 | |||
57 | return toastDisplayData; |
||
58 | } |
||
59 | |||
60 | public void Dispose() |
||
61 | { |
||
62 | while (tplDataflowLinks.TryTake(out var disposable)) |
||
63 | { |
||
64 | disposable.Dispose(); |
||
65 | } |
||
66 | } |
||
67 | |||
55 | office | 68 | private void DisplayForm(ToastDisplayData toastPayload) |
44 | office | 69 | { |
70 | var thread = new Thread(() => |
||
71 | { |
||
72 | try |
||
73 | { |
||
74 | var toastForm = new ToastForm(toastPayload.Title, toastPayload.Body) |
||
75 | { |
||
55 | office | 76 | Proxy = toastPayload.Proxy, |
44 | office | 77 | EnableChime = toastPayload.EnableChime, |
78 | Chime = toastPayload.Chime ?? toastPayload.Chime, |
||
79 | LingerTime = toastPayload.LingerTime, |
||
80 | Image = toastPayload.Image, |
||
81 | ContentType = toastPayload.Content, |
||
82 | EnablePin = toastPayload.EnablePin, |
||
49 | office | 83 | PinPoint = toastPayload.PinPoint, |
84 | UserAgent = toastPayload.UserAgent |
||
44 | office | 85 | }; |
86 | |||
87 | var formClosingEvent = new ManualResetEvent(false); |
||
88 | void OnFormClosing(object sender, EventArgs args) |
||
89 | { |
||
90 | toastForm.FormClosing -= OnFormClosing; |
||
91 | formClosingEvent.Set(); |
||
92 | } |
||
93 | toastForm.FormClosing += OnFormClosing; |
||
94 | |||
55 | office | 95 | _toastForms.TryAdd(toastForm.Handle, toastForm); |
44 | office | 96 | Application.Run(toastForm); |
97 | |||
98 | var timeout = toastPayload.EnablePin ? -1 : toastPayload.LingerTime; |
||
99 | formClosingEvent.WaitOne(timeout); |
||
55 | office | 100 | _toastForms.TryRemove(toastForm.Handle, out _); |
101 | |||
44 | office | 102 | toastForm.FormClosing -= OnFormClosing; |
103 | toastForm.Close(); |
||
104 | if (!toastForm.IsDisposed) |
||
105 | { |
||
106 | toastForm.Dispose(); |
||
107 | } |
||
108 | toastForm = null; |
||
109 | formClosingEvent.Dispose(); |
||
110 | formClosingEvent = null; |
||
111 | } |
||
112 | catch (Win32Exception) |
||
113 | { |
||
114 | // ignore window creation errors for now |
||
115 | } |
||
116 | catch (COMException) |
||
117 | { |
||
118 | // ignore window creation errors for now |
||
119 | } |
||
51 | office | 120 | catch(InvalidOperationException) |
121 | { |
||
122 | // animation clutter |
||
123 | } |
||
53 | office | 124 | catch (ArgumentException) |
125 | { |
||
126 | // animation clutter |
||
127 | } |
||
128 | catch(Exception exception) |
||
129 | { |
||
130 | Debug.WriteLine(exception); |
||
131 | } |
||
44 | office | 132 | }) |
133 | { |
||
134 | IsBackground = true |
||
135 | }; |
||
136 | thread.SetApartmentState(ApartmentState.STA); |
||
137 | thread.Start(); |
||
138 | } |
||
139 | |||
140 | public async Task Queue(ToastDisplayData toastPayload) |
||
141 | { |
||
142 | await _toastFormBufferBlock.SendAsync(toastPayload, _cancellationToken); |
||
143 | } |
||
55 | office | 144 | |
145 | public void Clear() |
||
146 | { |
||
147 | foreach (var (handle, toastForm) in new ConcurrentDictionary<IntPtr, ToastForm>(_toastForms).Select(x => (x.Key, x.Value))) |
||
148 | { |
||
149 | _toastForms.TryRemove(handle, out _); |
||
150 | toastForm.InvokeIfRequired(form => |
||
151 | { |
||
152 | form.Close(); |
||
153 | }); |
||
154 | } |
||
155 | } |
||
41 | office | 156 | } |
27 | office | 157 | } |