Toasts – Rev 53

Subversion Repositories:
Rev:
using System;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;
using Markdig;
using Markdig.Renderers;

namespace Toasts
{
    public class ToastDisplay : IDisposable
    {
        private readonly CancellationToken _cancellationToken;
        private readonly BufferBlock<ToastDisplayData> _toastFormBufferBlock;
        private ConcurrentBag<IDisposable> tplDataflowLinks;
        private readonly TransformBlock<ToastDisplayData, ToastDisplayData> _toastBodyTransformBlock;
        private readonly ActionBlock<ToastDisplayData> _toastFormActionBlock;

        private ToastDisplay()
        {
            tplDataflowLinks = new ConcurrentBag<IDisposable>();
        }

        public ToastDisplay(CancellationToken cancellationToken) : this()
        {
            _cancellationToken = cancellationToken;

            _toastFormBufferBlock = new BufferBlock<ToastDisplayData>(new DataflowBlockOptions { CancellationToken = _cancellationToken });
            _toastBodyTransformBlock = new TransformBlock<ToastDisplayData, ToastDisplayData>(ToastBodyToHTML,
                new ExecutionDataflowBlockOptions { CancellationToken = _cancellationToken });
            _toastFormActionBlock = new ActionBlock<ToastDisplayData>(DisplayForm, new ExecutionDataflowBlockOptions { CancellationToken = _cancellationToken });

            tplDataflowLinks.Add(_toastFormBufferBlock.LinkTo(_toastBodyTransformBlock, new DataflowLinkOptions { PropagateCompletion = true }));
            tplDataflowLinks.Add(_toastBodyTransformBlock.LinkTo(DataflowBlock.NullTarget<ToastDisplayData>(), new DataflowLinkOptions { PropagateCompletion = true }, result => result == null));
            tplDataflowLinks.Add(_toastBodyTransformBlock.LinkTo(_toastFormActionBlock, new DataflowLinkOptions { PropagateCompletion = true }));
        }

        private ToastDisplayData ToastBodyToHTML(ToastDisplayData toastDisplayData)
        {
            switch (toastDisplayData.Content)
            {
                case "text/markdown":
                    var pipeline = new MarkdownPipelineBuilder().UsePipeTables().Build();
                    var html = Markdown.ToHtml(toastDisplayData.Body, pipeline);
                    toastDisplayData.Body = html;
                    break;
            }

            return toastDisplayData;
        }

        public void Dispose()
        {
            while (tplDataflowLinks.TryTake(out var disposable))
            {
                disposable.Dispose();
            }
        }

        private static void DisplayForm(ToastDisplayData toastPayload)
        {
            var thread = new Thread(() =>
            {
                try
                {
                    var toastForm = new ToastForm(toastPayload.Title, toastPayload.Body)
                    {
                        EnableChime = toastPayload.EnableChime,
                        Chime = toastPayload.Chime ?? toastPayload.Chime,
                        LingerTime = toastPayload.LingerTime,
                        Image = toastPayload.Image,
                        ContentType = toastPayload.Content,
                        EnablePin = toastPayload.EnablePin,
                        PinPoint = toastPayload.PinPoint,
                        UserAgent = toastPayload.UserAgent
                    };

                    var formClosingEvent = new ManualResetEvent(false);
                    void OnFormClosing(object sender, EventArgs args)
                    {
                        toastForm.FormClosing -= OnFormClosing;
                        formClosingEvent.Set();
                    }
                    toastForm.FormClosing += OnFormClosing;

                    Application.Run(toastForm);

                    var timeout = toastPayload.EnablePin ? -1 : toastPayload.LingerTime;
                    formClosingEvent.WaitOne(timeout);
                    toastForm.FormClosing -= OnFormClosing;
                    toastForm.Close();
                    if (!toastForm.IsDisposed)
                    {
                        toastForm.Dispose();
                    }
                    toastForm = null;
                    formClosingEvent.Dispose();
                    formClosingEvent = null;
                }
                catch (Win32Exception)
                {
                    // ignore window creation errors for now
                }
                catch (COMException)
                {
                    // ignore window creation errors for now
                }
                catch(InvalidOperationException)
                {
                    // animation clutter
                }
                catch (ArgumentException)
                {
                    // animation clutter
                }
                catch(Exception exception)
                {
                    Debug.WriteLine(exception);
                }
            })
            {
                IsBackground = true
            };
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
        }

        public async Task Queue(ToastDisplayData toastPayload)
        {
            await _toastFormBufferBlock.SendAsync(toastPayload, _cancellationToken);
        }
    }
}

Generated by GNU Enscript 1.6.5.90.