mirror of
https://github.com/greenshot/greenshot
synced 2025-07-16 10:03:44 -07:00
FEATURE-731: Backport of the title detection code
This commit is contained in:
parent
4a0ec2448f
commit
1d0bdf23c1
17 changed files with 1080 additions and 447 deletions
|
@ -65,7 +65,7 @@ namespace GreenshotJiraPlugin.Forms {
|
||||||
{
|
{
|
||||||
if (!_jiraConnector.IsLoggedIn)
|
if (!_jiraConnector.IsLoggedIn)
|
||||||
{
|
{
|
||||||
await _jiraConnector.Login();
|
await _jiraConnector.LoginAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|
|
@ -78,12 +78,19 @@
|
||||||
<Compile Include="Forms\SettingsForm.Designer.cs">
|
<Compile Include="Forms\SettingsForm.Designer.cs">
|
||||||
<DependentUpon>SettingsForm.cs</DependentUpon>
|
<DependentUpon>SettingsForm.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Hooking\TitleChangeEventArgs.cs" />
|
||||||
|
<Compile Include="Hooking\TitleChangeEventDelegate.cs" />
|
||||||
|
<Compile Include="Hooking\WindowsEventHook.cs" />
|
||||||
|
<Compile Include="Hooking\WindowsTitleMonitor.cs" />
|
||||||
<Compile Include="IssueTypeBitmapCache.cs" />
|
<Compile Include="IssueTypeBitmapCache.cs" />
|
||||||
<Compile Include="JiraConnector.cs" />
|
<Compile Include="JiraConnector.cs" />
|
||||||
<Compile Include="JiraConfiguration.cs" />
|
<Compile Include="JiraConfiguration.cs" />
|
||||||
<Compile Include="JiraDestination.cs" />
|
<Compile Include="JiraDestination.cs" />
|
||||||
|
<Compile Include="JiraDetails.cs" />
|
||||||
|
<Compile Include="JiraEventArgs.cs" />
|
||||||
|
<Compile Include="JiraEventTypes.cs" />
|
||||||
|
<Compile Include="JiraMonitor.cs" />
|
||||||
<Compile Include="JiraPlugin.cs" />
|
<Compile Include="JiraPlugin.cs" />
|
||||||
<Compile Include="JiraUtils.cs" />
|
|
||||||
<Compile Include="LanguageKeys.cs" />
|
<Compile Include="LanguageKeys.cs" />
|
||||||
<Compile Include="Log4NetLogger.cs" />
|
<Compile Include="Log4NetLogger.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
|
50
GreenshotJiraPlugin/Hooking/TitleChangeEventArgs.cs
Normal file
50
GreenshotJiraPlugin/Hooking/TitleChangeEventArgs.cs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* dapplo - building blocks for desktop applications
|
||||||
|
* Copyright (C) Dapplo 2015-2016
|
||||||
|
*
|
||||||
|
* For more information see: http://dapplo.net/
|
||||||
|
* dapplo repositories are hosted on GitHub: https://github.com/dapplo
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 1 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace GreenshotJiraPlugin.Hooking
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Event arguments for the TitleChangeEvent
|
||||||
|
/// </summary>
|
||||||
|
public class TitleChangeEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// HWnd of the window which has a changed title
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr HWnd
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Title which is changed
|
||||||
|
/// </summary>
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
29
GreenshotJiraPlugin/Hooking/TitleChangeEventDelegate.cs
Normal file
29
GreenshotJiraPlugin/Hooking/TitleChangeEventDelegate.cs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* dapplo - building blocks for desktop applications
|
||||||
|
* Copyright (C) Dapplo 2015-2016
|
||||||
|
*
|
||||||
|
* For more information see: http://dapplo.net/
|
||||||
|
* dapplo repositories are hosted on GitHub: https://github.com/dapplo
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 1 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace GreenshotJiraPlugin.Hooking
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Delegate for the title change event
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventArgs"></param>
|
||||||
|
public delegate void TitleChangeEventDelegate(TitleChangeEventArgs eventArgs);
|
||||||
|
}
|
152
GreenshotJiraPlugin/Hooking/WindowsEventHook.cs
Normal file
152
GreenshotJiraPlugin/Hooking/WindowsEventHook.cs
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
/*
|
||||||
|
* dapplo - building blocks for desktop applications
|
||||||
|
* Copyright (C) Dapplo 2015-2016
|
||||||
|
*
|
||||||
|
* For more information see: http://dapplo.net/
|
||||||
|
* dapplo repositories are hosted on GitHub: https://github.com/dapplo
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 1 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using GreenshotPlugin.UnmanagedHelpers;
|
||||||
|
|
||||||
|
namespace GreenshotJiraPlugin.Hooking
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The WinEventHook can register handlers to become important windows events
|
||||||
|
/// This makes it possible to know a.o. when a window is created, moved, updated and closed.
|
||||||
|
/// </summary>
|
||||||
|
public class WindowsEventHook : IDisposable
|
||||||
|
{
|
||||||
|
private readonly WinEventDelegate _winEventHandler;
|
||||||
|
private GCHandle _gcHandle;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used with Register hook
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventType"></param>
|
||||||
|
/// <param name="hwnd"></param>
|
||||||
|
/// <param name="idObject"></param>
|
||||||
|
/// <param name="idChild"></param>
|
||||||
|
/// <param name="dwEventThread"></param>
|
||||||
|
/// <param name="dwmsEventTime"></param>
|
||||||
|
public delegate void WinEventHandler(WinEvent eventType, IntPtr hwnd, EventObjects idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a WindowsEventHook object
|
||||||
|
/// </summary>
|
||||||
|
public WindowsEventHook()
|
||||||
|
{
|
||||||
|
_winEventHandler = WinEventDelegateHandler;
|
||||||
|
_gcHandle = GCHandle.Alloc(_winEventHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region native code
|
||||||
|
[DllImport("user32", SetLastError = true)]
|
||||||
|
private static extern bool UnhookWinEvent(IntPtr hWinEventHook);
|
||||||
|
[DllImport("user32", SetLastError = true)]
|
||||||
|
private static extern IntPtr SetWinEventHook(WinEvent eventMin, WinEvent eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, int idProcess, int idThread, WinEventHookFlags dwFlags);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used with SetWinEventHook
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hWinEventHook"></param>
|
||||||
|
/// <param name="eventType"></param>
|
||||||
|
/// <param name="hwnd"></param>
|
||||||
|
/// <param name="idObject"></param>
|
||||||
|
/// <param name="idChild"></param>
|
||||||
|
/// <param name="dwEventThread"></param>
|
||||||
|
/// <param name="dwmsEventTime"></param>
|
||||||
|
private delegate void WinEventDelegate(IntPtr hWinEventHook, WinEvent eventType, IntPtr hwnd, EventObjects idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private readonly IDictionary<IntPtr, WinEventHandler> _winEventHandlers = new Dictionary<IntPtr, WinEventHandler>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Are hooks active?
|
||||||
|
/// </summary>
|
||||||
|
public bool IsHooked => _winEventHandlers.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hook a WinEvent
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="winEvent"></param>
|
||||||
|
/// <param name="winEventHandler"></param>
|
||||||
|
/// <returns>true if success</returns>
|
||||||
|
public void Hook(WinEvent winEvent, WinEventHandler winEventHandler)
|
||||||
|
{
|
||||||
|
Hook(winEvent, winEvent, winEventHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hook a WinEvent
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="winEventStart"></param>
|
||||||
|
/// <param name="winEventEnd"></param>
|
||||||
|
/// <param name="winEventHandler"></param>
|
||||||
|
public void Hook(WinEvent winEventStart, WinEvent winEventEnd, WinEventHandler winEventHandler)
|
||||||
|
{
|
||||||
|
var hookPtr = SetWinEventHook(winEventStart, winEventEnd, IntPtr.Zero, _winEventHandler, 0, 0, WinEventHookFlags.WINEVENT_SKIPOWNPROCESS | WinEventHookFlags.WINEVENT_OUTOFCONTEXT);
|
||||||
|
_winEventHandlers.Add(hookPtr, winEventHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove all hooks
|
||||||
|
/// </summary>
|
||||||
|
private void Unhook()
|
||||||
|
{
|
||||||
|
foreach (var hookPtr in _winEventHandlers.Keys)
|
||||||
|
{
|
||||||
|
if (hookPtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
UnhookWinEvent(hookPtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_winEventHandlers.Clear();
|
||||||
|
_gcHandle.Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Unhook();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Call the WinEventHandler for this event
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hWinEventHook"></param>
|
||||||
|
/// <param name="eventType"></param>
|
||||||
|
/// <param name="hWnd"></param>
|
||||||
|
/// <param name="idObject"></param>
|
||||||
|
/// <param name="idChild"></param>
|
||||||
|
/// <param name="dwEventThread"></param>
|
||||||
|
/// <param name="dwmsEventTime"></param>
|
||||||
|
private void WinEventDelegateHandler(IntPtr hWinEventHook, WinEvent eventType, IntPtr hWnd, EventObjects idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
|
||||||
|
{
|
||||||
|
WinEventHandler handler;
|
||||||
|
if (_winEventHandlers.TryGetValue(hWinEventHook, out handler))
|
||||||
|
{
|
||||||
|
handler(eventType, hWnd, idObject, idChild, dwEventThread, dwmsEventTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
139
GreenshotJiraPlugin/Hooking/WindowsTitleMonitor.cs
Normal file
139
GreenshotJiraPlugin/Hooking/WindowsTitleMonitor.cs
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
/*
|
||||||
|
* dapplo - building blocks for desktop applications
|
||||||
|
* Copyright (C) Dapplo 2015-2016
|
||||||
|
*
|
||||||
|
* For more information see: http://dapplo.net/
|
||||||
|
* dapplo repositories are hosted on GitHub: https://github.com/dapplo
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 1 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using GreenshotPlugin.Core;
|
||||||
|
using GreenshotPlugin.UnmanagedHelpers;
|
||||||
|
|
||||||
|
namespace GreenshotJiraPlugin.Hooking
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Monitor all title changes
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WindowsTitleMonitor : IDisposable
|
||||||
|
{
|
||||||
|
private WindowsEventHook _hook;
|
||||||
|
private readonly object _lockObject = new object();
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
private event TitleChangeEventDelegate _titleChangeEvent;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add / remove event handler to the title monitor
|
||||||
|
/// </summary>
|
||||||
|
public event TitleChangeEventDelegate TitleChangeEvent
|
||||||
|
{
|
||||||
|
add
|
||||||
|
{
|
||||||
|
lock (_lockObject)
|
||||||
|
{
|
||||||
|
if (_hook == null)
|
||||||
|
{
|
||||||
|
_hook = new WindowsEventHook();
|
||||||
|
_hook.Hook(WinEvent.EVENT_OBJECT_NAMECHANGE, WinEventHandler);
|
||||||
|
}
|
||||||
|
_titleChangeEvent += value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
remove
|
||||||
|
{
|
||||||
|
lock (_lockObject)
|
||||||
|
{
|
||||||
|
_titleChangeEvent -= value;
|
||||||
|
if (_titleChangeEvent == null || _titleChangeEvent.GetInvocationList().Length == 0)
|
||||||
|
{
|
||||||
|
if (_hook != null)
|
||||||
|
{
|
||||||
|
_hook.Dispose();
|
||||||
|
_hook = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// WinEventDelegate for the creation & destruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventType"></param>
|
||||||
|
/// <param name="hWnd"></param>
|
||||||
|
/// <param name="idObject"></param>
|
||||||
|
/// <param name="idChild"></param>
|
||||||
|
/// <param name="dwEventThread"></param>
|
||||||
|
/// <param name="dwmsEventTime"></param>
|
||||||
|
private void WinEventHandler(WinEvent eventType, IntPtr hWnd, EventObjects idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
|
||||||
|
{
|
||||||
|
if (hWnd == IntPtr.Zero || idObject != EventObjects.OBJID_WINDOW)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (eventType == WinEvent.EVENT_OBJECT_NAMECHANGE)
|
||||||
|
{
|
||||||
|
if (_titleChangeEvent != null)
|
||||||
|
{
|
||||||
|
string newTitle = new WindowDetails(hWnd).Text;
|
||||||
|
_titleChangeEvent(new TitleChangeEventArgs { HWnd = hWnd, Title = newTitle });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IDisposable Support
|
||||||
|
|
||||||
|
private bool _disposedValue; // To detect redundant calls
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose the underlying hook
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_disposedValue)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lock (_lockObject)
|
||||||
|
{
|
||||||
|
_hook?.Dispose();
|
||||||
|
}
|
||||||
|
_disposedValue = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Make sure the finalizer disposes the underlying hook
|
||||||
|
/// </summary>
|
||||||
|
~WindowsTitleMonitor()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose the underlying hook
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -47,12 +47,13 @@ namespace GreenshotJiraPlugin {
|
||||||
private DateTimeOffset _loggedInTime = DateTimeOffset.MinValue;
|
private DateTimeOffset _loggedInTime = DateTimeOffset.MinValue;
|
||||||
private bool _loggedIn;
|
private bool _loggedIn;
|
||||||
private readonly int _timeout;
|
private readonly int _timeout;
|
||||||
private readonly object _lock = new object();
|
|
||||||
private string _url;
|
|
||||||
private JiraApi _jiraApi;
|
private JiraApi _jiraApi;
|
||||||
private IssueTypeBitmapCache _issueTypeBitmapCache;
|
private IssueTypeBitmapCache _issueTypeBitmapCache;
|
||||||
private static readonly SvgBitmapHttpContentConverter SvgBitmapHttpContentConverterInstance = new SvgBitmapHttpContentConverter();
|
private static readonly SvgBitmapHttpContentConverter SvgBitmapHttpContentConverterInstance = new SvgBitmapHttpContentConverter();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize some basic stuff, in the case the SVG to bitmap converter
|
||||||
|
/// </summary>
|
||||||
static JiraConnector()
|
static JiraConnector()
|
||||||
{
|
{
|
||||||
if (HttpExtensionsGlobals.HttpContentConverters.All(x => x.GetType() != typeof(SvgBitmapHttpContentConverter)))
|
if (HttpExtensionsGlobals.HttpContentConverters.All(x => x.GetType() != typeof(SvgBitmapHttpContentConverter)))
|
||||||
|
@ -72,46 +73,48 @@ namespace GreenshotJiraPlugin {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose, logout the users
|
||||||
|
/// </summary>
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
if (_jiraApi != null)
|
if (_jiraApi != null)
|
||||||
{
|
{
|
||||||
Task.Run(async () => await Logout()).Wait();
|
Task.Run(async () => await LogoutAsync()).Wait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
public JiraConnector()
|
public JiraConnector()
|
||||||
{
|
{
|
||||||
_url = JiraConfig.Url.Replace(DefaultPostfix, "");
|
JiraConfig.Url = JiraConfig.Url.Replace(DefaultPostfix, "");
|
||||||
_timeout = JiraConfig.Timeout;
|
_timeout = JiraConfig.Timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Access the jira monitor
|
||||||
|
/// </summary>
|
||||||
|
public JiraMonitor Monitor { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Internal login which catches the exceptions
|
/// Internal login which catches the exceptions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>true if login was done sucessfully</returns>
|
/// <returns>true if login was done sucessfully</returns>
|
||||||
private async Task<bool> DoLogin(string user, string password)
|
private async Task<bool> DoLoginAsync(string user, string password)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
if (string.IsNullOrEmpty(user) || string.IsNullOrEmpty(password))
|
||||||
{
|
{
|
||||||
if (_url.EndsWith("wsdl"))
|
return false;
|
||||||
{
|
|
||||||
_url = _url.Replace(DefaultPostfix, "");
|
|
||||||
}
|
}
|
||||||
if (_jiraApi == null)
|
_jiraApi = new JiraApi(new Uri(JiraConfig.Url));
|
||||||
{
|
|
||||||
// recreate the service with the new url
|
|
||||||
_jiraApi = new JiraApi(new Uri(_url));
|
|
||||||
_issueTypeBitmapCache = new IssueTypeBitmapCache(_jiraApi);
|
_issueTypeBitmapCache = new IssueTypeBitmapCache(_jiraApi);
|
||||||
}
|
Monitor = new JiraMonitor();
|
||||||
}
|
await Monitor.AddJiraInstanceAsync(_jiraApi);
|
||||||
|
|
||||||
LoginInfo loginInfo;
|
LoginInfo loginInfo;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
loginInfo = await _jiraApi.StartSessionAsync(user, password);
|
loginInfo = await _jiraApi.StartSessionAsync(user, password);
|
||||||
// Worked, store the url in the configuration
|
|
||||||
JiraConfig.Url = _url;
|
|
||||||
IniConfig.Save();
|
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
|
@ -120,17 +123,21 @@ namespace GreenshotJiraPlugin {
|
||||||
return loginInfo != null;
|
return loginInfo != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Login() {
|
/// <summary>
|
||||||
await Logout();
|
/// Use the credentials dialog, this will show if there are not correct credentials.
|
||||||
|
/// If there are credentials, call the real login.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Task</returns>
|
||||||
|
public async Task LoginAsync() {
|
||||||
|
await LogoutAsync();
|
||||||
try {
|
try {
|
||||||
// Get the system name, so the user knows where to login to
|
// Get the system name, so the user knows where to login to
|
||||||
string systemName = _url.Replace(DefaultPostfix,"");
|
var credentialsDialog = new CredentialsDialog(JiraConfig.Url)
|
||||||
var credentialsDialog = new CredentialsDialog(systemName)
|
|
||||||
{
|
{
|
||||||
Name = null
|
Name = null
|
||||||
};
|
};
|
||||||
while (credentialsDialog.Show(credentialsDialog.Name) == DialogResult.OK) {
|
while (credentialsDialog.Show(credentialsDialog.Name) == DialogResult.OK) {
|
||||||
if (await DoLogin(credentialsDialog.Name, credentialsDialog.Password)) {
|
if (await DoLoginAsync(credentialsDialog.Name, credentialsDialog.Password)) {
|
||||||
if (credentialsDialog.SaveChecked) {
|
if (credentialsDialog.SaveChecked) {
|
||||||
credentialsDialog.Confirm(true);
|
credentialsDialog.Confirm(true);
|
||||||
}
|
}
|
||||||
|
@ -159,9 +166,10 @@ namespace GreenshotJiraPlugin {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// End the session, if there was one
|
/// End the session, if there was one
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task Logout() {
|
public async Task LogoutAsync() {
|
||||||
if (_jiraApi != null && _loggedIn)
|
if (_jiraApi != null && _loggedIn)
|
||||||
{
|
{
|
||||||
|
Monitor.Dispose();
|
||||||
await _jiraApi.EndSessionAsync();
|
await _jiraApi.EndSessionAsync();
|
||||||
_loggedIn = false;
|
_loggedIn = false;
|
||||||
}
|
}
|
||||||
|
@ -172,14 +180,14 @@ namespace GreenshotJiraPlugin {
|
||||||
/// Do not use ConfigureAwait to call this, as it will move await from the UI thread.
|
/// Do not use ConfigureAwait to call this, as it will move await from the UI thread.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private async Task CheckCredentials() {
|
private async Task CheckCredentialsAsync() {
|
||||||
if (_loggedIn) {
|
if (_loggedIn) {
|
||||||
if (_loggedInTime.AddMinutes(_timeout-1).CompareTo(DateTime.Now) < 0) {
|
if (_loggedInTime.AddMinutes(_timeout-1).CompareTo(DateTime.Now) < 0) {
|
||||||
await Logout();
|
await LogoutAsync();
|
||||||
await Login();
|
await LoginAsync();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await Login();
|
await LoginAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +197,7 @@ namespace GreenshotJiraPlugin {
|
||||||
/// <returns>List with filters</returns>
|
/// <returns>List with filters</returns>
|
||||||
public async Task<IList<Filter>> GetFavoriteFiltersAsync()
|
public async Task<IList<Filter>> GetFavoriteFiltersAsync()
|
||||||
{
|
{
|
||||||
await CheckCredentials();
|
await CheckCredentialsAsync();
|
||||||
return await _jiraApi.GetFavoriteFiltersAsync().ConfigureAwait(false);
|
return await _jiraApi.GetFavoriteFiltersAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +208,7 @@ namespace GreenshotJiraPlugin {
|
||||||
/// <returns>Issue</returns>
|
/// <returns>Issue</returns>
|
||||||
public async Task<Issue> GetIssueAsync(string issueKey)
|
public async Task<Issue> GetIssueAsync(string issueKey)
|
||||||
{
|
{
|
||||||
await CheckCredentials();
|
await CheckCredentialsAsync();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return await _jiraApi.GetIssueAsync(issueKey).ConfigureAwait(false);
|
return await _jiraApi.GetIssueAsync(issueKey).ConfigureAwait(false);
|
||||||
|
@ -220,7 +228,7 @@ namespace GreenshotJiraPlugin {
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task AttachAsync(string issueKey, IBinaryContainer content, CancellationToken cancellationToken = default(CancellationToken))
|
public async Task AttachAsync(string issueKey, IBinaryContainer content, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
await CheckCredentials();
|
await CheckCredentialsAsync();
|
||||||
using (var memoryStream = new MemoryStream())
|
using (var memoryStream = new MemoryStream())
|
||||||
{
|
{
|
||||||
content.WriteToStream(memoryStream);
|
content.WriteToStream(memoryStream);
|
||||||
|
@ -238,7 +246,7 @@ namespace GreenshotJiraPlugin {
|
||||||
/// <param name="cancellationToken">CancellationToken</param>
|
/// <param name="cancellationToken">CancellationToken</param>
|
||||||
public async Task AddCommentAsync(string issueKey, string body, string visibility = null, CancellationToken cancellationToken = default(CancellationToken))
|
public async Task AddCommentAsync(string issueKey, string body, string visibility = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
await CheckCredentials();
|
await CheckCredentialsAsync();
|
||||||
await _jiraApi.AddCommentAsync(issueKey, body, visibility, cancellationToken).ConfigureAwait(false);
|
await _jiraApi.AddCommentAsync(issueKey, body, visibility, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,7 +258,7 @@ namespace GreenshotJiraPlugin {
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<IList<Issue>> SearchAsync(Filter filter, CancellationToken cancellationToken = default(CancellationToken))
|
public async Task<IList<Issue>> SearchAsync(Filter filter, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
await CheckCredentials();
|
await CheckCredentialsAsync();
|
||||||
var searchResult = await _jiraApi.SearchAsync(filter.Jql, 20, new[] { "summary", "reporter", "assignee", "created", "issuetype" }, cancellationToken).ConfigureAwait(false);
|
var searchResult = await _jiraApi.SearchAsync(filter.Jql, 20, new[] { "summary", "reporter", "assignee", "created", "issuetype" }, cancellationToken).ConfigureAwait(false);
|
||||||
return searchResult.Issues;
|
return searchResult.Issues;
|
||||||
}
|
}
|
||||||
|
@ -266,8 +274,14 @@ namespace GreenshotJiraPlugin {
|
||||||
return await _issueTypeBitmapCache.GetOrCreateAsync(issue.Fields.IssueType, cancellationToken).ConfigureAwait(false);
|
return await _issueTypeBitmapCache.GetOrCreateAsync(issue.Fields.IssueType, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the base uri
|
||||||
|
/// </summary>
|
||||||
public Uri JiraBaseUri => _jiraApi.JiraBaseUri;
|
public Uri JiraBaseUri => _jiraApi.JiraBaseUri;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is the user "logged in?
|
||||||
|
/// </summary>
|
||||||
public bool IsLoggedIn => _loggedIn;
|
public bool IsLoggedIn => _loggedIn;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -61,7 +61,7 @@ namespace GreenshotJiraPlugin {
|
||||||
return Language.GetString("jira", LangKey.upload_menu_item);
|
return Language.GetString("jira", LangKey.upload_menu_item);
|
||||||
}
|
}
|
||||||
// Format the title of this destination
|
// Format the title of this destination
|
||||||
return Designation + " - " + _jiraIssue.Key + ": " + _jiraIssue.Fields.Summary.Substring(0, Math.Min(20, _jiraIssue.Fields.Summary.Length));
|
return _jiraIssue.Key + ": " + _jiraIssue.Fields.Summary.Substring(0, Math.Min(20, _jiraIssue.Fields.Summary.Length));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,15 +86,15 @@ namespace GreenshotJiraPlugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<IDestination> DynamicDestinations() {
|
public override IEnumerable<IDestination> DynamicDestinations()
|
||||||
if (JiraPlugin.Instance.CurrentJiraConnector == null || !JiraPlugin.Instance.CurrentJiraConnector.IsLoggedIn) {
|
{
|
||||||
|
var jiraConnector = JiraPlugin.Instance.CurrentJiraConnector;
|
||||||
|
if (jiraConnector == null || !jiraConnector.IsLoggedIn) {
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
var issues = JiraUtils.GetCurrentJirasAsync().Result;
|
foreach(var jiraDetails in jiraConnector.Monitor.RecentJiras)
|
||||||
if (issues != null) {
|
{
|
||||||
foreach(var jiraIssue in issues) {
|
yield return new JiraDestination(_jiraPlugin,jiraDetails.JiraIssue);
|
||||||
yield return new JiraDestination(_jiraPlugin, jiraIssue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
71
GreenshotJiraPlugin/JiraDetails.cs
Normal file
71
GreenshotJiraPlugin/JiraDetails.cs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* Greenshot - a free and open source screenshot tool
|
||||||
|
* Copyright (C) 2007-2016 Thomas Braun, Jens Klingen, Robin Krom
|
||||||
|
*
|
||||||
|
* For more information see: http://getgreenshot.org/
|
||||||
|
* The Greenshot project is hosted on GitHub: https://github.com/greenshot
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 1 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Dapplo.Jira.Entities;
|
||||||
|
|
||||||
|
namespace GreenshotJiraPlugin
|
||||||
|
{
|
||||||
|
public class JiraDetails : IComparable<JiraDetails>
|
||||||
|
{
|
||||||
|
public JiraDetails()
|
||||||
|
{
|
||||||
|
FirstSeenAt = SeenAt = DateTimeOffset.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ProjectKey
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Id
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string JiraKey => ProjectKey + "-" + Id;
|
||||||
|
|
||||||
|
public Issue JiraIssue
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTimeOffset FirstSeenAt
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTimeOffset SeenAt
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CompareTo(JiraDetails other)
|
||||||
|
{
|
||||||
|
return SeenAt.CompareTo(other.SeenAt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
GreenshotJiraPlugin/JiraEventArgs.cs
Normal file
40
GreenshotJiraPlugin/JiraEventArgs.cs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Greenshot - a free and open source screenshot tool
|
||||||
|
* Copyright (C) 2007-2016 Thomas Braun, Jens Klingen, Robin Krom
|
||||||
|
*
|
||||||
|
* For more information see: http://getgreenshot.org/
|
||||||
|
* The Greenshot project is hosted on GitHub: https://github.com/greenshot
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 1 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace GreenshotJiraPlugin
|
||||||
|
{
|
||||||
|
public class JiraEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public JiraEventTypes EventType
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JiraDetails Details
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
GreenshotJiraPlugin/JiraEventTypes.cs
Normal file
29
GreenshotJiraPlugin/JiraEventTypes.cs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Greenshot - a free and open source screenshot tool
|
||||||
|
* Copyright (C) 2007-2016 Thomas Braun, Jens Klingen, Robin Krom
|
||||||
|
*
|
||||||
|
* For more information see: http://getgreenshot.org/
|
||||||
|
* The Greenshot project is hosted on GitHub: https://github.com/greenshot
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 1 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace GreenshotJiraPlugin
|
||||||
|
{
|
||||||
|
public enum JiraEventTypes
|
||||||
|
{
|
||||||
|
OrderChanged,
|
||||||
|
DetectedNewJiraIssue
|
||||||
|
}
|
||||||
|
}
|
225
GreenshotJiraPlugin/JiraMonitor.cs
Normal file
225
GreenshotJiraPlugin/JiraMonitor.cs
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
/*
|
||||||
|
* Greenshot - a free and open source screenshot tool
|
||||||
|
* Copyright (C) 2007-2016 Thomas Braun, Jens Klingen, Robin Krom
|
||||||
|
*
|
||||||
|
* For more information see: http://getgreenshot.org/
|
||||||
|
* The Greenshot project is hosted on GitHub: https://github.com/greenshot
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 1 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Dapplo.Jira;
|
||||||
|
using Dapplo.Log.Facade;
|
||||||
|
using GreenshotJiraPlugin.Hooking;
|
||||||
|
|
||||||
|
namespace GreenshotJiraPlugin
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This class will monitor all _jira activity by registering for title changes
|
||||||
|
/// It keeps a list of the last "accessed" jiras, and makes it easy to upload to one.
|
||||||
|
/// Make sure this is instanciated on the UI thread!
|
||||||
|
/// </summary>
|
||||||
|
public class JiraMonitor : IDisposable
|
||||||
|
{
|
||||||
|
private static readonly LogSource Log = new LogSource();
|
||||||
|
private readonly Regex _jiraKeyPattern = new Regex(@"[A-Z][A-Z0-9]+\-[0-9]+");
|
||||||
|
private readonly WindowsTitleMonitor _monitor;
|
||||||
|
private readonly IList<JiraApi> _jiraInstances = new List<JiraApi>();
|
||||||
|
private readonly IDictionary<string, JiraApi> _projectJiraApiMap = new Dictionary<string, JiraApi>();
|
||||||
|
private readonly int _maxEntries;
|
||||||
|
private IDictionary<string, JiraDetails> _recentJiras = new Dictionary<string, JiraDetails>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register to this event to get events when new jira issues are detected
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<JiraEventArgs> JiraEvent;
|
||||||
|
|
||||||
|
public JiraMonitor(int maxEntries = 40)
|
||||||
|
{
|
||||||
|
_maxEntries = maxEntries;
|
||||||
|
_monitor = new WindowsTitleMonitor();
|
||||||
|
_monitor.TitleChangeEvent += MonitorTitleChangeEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Dispose
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose all managed resources
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">when true is passed all managed resources are disposed.</param>
|
||||||
|
protected void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!disposing)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// free managed resources
|
||||||
|
_monitor.TitleChangeEvent -= MonitorTitleChangeEvent;
|
||||||
|
_monitor.Dispose();
|
||||||
|
// free native resources if there are any.
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve the API belonging to a JiraDetails
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jiraDetails"></param>
|
||||||
|
/// <returns>JiraAPI</returns>
|
||||||
|
public JiraApi GetJiraApiForKey(JiraDetails jiraDetails)
|
||||||
|
{
|
||||||
|
return _projectJiraApiMap[jiraDetails.ProjectKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the "list" of recently seen Jiras
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<JiraDetails> RecentJiras =>
|
||||||
|
(from jiraDetails in _recentJiras.Values
|
||||||
|
orderby jiraDetails.SeenAt descending
|
||||||
|
select jiraDetails);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if this monitor has active instances
|
||||||
|
/// </summary>
|
||||||
|
public bool HasJiraInstances => _jiraInstances.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add an instance of a JIRA system
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jiraInstance"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
public async Task AddJiraInstanceAsync(JiraApi jiraInstance, CancellationToken token = default(CancellationToken))
|
||||||
|
{
|
||||||
|
_jiraInstances.Add(jiraInstance);
|
||||||
|
var projects = await jiraInstance.GetProjectsAsync(token).ConfigureAwait(false);
|
||||||
|
if (projects != null)
|
||||||
|
{
|
||||||
|
foreach (var project in projects)
|
||||||
|
{
|
||||||
|
if (!_projectJiraApiMap.ContainsKey(project.Key))
|
||||||
|
{
|
||||||
|
_projectJiraApiMap.Add(project.Key, jiraInstance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method will update details, like the title, and send an event to registed listeners of the JiraEvent
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jiraDetails">Contains the jira key to retrieve the title (XYZ-1234)</param>
|
||||||
|
/// <returns>Task</returns>
|
||||||
|
private async Task DetectedNewJiraIssueAsync(JiraDetails jiraDetails)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JiraApi jiraApi;
|
||||||
|
if (_projectJiraApiMap.TryGetValue(jiraDetails.ProjectKey, out jiraApi))
|
||||||
|
{
|
||||||
|
var issue = await jiraApi.GetIssueAsync(jiraDetails.JiraKey).ConfigureAwait(false);
|
||||||
|
jiraDetails.JiraIssue = issue;
|
||||||
|
}
|
||||||
|
// Send event
|
||||||
|
JiraEvent?.Invoke(this, new JiraEventArgs { Details = jiraDetails, EventType = JiraEventTypes.DetectedNewJiraIssue });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Warn().WriteLine("Couldn't retrieve JIRA title: {0}", ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle title changes, check for JIRA
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventArgs"></param>
|
||||||
|
private void MonitorTitleChangeEvent(TitleChangeEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
string windowTitle = eventArgs.Title;
|
||||||
|
if (string.IsNullOrEmpty(windowTitle))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var jiraKeyMatch = _jiraKeyPattern.Match(windowTitle);
|
||||||
|
if (!jiraKeyMatch.Success)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Found a possible JIRA title
|
||||||
|
var jiraKey = jiraKeyMatch.Value;
|
||||||
|
var jiraKeyParts = jiraKey.Split('-');
|
||||||
|
var projectKey = jiraKeyParts[0];
|
||||||
|
var jiraId = jiraKeyParts[1];
|
||||||
|
|
||||||
|
JiraApi jiraApi;
|
||||||
|
// Check if we have a JIRA instance with a project for this key
|
||||||
|
if (_projectJiraApiMap.TryGetValue(projectKey, out jiraApi))
|
||||||
|
{
|
||||||
|
// We have found a project for this _jira key, so it must be a valid & known JIRA
|
||||||
|
JiraDetails currentJiraDetails;
|
||||||
|
if (_recentJiras.TryGetValue(jiraKey, out currentJiraDetails))
|
||||||
|
{
|
||||||
|
// update
|
||||||
|
currentJiraDetails.SeenAt = DateTimeOffset.Now;
|
||||||
|
|
||||||
|
// Notify the order change
|
||||||
|
JiraEvent?.Invoke(this, new JiraEventArgs { Details = currentJiraDetails, EventType = JiraEventTypes.OrderChanged });
|
||||||
|
// Nothing else to do
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We detected an unknown JIRA, so add it to our list
|
||||||
|
currentJiraDetails = new JiraDetails()
|
||||||
|
{
|
||||||
|
Id = jiraId,
|
||||||
|
ProjectKey = projectKey
|
||||||
|
};
|
||||||
|
_recentJiras.Add(currentJiraDetails.JiraKey, currentJiraDetails);
|
||||||
|
|
||||||
|
// Make sure we don't collect _jira's until the memory is full
|
||||||
|
if (_recentJiras.Count > _maxEntries)
|
||||||
|
{
|
||||||
|
// Add it to the list of recent Jiras
|
||||||
|
IList<JiraDetails> clonedList = new List<JiraDetails>(_recentJiras.Values);
|
||||||
|
_recentJiras = (from jiraDetails in clonedList
|
||||||
|
orderby jiraDetails.SeenAt descending
|
||||||
|
select jiraDetails).Take(_maxEntries).ToDictionary(jd => jd.JiraKey, jd => jd);
|
||||||
|
}
|
||||||
|
// Now we can get the title from JIRA itself
|
||||||
|
// ReSharper disable once UnusedVariable
|
||||||
|
var updateTitleTask = DetectedNewJiraIssueAsync(currentJiraDetails);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Info().WriteLine("Couldn't match possible JIRA key {0} to projects in a configured JIRA instance, ignoring", projectKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,9 +33,9 @@ namespace GreenshotJiraPlugin {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class JiraPlugin : IGreenshotPlugin {
|
public class JiraPlugin : IGreenshotPlugin {
|
||||||
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(JiraPlugin));
|
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(JiraPlugin));
|
||||||
private JiraConnector _jiraConnector;
|
|
||||||
private JiraConfiguration _config;
|
private JiraConfiguration _config;
|
||||||
private static JiraPlugin _instance;
|
private static JiraPlugin _instance;
|
||||||
|
private JiraConnector _jiraConnector;
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
Dispose(true);
|
Dispose(true);
|
||||||
|
@ -44,9 +44,9 @@ namespace GreenshotJiraPlugin {
|
||||||
|
|
||||||
protected void Dispose(bool disposing) {
|
protected void Dispose(bool disposing) {
|
||||||
if (disposing) {
|
if (disposing) {
|
||||||
if (_jiraConnector != null) {
|
if (JiraConnector != null) {
|
||||||
_jiraConnector.Dispose();
|
JiraConnector.Dispose();
|
||||||
_jiraConnector = null;
|
JiraConnector = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,9 +66,23 @@ namespace GreenshotJiraPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
//Needed for a fail-fast
|
//Needed for a fail-fast
|
||||||
public JiraConnector CurrentJiraConnector => _jiraConnector;
|
public JiraConnector CurrentJiraConnector => JiraConnector;
|
||||||
|
|
||||||
public JiraConnector JiraConnector => _jiraConnector ?? (_jiraConnector = new JiraConnector());
|
public JiraConnector JiraConnector
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_instance)
|
||||||
|
{
|
||||||
|
if (_jiraConnector == null)
|
||||||
|
{
|
||||||
|
JiraConnector = new JiraConnector();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _jiraConnector;
|
||||||
|
}
|
||||||
|
private set { _jiraConnector = value; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implementation of the IGreenshotPlugin.Initialize
|
/// Implementation of the IGreenshotPlugin.Initialize
|
||||||
|
@ -80,14 +94,15 @@ namespace GreenshotJiraPlugin {
|
||||||
// Register configuration (don't need the configuration itself)
|
// Register configuration (don't need the configuration itself)
|
||||||
_config = IniConfig.GetIniSection<JiraConfiguration>();
|
_config = IniConfig.GetIniSection<JiraConfiguration>();
|
||||||
LogSettings.RegisterDefaultLogger<Log4NetLogger>();
|
LogSettings.RegisterDefaultLogger<Log4NetLogger>();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Shutdown() {
|
public void Shutdown() {
|
||||||
Log.Debug("Jira Plugin shutdown.");
|
Log.Debug("Jira Plugin shutdown.");
|
||||||
if (_jiraConnector != null)
|
if (JiraConnector != null)
|
||||||
{
|
{
|
||||||
Task.Run(async () => await _jiraConnector.Logout());
|
Task.Run(async () => await JiraConnector.LogoutAsync());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,12 +113,12 @@ namespace GreenshotJiraPlugin {
|
||||||
string url = _config.Url;
|
string url = _config.Url;
|
||||||
if (ShowConfigDialog()) {
|
if (ShowConfigDialog()) {
|
||||||
// check for re-login
|
// check for re-login
|
||||||
if (_jiraConnector != null && _jiraConnector.IsLoggedIn && !string.IsNullOrEmpty(url)) {
|
if (JiraConnector != null && JiraConnector.IsLoggedIn && !string.IsNullOrEmpty(url)) {
|
||||||
if (!url.Equals(_config.Url)) {
|
if (!url.Equals(_config.Url)) {
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await _jiraConnector.Logout();
|
await JiraConnector.LogoutAsync();
|
||||||
await _jiraConnector.Login();
|
await JiraConnector.LoginAsync();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
/*
|
|
||||||
* Greenshot - a free and open source screenshot tool
|
|
||||||
* Copyright (C) 2007-2016 Thomas Braun, Jens Klingen, Robin Krom
|
|
||||||
*
|
|
||||||
* For more information see: http://getgreenshot.org/
|
|
||||||
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 1 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Dapplo.Jira.Entities;
|
|
||||||
using Greenshot.IniFile;
|
|
||||||
using GreenshotPlugin.Core;
|
|
||||||
|
|
||||||
namespace GreenshotJiraPlugin {
|
|
||||||
/// <summary>
|
|
||||||
/// Description of JiraUtils.
|
|
||||||
/// </summary>
|
|
||||||
public static class JiraUtils {
|
|
||||||
private static readonly Regex JiraKeyRegex = new Regex(@"/browse/([A-Z0-9]+\-[0-9]+)");
|
|
||||||
private static readonly JiraConfiguration Config = IniConfig.GetIniSection<JiraConfiguration>();
|
|
||||||
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(JiraUtils));
|
|
||||||
|
|
||||||
public static async Task<IList<Issue>> GetCurrentJirasAsync() {
|
|
||||||
// Make sure we suppress the login
|
|
||||||
var jirakeys = new List<string>();
|
|
||||||
foreach(string url in IEHelper.GetIEUrls()) {
|
|
||||||
if (url == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var jiraKeyMatch = JiraKeyRegex.Matches(url);
|
|
||||||
if (jiraKeyMatch.Count > 0) {
|
|
||||||
string jiraKey = jiraKeyMatch[0].Groups[1].Value;
|
|
||||||
jirakeys.Add(jiraKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!string.IsNullOrEmpty(Config.LastUsedJira) && !jirakeys.Contains(Config.LastUsedJira)) {
|
|
||||||
jirakeys.Add(Config.LastUsedJira);
|
|
||||||
}
|
|
||||||
if (jirakeys.Count > 0) {
|
|
||||||
var jiraIssues = new List<Issue>();
|
|
||||||
foreach(string jiraKey in jirakeys) {
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var issue = await JiraPlugin.Instance.JiraConnector.GetIssueAsync(jiraKey).ConfigureAwait(false);
|
|
||||||
if (issue != null)
|
|
||||||
{
|
|
||||||
jiraIssues.Add(issue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Error(ex);
|
|
||||||
// Remove issue from the last used jira config, as it caused an issue (probably not there)
|
|
||||||
if (Config.LastUsedJira == jiraKey)
|
|
||||||
{
|
|
||||||
Config.LastUsedJira = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (jiraIssues.Count > 0) {
|
|
||||||
return jiraIssues;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -102,65 +102,25 @@ namespace GreenshotPlugin.Core {
|
||||||
Banner = banner;
|
Banner = banner;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _alwaysDisplay;
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets if the dialog will be shown even if the credentials
|
/// Gets or sets if the dialog will be shown even if the credentials
|
||||||
/// can be returned from an existing credential in the credential manager.
|
/// can be returned from an existing credential in the credential manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AlwaysDisplay {
|
public bool AlwaysDisplay { get; set; }
|
||||||
get {
|
|
||||||
return _alwaysDisplay;
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
_alwaysDisplay = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _excludeCertificates = true;
|
|
||||||
/// <summary>Gets or sets if the dialog is populated with name/password only.</summary>
|
/// <summary>Gets or sets if the dialog is populated with name/password only.</summary>
|
||||||
public bool ExcludeCertificates {
|
public bool ExcludeCertificates { get; set; } = true;
|
||||||
get {
|
|
||||||
return _excludeCertificates;
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
_excludeCertificates = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _persist = true;
|
|
||||||
/// <summary>Gets or sets if the credentials are to be persisted in the credential manager.</summary>
|
/// <summary>Gets or sets if the credentials are to be persisted in the credential manager.</summary>
|
||||||
public bool Persist {
|
public bool Persist { get; set; } = true;
|
||||||
get {
|
|
||||||
return _persist;
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
_persist = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _incorrectPassword;
|
|
||||||
/// <summary>Gets or sets if the incorrect password balloontip needs to be shown. Introduced AFTER Windows XP</summary>Gets></summary>
|
/// <summary>Gets or sets if the incorrect password balloontip needs to be shown. Introduced AFTER Windows XP</summary>Gets></summary>
|
||||||
public bool IncorrectPassword {
|
public bool IncorrectPassword { get; set; }
|
||||||
get {
|
|
||||||
return _incorrectPassword;
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
_incorrectPassword = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _keepName;
|
|
||||||
/// <summary>Gets or sets if the name is read-only.</summary>
|
/// <summary>Gets or sets if the name is read-only.</summary>
|
||||||
public bool KeepName {
|
public bool KeepName { get; set; }
|
||||||
get {
|
|
||||||
return _keepName;
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
_keepName = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string _name = String.Empty;
|
private string _name = string.Empty;
|
||||||
/// <summary>Gets or sets the name for the credentials.</summary>
|
/// <summary>Gets or sets the name for the credentials.</summary>
|
||||||
public string Name {
|
public string Name {
|
||||||
get {
|
get {
|
||||||
|
@ -169,18 +129,18 @@ namespace GreenshotPlugin.Core {
|
||||||
set {
|
set {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
if (value.Length > CREDUI.MAX_USERNAME_LENGTH) {
|
if (value.Length > CREDUI.MAX_USERNAME_LENGTH) {
|
||||||
string message = String.Format(
|
string message = string.Format(
|
||||||
Thread.CurrentThread.CurrentUICulture,
|
Thread.CurrentThread.CurrentUICulture,
|
||||||
"The name has a maximum length of {0} characters.",
|
"The name has a maximum length of {0} characters.",
|
||||||
CREDUI.MAX_USERNAME_LENGTH);
|
CREDUI.MAX_USERNAME_LENGTH);
|
||||||
throw new ArgumentException(message, "Name");
|
throw new ArgumentException(message, nameof(Name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_name = value;
|
_name = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string _password = String.Empty;
|
private string _password = string.Empty;
|
||||||
/// <summary>Gets or sets the password for the credentials.</summary>
|
/// <summary>Gets or sets the password for the credentials.</summary>
|
||||||
public string Password {
|
public string Password {
|
||||||
get {
|
get {
|
||||||
|
@ -189,41 +149,25 @@ namespace GreenshotPlugin.Core {
|
||||||
set {
|
set {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
if (value.Length > CREDUI.MAX_PASSWORD_LENGTH) {
|
if (value.Length > CREDUI.MAX_PASSWORD_LENGTH) {
|
||||||
string message = String.Format(
|
string message = string.Format(
|
||||||
Thread.CurrentThread.CurrentUICulture,
|
Thread.CurrentThread.CurrentUICulture,
|
||||||
"The password has a maximum length of {0} characters.",
|
"The password has a maximum length of {0} characters.",
|
||||||
CREDUI.MAX_PASSWORD_LENGTH);
|
CREDUI.MAX_PASSWORD_LENGTH);
|
||||||
throw new ArgumentException(message, "Password");
|
throw new ArgumentException(message, nameof(Password));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_password = value;
|
_password = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _saveChecked;
|
|
||||||
/// <summary>Gets or sets if the save checkbox status.</summary>
|
/// <summary>Gets or sets if the save checkbox status.</summary>
|
||||||
public bool SaveChecked {
|
public bool SaveChecked { get; set; }
|
||||||
get {
|
|
||||||
return _saveChecked;
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
_saveChecked = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _saveDisplayed = true;
|
|
||||||
/// <summary>Gets or sets if the save checkbox is displayed.</summary>
|
/// <summary>Gets or sets if the save checkbox is displayed.</summary>
|
||||||
/// <remarks>This value only has effect if Persist is true.</remarks>
|
/// <remarks>This value only has effect if Persist is true.</remarks>
|
||||||
public bool SaveDisplayed {
|
public bool SaveDisplayed { get; set; } = true;
|
||||||
get {
|
|
||||||
return _saveDisplayed;
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
_saveDisplayed = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string _target = String.Empty;
|
private string _target = string.Empty;
|
||||||
/// <summary>Gets or sets the name of the target for the credentials, typically a server name.</summary>
|
/// <summary>Gets or sets the name of the target for the credentials, typically a server name.</summary>
|
||||||
public string Target {
|
public string Target {
|
||||||
get {
|
get {
|
||||||
|
@ -232,18 +176,19 @@ namespace GreenshotPlugin.Core {
|
||||||
set {
|
set {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
throw new ArgumentException("The target cannot be a null value.", "Target");
|
throw new ArgumentException("The target cannot be a null value.", "Target");
|
||||||
} else if (value.Length > CREDUI.MAX_GENERIC_TARGET_LENGTH) {
|
}
|
||||||
string message = String.Format(
|
if (value.Length > CREDUI.MAX_GENERIC_TARGET_LENGTH) {
|
||||||
|
string message = string.Format(
|
||||||
Thread.CurrentThread.CurrentUICulture,
|
Thread.CurrentThread.CurrentUICulture,
|
||||||
"The target has a maximum length of {0} characters.",
|
"The target has a maximum length of {0} characters.",
|
||||||
CREDUI.MAX_GENERIC_TARGET_LENGTH);
|
CREDUI.MAX_GENERIC_TARGET_LENGTH);
|
||||||
throw new ArgumentException(message, "Target");
|
throw new ArgumentException(message, nameof(Target));
|
||||||
}
|
}
|
||||||
_target = value;
|
_target = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string _caption = String.Empty;
|
private string _caption = string.Empty;
|
||||||
/// <summary>Gets or sets the caption of the dialog.</summary>
|
/// <summary>Gets or sets the caption of the dialog.</summary>
|
||||||
/// <remarks>A null value will cause a system default caption to be used.</remarks>
|
/// <remarks>A null value will cause a system default caption to be used.</remarks>
|
||||||
public string Caption {
|
public string Caption {
|
||||||
|
@ -253,18 +198,18 @@ namespace GreenshotPlugin.Core {
|
||||||
set {
|
set {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
if (value.Length > CREDUI.MAX_CAPTION_LENGTH) {
|
if (value.Length > CREDUI.MAX_CAPTION_LENGTH) {
|
||||||
string message = String.Format(
|
string message = string.Format(
|
||||||
Thread.CurrentThread.CurrentUICulture,
|
Thread.CurrentThread.CurrentUICulture,
|
||||||
"The caption has a maximum length of {0} characters.",
|
"The caption has a maximum length of {0} characters.",
|
||||||
CREDUI.MAX_CAPTION_LENGTH);
|
CREDUI.MAX_CAPTION_LENGTH);
|
||||||
throw new ArgumentException(message, "Caption");
|
throw new ArgumentException(message, nameof(Caption));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_caption = value;
|
_caption = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string _message = String.Empty;
|
private string _message = string.Empty;
|
||||||
/// <summary>Gets or sets the message of the dialog.</summary>
|
/// <summary>Gets or sets the message of the dialog.</summary>
|
||||||
/// <remarks>A null value will cause a system default message to be used.</remarks>
|
/// <remarks>A null value will cause a system default message to be used.</remarks>
|
||||||
public string Message {
|
public string Message {
|
||||||
|
@ -274,11 +219,11 @@ namespace GreenshotPlugin.Core {
|
||||||
set {
|
set {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
if (value.Length > CREDUI.MAX_MESSAGE_LENGTH) {
|
if (value.Length > CREDUI.MAX_MESSAGE_LENGTH) {
|
||||||
string message = String.Format(
|
string message = string.Format(
|
||||||
Thread.CurrentThread.CurrentUICulture,
|
Thread.CurrentThread.CurrentUICulture,
|
||||||
"The message has a maximum length of {0} characters.",
|
"The message has a maximum length of {0} characters.",
|
||||||
CREDUI.MAX_MESSAGE_LENGTH);
|
CREDUI.MAX_MESSAGE_LENGTH);
|
||||||
throw new ArgumentException(message, "Message");
|
throw new ArgumentException(message, nameof(Message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_message = value;
|
_message = value;
|
||||||
|
@ -295,10 +240,10 @@ namespace GreenshotPlugin.Core {
|
||||||
set {
|
set {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
if (value.Width != ValidBannerWidth) {
|
if (value.Width != ValidBannerWidth) {
|
||||||
throw new ArgumentException("The banner image width must be 320 pixels.", "Banner");
|
throw new ArgumentException("The banner image width must be 320 pixels.", nameof(Banner));
|
||||||
}
|
}
|
||||||
if (value.Height != ValidBannerHeight) {
|
if (value.Height != ValidBannerHeight) {
|
||||||
throw new ArgumentException("The banner image height must be 60 pixels.", "Banner");
|
throw new ArgumentException("The banner image height must be 60 pixels.", nameof(Banner));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_banner = value;
|
_banner = value;
|
||||||
|
|
|
@ -1179,7 +1179,7 @@ namespace GreenshotPlugin.UnmanagedHelpers {
|
||||||
/// Used for User32.SetWinEventHook
|
/// Used for User32.SetWinEventHook
|
||||||
/// See: http://msdn.microsoft.com/en-us/library/windows/desktop/dd373640%28v=vs.85%29.aspx
|
/// See: http://msdn.microsoft.com/en-us/library/windows/desktop/dd373640%28v=vs.85%29.aspx
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming"), Flags]
|
||||||
public enum WinEventHookFlags
|
public enum WinEventHookFlags
|
||||||
{
|
{
|
||||||
WINEVENT_SKIPOWNTHREAD = 1,
|
WINEVENT_SKIPOWNTHREAD = 1,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue