Toasts – Blame information for rev 55

Subversion Repositories:
Rev:
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 }