/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Windows.Forms;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Threading;
using Task = System.Threading.Tasks.Task;
namespace QtVsTools.Core
{
using VisualStudio;
public static class Messages
{
private static OutputWindowPane Pane { get; set; }
private static readonly string PaneName = "Qt VS Tools";
private static readonly Guid PaneGuid = new Guid("8f6a1e44-fa0b-49e5-9934-1c050555350e");
///
/// Show a message on the output pane.
///
public static void Print(string text, bool clear = false, bool activate = false)
{
msgQueue.Enqueue(new Msg()
{
Clear = clear,
Text = text,
Activate = activate
});
FlushMessages();
}
public static void Log(this Exception exception, bool clear = false, bool activate = false)
{
msgQueue.Enqueue(new Msg()
{
Clear = clear,
Text = ExceptionToString(exception),
Activate = activate
});
FlushMessages();
}
///
/// Activates the message pane of the Qt VS Tools extension.
///
public static void ActivateMessagePane()
{
msgQueue.Enqueue(new Msg()
{
Activate = true
});
FlushMessages();
}
static async Task OutputWindowPane_ActivateAsync()
{
await OutputWindowPane_InitAsync();
await Pane?.ActivateAsync();
}
private static string ExceptionToString(System.Exception exception)
{
return $"An exception ({exception.GetType().Name}) occurred.\r\n"
+ $"Message:\r\n {exception.Message}\r\n"
+ $"Stack Trace:\r\n {exception.StackTrace.Trim()}\r\n";
}
private static readonly string ErrorString = SR.GetString("Messages_ErrorOccured");
private static readonly string WarningString = SR.GetString("Messages_Warning");
private static readonly string SolutionString = SR.GetString("Messages_SolveProblem");
public static void DisplayCriticalErrorMessage(string msg)
{
MessageBox.Show(ErrorString +
msg,
SR.GetString("Resources_QtVsTools"), MessageBoxButtons.OK, MessageBoxIcon.Error);
}
public static void DisplayErrorMessage(System.Exception e)
{
MessageBox.Show(ExceptionToString(e),
SR.GetString("Resources_QtVsTools"), MessageBoxButtons.OK, MessageBoxIcon.Error);
}
public static void DisplayErrorMessage(string msg)
{
MessageBox.Show(ErrorString +
msg,
SR.GetString("Resources_QtVsTools"), MessageBoxButtons.OK, MessageBoxIcon.Error);
}
public static void DisplayWarningMessage(System.Exception e, string solution)
{
MessageBox.Show(WarningString +
ExceptionToString(e) +
SolutionString +
solution,
SR.GetString("Resources_QtVsTools"), MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
public static void DisplayWarningMessage(string msg)
{
MessageBox.Show(WarningString +
msg,
SR.GetString("Resources_QtVsTools"), MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
public static void ClearPane()
{
msgQueue.Enqueue(new Msg()
{
Clear = true
});
FlushMessages();
}
static async Task OutputWindowPane_ClearAsync()
{
await OutputWindowPane_InitAsync();
await Pane?.ClearAsync();
}
class Msg
{
public bool Clear { get; set; } = false;
public string Text { get; set; } = null;
public bool Activate { get; set; } = false;
}
static readonly ConcurrentQueue msgQueue = new ConcurrentQueue();
private static async Task OutputWindowPane_InitAsync()
{
try {
if (Pane == null)
Pane = await OutputWindowPane.CreateAsync(PaneName, PaneGuid);
} catch (Exception ex) {
System.Diagnostics.Debug.WriteLine(ex);
}
}
public static JoinableTaskFactory JoinableTaskFactory { get; set; }
static readonly object staticCriticalSection = new object();
static Task FlushTask { get; set; }
static EventWaitHandle MessageReady { get; set; }
static void FlushMessages()
{
lock (staticCriticalSection) {
if (FlushTask == null) {
MessageReady = new EventWaitHandle(false, EventResetMode.AutoReset);
FlushTask = Task.Run(async () =>
{
var package = VsServiceProvider.Instance as Package;
while (!package.Zombied) {
if (!await MessageReady.ToTask(3000))
continue;
while (!msgQueue.IsEmpty) {
if (!msgQueue.TryDequeue(out Msg msg)) {
await Task.Yield();
continue;
}
if (msg.Clear)
await OutputWindowPane_ClearAsync();
if (msg.Text != null)
await OutputWindowPane_PrintAsync(msg.Text);
if (msg.Activate)
await OutputWindowPane_ActivateAsync();
}
}
});
}
}
MessageReady.Set();
}
static async Task OutputWindowPane_PrintAsync(string text)
{
var active = await OutputWindowPane.GetActiveAsync();
await OutputWindowPane_InitAsync();
await Pane.PrintAsync(text);
(active?.ActivateAsync()).Forget();
}
}
}