mirror of
https://github.com/greenshot/greenshot
synced 2025-08-21 05:53:27 -07:00
Moving back to trunk!
git-svn-id: http://svn.code.sf.net/p/greenshot/code/trunk@1602 7dccd23d-a4a3-4e1f-8c07-b4c1b4018ab4
This commit is contained in:
parent
ad265b2c54
commit
8d458998a1
332 changed files with 17647 additions and 9466 deletions
180
GreenshotPlugin/Core/AbstractDestination.cs
Normal file
180
GreenshotPlugin/Core/AbstractDestination.cs
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
* For more information see: http://getgreenshot.org/
|
||||
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using System.Reflection;
|
||||
|
||||
using Microsoft.Win32;
|
||||
|
||||
using Greenshot.Plugin;
|
||||
|
||||
namespace GreenshotPlugin.Core {
|
||||
/// <summary>
|
||||
/// Description of AbstractDestination.
|
||||
/// </summary>
|
||||
public abstract class AbstractDestination : IDestination {
|
||||
private const string PATH_KEY = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\";
|
||||
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(AbstractDestination));
|
||||
|
||||
public static string GetExePath(string exeName) {
|
||||
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(PATH_KEY + exeName, false)) {
|
||||
if (key != null) {
|
||||
// "" is the default key, which should point to the outlook location
|
||||
return (string)key.GetValue("");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internaly used to create an icon
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
public static Bitmap GetExeIcon(string path) {
|
||||
if (!File.Exists(path)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
using (Icon appIcon = Icon.ExtractAssociatedIcon(path)) {
|
||||
if (appIcon != null) {
|
||||
return appIcon.ToBitmap();
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual int CompareTo(object obj) {
|
||||
IDestination other = obj as IDestination;
|
||||
if (other == null) {
|
||||
return 1;
|
||||
}
|
||||
if (Priority == other.Priority) {
|
||||
return Description.CompareTo(other.Description);
|
||||
}
|
||||
return Priority - other.Priority;
|
||||
}
|
||||
|
||||
public abstract string Designation {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract string Description {
|
||||
get;
|
||||
}
|
||||
|
||||
public virtual int Priority {
|
||||
get {
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Image DisplayIcon {
|
||||
get {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Keys EditorShortcutKeys {
|
||||
get {
|
||||
return Keys.None;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a menu item
|
||||
/// </summary>
|
||||
/// <param name="destinationClickHandler"></param>
|
||||
/// <returns>ToolStripMenuItem</returns>
|
||||
public virtual ToolStripMenuItem GetMenuItem(EventHandler destinationClickHandler) {
|
||||
ToolStripMenuItem basisMenuItem;
|
||||
basisMenuItem = new ToolStripMenuItem(Description);
|
||||
basisMenuItem.Image = DisplayIcon;
|
||||
basisMenuItem.Tag = this;
|
||||
basisMenuItem.Text = Description;
|
||||
if (isDynamic) {
|
||||
basisMenuItem.DropDownOpening += delegate (object source, EventArgs eventArgs) {
|
||||
if (basisMenuItem.DropDownItems.Count == 0) {
|
||||
List<IDestination> subDestinations = new List<IDestination>();
|
||||
subDestinations.AddRange(DynamicDestinations());
|
||||
if (subDestinations.Count > 0) {
|
||||
subDestinations.Sort();
|
||||
ToolStripMenuItem destinationMenuItem = new ToolStripMenuItem(Description);
|
||||
destinationMenuItem.Tag = this;
|
||||
destinationMenuItem.Image = DisplayIcon;
|
||||
destinationMenuItem.Click += destinationClickHandler;
|
||||
basisMenuItem.DropDownItems.Add(destinationMenuItem);
|
||||
foreach(IDestination subDestination in subDestinations) {
|
||||
destinationMenuItem = new ToolStripMenuItem(subDestination.Description);
|
||||
destinationMenuItem.Tag = subDestination;
|
||||
destinationMenuItem.Image = subDestination.DisplayIcon;
|
||||
destinationMenuItem.Click += destinationClickHandler;
|
||||
basisMenuItem.DropDownItems.Add(destinationMenuItem);
|
||||
}
|
||||
} else {
|
||||
// Setting base "click" only if there are no sub-destinations
|
||||
|
||||
// Make sure any previous handler is removed, otherwise it would be added multiple times!
|
||||
basisMenuItem.Click -= destinationClickHandler;
|
||||
basisMenuItem.Click += destinationClickHandler;
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
basisMenuItem.Click += destinationClickHandler;
|
||||
}
|
||||
|
||||
return basisMenuItem;
|
||||
}
|
||||
|
||||
public virtual IEnumerable<IDestination> DynamicDestinations() {
|
||||
yield break;
|
||||
}
|
||||
|
||||
public virtual void Dispose() {
|
||||
}
|
||||
|
||||
public virtual bool isDynamic {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool isActive {
|
||||
get {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract bool ExportCapture(ISurface surface, ICaptureDetails captureDetails);
|
||||
|
||||
public override string ToString() {
|
||||
return Description;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
75
GreenshotPlugin/Core/AbstractProcessor.cs
Normal file
75
GreenshotPlugin/Core/AbstractProcessor.cs
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
* For more information see: http://getgreenshot.org/
|
||||
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using System.Reflection;
|
||||
|
||||
using Microsoft.Win32;
|
||||
|
||||
using Greenshot.Plugin;
|
||||
|
||||
namespace GreenshotPlugin.Core {
|
||||
/// <summary>
|
||||
/// Description of AbstractProcessor.
|
||||
/// </summary>
|
||||
public abstract class AbstractProcessor : IProcessor {
|
||||
|
||||
public virtual int CompareTo(object obj) {
|
||||
IProcessor other = obj as IProcessor;
|
||||
if (other == null) {
|
||||
return 1;
|
||||
}
|
||||
if (Priority == other.Priority) {
|
||||
return Description.CompareTo(other.Description);
|
||||
}
|
||||
return Priority - other.Priority;
|
||||
}
|
||||
|
||||
public abstract string Designation {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract string Description {
|
||||
get;
|
||||
}
|
||||
|
||||
public virtual int Priority {
|
||||
get {
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Dispose() {
|
||||
}
|
||||
|
||||
public virtual bool isActive {
|
||||
get {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract bool ProcessCapture(ISurface surface, ICaptureDetails captureDetails);
|
||||
}
|
||||
}
|
288
GreenshotPlugin/Core/AccessibleHelper.cs
Normal file
288
GreenshotPlugin/Core/AccessibleHelper.cs
Normal file
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
* For more information see: http://getgreenshot.org/
|
||||
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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.Runtime.InteropServices;
|
||||
|
||||
using Accessibility;
|
||||
|
||||
namespace GreenshotPlugin.Core {
|
||||
|
||||
/// <summary>
|
||||
/// See: http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/03a8c835-e9e4-405b-8345-6c3d36bc8941
|
||||
/// This should really be cleaned up, there is little OO behind this class!
|
||||
/// Maybe move the basic Accessible functions to WindowDetails!?
|
||||
/// </summary>
|
||||
public class Accessible {
|
||||
private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(Accessible));
|
||||
|
||||
#region Interop
|
||||
private static int AccessibleObjectFromWindow(IntPtr hWnd, OBJID idObject, ref IAccessible acc) {
|
||||
Guid guid = new Guid("{618736e0-3c3d-11cf-810c-00aa00389b71}"); // IAccessible
|
||||
object obj = null;
|
||||
int num = AccessibleObjectFromWindow(hWnd, (uint)idObject, ref guid, ref obj);
|
||||
acc = (IAccessible)obj;
|
||||
return num;
|
||||
}
|
||||
[DllImport("oleacc.dll")]
|
||||
public static extern int AccessibleObjectFromWindow(IntPtr hwnd, uint id, ref Guid iid, [In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object ppvObject);
|
||||
[DllImport("oleacc.dll")]
|
||||
public static extern int AccessibleChildren(IAccessible paccContainer, int iChildStart, int cChildren, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] object[] rgvarChildren, out int pcObtained);
|
||||
|
||||
[DllImport("oleacc.dll", PreserveSig=false)]
|
||||
[return: MarshalAs(UnmanagedType.Interface)]
|
||||
public static extern object ObjectFromLresult(UIntPtr lResult, [MarshalAs(UnmanagedType.LPStruct)] Guid refiid, IntPtr wParam);
|
||||
#endregion
|
||||
|
||||
private enum OBJID : uint {
|
||||
OBJID_WINDOW = 0x00000000,
|
||||
}
|
||||
|
||||
private const int IE_ACTIVE_TAB = 2097154;
|
||||
private const int CHILDID_SELF = 0;
|
||||
private IAccessible accessible;
|
||||
private Accessible[] Children {
|
||||
get {
|
||||
int num = 0;
|
||||
object[] res = GetAccessibleChildren(accessible, out num);
|
||||
if (res == null) {
|
||||
return new Accessible[0];
|
||||
}
|
||||
|
||||
List<Accessible> list = new List<Accessible>(res.Length);
|
||||
foreach (object obj in res) {
|
||||
IAccessible acc = obj as IAccessible;
|
||||
if (acc != null) {
|
||||
list.Add(new Accessible(acc));
|
||||
}
|
||||
}
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private string Name {
|
||||
get {
|
||||
return accessible.get_accName(CHILDID_SELF);
|
||||
}
|
||||
}
|
||||
|
||||
private int ChildCount {
|
||||
get {
|
||||
return accessible.accChildCount;
|
||||
}
|
||||
}
|
||||
|
||||
public Accessible(IntPtr hWnd) {
|
||||
AccessibleObjectFromWindow(hWnd, OBJID.OBJID_WINDOW, ref accessible);
|
||||
if (accessible == null) {
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
|
||||
public void ActivateIETab(string tabCaptionToActivate) {
|
||||
foreach (Accessible accessor in Children) {
|
||||
foreach (var child in accessor.Children) {
|
||||
foreach (var tab in child.Children) {
|
||||
if (tab.Name == tabCaptionToActivate) {
|
||||
tab.Activate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CloseIETab(string tabCaptionToClose) {
|
||||
foreach (Accessible accessor in Children) {
|
||||
foreach (var child in accessor.Children) {
|
||||
foreach (var tab in child.Children) {
|
||||
if (tab.Name == tabCaptionToClose) {
|
||||
foreach (var CloseTab in tab.Children) {
|
||||
CloseTab.Activate();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ActivateIETab(int tabIndexToActivate) {
|
||||
var index = 0;
|
||||
foreach (Accessible accessor in Children) {
|
||||
foreach (var child in accessor.Children) {
|
||||
foreach (var tab in child.Children) {
|
||||
if (tabIndexToActivate >= child.ChildCount -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (index == tabIndexToActivate) {
|
||||
tab.Activate();
|
||||
return;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string IEActiveTabUrl {
|
||||
get {
|
||||
foreach (Accessible accessor in Children) {
|
||||
foreach (var child in accessor.Children) {
|
||||
foreach (var tab in child.Children) {
|
||||
object tabIndex = tab.accessible.get_accState(CHILDID_SELF);
|
||||
|
||||
if ((int)tabIndex == IE_ACTIVE_TAB) {
|
||||
var description = tab.accessible.get_accDescription(CHILDID_SELF);
|
||||
|
||||
if (!string.IsNullOrEmpty(description)) {
|
||||
if (description.Contains(Environment.NewLine)) {
|
||||
var url = description.Substring(description.IndexOf(Environment.NewLine)).Trim();
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return String.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public int IEActiveTabIndex {
|
||||
get {
|
||||
var index = 0;
|
||||
foreach (Accessible accessor in Children) {
|
||||
foreach (var child in accessor.Children) {
|
||||
foreach (var tab in child.Children) {
|
||||
object tabIndex = tab.accessible.get_accState(0);
|
||||
|
||||
if ((int)tabIndex == IE_ACTIVE_TAB) {
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public string IEActiveTabCaption {
|
||||
get {
|
||||
foreach (Accessible accessor in Children) {
|
||||
foreach (var child in accessor.Children) {
|
||||
foreach (var tab in child.Children) {
|
||||
object tabIndex = tab.accessible.get_accState(0);
|
||||
|
||||
if ((int)tabIndex == IE_ACTIVE_TAB) {
|
||||
return tab.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return String.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> IETabCaptions {
|
||||
get {
|
||||
var captionList = new List<string>();
|
||||
|
||||
foreach (Accessible accessor in Children) {
|
||||
foreach (var child in accessor.Children) {
|
||||
foreach (var tab in child.Children) {
|
||||
captionList.Add(tab.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (captionList.Count > 0) {
|
||||
captionList.RemoveAt(captionList.Count - 1);
|
||||
}
|
||||
|
||||
return captionList;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public List<string> IETabUrls {
|
||||
get {
|
||||
var urlList = new List<string>();
|
||||
|
||||
foreach (Accessible accessor in Children) {
|
||||
foreach (var child in accessor.Children) {
|
||||
foreach (var tab in child.Children) {
|
||||
object tabIndex = tab.accessible.get_accState(CHILDID_SELF);
|
||||
var description = tab.accessible.get_accDescription(CHILDID_SELF);
|
||||
if (!string.IsNullOrEmpty(description)) {
|
||||
if (description.Contains(Environment.NewLine)) {
|
||||
var url = description.Substring(description.IndexOf(Environment.NewLine)).Trim();
|
||||
urlList.Add(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return urlList;
|
||||
}
|
||||
}
|
||||
|
||||
public int IETabCount {
|
||||
get {
|
||||
foreach (Accessible accessor in Children) {
|
||||
foreach (var child in accessor.Children) {
|
||||
foreach (var tab in child.Children) {
|
||||
return child.ChildCount - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private Accessible(IAccessible acc) {
|
||||
if (acc == null) {
|
||||
throw new Exception();
|
||||
}
|
||||
accessible = acc;
|
||||
}
|
||||
|
||||
private void Activate() {
|
||||
accessible.accDoDefaultAction(CHILDID_SELF);
|
||||
}
|
||||
|
||||
private static object[] GetAccessibleChildren(IAccessible ao, out int childs) {
|
||||
childs = 0;
|
||||
object[] ret = null;
|
||||
int count = ao.accChildCount;
|
||||
|
||||
if (count > 0) {
|
||||
ret = new object[count];
|
||||
AccessibleChildren(ao, 0, count, ret, out childs);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
365
GreenshotPlugin/Core/BitmapBuffer.cs
Normal file
365
GreenshotPlugin/Core/BitmapBuffer.cs
Normal file
|
@ -0,0 +1,365 @@
|
|||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
* For more information see: http://getgreenshot.org/
|
||||
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace GreenshotPlugin.Core {
|
||||
/// <summary>
|
||||
/// The BitmapBuffer is exactly what it says, it buffers a Bitmap.
|
||||
/// And it is possible to Draw on the Bitmap with direct memory access for better performance
|
||||
/// </summary>
|
||||
[Serializable()]
|
||||
public unsafe class BitmapBuffer : IDisposable {
|
||||
private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(BitmapBuffer));
|
||||
private bool clone;
|
||||
private Bitmap bitmap;
|
||||
public Bitmap Bitmap {
|
||||
get {return bitmap;}
|
||||
}
|
||||
[NonSerialized]
|
||||
private BitmapData bmData;
|
||||
[NonSerialized]
|
||||
private Rectangle rect;
|
||||
[NonSerialized]
|
||||
private byte* pointer;
|
||||
[NonSerialized]
|
||||
private int stride; /* bytes per pixel row */
|
||||
[NonSerialized]
|
||||
private int aIndex = -1;
|
||||
[NonSerialized]
|
||||
private int rIndex = -1;
|
||||
[NonSerialized]
|
||||
private int gIndex = -1;
|
||||
[NonSerialized]
|
||||
private int bIndex = -1;
|
||||
[NonSerialized]
|
||||
private int bytesPerPixel;
|
||||
[NonSerialized]
|
||||
private bool bitsLocked = false;
|
||||
|
||||
public Size Size {
|
||||
get {return rect.Size;}
|
||||
}
|
||||
public int Length {
|
||||
get {return rect.Width*rect.Height;}
|
||||
}
|
||||
public int Width {
|
||||
get {return rect.Width;}
|
||||
}
|
||||
public int Height {
|
||||
get {return rect.Height;}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a BitmapBuffer from a Bitmap
|
||||
/// </summary>
|
||||
/// <param name="sourceBmp">Bitmap</param>
|
||||
public BitmapBuffer(Bitmap bmp) : this(bmp, Rectangle.Empty) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a BitmapBuffer from a Bitmap a flag if we need a clone
|
||||
/// </summary>
|
||||
/// <param name="sourceBmp">Bitmap</param>
|
||||
/// <param name="clone">bool specifying if the bitmap needs to be cloned</param>
|
||||
public BitmapBuffer(Bitmap sourceBmp, bool clone) : this(sourceBmp, Rectangle.Empty, clone) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a BitmapBuffer from a Bitmap and a Rectangle specifying what part from the Bitmap to take.
|
||||
/// </summary>
|
||||
/// <param name="sourceBmp">Bitmap</param>
|
||||
/// <param name="applyRect">Rectangle</param>
|
||||
public BitmapBuffer(Bitmap sourceBmp, Rectangle applyRect) : this(sourceBmp, applyRect, true) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a BitmapBuffer from a Bitmap, a Rectangle specifying what part from the Bitmap to take and a flag if we need a clone
|
||||
/// </summary>
|
||||
/// <param name="sourceBmp">Bitmap</param>
|
||||
/// <param name="applyRect">Rectangle</param>
|
||||
/// <param name="clone">bool specifying if the bitmap needs to be cloned</param>
|
||||
public BitmapBuffer(Bitmap sourceBmp, Rectangle applyRect, bool clone) {
|
||||
this.clone = clone;
|
||||
Rectangle sourceRect = new Rectangle(applyRect.X, applyRect.Y, applyRect.Width, applyRect.Height);
|
||||
Rectangle bitmapRect = new Rectangle(0,0, sourceBmp.Width, sourceBmp.Height);
|
||||
|
||||
if(sourceRect.IsEmpty) {
|
||||
sourceRect = bitmapRect;
|
||||
} else {
|
||||
sourceRect.Intersect(bitmapRect);
|
||||
}
|
||||
// Does the rect have any pixels?
|
||||
if (sourceRect.Height <= 0 || sourceRect.Width <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (SupportsPixelFormat(sourceBmp)) {
|
||||
if (clone) {
|
||||
// Create copy with supported format
|
||||
this.bitmap = sourceBmp.Clone(sourceRect, sourceBmp.PixelFormat);
|
||||
} else {
|
||||
this.bitmap = sourceBmp;
|
||||
}
|
||||
} else {
|
||||
// We can only clone, as we don't support the pixel format!
|
||||
if (!clone) {
|
||||
throw new ArgumentException("Not supported pixel format: " + sourceBmp.PixelFormat);
|
||||
}
|
||||
// When sourceRect is the whole bitmap there is a GDI+ bug in Clone
|
||||
// Clone will than return the same PixelFormat as the source
|
||||
// a quick workaround is using new Bitmap which uses a default of Format32bppArgb
|
||||
if (sourceRect.Equals(bitmapRect)) {
|
||||
this.bitmap = new Bitmap(sourceBmp);
|
||||
} else {
|
||||
this.bitmap = sourceBmp.Clone(sourceRect, PixelFormat.Format32bppArgb);
|
||||
}
|
||||
}
|
||||
// Set "this" rect to location 0,0
|
||||
// as the Cloned Bitmap is only the part we want to work with
|
||||
this.rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~BitmapBuffer() {
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* The public accessible Dispose
|
||||
* Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice
|
||||
*/
|
||||
public void Dispose() {
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
// The bulk of the clean-up code is implemented in Dispose(bool)
|
||||
|
||||
/**
|
||||
* This Dispose is called from the Dispose and the Destructor.
|
||||
* When disposing==true all non-managed resources should be freed too!
|
||||
*/
|
||||
protected virtual void Dispose(bool disposing) {
|
||||
Unlock();
|
||||
if (disposing) {
|
||||
if (bitmap != null && clone) {
|
||||
bitmap.Dispose();
|
||||
}
|
||||
}
|
||||
bitmap = null;
|
||||
bmData = null;
|
||||
pointer = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when deserializing the object
|
||||
*/
|
||||
public BitmapBuffer(SerializationInfo info, StreamingContext ctxt) {
|
||||
this.bitmap = (Bitmap)info.GetValue("bitmap", typeof(Bitmap));
|
||||
this.rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
|
||||
// The rest will be set when Lock is called
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when serializing the object
|
||||
*/
|
||||
public void GetObjectData(SerializationInfo info, StreamingContext ctxt) {
|
||||
Unlock();
|
||||
info.AddValue("bitmap", this.bitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock the bitmap so we have direct access to the memory
|
||||
*/
|
||||
public void Lock() {
|
||||
if(rect.Width > 0 && rect.Height > 0) {
|
||||
bmData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, bitmap.PixelFormat);
|
||||
bitsLocked = true;
|
||||
|
||||
System.IntPtr Scan0 = bmData.Scan0;
|
||||
pointer = (byte*)(void*)Scan0;
|
||||
|
||||
PrepareForPixelFormat();
|
||||
stride = bmData.Stride;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock the System Memory
|
||||
*/
|
||||
private void Unlock() {
|
||||
if(bitsLocked) {
|
||||
bitmap.UnlockBits(bmData);
|
||||
bitsLocked = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the stored bitmap to the destionation bitmap at the supplied point
|
||||
*/
|
||||
public void DrawTo(Graphics graphics, Point destination) {
|
||||
DrawTo(graphics, null, destination);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the stored Bitmap on the Destination bitmap with the specified rectangle
|
||||
* Be aware that the stored bitmap will be resized to the specified rectangle!!
|
||||
*/
|
||||
public void DrawTo(Graphics graphics, Rectangle destinationRect) {
|
||||
DrawTo(graphics, destinationRect, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* private helper to draw the bitmap
|
||||
*/
|
||||
private void DrawTo(Graphics graphics, Rectangle? destinationRect, Point? destination) {
|
||||
if (destinationRect.HasValue) {
|
||||
// Does the rect have any pixels?
|
||||
if (destinationRect.Value.Height <= 0 || destinationRect.Value.Width <= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Make sure this.bitmap is unlocked
|
||||
Unlock();
|
||||
|
||||
|
||||
if (destinationRect.HasValue) {
|
||||
graphics.DrawImage(this.bitmap, destinationRect.Value);
|
||||
} else if (destination.HasValue) {
|
||||
graphics.DrawImage(this.bitmap, destination.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the color at location x,y
|
||||
* Before the first time this is called the Lock() should be called once!
|
||||
*/
|
||||
public Color GetColorAt(int x, int y) {
|
||||
if(x>=0 && y>=0 && x<rect.Width && y<rect.Height) {
|
||||
int offset = x*bytesPerPixel+y*stride;
|
||||
int a = (aIndex==-1) ? 255 : (int)pointer[aIndex+offset];
|
||||
return Color.FromArgb(a, pointer[rIndex+offset], pointer[gIndex+offset], pointer[bIndex+offset]);
|
||||
} else {
|
||||
return Color.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the color at location x,y
|
||||
* Before the first time this is called the Lock() should be called once!
|
||||
*/
|
||||
public Color GetColorAtWithoutAlpha(int x, int y) {
|
||||
if(x>=0 && y>=0 && x<rect.Width && y<rect.Height) {
|
||||
int offset = x*bytesPerPixel+y*stride;
|
||||
return Color.FromArgb(255, pointer[rIndex+offset], pointer[gIndex+offset], pointer[bIndex+offset]);
|
||||
} else {
|
||||
return Color.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the color at location x,y
|
||||
* Before the first time this is called the Lock() should be called once!
|
||||
*/
|
||||
public void SetColorAt(int x, int y, Color color) {
|
||||
if(x>=0 && y>=0 && x<rect.Width && y<rect.Height) {
|
||||
int offset = x*bytesPerPixel+y*stride;
|
||||
if(aIndex!=-1) pointer[aIndex+offset] = color.A;
|
||||
pointer[rIndex+offset] = color.R;
|
||||
pointer[gIndex+offset] = color.G;
|
||||
pointer[bIndex+offset] = color.B;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the color at location x,y as an array
|
||||
* Before the first time this is called the Lock() should be called once!
|
||||
*/
|
||||
public int[] GetColorArrayAt(int x, int y) {
|
||||
if(x>=0 && y>=0 && x<rect.Width && y<rect.Height) {
|
||||
int offset = x*bytesPerPixel+y*stride;
|
||||
int a = (aIndex==-1) ? 255 : (int)pointer[aIndex+offset];
|
||||
return new int[]{a, pointer[rIndex+offset], pointer[gIndex+offset], pointer[bIndex+offset]};
|
||||
} else {
|
||||
return new int[]{0,0,0,0};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the color at location x,y as an array
|
||||
* Before the first time this is called the Lock() should be called once!
|
||||
*/
|
||||
public void SetColorArrayAt(int x, int y, int[] colors) {
|
||||
if(x>=0 && y>=0 && x<rect.Width && y<rect.Height) {
|
||||
int offset = x*bytesPerPixel+y*stride;
|
||||
if(aIndex!=-1) pointer[aIndex+offset] = (byte)colors[0];
|
||||
pointer[rIndex+offset] = (byte)colors[1];
|
||||
pointer[gIndex+offset] = (byte)colors[2];
|
||||
pointer[bIndex+offset] = (byte)colors[3];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the supplied Bitmap has a PixelFormat we support
|
||||
*/
|
||||
private bool SupportsPixelFormat(Bitmap bitmap) {
|
||||
return (bitmap.PixelFormat.Equals(PixelFormat.Format32bppArgb) ||
|
||||
bitmap.PixelFormat.Equals(PixelFormat.Format32bppRgb) ||
|
||||
bitmap.PixelFormat.Equals(PixelFormat.Format24bppRgb));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set some internal values for accessing the bitmap according to the PixelFormat
|
||||
*/
|
||||
private void PrepareForPixelFormat() {
|
||||
// aIndex is only set if the pixel format supports "A".
|
||||
aIndex = -1;
|
||||
switch(bitmap.PixelFormat) {
|
||||
case PixelFormat.Format32bppArgb:
|
||||
bIndex = 0;
|
||||
gIndex = 1;
|
||||
rIndex = 2;
|
||||
aIndex = 3;
|
||||
bytesPerPixel = 4;
|
||||
break;
|
||||
case PixelFormat.Format32bppRgb:
|
||||
bIndex = 0;
|
||||
gIndex = 1;
|
||||
rIndex = 2;
|
||||
bytesPerPixel = 4;
|
||||
break;
|
||||
case PixelFormat.Format24bppRgb:
|
||||
bIndex = 0;
|
||||
gIndex = 1;
|
||||
rIndex = 2;
|
||||
bytesPerPixel = 3;
|
||||
break;
|
||||
default:
|
||||
throw new FormatException("Bitmap.Pixelformat."+bitmap.PixelFormat+" is currently not supported. Supported: Format32bpp(A)Rgb, Format24bppRgb");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
145
GreenshotPlugin/Core/CacheHelper.cs
Normal file
145
GreenshotPlugin/Core/CacheHelper.cs
Normal file
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
* For more information see: http://getgreenshot.org/
|
||||
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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;
|
||||
using System.Collections.Generic;
|
||||
using System.Web;
|
||||
using System.Web.Caching;
|
||||
|
||||
namespace GreenshotPlugin.Core {
|
||||
public delegate void CacheObjectExpired(string key, object cacheValue);
|
||||
|
||||
/// <summary>
|
||||
/// Description of CacheHelper.
|
||||
/// </summary>
|
||||
public class CacheHelper<T> {
|
||||
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger("CacheHelper");
|
||||
private Cache cache = HttpRuntime.Cache;
|
||||
private string prefix;
|
||||
private double defaultExpiration = 10*60; // 10 Minutes
|
||||
private CacheItemRemovedCallback defaultCallback = null;
|
||||
private CacheObjectExpired expiredCallback = null;
|
||||
|
||||
public CacheHelper(string prefix) {
|
||||
defaultCallback = new CacheItemRemovedCallback(OnRemoved);
|
||||
this.prefix = prefix + ".";
|
||||
}
|
||||
|
||||
public CacheHelper(string prefix, double defaultExpiration) : this(prefix) {
|
||||
this.defaultExpiration = defaultExpiration;
|
||||
}
|
||||
|
||||
public CacheHelper(string prefix, double defaultExpiration, CacheObjectExpired expiredCallback) : this(prefix, defaultExpiration) {
|
||||
this.expiredCallback = expiredCallback;
|
||||
}
|
||||
|
||||
private void OnRemoved(string key, object cacheValue, CacheItemRemovedReason reason) {
|
||||
LOG.DebugFormat("The item with key '{0}' is being removed from the cache with reason: {1}", key, reason);
|
||||
switch (reason) {
|
||||
case CacheItemRemovedReason.Expired:
|
||||
if (expiredCallback != null) {
|
||||
expiredCallback.Invoke(key, cacheValue);
|
||||
}
|
||||
break;
|
||||
case CacheItemRemovedReason.Underused:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insert value into the cache using default expiration & default callback
|
||||
/// </summary>
|
||||
/// <param name="o">Item to be cached</param>
|
||||
/// <param name="key">Name of item</param>
|
||||
public void Add(string key, T o) {
|
||||
if (defaultCallback != null) {
|
||||
cache.Insert(prefix + key, o, null, DateTime.Now.AddSeconds(defaultExpiration), Cache.NoSlidingExpiration, CacheItemPriority.Default, defaultCallback);
|
||||
} else {
|
||||
cache.Insert(prefix + key, o, null, DateTime.Now.AddSeconds(defaultExpiration), Cache.NoSlidingExpiration);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all the methods for this cache
|
||||
/// </summary>
|
||||
/// <returns>IEnumerator of the type</returns>
|
||||
public IEnumerable<T> GetElements() {
|
||||
IDictionaryEnumerator cacheEnum = cache.GetEnumerator();
|
||||
while (cacheEnum.MoveNext()) {
|
||||
string key = cacheEnum.Key as string;
|
||||
if (!string.IsNullOrEmpty(key) && key.StartsWith(prefix)) {
|
||||
yield return (T)cacheEnum.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insert value into the cache using the supplied expiration time in seconds
|
||||
/// </summary>
|
||||
/// <param name="o">Item to be cached</param>
|
||||
/// <param name="key">Name of item</param>
|
||||
/// <param name="seconds">expiration time in "double" seconds</param>
|
||||
public void Add(string key, T o, double seconds) {
|
||||
cache.Insert(prefix + key, o, null, DateTime.Now.AddSeconds(seconds), Cache.NoSlidingExpiration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insert value into the cache using
|
||||
/// appropriate name/value pairs
|
||||
/// </summary>
|
||||
/// <param name="o">Item to be cached</param>
|
||||
/// <param name="key">Name of item</param>
|
||||
public void Add(string key, T o, CacheItemRemovedCallback callback) {
|
||||
cache.Insert(prefix + key, o, null, DateTime.Now.AddSeconds(defaultExpiration), Cache.NoSlidingExpiration, CacheItemPriority.Default, callback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove item from cache
|
||||
/// </summary>
|
||||
/// <param name="key">Name of cached item</param>
|
||||
public void Remove(string key) {
|
||||
cache.Remove(prefix + key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for item in cache
|
||||
/// </summary>
|
||||
/// <param name="key">Name of cached item</param>
|
||||
/// <returns></returns>
|
||||
public bool Exists(string key) {
|
||||
return cache[prefix + key] != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve cached item
|
||||
/// </summary>
|
||||
/// <param name="key">Name of cached item</param>
|
||||
/// <returns>Cached item as type</returns>
|
||||
public T Get(string key) {
|
||||
try {
|
||||
return (T) cache[prefix + key];
|
||||
} catch {
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,726 +0,0 @@
|
|||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
* For more information see: http://getgreenshot.org/
|
||||
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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.Drawing;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace GreenshotPlugin.Core {
|
||||
/// <summary>
|
||||
/// Attribute for telling that this class is linked to a section in the ini-configuration
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
|
||||
public class IniSectionAttribute : Attribute {
|
||||
private string name;
|
||||
public IniSectionAttribute(string name) {
|
||||
this.name = name;
|
||||
}
|
||||
public string Description;
|
||||
public string Name {
|
||||
get {return name;}
|
||||
set {name = value;}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attribute for telling that a field is linked to a property in the ini-configuration selection
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple=false)]
|
||||
public class IniPropertyAttribute : Attribute {
|
||||
private string name;
|
||||
public IniPropertyAttribute(string name) {
|
||||
this.name = name;
|
||||
}
|
||||
public string Description;
|
||||
public string Separator = ",";
|
||||
public string DefaultValue;
|
||||
public string Name {
|
||||
get {return name;}
|
||||
set {name = value;}
|
||||
}
|
||||
}
|
||||
public static class IniReader{
|
||||
private const string SECTION_START = "[";
|
||||
private const string SECTION_END = "]";
|
||||
private const string COMMENT = ";";
|
||||
private static char[] ASSIGNMENT = new char[] { '=' };
|
||||
|
||||
/**
|
||||
* Read an ini file to a Dictionary, each key is a section and the value is a Dictionary with name and values.
|
||||
*/
|
||||
public static Dictionary<string, Dictionary<string, string>> read(string path, Encoding encoding) {
|
||||
Dictionary<string, Dictionary<string, string>> ini = new Dictionary<string, Dictionary<string,string>>();
|
||||
using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 1024)) {
|
||||
using (StreamReader reader = new StreamReader(fileStream, encoding)) {
|
||||
Dictionary<string, string> nameValues = new Dictionary<string, string>();
|
||||
while (!reader.EndOfStream) {
|
||||
string line = reader.ReadLine();
|
||||
if (line != null) {
|
||||
string cleanLine = line.Trim();
|
||||
if (cleanLine.Length == 0 || cleanLine.StartsWith(COMMENT)) {
|
||||
continue;
|
||||
}
|
||||
if (cleanLine.StartsWith(SECTION_START)) {
|
||||
string section = line.Replace(SECTION_START, "").Replace(SECTION_END, "").Trim();
|
||||
nameValues = new Dictionary<string, string>();
|
||||
ini.Add(section, nameValues);
|
||||
} else {
|
||||
string[] keyvalueSplitter = line.Split(ASSIGNMENT, 2);
|
||||
string name = keyvalueSplitter[0];
|
||||
string inivalue = keyvalueSplitter.Length > 1 ? keyvalueSplitter[1] : null;
|
||||
if (nameValues.ContainsKey(name)) {
|
||||
nameValues[name] = inivalue;
|
||||
} else {
|
||||
nameValues.Add(name, inivalue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ini;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for all IniSections
|
||||
/// </summary>
|
||||
public abstract class IniSection {
|
||||
/// Flag to specify if values have been changed
|
||||
public bool IsDirty = false;
|
||||
|
||||
/// <summary>
|
||||
/// Supply values we can't put as defaults
|
||||
/// </summary>
|
||||
/// <param name="property">The property to return a default for</param>
|
||||
/// <returns>object with the default value for the supplied property</returns>
|
||||
public virtual object GetDefault(string property) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method will be called before converting the property, making to possible to correct a certain value
|
||||
/// Can be used when migration is needed
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the property</param>
|
||||
/// <param name="propertyValue">The string value of the property</param>
|
||||
/// <returns>string with the propertyValue, modified or not...</returns>
|
||||
public virtual string PreCheckValue(string propertyName, string propertyValue) {
|
||||
return propertyValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method will be called after reading the configuration, so eventually some corrections can be made
|
||||
/// </summary>
|
||||
public virtual void PostCheckValues() {
|
||||
}
|
||||
}
|
||||
|
||||
public class IniConfig {
|
||||
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(IniConfig));
|
||||
private const string CONFIG_FILE_NAME = "greenshot.ini";
|
||||
private const string DEFAULTS_CONFIG_FILE_NAME = "greenshot-defaults.ini";
|
||||
private const string FIXED_CONFIG_FILE_NAME = "greenshot-fixed.ini";
|
||||
private const string SUBDIRECTORY_NAME = "Greenshot";
|
||||
private static FileSystemWatcher watcher;
|
||||
|
||||
/// <summary>
|
||||
/// Static code for loading
|
||||
/// </summary>
|
||||
static IniConfig() {
|
||||
iniLocation = CreateIniLocation(CONFIG_FILE_NAME);
|
||||
// Load the defaults
|
||||
Read(CreateIniLocation(DEFAULTS_CONFIG_FILE_NAME));
|
||||
// Load the normal
|
||||
Read(CreateIniLocation(iniLocation));
|
||||
// Load the fixed settings
|
||||
Read(CreateIniLocation(FIXED_CONFIG_FILE_NAME));
|
||||
WatchConfigFile(true);
|
||||
}
|
||||
|
||||
private static string iniLocation = null;
|
||||
private static Dictionary<string, IniSection> sectionMap = new Dictionary<string, IniSection>();
|
||||
private static Dictionary<string, Dictionary<string, string >> iniProperties = new Dictionary<string, Dictionary<string, string>>();
|
||||
public static event FileSystemEventHandler IniChanged;
|
||||
|
||||
private static void WatchConfigFile(bool sendEvents) {
|
||||
// Wait with watching untill the file is there
|
||||
if (Directory.Exists(Path.GetDirectoryName(iniLocation))) {
|
||||
if (watcher == null) {
|
||||
LOG.DebugFormat("Starting FileSystemWatcher for {0}", iniLocation);
|
||||
// Monitor the ini file
|
||||
watcher = new FileSystemWatcher();
|
||||
watcher.Path = Path.GetDirectoryName(iniLocation);
|
||||
watcher.Filter = "*.ini";
|
||||
watcher.NotifyFilter = NotifyFilters.LastWrite;
|
||||
watcher.Changed += new FileSystemEventHandler(ConfigFileChanged);
|
||||
}
|
||||
}
|
||||
if (watcher != null) {
|
||||
watcher.EnableRaisingEvents = sendEvents;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ConfigFileChanged(object source, FileSystemEventArgs e) {
|
||||
if (iniLocation.Equals(e.FullPath)) {
|
||||
LOG.InfoFormat("Config file {0} was changed, reloading", e.FullPath);
|
||||
|
||||
// Try to reread the configuration
|
||||
int retries = 10;
|
||||
bool configRead = false;
|
||||
while(!configRead && retries != 0) {
|
||||
try {
|
||||
IniConfig.Reload();
|
||||
configRead = true;
|
||||
} catch (IOException) {
|
||||
retries--;
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
if (configRead && IniChanged != null) {
|
||||
IniChanged.Invoke(source, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create the location of the configuration file
|
||||
/// </summary>
|
||||
private static string CreateIniLocation(string configFilename) {
|
||||
// check if file is in the same location as started from, if this is the case
|
||||
// we will use this file instead of the Applicationdate folder
|
||||
// Done for Feature Request #2741508
|
||||
if (File.Exists(Path.Combine(Application.StartupPath, configFilename))) {
|
||||
return Path.Combine(Application.StartupPath, configFilename);
|
||||
}
|
||||
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), SUBDIRECTORY_NAME + @"\" + configFilename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reload the Ini file
|
||||
/// </summary>
|
||||
public static void Reload() {
|
||||
// Load the normal
|
||||
Read(iniLocation);
|
||||
// Load the fixed settings
|
||||
Read(CreateIniLocation(FIXED_CONFIG_FILE_NAME));
|
||||
foreach(IniSection section in sectionMap.Values) {
|
||||
FillIniSection(section);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the ini file into the Dictionary
|
||||
/// </summary>
|
||||
/// <param name="iniLocation">Path & Filename of ini file</param>
|
||||
private static void Read(string iniLocation) {
|
||||
if (!File.Exists(iniLocation)) {
|
||||
LOG.Info("Can't find file: " + iniLocation);
|
||||
return;
|
||||
}
|
||||
LOG.Info("Reading ini-properties from file: " + iniLocation);
|
||||
iniProperties = IniReader.read(iniLocation, Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A generic method which returns an instance of the supplied type, filled with it's configuration
|
||||
/// </summary>
|
||||
/// <returns>Filled instance of IniSection type which was supplied</returns>
|
||||
public static T GetIniSection<T>() where T : IniSection {
|
||||
T section;
|
||||
|
||||
Type iniSectionType = typeof(T);
|
||||
string sectionName = getSectionName(iniSectionType);
|
||||
if (sectionMap.ContainsKey(sectionName)) {
|
||||
LOG.Debug("Returning pre-mapped section " + sectionName);
|
||||
section = (T)sectionMap[sectionName];
|
||||
} else {
|
||||
// Create instance of this type
|
||||
section = (T)Activator.CreateInstance(iniSectionType);
|
||||
|
||||
// Store for later save & retrieval
|
||||
sectionMap.Add(sectionName, section);
|
||||
FillIniSection(section);
|
||||
LOG.Debug("Returning newly mapped section " + sectionName);
|
||||
}
|
||||
if (section.IsDirty) {
|
||||
IniConfig.Save();
|
||||
}
|
||||
return section;
|
||||
}
|
||||
|
||||
private static void FillIniSection(IniSection section) {
|
||||
Type iniSectionType = section.GetType();
|
||||
string sectionName = getSectionName(iniSectionType);
|
||||
// Get the properties for the section
|
||||
Dictionary<string, string> properties = null;
|
||||
if (iniProperties.ContainsKey(sectionName)) {
|
||||
properties = iniProperties[sectionName];
|
||||
} else {
|
||||
iniProperties.Add(sectionName, new Dictionary<string, string>());
|
||||
properties = iniProperties[sectionName];
|
||||
}
|
||||
|
||||
// Iterate over the fields and fill them
|
||||
FieldInfo[] fields = iniSectionType.GetFields();
|
||||
foreach(FieldInfo field in fields) {
|
||||
if (Attribute.IsDefined(field, typeof(IniPropertyAttribute))) {
|
||||
IniPropertyAttribute iniPropertyAttribute = (IniPropertyAttribute)field.GetCustomAttributes(typeof(IniPropertyAttribute), false)[0];
|
||||
string propertyName = iniPropertyAttribute.Name;
|
||||
string propertyDefaultValue = iniPropertyAttribute.DefaultValue;
|
||||
string fieldSeparator = iniPropertyAttribute.Separator;
|
||||
// Get the type, or the underlying type for nullables
|
||||
Type fieldType = field.FieldType;
|
||||
|
||||
// Get the value from the ini file, if there is none take the default
|
||||
if (!properties.ContainsKey(propertyName) && propertyDefaultValue != null) {
|
||||
// Mark as dirty, we didn't use properties from the file (even defaults from the default file are allowed)
|
||||
section.IsDirty = true;
|
||||
LOG.Debug("Passing default: " + propertyName + "=" + propertyDefaultValue);
|
||||
}
|
||||
|
||||
// Try to get the field value from the properties or use the default value
|
||||
object fieldValue = null;
|
||||
try {
|
||||
fieldValue = CreateFieldValue(fieldType, section, sectionName, propertyName, propertyDefaultValue, fieldSeparator);
|
||||
} catch (Exception e) {
|
||||
LOG.Warn("Couldn't parse field: " + sectionName + "." + propertyName, e);
|
||||
}
|
||||
|
||||
// If still no value, check if the GetDefault delivers a value
|
||||
if (fieldValue == null) {
|
||||
// Use GetDefault to fill the field if none is set
|
||||
fieldValue = section.GetDefault(propertyName);
|
||||
}
|
||||
|
||||
// Set the value
|
||||
try {
|
||||
field.SetValue(section,fieldValue);
|
||||
} catch (Exception e) {
|
||||
LOG.Warn("Couldn't set field: " + sectionName + "." + propertyName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method for creating a value
|
||||
/// </summary>
|
||||
/// <param name="fieldType">Type of the value to create</param>
|
||||
/// <param name="propertyValue">Value as string</param>
|
||||
/// <returns>object instance of the value</returns>
|
||||
private static object CreateFieldValue(Type fieldType, IniSection section, string sectionName, string propertyName, string defaultValue, string arraySeparator) {
|
||||
Dictionary<string, string> properties = iniProperties[sectionName];
|
||||
bool defaultUsed = false;
|
||||
string propertyValue = null;
|
||||
if (properties.ContainsKey(propertyName) && properties[propertyName] != null) {
|
||||
propertyValue = section.PreCheckValue(propertyName, properties[propertyName]);
|
||||
} else if (defaultValue != null && defaultValue.Trim().Length != 0) {
|
||||
propertyValue = defaultValue;
|
||||
defaultUsed = true;
|
||||
} else {
|
||||
LOG.DebugFormat("Property {0} has no value or default value", propertyName);
|
||||
}
|
||||
|
||||
// Now set the value
|
||||
if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(List<>)) {
|
||||
object list = Activator.CreateInstance(fieldType);
|
||||
// Logic for List<>
|
||||
if (propertyValue == null) {
|
||||
return list;
|
||||
}
|
||||
string[] arrayValues = propertyValue.Split(new string[] {arraySeparator}, StringSplitOptions.None);
|
||||
if (arrayValues == null || arrayValues.Length == 0) {
|
||||
return list;
|
||||
}
|
||||
bool addedElements = false;
|
||||
bool parseProblems = false;
|
||||
MethodInfo addMethodInfo = fieldType.GetMethod("Add");
|
||||
|
||||
foreach(string arrayValue in arrayValues) {
|
||||
if (arrayValue != null && arrayValue.Length > 0) {
|
||||
object newValue = null;
|
||||
try {
|
||||
newValue = ConvertValueToFieldType(fieldType.GetGenericArguments()[0], arrayValue);
|
||||
} catch (Exception e) {
|
||||
LOG.Error("Problem converting " + arrayValue + " to type " + fieldType.FullName, e);
|
||||
parseProblems = true;
|
||||
}
|
||||
if (newValue != null) {
|
||||
addMethodInfo.Invoke(list, new object[] {newValue});
|
||||
addedElements = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Try to fallback on a default
|
||||
if (!addedElements && parseProblems) {
|
||||
try {
|
||||
object fallbackValue = ConvertValueToFieldType(fieldType.GetGenericArguments()[0], defaultValue);
|
||||
addMethodInfo.Invoke(list, new object[] {fallbackValue});
|
||||
return list;
|
||||
} catch (Exception e) {
|
||||
LOG.Error("Problem converting " + defaultValue + " to type " + fieldType.FullName, e);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
} else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) {
|
||||
// Logic for Dictionary<,>
|
||||
Type type1 = fieldType.GetGenericArguments()[0];
|
||||
Type type2 = fieldType.GetGenericArguments()[1];
|
||||
LOG.Info(String.Format("Found Dictionary<{0},{1}>",type1.Name, type2.Name));
|
||||
object dictionary = Activator.CreateInstance(fieldType);
|
||||
MethodInfo addMethodInfo = fieldType.GetMethod("Add");
|
||||
bool addedElements = false;
|
||||
foreach(string key in properties.Keys) {
|
||||
if (key != null && key.StartsWith(propertyName + ".")) {
|
||||
// What "key" do we need to store it under?
|
||||
string subPropertyName = key.Substring(propertyName.Length + 1);
|
||||
string stringValue = properties[key];
|
||||
object newValue1 = null;
|
||||
object newValue2 = null;
|
||||
try {
|
||||
newValue1 = ConvertValueToFieldType(type1, subPropertyName);
|
||||
} catch (Exception e) {
|
||||
LOG.Error("Problem converting " + subPropertyName + " to type " + type1.FullName, e);
|
||||
}
|
||||
try {
|
||||
newValue2 = ConvertValueToFieldType(type2, stringValue);
|
||||
} catch (Exception e) {
|
||||
LOG.Error("Problem converting " + stringValue + " to type " + type2.FullName, e);
|
||||
}
|
||||
addMethodInfo.Invoke(dictionary, new object[] {newValue1, newValue2});
|
||||
addedElements = true;
|
||||
}
|
||||
}
|
||||
// No need to return something that isn't filled!
|
||||
if (addedElements) {
|
||||
return dictionary;
|
||||
}
|
||||
} else {
|
||||
if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) {
|
||||
// We are dealing with a generic type that is nullable
|
||||
fieldType = Nullable.GetUnderlyingType(fieldType);
|
||||
}
|
||||
object newValue = null;
|
||||
try {
|
||||
newValue = ConvertValueToFieldType(fieldType, propertyValue);
|
||||
} catch (Exception e1) {
|
||||
newValue = null;
|
||||
if (!defaultUsed) {
|
||||
try {
|
||||
newValue = ConvertValueToFieldType(fieldType, defaultValue);
|
||||
} catch (Exception e2) {
|
||||
LOG.Error("Problem converting " + propertyValue + " to type " + fieldType.FullName, e2);
|
||||
}
|
||||
} else {
|
||||
LOG.Error("Problem converting " + propertyValue + " to type " + fieldType.FullName, e1);
|
||||
}
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static object ConvertValueToFieldType(Type fieldType, string valueString) {
|
||||
if (valueString == null) {
|
||||
return null;
|
||||
}
|
||||
if (fieldType == typeof(string)) {
|
||||
return valueString;
|
||||
} else if (fieldType == typeof(bool) || fieldType == typeof(bool?)) {
|
||||
if (valueString.Length > 0) {
|
||||
return bool.Parse(valueString);
|
||||
}
|
||||
} else if (fieldType == typeof(int) || fieldType == typeof(int?)) {
|
||||
if (valueString.Length > 0) {
|
||||
return int.Parse(valueString);
|
||||
}
|
||||
} else if (fieldType == typeof(uint) || fieldType == typeof(uint?)) {
|
||||
if (valueString.Length > 0) {
|
||||
return uint.Parse(valueString);
|
||||
}
|
||||
return 0;
|
||||
} else if (fieldType == typeof(Point)) {
|
||||
if (valueString.Length > 0) {
|
||||
string[] pointValues = valueString.Split(new Char[] {','});
|
||||
int x = int.Parse(pointValues[0].Trim());
|
||||
int y = int.Parse(pointValues[1].Trim());
|
||||
return new Point(x, y);
|
||||
}
|
||||
} else if (fieldType == typeof(DateTime)) {
|
||||
if (valueString.Length > 0) {
|
||||
return DateTime.Parse(valueString);
|
||||
}
|
||||
} else if (fieldType == typeof(Size)) {
|
||||
if (valueString.Length > 0) {
|
||||
string[] sizeValues = valueString.Split(new Char[] {','});
|
||||
int width = int.Parse(sizeValues[0].Trim());
|
||||
int height = int.Parse(sizeValues[1].Trim());
|
||||
return new Size(width, height);
|
||||
}
|
||||
} else if (fieldType == typeof(Rectangle)) {
|
||||
if (valueString.Length > 0) {
|
||||
string[] rectValues = valueString.Split(new Char[] {','});
|
||||
int x = int.Parse(rectValues[0].Trim());
|
||||
int y = int.Parse(rectValues[1].Trim());
|
||||
int width = int.Parse(rectValues[2].Trim());
|
||||
int height = int.Parse(rectValues[3].Trim());
|
||||
return new Rectangle(x, y, width, height);
|
||||
}
|
||||
} else if (fieldType == typeof(Color)) {
|
||||
if (valueString.Length > 0) {
|
||||
string[] colorValues = valueString.Split(new Char[] {','});
|
||||
int alpha = int.Parse(colorValues[0].Trim());
|
||||
int red = int.Parse(colorValues[1].Trim());
|
||||
int green = int.Parse(colorValues[2].Trim());
|
||||
int blue = int.Parse(colorValues[3].Trim());
|
||||
return Color.FromArgb(alpha, red, green, blue);
|
||||
}
|
||||
} else if (fieldType == typeof(object)) {
|
||||
if (valueString.Length > 0) {
|
||||
//LOG.Debug("Parsing: " + valueString);
|
||||
string[] values = valueString.Split(new Char[] {':'});
|
||||
//LOG.Debug("Type: " + values[0]);
|
||||
//LOG.Debug("Value: " + values[1]);
|
||||
Type fieldTypeForValue = Type.GetType(values[0], true);
|
||||
//LOG.Debug("Type after GetType: " + fieldTypeForValue);
|
||||
return ConvertValueToFieldType(fieldTypeForValue, values[1]);
|
||||
}
|
||||
} else if (fieldType.IsEnum) {
|
||||
if (valueString.Length > 0) {
|
||||
try {
|
||||
return Enum.Parse(fieldType, valueString);
|
||||
} catch (ArgumentException ae) {
|
||||
LOG.InfoFormat("Couldn't match {0} to {1}, trying case-insentive match", valueString, fieldType);
|
||||
foreach(Enum enumValue in Enum.GetValues(fieldType)) {
|
||||
if (enumValue.ToString().Equals(valueString, StringComparison.InvariantCultureIgnoreCase)) {
|
||||
LOG.Info("Match found...");
|
||||
return enumValue;
|
||||
}
|
||||
}
|
||||
throw ae;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string ConvertValueToString(Type fieldType, object valueObject) {
|
||||
if (valueObject == null) {
|
||||
// If there is nothing, deliver nothing!
|
||||
return "";
|
||||
}
|
||||
if (fieldType == typeof(Point)) {
|
||||
// Point to String
|
||||
Point p = (Point)valueObject;
|
||||
return String.Format("{0},{1}", p.X, p.Y);
|
||||
} else if (fieldType == typeof(Size)) {
|
||||
// Size to String
|
||||
Size size = (Size)valueObject;
|
||||
return String.Format("{0},{1}", size.Width, size.Height);
|
||||
} else if (fieldType == typeof(Rectangle)) {
|
||||
// Rectangle to String
|
||||
Rectangle rectangle = (Rectangle)valueObject;
|
||||
return String.Format("{0},{1},{2},{3}", rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
|
||||
} else if (fieldType == typeof(Color)) {
|
||||
// Color to String
|
||||
Color color = (Color)valueObject;
|
||||
return String.Format("{0},{1},{2},{3}", color.A, color.R, color.G, color.B);
|
||||
} else if (fieldType == typeof(object)) {
|
||||
// object to String, this is the hardest
|
||||
// Format will be "FQTypename[,Assemblyname]:Value"
|
||||
|
||||
// Get the type so we can call ourselves recursive
|
||||
Type valueType = valueObject.GetType();
|
||||
|
||||
// Get the value as string
|
||||
string ourValue = ConvertValueToString(valueType, valueObject);
|
||||
|
||||
// Get the valuetype as string
|
||||
string valueTypeName = valueType.FullName;
|
||||
// Find the assembly name and only append it if it's not already in the fqtypename (like System.Drawing)
|
||||
string assemblyName = valueType.Assembly.FullName;
|
||||
// correct assemblyName, this also has version information etc.
|
||||
if (assemblyName.StartsWith("Green")) {
|
||||
assemblyName = assemblyName.Substring(0,assemblyName.IndexOf(','));
|
||||
}
|
||||
return String.Format("{0},{1}:{2}", valueTypeName, assemblyName, ourValue);
|
||||
}
|
||||
// All other types
|
||||
return valueObject.ToString();
|
||||
}
|
||||
|
||||
private static string getSectionName(Type iniSectionType) {
|
||||
Attribute[] classAttributes = Attribute.GetCustomAttributes(iniSectionType);
|
||||
foreach(Attribute attribute in classAttributes) {
|
||||
if (attribute is IniSectionAttribute) {
|
||||
IniSectionAttribute iniSectionAttribute = (IniSectionAttribute)attribute;
|
||||
return iniSectionAttribute.Name;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> getProperties(string section) {
|
||||
if (iniProperties.ContainsKey(section)) {
|
||||
return iniProperties[section];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string GetProperty(string section, string name) {
|
||||
Dictionary<string, string> properties = getProperties(section);
|
||||
if (properties != null && properties.ContainsKey(name)) {
|
||||
return properties[name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Split property with ',' and return the splitted string as a string[]
|
||||
/// </summary>
|
||||
public static string[] GetPropertyAsArray(string section, string key) {
|
||||
Dictionary<string, string> properties = getProperties(section);
|
||||
string value = GetProperty(section, key);
|
||||
if (value != null) {
|
||||
return value.Split(new Char[] {','});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool GetBoolProperty(string section, string key) {
|
||||
Dictionary<string, string> properties = getProperties(section);
|
||||
string value = GetProperty(section, key);
|
||||
return bool.Parse(value);
|
||||
}
|
||||
|
||||
public static int GetIntProperty(string section, string key) {
|
||||
Dictionary<string, string> properties = getProperties(section);
|
||||
string value = GetProperty(section, key);
|
||||
return int.Parse(value);
|
||||
}
|
||||
|
||||
public static void Save() {
|
||||
try {
|
||||
SaveInternally();
|
||||
} catch (Exception e) {
|
||||
LOG.Error("A problem occured while writing the configuration file to: " + iniLocation, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SaveInternally() {
|
||||
WatchConfigFile(false);
|
||||
|
||||
LOG.Info("Saving configuration to: " + iniLocation);
|
||||
if (!Directory.Exists(Path.GetDirectoryName(iniLocation))) {
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(iniLocation));
|
||||
}
|
||||
TextWriter writer = new StreamWriter(iniLocation, false, Encoding.UTF8);
|
||||
foreach(IniSection section in sectionMap.Values) {
|
||||
Type classType = section.GetType();
|
||||
Attribute[] classAttributes = Attribute.GetCustomAttributes(classType);
|
||||
foreach(Attribute attribute in classAttributes) {
|
||||
if (attribute is IniSectionAttribute) {
|
||||
IniSectionAttribute iniSectionAttribute = (IniSectionAttribute)attribute;
|
||||
writer.WriteLine("; {0}", iniSectionAttribute.Description);
|
||||
writer.WriteLine("[{0}]", iniSectionAttribute.Name);
|
||||
FieldInfo[] fields = classType.GetFields();
|
||||
foreach(FieldInfo field in fields) {
|
||||
if (Attribute.IsDefined(field, typeof(IniPropertyAttribute))) {
|
||||
IniPropertyAttribute iniPropertyAttribute = (IniPropertyAttribute)field.GetCustomAttributes(typeof(IniPropertyAttribute), false)[0];
|
||||
writer.WriteLine("; {0}", iniPropertyAttribute.Description);
|
||||
object value = field.GetValue(section);
|
||||
Type fieldType = field.FieldType;
|
||||
if (value == null) {
|
||||
value = iniPropertyAttribute.DefaultValue;
|
||||
fieldType = typeof(string);
|
||||
}
|
||||
if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(List<>)) {
|
||||
Type valueType = fieldType.GetGenericArguments()[0];
|
||||
writer.Write("{0}=", iniPropertyAttribute.Name);
|
||||
int listCount = (int)fieldType.GetProperty("Count").GetValue(value, null);
|
||||
// Loop though generic list
|
||||
for (int index = 0; index < listCount; index++) {
|
||||
object item = fieldType.GetMethod("get_Item").Invoke(value, new object[] { index });
|
||||
|
||||
// Now you have an instance of the item in the generic list
|
||||
if (index < listCount -1) {
|
||||
writer.Write("{0}" + iniPropertyAttribute.Separator, ConvertValueToString(valueType, item));
|
||||
} else {
|
||||
writer.Write("{0}", ConvertValueToString(valueType, item));
|
||||
}
|
||||
}
|
||||
writer.WriteLine();
|
||||
} else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) {
|
||||
// Handle dictionaries.
|
||||
Type valueType1 = fieldType.GetGenericArguments()[0];
|
||||
Type valueType2 = fieldType.GetGenericArguments()[1];
|
||||
// Get the methods we need to deal with dictionaries.
|
||||
var keys = fieldType.GetProperty("Keys").GetValue(value, null);
|
||||
var item = fieldType.GetProperty("Item");
|
||||
var enumerator = keys.GetType().GetMethod("GetEnumerator").Invoke(keys, null);
|
||||
var moveNext = enumerator.GetType().GetMethod("MoveNext");
|
||||
var current = enumerator.GetType().GetProperty("Current").GetGetMethod();
|
||||
// Get all the values.
|
||||
while ((bool)moveNext.Invoke(enumerator, null)) {
|
||||
var key = current.Invoke(enumerator, null);
|
||||
var valueObject = item.GetValue(value, new object[] { key });
|
||||
// Write to ini file!
|
||||
writer.WriteLine("{0}.{1}={2}", iniPropertyAttribute.Name, ConvertValueToString(valueType1, key), ConvertValueToString(valueType2, valueObject));
|
||||
}
|
||||
} else {
|
||||
writer.WriteLine("{0}={1}", iniPropertyAttribute.Name, ConvertValueToString(fieldType, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
writer.WriteLine();
|
||||
section.IsDirty = false;
|
||||
}
|
||||
writer.WriteLine();
|
||||
// Write left over properties
|
||||
foreach(string sectionName in iniProperties.Keys) {
|
||||
// Check if the section is one that is "registered", if so skip it!
|
||||
if (!sectionMap.ContainsKey(sectionName)) {
|
||||
writer.WriteLine("; The section {0} is not registered, maybe a plugin hasn't claimed it due to errors or some functionality isn't used yet.", sectionName);
|
||||
// Write section name
|
||||
writer.WriteLine("[{0}]", sectionName);
|
||||
Dictionary<string, string> properties = iniProperties[sectionName];
|
||||
// Loop and write properties
|
||||
foreach(string propertyName in properties.Keys) {
|
||||
writer.WriteLine("{0}={1}", propertyName, properties[propertyName]);
|
||||
}
|
||||
writer.WriteLine();
|
||||
}
|
||||
}
|
||||
writer.Close();
|
||||
WatchConfigFile(true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,12 +20,16 @@
|
|||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using Greenshot.Plugin;
|
||||
using IniFile;
|
||||
|
||||
namespace GreenshotPlugin.Core {
|
||||
public enum Destination {
|
||||
Editor, FileDefault, FileWithDialog, Clipboard, Printer, EMail
|
||||
public enum ClipboardFormat {
|
||||
PNG, DIB, HTML
|
||||
}
|
||||
public enum OutputFormat {
|
||||
bmp, gif, jpg, png, tiff
|
||||
|
@ -34,24 +38,19 @@ namespace GreenshotPlugin.Core {
|
|||
Screen, GDI, Aero, AeroTransparent, Auto
|
||||
}
|
||||
public enum EmailFormat {
|
||||
TXT, HTML
|
||||
}
|
||||
public enum UpdateCheckInterval {
|
||||
Never,
|
||||
Daily,
|
||||
Weekly,
|
||||
Monthly
|
||||
MAPI, OUTLOOK_TXT, OUTLOOK_HTML
|
||||
}
|
||||
public enum EmailExport {
|
||||
AlwaysNew,
|
||||
TryOpenElseNew
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Description of CoreConfiguration.
|
||||
/// </summary>
|
||||
[IniSection("Core", Description="Greenshot core configuration")]
|
||||
public class CoreConfiguration : IniSection {
|
||||
[IniProperty("Language", Description="The language in IETF format (e.g. en-EN)", DefaultValue="en-EN")]
|
||||
[IniProperty("Language", Description="The language in IETF format (e.g. en-EN)")]
|
||||
public string Language;
|
||||
|
||||
[IniProperty("RegionHotkey", Description="Hotkey for starting the region capture", DefaultValue="PrintScreen")]
|
||||
|
@ -68,7 +67,9 @@ namespace GreenshotPlugin.Core {
|
|||
[IniProperty("IsFirstLaunch", Description="Is this the first time launch?", DefaultValue="true")]
|
||||
public bool IsFirstLaunch;
|
||||
[IniProperty("Destinations", Separator=",", Description="Which destinations? Options are: Editor, FileDefault, FileWithDialog, Clipboard, Printer, EMail", DefaultValue="Editor")]
|
||||
public List<Destination> OutputDestinations = new List<Destination>();
|
||||
public List<string> OutputDestinations = new List<string>();
|
||||
[IniProperty("ClipboardFormats", Separator=",", Description="Specify which formats we copy on the clipboard? Options are: PNG,HTML and DIB", DefaultValue="PNG,HTML,DIB")]
|
||||
public List<ClipboardFormat> ClipboardFormats = new List<ClipboardFormat>();
|
||||
|
||||
[IniProperty("CaptureMousepointer", Description="Should the mouse be captured?", DefaultValue="true")]
|
||||
public bool CaptureMousepointer;
|
||||
|
@ -78,11 +79,16 @@ namespace GreenshotPlugin.Core {
|
|||
public int CaptureDelay;
|
||||
[IniProperty("WindowCaptureMode", Description="The capture mode used to capture a Window.", DefaultValue="Auto")]
|
||||
public WindowCaptureMode WindowCaptureMode;
|
||||
[IniProperty("WindowCaptureAllChildLocations", Description="Enable/disable capture all children, very slow but will make it possible to use this information in the editor.", DefaultValue="False")]
|
||||
public bool WindowCaptureAllChildLocations;
|
||||
|
||||
[IniProperty("DWMBackgroundColor", Description="The background color for a DWM window capture.")]
|
||||
public Color DWMBackgroundColor;
|
||||
|
||||
[IniProperty("PlayCameraSound", Description="Play a camera sound after taking a capture.", DefaultValue="false")]
|
||||
[IniProperty("PlayCameraSound", LanguageKey="settings_playsound",Description="Play a camera sound after taking a capture.", DefaultValue="false")]
|
||||
public bool PlayCameraSound = false;
|
||||
[IniProperty("ShowTrayNotification", LanguageKey="settings_shownotify",Description="Show a notification from the systray when a capture is taken.", DefaultValue="true")]
|
||||
public bool ShowTrayNotification = true;
|
||||
|
||||
[IniProperty("OutputFilePath", Description="Output file path.")]
|
||||
public string OutputFilePath;
|
||||
|
@ -93,8 +99,8 @@ namespace GreenshotPlugin.Core {
|
|||
[IniProperty("OutputFileReduceColors", Description="If set to true, than the colors of the output file are reduced to 256 (8-bit) colors", DefaultValue="false")]
|
||||
public bool OutputFileReduceColors;
|
||||
|
||||
[IniProperty("OutputEMailFormat", Description="Default type for emails. (txt, html)", DefaultValue="html")]
|
||||
public EmailFormat OutputEMailFormat = EmailFormat.HTML;
|
||||
[IniProperty("OutputEMailFormat", Description="Default type for emails. (txt, html)")]
|
||||
public EmailFormat OutputEMailFormat;
|
||||
[IniProperty("OutputOutlookMethod", Description="How to export to outlook (AlwaysNew= always open a new one, TryOpenElseNew=look for open email else create a new)", DefaultValue="AlwaysNew")]
|
||||
public EmailExport OutputOutlookMethod;
|
||||
|
||||
|
@ -107,27 +113,32 @@ namespace GreenshotPlugin.Core {
|
|||
public int OutputFileJpegQuality;
|
||||
[IniProperty("OutputFilePromptJpegQuality", Description="Ask for the JPEQ quality before saving?", DefaultValue="false")]
|
||||
public bool OutputFilePromptJpegQuality;
|
||||
[IniProperty("OutputFileIncrementingNumber", Description="The number for the %NUM% in the filename pattern, is increased automatically after each save.", DefaultValue="1")]
|
||||
[IniProperty("OutputFileIncrementingNumber", Description="The number for the ${NUM} in the filename pattern, is increased automatically after each save.", DefaultValue="1")]
|
||||
public uint OutputFileIncrementingNumber;
|
||||
|
||||
[IniProperty("OutputPrintPromptOptions", Description="Ask for print options when printing?", DefaultValue="true")]
|
||||
[IniProperty("OutputPrintPromptOptions", LanguageKey="settings_alwaysshowprintoptionsdialog", Description="Ask for print options when printing?", DefaultValue="true")]
|
||||
public bool OutputPrintPromptOptions;
|
||||
[IniProperty("OutputPrintAllowRotate", Description="Allow rotating the picture for fitting on paper?", DefaultValue="true")]
|
||||
[IniProperty("OutputPrintAllowRotate", LanguageKey="printoptions_allowrotate", Description="Allow rotating the picture for fitting on paper?", DefaultValue="true")]
|
||||
public bool OutputPrintAllowRotate;
|
||||
[IniProperty("OutputPrintAllowEnlarge", Description="Allow growing the picture for fitting on paper?", DefaultValue="true")]
|
||||
[IniProperty("OutputPrintAllowEnlarge", LanguageKey="printoptions_allowenlarge", Description="Allow growing the picture for fitting on paper?", DefaultValue="true")]
|
||||
public bool OutputPrintAllowEnlarge;
|
||||
[IniProperty("OutputPrintAllowShrink", Description="Allow shrinking the picture for fitting on paper?", DefaultValue="true")]
|
||||
[IniProperty("OutputPrintAllowShrink", LanguageKey="printoptions_allowshrink", Description="Allow shrinking the picture for fitting on paper?", DefaultValue="true")]
|
||||
public bool OutputPrintAllowShrink;
|
||||
[IniProperty("OutputPrintCenter", Description="Center image when printing?", DefaultValue="true")]
|
||||
[IniProperty("OutputPrintCenter", LanguageKey="printoptions_allowcenter", Description="Center image when printing?", DefaultValue="true")]
|
||||
public bool OutputPrintCenter;
|
||||
[IniProperty("OutputPrintInverted", Description="Print image inverted (use e.g. for console captures)", DefaultValue="false")]
|
||||
[IniProperty("OutputPrintInverted", LanguageKey="printoptions_inverted", Description="Print image inverted (use e.g. for console captures)", DefaultValue="false")]
|
||||
public bool OutputPrintInverted;
|
||||
[IniProperty("OutputPrintTimestamp", Description="Print timestamp on print?", DefaultValue="true")]
|
||||
[IniProperty("OutputPrintTimestamp", LanguageKey="printoptions_timestamp", Description="Print timestamp on print?", DefaultValue="true")]
|
||||
public bool OutputPrintTimestamp;
|
||||
|
||||
[IniProperty("UseProxy", Description="Use your global proxy?", DefaultValue="True")]
|
||||
public bool UseProxy;
|
||||
[IniProperty("IECapture", Description="Enable/disable IE capture", DefaultValue="True")]
|
||||
public bool IECapture;
|
||||
[IniProperty("IEFieldCapture", Description="Enable/disable IE field capture, very slow but will make it possible to annotate the fields of a capture in the editor.", DefaultValue="False")]
|
||||
public bool IEFieldCapture;
|
||||
[IniProperty("AutoCropDifference", Description="Sets how to compare the colors for the autocrop detection, the higher the more is 'selected'. Possible values are from 0 to 255, where everything above ~150 doesn't make much sense!", DefaultValue="10")]
|
||||
public int AutoCropDifference;
|
||||
|
||||
[IniProperty("IncludePlugins", Description="Comma separated list of Plugins which are allowed. If something in the list, than every plugin not in the list will not be loaded!")]
|
||||
public List<string> IncludePlugins;
|
||||
|
@ -140,6 +151,78 @@ namespace GreenshotPlugin.Core {
|
|||
[IniProperty("LastUpdateCheck", Description="Last update check")]
|
||||
public DateTime LastUpdateCheck;
|
||||
|
||||
[IniProperty("ThumnailPreview", Description="Enable/disable thumbnail previews", DefaultValue="True")]
|
||||
public bool ThumnailPreview;
|
||||
|
||||
[IniProperty("NoGDICaptureForProduct", Description="List of products for which GDI capturing doesn't work.", DefaultValue="IntelliJ IDEA")]
|
||||
public List<string> NoGDICaptureForProduct;
|
||||
[IniProperty("NoDWMCaptureForProduct", Description="List of products for which DWM capturing doesn't work.", DefaultValue="Citrix ICA Client")]
|
||||
public List<string> NoDWMCaptureForProduct;
|
||||
|
||||
[IniProperty("OptimizeForRDP", Description="Make some optimizations for remote desktop usage", DefaultValue="False")]
|
||||
public bool OptimizeForRDP;
|
||||
|
||||
[IniProperty("ActiveTitleFixes", Description="The fixes that are active.")]
|
||||
public List<string> ActiveTitleFixes;
|
||||
|
||||
[IniProperty("TitleFixMatcher", Description="The regular expressions to match the title with.")]
|
||||
public Dictionary<string, string> TitleFixMatcher;
|
||||
|
||||
[IniProperty("TitleFixReplacer", Description="The replacements for the matchers.")]
|
||||
public Dictionary<string, string> TitleFixReplacer;
|
||||
|
||||
[IniProperty("ExperimentalFeatures", Description="A list which allows us to enable certain experimental features", ExcludeIfNull=true)]
|
||||
public List<string> ExperimentalFeatures;
|
||||
|
||||
/// <summary>
|
||||
/// A helper method which returns true if the supplied experimental feature is enabled
|
||||
/// </summary>
|
||||
/// <param name="experimentalFeature"></param>
|
||||
/// <returns></returns>
|
||||
public bool isExperimentalFeatureEnabled(string experimentalFeature) {
|
||||
return (ExperimentalFeatures != null && ExperimentalFeatures.Contains(experimentalFeature));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to check if it is allowed to capture the process using DWM
|
||||
/// </summary>
|
||||
/// <param name="process">Process owning the window</param>
|
||||
/// <returns>true if it's allowed</returns>
|
||||
public bool isDWMAllowed(Process process) {
|
||||
if (process != null) {
|
||||
if (NoDWMCaptureForProduct != null && NoDWMCaptureForProduct.Count > 0) {
|
||||
try {
|
||||
string productName = process.MainModule.FileVersionInfo.ProductName;
|
||||
if (productName != null && NoDWMCaptureForProduct.Contains(productName.ToLower())) {
|
||||
return false;
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to check if it is allowed to capture the process using GDI
|
||||
/// </summary>
|
||||
/// <param name="processName">Process owning the window</param>
|
||||
/// <returns>true if it's allowed</returns>
|
||||
public bool isGDIAllowed(Process process) {
|
||||
if (process != null) {
|
||||
if (NoGDICaptureForProduct != null && NoGDICaptureForProduct.Count > 0) {
|
||||
try {
|
||||
string productName = process.MainModule.FileVersionInfo.ProductName;
|
||||
if (productName != null && NoGDICaptureForProduct.Contains(productName.ToLower())) {
|
||||
return false;
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// change to false for releases
|
||||
public bool CheckUnstable = true;
|
||||
|
||||
|
@ -154,12 +237,51 @@ namespace GreenshotPlugin.Core {
|
|||
case "PluginBacklist":
|
||||
return new List<string>();
|
||||
case "OutputFileAsFullpath":
|
||||
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop),"dummy.png");
|
||||
if (IniConfig.IsPortable) {
|
||||
return Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots\dummy.png");
|
||||
} else {
|
||||
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop),"dummy.png");
|
||||
}
|
||||
case "OutputFilePath":
|
||||
if (IniConfig.IsPortable) {
|
||||
string pafOutputFilePath = Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots");
|
||||
if (!Directory.Exists(pafOutputFilePath)) {
|
||||
try {
|
||||
Directory.CreateDirectory(pafOutputFilePath);
|
||||
return pafOutputFilePath;
|
||||
} catch(Exception) {
|
||||
// Problem creating directory, fallback to Desktop
|
||||
}
|
||||
} else {
|
||||
return pafOutputFilePath;
|
||||
}
|
||||
}
|
||||
return Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
|
||||
|
||||
case "DWMBackgroundColor":
|
||||
return Color.White;
|
||||
case "OutputEMailFormat":
|
||||
if (EmailConfigHelper.HasOutlook()) {
|
||||
return EmailFormat.OUTLOOK_HTML;
|
||||
}
|
||||
return EmailFormat.MAPI;
|
||||
case "ActiveTitleFixes":
|
||||
List<string> activeDefaults = new List<string>();
|
||||
activeDefaults.Add("Firefox");
|
||||
activeDefaults.Add("IE");
|
||||
activeDefaults.Add("Chrome");
|
||||
return activeDefaults;
|
||||
case "TitleFixMatcher":
|
||||
Dictionary<string, string> matcherDefaults = new Dictionary<string, string>();
|
||||
matcherDefaults.Add("Firefox", " - Mozilla Firefox.*");
|
||||
matcherDefaults.Add("IE", " - (Microsoft|Windows) Internet Explorer.*");
|
||||
matcherDefaults.Add("Chrome", " - Google Chrome.*");
|
||||
return matcherDefaults;
|
||||
case "TitleFixReplacer":
|
||||
Dictionary<string, string> replacerDefaults = new Dictionary<string, string>();
|
||||
replacerDefaults.Add("Firefox", "");
|
||||
replacerDefaults.Add("IE", "");
|
||||
replacerDefaults.Add("Chrome", "");
|
||||
return replacerDefaults;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -178,18 +300,51 @@ namespace GreenshotPlugin.Core {
|
|||
return propertyValue.Replace('|',',');
|
||||
}
|
||||
}
|
||||
if("OutputFilePath".Equals(propertyName)) {
|
||||
if (string.IsNullOrEmpty(propertyValue)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return base.PreCheckValue(propertyName, propertyValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method will be called after reading the configuration, so eventually some corrections can be made
|
||||
/// </summary>
|
||||
public override void PostCheckValues() {
|
||||
public override void AfterLoad() {
|
||||
if (OutputDestinations == null) {
|
||||
OutputDestinations = new List<Destination>();
|
||||
OutputDestinations = new List<string>();
|
||||
}
|
||||
// Make sure there is an output!
|
||||
if (OutputDestinations.Count == 0) {
|
||||
OutputDestinations.Add(Destination.Editor);
|
||||
OutputDestinations.Add("Editor");
|
||||
}
|
||||
// Check for Outlook, if it's not installed force email format to MAPI
|
||||
if (OutputEMailFormat != EmailFormat.MAPI && !EmailConfigHelper.HasOutlook()) {
|
||||
OutputEMailFormat = EmailFormat.MAPI;
|
||||
}
|
||||
// Prevent both settings at once, bug #3435056
|
||||
if (OutputDestinations.Contains("Clipboard") && OutputFileCopyPathToClipboard) {
|
||||
OutputFileCopyPathToClipboard = false;
|
||||
}
|
||||
|
||||
// Make sure the lists are lowercase, to speedup the check
|
||||
if (NoGDICaptureForProduct != null) {
|
||||
for(int i=0; i< NoGDICaptureForProduct.Count; i++) {
|
||||
NoGDICaptureForProduct[i] = NoGDICaptureForProduct[i].ToLower();
|
||||
}
|
||||
}
|
||||
if (NoDWMCaptureForProduct != null) {
|
||||
for(int i=0; i< NoDWMCaptureForProduct.Count; i++) {
|
||||
NoDWMCaptureForProduct[i] = NoDWMCaptureForProduct[i].ToLower();
|
||||
}
|
||||
}
|
||||
|
||||
if (AutoCropDifference < 0) {
|
||||
AutoCropDifference = 0;
|
||||
}
|
||||
if (AutoCropDifference > 255) {
|
||||
AutoCropDifference = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
*/
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
|
38
GreenshotPlugin/Core/DisplayKeyAttribute.cs
Normal file
38
GreenshotPlugin/Core/DisplayKeyAttribute.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
* For more information see: http://getgreenshot.org/
|
||||
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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 GreenshotPlugin.Core {
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public sealed class DisplayKeyAttribute : Attribute {
|
||||
private readonly string value;
|
||||
public string Value {
|
||||
get { return value; }
|
||||
}
|
||||
|
||||
public DisplayKeyAttribute(string v) {
|
||||
this.value = v;
|
||||
}
|
||||
|
||||
public DisplayKeyAttribute() {
|
||||
}
|
||||
}
|
||||
}
|
84
GreenshotPlugin/Core/EmailConfigHelper.cs
Normal file
84
GreenshotPlugin/Core/EmailConfigHelper.cs
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
* For more information see: http://getgreenshot.org/
|
||||
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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.IO;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace GreenshotPlugin.Core {
|
||||
/// <summary>
|
||||
/// Description of EmailConfigHelper.
|
||||
/// </summary>
|
||||
public static class EmailConfigHelper {
|
||||
private const string OUTLOOK_PATH_KEY = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\OUTLOOK.EXE";
|
||||
private const string MAPI_CLIENT_KEY = @"SOFTWARE\Clients\Mail";
|
||||
private const string MAPI_LOCATION_KEY = @"SOFTWARE\Microsoft\Windows Messaging Subsystem";
|
||||
private const string MAPI_KEY = @"MAPI";
|
||||
|
||||
public static string GetMapiClient() {
|
||||
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(MAPI_CLIENT_KEY, false)) {
|
||||
if (key != null) {
|
||||
return (string)key.GetValue("");
|
||||
}
|
||||
}
|
||||
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(MAPI_CLIENT_KEY, false)) {
|
||||
if (key != null) {
|
||||
return (string)key.GetValue("");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool HasMAPI() {
|
||||
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(MAPI_LOCATION_KEY, false)) {
|
||||
if (key != null) {
|
||||
return "1".Equals(key.GetValue(MAPI_KEY, "0"));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetOutlookExePath() {
|
||||
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(OUTLOOK_PATH_KEY, false)) {
|
||||
if (key != null) {
|
||||
// "" is the default key, which should point to the outlook location
|
||||
return (string)key.GetValue("");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if Outlook is installed
|
||||
/// </summary>
|
||||
/// <returns>Returns true if outlook is installed</returns>
|
||||
public static bool HasOutlook() {
|
||||
string outlookPath = GetOutlookExePath();
|
||||
if (outlookPath != null) {
|
||||
if (File.Exists(outlookPath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
90
GreenshotPlugin/Core/EnumExtensions.cs
Normal file
90
GreenshotPlugin/Core/EnumExtensions.cs
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
* For more information see: http://getgreenshot.org/
|
||||
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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 GreenshotPlugin.Core {
|
||||
public static class EnumerationExtensions {
|
||||
public static bool Has<T>(this System.Enum type, T value) {
|
||||
Type underlyingType = Enum.GetUnderlyingType(value.GetType());
|
||||
try {
|
||||
if (underlyingType == typeof(int)) {
|
||||
return (((int)(object)type & (int)(object)value) == (int)(object)value);
|
||||
} else if (underlyingType == typeof(uint)) {
|
||||
return (((uint)(object)type & (uint)(object)value) == (uint)(object)value);
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool Is<T>(this System.Enum type, T value) {
|
||||
Type underlyingType = Enum.GetUnderlyingType(value.GetType());
|
||||
try {
|
||||
if (underlyingType == typeof(int)) {
|
||||
return (int)(object)type == (int)(object)value;
|
||||
} else if (underlyingType == typeof(uint)) {
|
||||
return (uint)(object)type == (uint)(object)value;
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a flag to an enum
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static T Add<T>(this System.Enum type, T value) {
|
||||
Type underlyingType = Enum.GetUnderlyingType(value.GetType());
|
||||
try {
|
||||
if (underlyingType == typeof(int)) {
|
||||
return (T)(object)(((int)(object)type | (int)(object)value));
|
||||
} else if (underlyingType == typeof(uint)) {
|
||||
return (T)(object)(((uint)(object)type | (uint)(object)value));
|
||||
}
|
||||
} catch(Exception ex) {
|
||||
throw new ArgumentException(string.Format("Could not append value '{0}' to enumerated type '{1}'.", value, typeof(T).Name), ex);
|
||||
}
|
||||
throw new ArgumentException(string.Format("Could not append value '{0}' to enumerated type '{1}'.", value, typeof(T).Name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a flag from an enum type
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static T Remove<T>(this System.Enum type, T value) {
|
||||
Type underlyingType = Enum.GetUnderlyingType(value.GetType());
|
||||
try {
|
||||
if (underlyingType == typeof(int)) {
|
||||
return (T)(object)(((int)(object)type & ~(int)(object)value));
|
||||
} else if (underlyingType == typeof(uint)) {
|
||||
return (T)(object)(((uint)(object)type & ~(uint)(object)value));
|
||||
}
|
||||
} catch(Exception ex) {
|
||||
throw new ArgumentException(string.Format("Could not remove value '{0}' from enumerated type '{1}'.", value, typeof(T).Name), ex);
|
||||
}
|
||||
throw new ArgumentException(string.Format("Could not remove value '{0}' from enumerated type '{1}'.", value, typeof(T).Name));
|
||||
}
|
||||
}
|
||||
}
|
27
GreenshotPlugin/Core/ExtensionAttribute.cs
Normal file
27
GreenshotPlugin/Core/ExtensionAttribute.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
* For more information see: http://getgreenshot.org/
|
||||
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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 System.Runtime.CompilerServices {
|
||||
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public sealed class ExtensionAttribute : Attribute {}
|
||||
}
|
|
@ -26,7 +26,7 @@ namespace GreenshotPlugin.Core {
|
|||
/// <summary>
|
||||
/// Centralized storage of the icons & bitmaps
|
||||
/// </summary>
|
||||
public class GreenshotResources {
|
||||
public static class GreenshotResources {
|
||||
private static ComponentResourceManager greenshotResources = new ComponentResourceManager(typeof(GreenshotResources));
|
||||
|
||||
public static Image getImage(string imageName) {
|
||||
|
|
|
@ -19,13 +19,14 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace GreenshotPlugin.Core {
|
||||
/// <summary>
|
||||
/// Description of IEHelper.
|
||||
/// </summary>
|
||||
public class IEHelper {
|
||||
public static class IEHelper {
|
||||
// Internet explorer Registry key
|
||||
private const string IE_KEY = @"Software\Microsoft\Internet Explorer";
|
||||
/// <summary>
|
||||
|
@ -33,20 +34,65 @@ namespace GreenshotPlugin.Core {
|
|||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static int IEVersion() {
|
||||
int version = 7;
|
||||
int version = 7;
|
||||
// Seeing if IE 9 is used, here we need another offset!
|
||||
using (RegistryKey ieKey = Registry.LocalMachine.OpenSubKey(IE_KEY, false)) {
|
||||
if (ieKey != null) {
|
||||
object versionKey = ieKey.GetValue("Version");
|
||||
if (versionKey != null) {
|
||||
int.TryParse(versionKey.ToString().Substring(0,1), out version);
|
||||
}
|
||||
}
|
||||
if (ieKey != null) {
|
||||
object versionKey = ieKey.GetValue("Version");
|
||||
if (versionKey != null) {
|
||||
int.TryParse(versionKey.ToString().Substring(0,1), out version);
|
||||
}
|
||||
}
|
||||
}
|
||||
return version;
|
||||
return version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the DirectUI window for MSAA (Accessible)
|
||||
/// </summary>
|
||||
/// <param name="browserWindowDetails">The browser WindowDetails</param>
|
||||
/// <returns>WindowDetails for the DirectUI window</returns>
|
||||
public static WindowDetails GetDirectUI(WindowDetails browserWindowDetails) {
|
||||
WindowDetails tmpWD = browserWindowDetails;
|
||||
// Since IE 9 the TabBandClass is less deep!
|
||||
if (IEHelper.IEVersion() < 9) {
|
||||
tmpWD = tmpWD.GetChild("CommandBarClass");
|
||||
if (tmpWD != null) {
|
||||
tmpWD = tmpWD.GetChild("ReBarWindow32");
|
||||
}
|
||||
}
|
||||
if (tmpWD != null) {
|
||||
tmpWD = tmpWD.GetChild("TabBandClass");
|
||||
}
|
||||
if (tmpWD != null) {
|
||||
tmpWD = tmpWD.GetChild("DirectUIHWND");;
|
||||
}
|
||||
return tmpWD;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return an IEnumerable with the currently opened IE urls
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<string> GetIEUrls() {
|
||||
List<string> urls = new List<string>();
|
||||
// Find the IE window
|
||||
foreach (WindowDetails ieWindow in WindowDetails.GetAllWindows("IEFrame")) {
|
||||
WindowDetails directUIWD = GetDirectUI(ieWindow);
|
||||
if (directUIWD != null) {
|
||||
Accessible ieAccessible = new Accessible(directUIWD.Handle);
|
||||
List<string> ieUrls = ieAccessible.IETabUrls;
|
||||
if (ieUrls != null && ieUrls.Count > 0) {
|
||||
foreach(string url in ieUrls) {
|
||||
if (!urls.Contains(url)) {
|
||||
urls.Add(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEHelper() {
|
||||
return urls;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
301
GreenshotPlugin/Core/ImageHelper.cs
Normal file
301
GreenshotPlugin/Core/ImageHelper.cs
Normal file
|
@ -0,0 +1,301 @@
|
|||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
* For more information see: http://getgreenshot.org/
|
||||
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Drawing.Drawing2D;
|
||||
|
||||
using GreenshotPlugin.UnmanagedHelpers;
|
||||
using GreenshotPlugin.Core;
|
||||
using IniFile;
|
||||
|
||||
namespace GreenshotPlugin.Core {
|
||||
/// <summary>
|
||||
/// Description of ImageHelper.
|
||||
/// </summary>
|
||||
public static class ImageHelper {
|
||||
private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ImageHelper));
|
||||
private static CoreConfiguration conf = IniConfig.GetIniSection<CoreConfiguration>();
|
||||
|
||||
public static Image CreateThumbnail(Image image, int thumbWidth, int thumbHeight) {
|
||||
return CreateThumbnail(image, thumbWidth, thumbHeight, -1, -1);
|
||||
}
|
||||
public static Image CreateThumbnail(Image image, int thumbWidth, int thumbHeight, int maxWidth, int maxHeight) {
|
||||
int srcWidth=image.Width;
|
||||
int srcHeight=image.Height;
|
||||
if (thumbHeight < 0) {
|
||||
thumbHeight = (int)(thumbWidth * ((float)srcHeight / (float)srcWidth));
|
||||
}
|
||||
if (thumbWidth < 0) {
|
||||
thumbWidth = (int)(thumbHeight * ((float)srcWidth / (float)srcHeight));
|
||||
}
|
||||
if (maxWidth > 0 && thumbWidth > maxWidth) {
|
||||
thumbWidth = Math.Min(thumbWidth, maxWidth);
|
||||
thumbHeight = (int)(thumbWidth * ((float)srcHeight / (float)srcWidth));
|
||||
}
|
||||
if (maxHeight > 0 && thumbHeight > maxHeight) {
|
||||
thumbHeight = Math.Min(thumbHeight, maxHeight);
|
||||
thumbWidth = (int)(thumbHeight * ((float)srcWidth / (float)srcHeight));
|
||||
}
|
||||
|
||||
Bitmap bmp = new Bitmap(thumbWidth, thumbHeight);
|
||||
using (Graphics gr = System.Drawing.Graphics.FromImage(bmp)) {
|
||||
gr.SmoothingMode = SmoothingMode.HighQuality ;
|
||||
gr.CompositingQuality = CompositingQuality.HighQuality;
|
||||
gr.InterpolationMode = InterpolationMode.High;
|
||||
System.Drawing.Rectangle rectDestination = new System.Drawing.Rectangle(0, 0, thumbWidth, thumbHeight);
|
||||
gr.DrawImage(image, rectDestination, 0, 0, srcWidth, srcHeight, GraphicsUnit.Pixel);
|
||||
}
|
||||
return bmp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Crops the image to the specified rectangle
|
||||
/// </summary>
|
||||
/// <param name="image">Image to crop</param>
|
||||
/// <param name="cropRectangle">Rectangle with bitmap coordinates, will be "intersected" to the bitmap</param>
|
||||
public static bool Crop(ref Image image, ref Rectangle cropRectangle) {
|
||||
Image returnImage = null;
|
||||
if (image != null && image is Bitmap && ((image.Width * image.Height) > 0)) {
|
||||
cropRectangle.Intersect(new Rectangle(0,0, image.Width, image.Height));
|
||||
if (cropRectangle.Width != 0 || cropRectangle.Height != 0) {
|
||||
returnImage = (image as Bitmap).Clone(cropRectangle, image.PixelFormat);
|
||||
image.Dispose();
|
||||
image = returnImage;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
LOG.Warn("Can't crop a null/zero size image!");
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Rectangle FindAutoCropRectangle(BitmapBuffer buffer, Point colorPoint) {
|
||||
Rectangle cropRectangle = Rectangle.Empty;
|
||||
Color referenceColor = buffer.GetColorAtWithoutAlpha(colorPoint.X,colorPoint.Y);
|
||||
Point min = new Point(int.MaxValue, int.MaxValue);
|
||||
Point max = new Point(int.MinValue, int.MinValue);
|
||||
|
||||
if (conf.AutoCropDifference > 0) {
|
||||
for(int y = 0; y < buffer.Height; y++) {
|
||||
for(int x = 0; x < buffer.Width; x++) {
|
||||
Color currentColor = buffer.GetColorAt(x, y);
|
||||
int diffR = Math.Abs(currentColor.R - referenceColor.R);
|
||||
int diffG = Math.Abs(currentColor.G - referenceColor.G);
|
||||
int diffB = Math.Abs(currentColor.B - referenceColor.B);
|
||||
if (((diffR+diffG+diffB)/3) > conf.AutoCropDifference) {
|
||||
if (x < min.X) min.X = x;
|
||||
if (y < min.Y) min.Y = y;
|
||||
if (x > max.X) max.X = x;
|
||||
if (y > max.Y) max.Y = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int y = 0; y < buffer.Height; y++) {
|
||||
for(int x = 0; x < buffer.Width; x++) {
|
||||
Color currentColor = buffer.GetColorAtWithoutAlpha(x, y);
|
||||
if (referenceColor.Equals(currentColor)) {
|
||||
if (x < min.X) min.X = x;
|
||||
if (y < min.Y) min.Y = y;
|
||||
if (x > max.X) max.X = x;
|
||||
if (y > max.Y) max.Y = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(Point.Empty.Equals(min) && max.Equals(new Point(buffer.Width-1, buffer.Height-1)))) {
|
||||
if (!(min.X == int.MaxValue || min.Y == int.MaxValue || max.X == int.MinValue || min.X == int.MinValue)) {
|
||||
cropRectangle = new Rectangle(min.X, min.Y, max.X - min.X + 1, max.Y - min.Y + 1);
|
||||
}
|
||||
}
|
||||
return cropRectangle;
|
||||
}
|
||||
/// <summary>
|
||||
/// Get a rectangle for the image which crops the image of all colors equal to that on 0,0
|
||||
/// </summary>
|
||||
/// <param name="image"></param>
|
||||
/// <returns>Rectangle</returns>
|
||||
public static Rectangle FindAutoCropRectangle(Image image) {
|
||||
Rectangle cropRectangle = Rectangle.Empty;
|
||||
using (BitmapBuffer buffer = new BitmapBuffer((Bitmap)image, false)) {
|
||||
buffer.Lock();
|
||||
Rectangle currentRectangle = Rectangle.Empty;
|
||||
List<Point> checkPoints = new List<Point>();
|
||||
// Top Left
|
||||
checkPoints.Add(new Point(0, 0));
|
||||
// Bottom Left
|
||||
checkPoints.Add(new Point(0, image.Height-1));
|
||||
// Top Right
|
||||
checkPoints.Add(new Point(image.Width-1, 0));
|
||||
// Bottom Right
|
||||
checkPoints.Add( new Point(image.Width-1, image.Height-1));
|
||||
|
||||
// find biggest area
|
||||
foreach(Point checkPoint in checkPoints) {
|
||||
currentRectangle = FindAutoCropRectangle(buffer, checkPoint);
|
||||
if (currentRectangle.Width * currentRectangle.Height > cropRectangle.Width * cropRectangle.Height) {
|
||||
cropRectangle = currentRectangle;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cropRectangle;
|
||||
}
|
||||
|
||||
public static Bitmap LoadBitmap(string filename) {
|
||||
if (string.IsNullOrEmpty(filename)) {
|
||||
return null;
|
||||
}
|
||||
Bitmap fileBitmap = null;
|
||||
LOG.InfoFormat("Loading image from file {0}", filename);
|
||||
// Fixed lock problem Bug #3431881
|
||||
using (Stream imageFileStream = File.OpenRead(filename)) {
|
||||
// And fixed problem that the bitmap stream is disposed... by Cloning the image
|
||||
// This also ensures the bitmap is correctly created
|
||||
|
||||
if (filename.EndsWith(".ico")) {
|
||||
// Icon logic, try to get the Vista icon, else the biggest possible
|
||||
try {
|
||||
using (Image tmpImage = ExtractVistaIcon(imageFileStream)) {
|
||||
if (tmpImage != null) {
|
||||
fileBitmap = CloneImageToBitmap(tmpImage);
|
||||
}
|
||||
}
|
||||
} catch (Exception vistaIconException) {
|
||||
LOG.Warn("Can't read icon from " + filename, vistaIconException);
|
||||
}
|
||||
if (fileBitmap == null) {
|
||||
try {
|
||||
// No vista icon, try normal icon
|
||||
imageFileStream.Position = 0;
|
||||
// We create a copy of the bitmap, so everything else can be disposed
|
||||
using (Icon tmpIcon = new Icon(imageFileStream, new Size(1024,1024))) {
|
||||
using (Image tmpImage = tmpIcon.ToBitmap()) {
|
||||
fileBitmap = ImageHelper.CloneImageToBitmap(tmpImage);
|
||||
}
|
||||
}
|
||||
} catch (Exception iconException) {
|
||||
LOG.Warn("Can't read icon from " + filename, iconException);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fileBitmap == null) {
|
||||
// We create a copy of the bitmap, so everything else can be disposed
|
||||
imageFileStream.Position = 0;
|
||||
using (Image tmpImage = Image.FromStream(imageFileStream, true, true)) {
|
||||
fileBitmap = ImageHelper.CloneImageToBitmap(tmpImage);
|
||||
}
|
||||
}
|
||||
}
|
||||
return fileBitmap;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clone the image to a bitmap
|
||||
/// </summary>
|
||||
/// <param name="srcImage">Image to clone</param>
|
||||
/// <returns>Bitmap</returns>
|
||||
public static Bitmap CloneImageToBitmap(Image srcImage) {
|
||||
Bitmap returnImage;
|
||||
int width = srcImage.Width;
|
||||
int height = srcImage.Height;
|
||||
float horizontalResolution = srcImage.HorizontalResolution;
|
||||
float verticalResolution = srcImage.VerticalResolution;
|
||||
PixelFormat pixelFormat = srcImage.PixelFormat;
|
||||
if (srcImage is Metafile) {
|
||||
pixelFormat = PixelFormat.Format32bppArgb;
|
||||
}
|
||||
// Make sure Greenshot supports the pixelformat, if not convert to one we support
|
||||
if (!isSupported(pixelFormat)) {
|
||||
pixelFormat = PixelFormat.Format24bppRgb;
|
||||
}
|
||||
returnImage = new Bitmap(width, height, pixelFormat);
|
||||
returnImage.SetResolution(horizontalResolution, verticalResolution);
|
||||
using (Graphics graphics = Graphics.FromImage(returnImage)) {
|
||||
if (Image.IsAlphaPixelFormat(pixelFormat)) {
|
||||
graphics.Clear(Color.Transparent);
|
||||
} else {
|
||||
graphics.Clear(Color.White);
|
||||
}
|
||||
graphics.DrawImageUnscaled(srcImage, 0, 0);
|
||||
}
|
||||
return returnImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we support the supplied PixelFormat
|
||||
*/
|
||||
private static bool isSupported(PixelFormat pixelformat) {
|
||||
return (PixelFormat.Format32bppArgb.Equals(pixelformat)||
|
||||
PixelFormat.Format32bppRgb.Equals(pixelformat) ||
|
||||
PixelFormat.Format24bppRgb.Equals(pixelformat));
|
||||
}
|
||||
|
||||
// Based on: http://www.codeproject.com/KB/cs/IconExtractor.aspx
|
||||
// And a hint from: http://www.codeproject.com/KB/cs/IconLib.aspx
|
||||
public static Bitmap ExtractVistaIcon(Stream iconStream) {
|
||||
const int SizeICONDIR = 6;
|
||||
const int SizeICONDIRENTRY = 16;
|
||||
Bitmap bmpPngExtracted = null;
|
||||
try {
|
||||
byte[] srcBuf = new byte[iconStream.Length];
|
||||
iconStream.Read(srcBuf, 0, (int)iconStream.Length);
|
||||
int iCount = BitConverter.ToInt16(srcBuf, 4);
|
||||
for (int iIndex=0; iIndex<iCount; iIndex++) {
|
||||
int iWidth = srcBuf[SizeICONDIR + SizeICONDIRENTRY * iIndex];
|
||||
int iHeight = srcBuf[SizeICONDIR + SizeICONDIRENTRY * iIndex + 1];
|
||||
int iBitCount = BitConverter.ToInt16(srcBuf, SizeICONDIR + SizeICONDIRENTRY * iIndex + 6);
|
||||
if (iWidth == 0 && iHeight == 0) {
|
||||
int iImageSize = BitConverter.ToInt32(srcBuf, SizeICONDIR + SizeICONDIRENTRY * iIndex + 8);
|
||||
int iImageOffset = BitConverter.ToInt32(srcBuf, SizeICONDIR + SizeICONDIRENTRY * iIndex + 12);
|
||||
using (MemoryStream destStream = new MemoryStream()) {
|
||||
using (BinaryWriter writer = new BinaryWriter(destStream)) {
|
||||
writer.Write(srcBuf, iImageOffset, iImageSize);
|
||||
destStream.Seek(0, System.IO.SeekOrigin.Begin);
|
||||
bmpPngExtracted = new Bitmap(destStream); // This is PNG! :)
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return bmpPngExtracted;
|
||||
}
|
||||
|
||||
public static Icon ExtractAssociatedIcon(this Icon icon, string location) {
|
||||
IntPtr large;
|
||||
IntPtr small;
|
||||
Shell32.ExtractIconEx(location, 0, out large, out small, 1);
|
||||
Icon returnIcon = Icon.FromHandle(small);
|
||||
if (!IntPtr.Zero.Equals(small)){
|
||||
User32.DestroyIcon(small);
|
||||
}
|
||||
if (!IntPtr.Zero.Equals(large)){
|
||||
User32.DestroyIcon(large);
|
||||
}
|
||||
return returnIcon;
|
||||
}
|
||||
}
|
||||
}
|
73
GreenshotPlugin/Core/InterfaceUtils.cs
Normal file
73
GreenshotPlugin/Core/InterfaceUtils.cs
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
* For more information see: http://getgreenshot.org/
|
||||
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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.Threading;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Greenshot.Plugin;
|
||||
|
||||
namespace GreenshotPlugin.Core {
|
||||
/// <summary>
|
||||
/// Description of InterfaceUtils.
|
||||
/// </summary>
|
||||
public static class InterfaceUtils {
|
||||
private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(InterfaceUtils));
|
||||
|
||||
public static List<Type> GetSubclassesOf(Type type, bool excludeSystemTypes) {
|
||||
List<Type> list = new List<Type>();
|
||||
foreach(Assembly currentAssembly in Thread.GetDomain().GetAssemblies()) {
|
||||
try {
|
||||
Type[] types = currentAssembly.GetTypes();
|
||||
if (!excludeSystemTypes || (excludeSystemTypes && !currentAssembly.FullName.StartsWith("System."))) {
|
||||
foreach(Type currentType in types) {
|
||||
if (type.IsInterface) {
|
||||
if (currentType.GetInterface(type.FullName) != null) {
|
||||
list.Add(currentType);
|
||||
}
|
||||
} else if (currentType.IsSubclassOf(type)) {
|
||||
list.Add(currentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<IProcessor> GetProcessors() {
|
||||
List<IProcessor> processors = new List<IProcessor>();
|
||||
foreach(Type processorType in GetSubclassesOf(typeof(IProcessor), true)) {
|
||||
if (!processorType.IsAbstract) {
|
||||
IProcessor processor = (IProcessor)Activator.CreateInstance(processorType);
|
||||
if (processor.isActive) {
|
||||
LOG.DebugFormat("Found processor {0} with designation {1}", processorType.Name, processor.Designation);
|
||||
processors.Add(processor);
|
||||
} else {
|
||||
LOG.DebugFormat("Ignoring processor {0} with designation {1}", processorType.Name, processor.Designation);
|
||||
}
|
||||
}
|
||||
}
|
||||
return processors;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,17 +22,24 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using System.Xml;
|
||||
|
||||
using IniFile;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace GreenshotPlugin.Core {
|
||||
|
||||
public interface ILanguage {
|
||||
void Load();
|
||||
bool hasKey(Enum key);
|
||||
bool hasKey(string key);
|
||||
string GetString(Enum id);
|
||||
string GetString(string id);
|
||||
string GetFormattedString(Enum id, object param);
|
||||
string GetFormattedString(string id, object param);
|
||||
string GetHelpFilePath();
|
||||
|
||||
/// <summary>
|
||||
|
@ -42,6 +49,8 @@ namespace GreenshotPlugin.Core {
|
|||
/// <returns>Actuall IETF </returns>
|
||||
string SetLanguage(string cultureInfo);
|
||||
void SynchronizeLanguageToCulture();
|
||||
void FreeResources();
|
||||
|
||||
string CurrentLanguage {
|
||||
get;
|
||||
}
|
||||
|
@ -58,7 +67,6 @@ namespace GreenshotPlugin.Core {
|
|||
LanguageConfiguration CurrentLanguageConfiguration {
|
||||
get;
|
||||
}
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// Description of Language.
|
||||
|
@ -68,13 +76,36 @@ namespace GreenshotPlugin.Core {
|
|||
private static char [] TRIMCHARS = new char[] {' ', '\t', '\n', '\r'};
|
||||
private const string DEFAULT_LANGUAGE= "en-US";
|
||||
private static string APPLICATIONDATA_LANGUAGE_PATH = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),@"Greenshot\Languages\");
|
||||
private static string STARTUP_LANGUAGE_PATH = Path.Combine(Application.StartupPath, @"Languages\");
|
||||
private static string APPLICATION_PATH = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
private static string STARTUP_LANGUAGE_PATH = Path.Combine(APPLICATION_PATH, @"Languages");
|
||||
private static string PAF_LANGUAGE_PATH = Path.Combine(APPLICATION_PATH, @"App\Greenshot\Languages");
|
||||
private const string HELP_FILENAME_PATTERN = @"help-*.html";
|
||||
private const string LANGUAGE_GROUPS_KEY = @"SYSTEM\CurrentControlSet\Control\Nls\Language Groups";
|
||||
|
||||
private Dictionary<string, string> strings = new Dictionary<string, string>();
|
||||
private List<LanguageConfiguration> languages = new List<LanguageConfiguration>();
|
||||
private string currentIETF = null;
|
||||
private string languageFilePattern;
|
||||
private static List<string> supportedLanguageGroups = new List<string>();
|
||||
|
||||
static LanguageContainer() {
|
||||
try {
|
||||
using (RegistryKey languageGroupsKey = Registry.LocalMachine.OpenSubKey(LANGUAGE_GROUPS_KEY, false)) {
|
||||
if (languageGroupsKey != null) {
|
||||
string [] groups = languageGroupsKey.GetValueNames();
|
||||
foreach(string group in groups) {
|
||||
string groupValue = (string)languageGroupsKey.GetValue(group);
|
||||
bool isGroupInstalled = "1".Equals(groupValue);
|
||||
if (isGroupInstalled) {
|
||||
supportedLanguageGroups.Add(group.ToLower());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(Exception e) {
|
||||
LOG.Warn("Couldn't read the installed language groups.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public LanguageContainer() {
|
||||
}
|
||||
|
@ -129,7 +160,7 @@ namespace GreenshotPlugin.Core {
|
|||
public string SetLanguage(string wantedIETF) {
|
||||
LOG.Debug("SetLanguage called for : " + wantedIETF);
|
||||
Dictionary<string, LanguageConfiguration> identifiedLanguages = new Dictionary<string, LanguageConfiguration>();
|
||||
|
||||
|
||||
if (languages == null || languages.Count == 0) {
|
||||
throw new FileNotFoundException("No language files found!");
|
||||
}
|
||||
|
@ -137,7 +168,11 @@ namespace GreenshotPlugin.Core {
|
|||
// Find selected languages in available languages
|
||||
foreach(LanguageConfiguration language in languages) {
|
||||
LOG.Debug("Found language: " + language.Ietf);
|
||||
identifiedLanguages.Add(language.Ietf, language);
|
||||
if (!identifiedLanguages.ContainsKey(language.Ietf)) {
|
||||
identifiedLanguages.Add(language.Ietf, language);
|
||||
} else {
|
||||
LOG.WarnFormat("Found double language file: {0}", language.File);
|
||||
}
|
||||
}
|
||||
|
||||
LanguageConfiguration selectedLanguage = null;
|
||||
|
@ -181,12 +216,23 @@ namespace GreenshotPlugin.Core {
|
|||
foreach(Resource resource in selectedLanguage.Resources) {
|
||||
AddResource(resource);
|
||||
}
|
||||
|
||||
// Make sure we have all the missing resources, right after setting the language
|
||||
// this way we can free all other resources.
|
||||
AdoptMissingResourcesFromDefaultLanguage();
|
||||
|
||||
currentIETF = selectedLanguage.Ietf;
|
||||
Thread.CurrentThread.CurrentUICulture = new CultureInfo(currentIETF);
|
||||
|
||||
return currentIETF;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Free all language resources which aren't needed
|
||||
/// </summary>
|
||||
public void FreeResources() {
|
||||
languages = null;
|
||||
}
|
||||
|
||||
private void AddResource(Resource resource) {
|
||||
try {
|
||||
|
@ -205,18 +251,32 @@ namespace GreenshotPlugin.Core {
|
|||
private List<LanguageConfiguration> LoadFiles(string languageFilePattern) {
|
||||
List<LanguageConfiguration> loadedLanguages = new List<LanguageConfiguration>();
|
||||
List<string> languageDirectories = new List<string>();
|
||||
if (IniConfig.IsPortable) {
|
||||
languageDirectories.Add(PAF_LANGUAGE_PATH);
|
||||
} else {
|
||||
languageDirectories.Add(APPLICATIONDATA_LANGUAGE_PATH);
|
||||
}
|
||||
languageDirectories.Add(STARTUP_LANGUAGE_PATH);
|
||||
languageDirectories.Add(APPLICATIONDATA_LANGUAGE_PATH);
|
||||
foreach(string path in languageDirectories) {
|
||||
// Search in executable directory
|
||||
if (Directory.Exists(path)) {
|
||||
LOG.DebugFormat("Searching language directory '{0}' for language files with pattern '{1}'", path, languageFilePattern);
|
||||
try {
|
||||
foreach(string languageFile in Directory.GetFiles(path, languageFilePattern, SearchOption.AllDirectories)) {
|
||||
LOG.DebugFormat("Found language file: {0}", languageFile);
|
||||
LanguageConfiguration languageConfig = LanguageConfiguration.Load(languageFile);
|
||||
if (languageConfig != null) {
|
||||
LOG.Info("Loaded language: " + languageConfig.Description);
|
||||
loadedLanguages.Add(languageConfig);
|
||||
if (string.IsNullOrEmpty(languageConfig.LanguageGroup) || supportedLanguageGroups.Contains(languageConfig.LanguageGroup)) {
|
||||
LOG.InfoFormat("Loaded language: {0}", languageConfig.Description);
|
||||
loadedLanguages.Add(languageConfig);
|
||||
} else {
|
||||
LOG.InfoFormat("Skipping unsupported language: {0}", languageConfig.Description);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (DirectoryNotFoundException) {
|
||||
LOG.InfoFormat("Non existing language directory: {0}", path);
|
||||
} catch (Exception e) {
|
||||
LOG.Error("Error trying for read directory " + path, e);
|
||||
}
|
||||
}
|
||||
return loadedLanguages;
|
||||
|
@ -289,26 +349,36 @@ namespace GreenshotPlugin.Core {
|
|||
EnumClass.AppendLine("}");
|
||||
LOG.Debug("LangKeys should be: \r\n" + EnumClass.ToString());
|
||||
}
|
||||
|
||||
public bool hasKey(Enum key) {
|
||||
return hasKey(key.ToString());
|
||||
}
|
||||
|
||||
public string GetString(Enum id) {
|
||||
if(!strings.ContainsKey(id.ToString())) {
|
||||
AdoptMissingResourcesFromDefaultLanguage();
|
||||
}
|
||||
public bool hasKey(string key) {
|
||||
return strings.ContainsKey(key);
|
||||
}
|
||||
|
||||
public string GetString(Enum key) {
|
||||
return GetString(key.ToString());
|
||||
}
|
||||
|
||||
public string GetString(string key) {
|
||||
try {
|
||||
return strings[id.ToString()];
|
||||
return strings[key];
|
||||
} catch (KeyNotFoundException) {
|
||||
return "string ###"+id+"### not found";
|
||||
return "string ###"+key+"### not found";
|
||||
}
|
||||
}
|
||||
|
||||
public string GetFormattedString(Enum id, object param) {
|
||||
if(!strings.ContainsKey(id.ToString())) {
|
||||
AdoptMissingResourcesFromDefaultLanguage();
|
||||
}
|
||||
public string GetFormattedString(Enum key, object param) {
|
||||
return GetFormattedString(key.ToString(), param);
|
||||
}
|
||||
|
||||
public string GetFormattedString(string key, object param) {
|
||||
try {
|
||||
return String.Format(strings[id.ToString()], param);
|
||||
return String.Format(strings[key], param);
|
||||
} catch (KeyNotFoundException) {
|
||||
return "string ###"+id+"### not found";
|
||||
return "string ###"+key+"### not found";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -399,6 +469,16 @@ namespace GreenshotPlugin.Core {
|
|||
}
|
||||
}
|
||||
|
||||
public string languageGroup;
|
||||
public string LanguageGroup {
|
||||
get {
|
||||
return languageGroup;
|
||||
}
|
||||
set {
|
||||
languageGroup = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string file;
|
||||
public string File {
|
||||
get {
|
||||
|
@ -431,6 +511,10 @@ namespace GreenshotPlugin.Core {
|
|||
ret.Description = node.Attributes["description"].Value;
|
||||
ret.Ietf = node.Attributes["ietf"].Value;
|
||||
ret.Version = node.Attributes["version"].Value;
|
||||
if (node.Attributes["languagegroup"] != null) {
|
||||
string languageGroup = node.Attributes["languagegroup"].Value;
|
||||
ret.LanguageGroup = languageGroup.ToLower();
|
||||
}
|
||||
|
||||
XmlNodeList resourceNodes = doc.GetElementsByTagName("resource");
|
||||
ret.Resources = new List<Resource>(resourceNodes.Count);
|
||||
|
|
61
GreenshotPlugin/Core/LogHelper.cs
Normal file
61
GreenshotPlugin/Core/LogHelper.cs
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
* For more information see: http://getgreenshot.org/
|
||||
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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.IO;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using log4net;
|
||||
using log4net.Appender;
|
||||
using log4net.Config;
|
||||
using log4net.Repository.Hierarchy;
|
||||
|
||||
namespace GreenshotPlugin.Core {
|
||||
/// <summary>
|
||||
/// Description of LogHelper.
|
||||
/// </summary>
|
||||
public class LogHelper {
|
||||
private const string LOG4NET_FILE = "log4net.xml";
|
||||
// Initialize Log4J
|
||||
public static string InitializeLog4NET() {
|
||||
// Setup log4j, currently the file is called log4net.xml
|
||||
string pafLog4NetFilename = Path.Combine(Application.StartupPath, @"App\Greenshot\" + LOG4NET_FILE);
|
||||
string log4netFilename = Path.Combine(Application.StartupPath, LOG4NET_FILE);
|
||||
if (File.Exists(log4netFilename)) {
|
||||
XmlConfigurator.Configure(new FileInfo(log4netFilename));
|
||||
} else if (File.Exists(pafLog4NetFilename)) {
|
||||
XmlConfigurator.Configure(new FileInfo(pafLog4NetFilename));
|
||||
} else {
|
||||
MessageBox.Show("Can't find file " + LOG4NET_FILE);
|
||||
}
|
||||
|
||||
// Get the logfile
|
||||
try {
|
||||
IAppender appender = ((Hierarchy)LogManager.GetRepository()).Root.Appenders[0];
|
||||
if (appender is FileAppender) {
|
||||
return ((FileAppender)appender).File;
|
||||
}
|
||||
} catch (Exception) {
|
||||
// Ignore
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,11 +24,13 @@ using System.IO;
|
|||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
using IniFile;
|
||||
|
||||
namespace GreenshotPlugin.Core {
|
||||
/// <summary>
|
||||
/// Description of NetworkHelper.
|
||||
/// </summary>
|
||||
public class NetworkHelper {
|
||||
public static class NetworkHelper {
|
||||
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(NetworkHelper));
|
||||
private static CoreConfiguration config = IniConfig.GetIniSection<CoreConfiguration>();
|
||||
|
||||
|
@ -64,8 +66,9 @@ namespace GreenshotPlugin.Core {
|
|||
HttpWebRequest request = (HttpWebRequest)NetworkHelper.CreatedWebRequest(url);
|
||||
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
|
||||
if (request.HaveResponse) {
|
||||
Image image = Image.FromStream(response.GetResponseStream());
|
||||
return (image.Height > 16 && image.Width > 16) ? new Bitmap(image, 16, 16) : new Bitmap(image);
|
||||
using (Image image = Image.FromStream(response.GetResponseStream())) {
|
||||
return (image.Height > 16 && image.Width > 16) ? new Bitmap(image, 16, 16) : new Bitmap(image);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
|
|
84
GreenshotPlugin/Core/Objects.cs
Normal file
84
GreenshotPlugin/Core/Objects.cs
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
* For more information see: http://getgreenshot.org/
|
||||
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
|
||||
namespace GreenshotPlugin.Core {
|
||||
public static class Objects {
|
||||
|
||||
/// <summary>
|
||||
/// Perform a deep Copy of the object.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object being copied.</typeparam>
|
||||
/// <param name="source">The object instance to copy.</param>
|
||||
/// <returns>The copied object.</returns>
|
||||
public static T Clone<T>(this T source) {
|
||||
if (!typeof(T).IsSerializable) {
|
||||
throw new ArgumentException("The type must be serializable.", "source");
|
||||
}
|
||||
|
||||
// Don't serialize a null object, simply return the default for that object
|
||||
if (Object.ReferenceEquals(source, null)) {
|
||||
return default(T);
|
||||
}
|
||||
|
||||
IFormatter formatter = new BinaryFormatter();
|
||||
Stream stream = new MemoryStream();
|
||||
using (stream) {
|
||||
formatter.Serialize(stream, source);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return (T)formatter.Deserialize(stream);
|
||||
}
|
||||
}
|
||||
|
||||
public static void CloneTo<T>(this T source, T destination) {
|
||||
Type type = typeof(T);
|
||||
FieldInfo[] myObjectFields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
foreach (FieldInfo fi in myObjectFields) {
|
||||
fi.SetValue(destination, fi.GetValue(source));
|
||||
}
|
||||
PropertyInfo[] myObjectProperties = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
foreach (PropertyInfo pi in myObjectProperties) {
|
||||
pi.SetValue(destination, pi.GetValue(source, null), null);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CompareLists<T>(IList<T> l1, IList<T> l2) {
|
||||
if (l1.Count != l2.Count) {
|
||||
return false;
|
||||
}
|
||||
int matched = 0;
|
||||
foreach(T item in l1) {
|
||||
if (!l2.Contains(item)) {
|
||||
return false;
|
||||
}
|
||||
matched++;
|
||||
}
|
||||
return matched == l1.Count;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,17 +19,37 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using Greenshot.Plugin;
|
||||
|
||||
namespace GreenshotPlugin.Core {
|
||||
/// <summary>
|
||||
/// Description of PluginUtils.
|
||||
/// </summary>
|
||||
public class PluginUtils {
|
||||
private PluginUtils() {
|
||||
}
|
||||
public static class PluginUtils {
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to add a MenuItem to the File MenuItem of an ImageEditor
|
||||
/// </summary>
|
||||
/// <param name="image">Image to display in the menu</param>
|
||||
/// <param name="text">Text to display in the menu</param>
|
||||
/// <param name="tag">The TAG value</param>
|
||||
/// <param name="shortcutKeys">Keys which can be used as shortcut</param>
|
||||
/// <param name="handler">The onclick handler</param>
|
||||
public static void AddToFileMenu(IImageEditor imageEditor, Image image, string text, object tag, Keys? shortcutKeys, System.EventHandler handler) {
|
||||
System.Windows.Forms.ToolStripMenuItem item = new System.Windows.Forms.ToolStripMenuItem();
|
||||
item.Image = image;
|
||||
item.Text = text;
|
||||
item.Tag = tag;
|
||||
if (shortcutKeys.HasValue) {
|
||||
item.ShortcutKeys = shortcutKeys.Value;
|
||||
}
|
||||
item.Click += handler;
|
||||
AddToFileMenu(imageEditor, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to add a MenuItem to the File MenuItem of an ImageEditor
|
||||
/// </summary>
|
||||
|
@ -69,5 +89,28 @@ namespace GreenshotPlugin.Core {
|
|||
toolStripMenuItem.DropDownItems.Add(item);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Helper method to add a MenuItem to the Greenshot context menu
|
||||
/// </summary>
|
||||
/// <param name="imageEditor"></param>
|
||||
/// <param name="item"></param>
|
||||
public static void AddToContextMenu(IGreenshotHost host, ToolStripMenuItem item) {
|
||||
// Here we can hang ourselves to the main context menu!
|
||||
ContextMenuStrip contextMenu = host.MainMenu;
|
||||
bool addedItem = false;
|
||||
|
||||
// Try to find a separator, so we insert ourselves after it
|
||||
for(int i=0; i < contextMenu.Items.Count; i++) {
|
||||
if (contextMenu.Items[i].GetType() == typeof(ToolStripSeparator)) {
|
||||
contextMenu.Items.Insert(i+1, item);
|
||||
addedItem = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If we didn't insert the item, we just add it...
|
||||
if (!addedItem) {
|
||||
contextMenu.Items.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,124 +0,0 @@
|
|||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
* For more information see: http://getgreenshot.org/
|
||||
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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.IO;
|
||||
|
||||
namespace GreenshotPlugin.Core {
|
||||
/// <summary>
|
||||
/// A Class to representate a simple "java" properties file
|
||||
/// </summary>
|
||||
public class Properties : Dictionary<string, string >{
|
||||
private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(Properties));
|
||||
|
||||
public string GetProperty(string key) {
|
||||
try {
|
||||
return this[key];
|
||||
} catch (KeyNotFoundException) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Split property with ',' and return the splitted string as a string[]
|
||||
/// </summary>
|
||||
public string[] GetPropertyAsArray(string key) {
|
||||
try {
|
||||
string array = this[key];
|
||||
return array.Split(new Char[] {','});
|
||||
} catch (KeyNotFoundException) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public bool GetBoolProperty(string key) {
|
||||
if (this.ContainsKey(key)) {
|
||||
return bool.Parse(this[key]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public uint GetUIntProperty(string key) {
|
||||
return uint.Parse(this[key]);
|
||||
}
|
||||
public int GetIntProperty(string key) {
|
||||
return int.Parse(this[key]);
|
||||
}
|
||||
public void AddProperty(string key, string value) {
|
||||
Add(key, value);
|
||||
}
|
||||
public void AddBoolProperty(string key, bool value) {
|
||||
AddProperty(key, value.ToString());
|
||||
}
|
||||
public void ChangeProperty(string key, string value) {
|
||||
if (this.ContainsKey(key)) {
|
||||
this[key] = value;
|
||||
} else {
|
||||
throw new KeyNotFoundException(key);
|
||||
}
|
||||
}
|
||||
public void ChangeBoolProperty(string key, bool value) {
|
||||
ChangeProperty(key, value.ToString());
|
||||
}
|
||||
|
||||
public void write(string filename) {
|
||||
using ( TextWriter textWriter = new StreamWriter(filename)) {
|
||||
foreach(string key in Keys) {
|
||||
textWriter.WriteLine(key +"=" + this[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void write(string filename, string header) {
|
||||
using ( TextWriter textWriter = new StreamWriter(filename)) {
|
||||
if (header != null) {
|
||||
textWriter.WriteLine(header);
|
||||
}
|
||||
foreach(string key in Keys) {
|
||||
textWriter.WriteLine(key +"=" + this[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read properties file
|
||||
public static Properties read(string filename) {
|
||||
LOG.Debug("Reading properties from file: " + filename);
|
||||
if (!File.Exists(filename)) {
|
||||
return null;
|
||||
}
|
||||
Properties properties = new Properties();
|
||||
foreach (string line in File.ReadAllLines(filename)) {
|
||||
if (line == null) {
|
||||
continue;
|
||||
}
|
||||
string currentLine = line.Trim();
|
||||
if (!currentLine.StartsWith("#") && currentLine.IndexOf('=') > 0) {
|
||||
string [] split = currentLine.Split(new Char[] {'='}, 2);
|
||||
if (split != null && split.Length == 2) {
|
||||
string name = split[0];
|
||||
if (name == null || name.Length < 1) {
|
||||
continue;
|
||||
}
|
||||
properties.Add(name.Trim(), split[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
}
|
182
GreenshotPlugin/Core/SourceForgeHelper.cs
Normal file
182
GreenshotPlugin/Core/SourceForgeHelper.cs
Normal file
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
* For more information see: http://getgreenshot.org/
|
||||
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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.Globalization;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
|
||||
namespace GreenshotPlugin.Core {
|
||||
public class SourceforgeFile {
|
||||
private string file;
|
||||
public string File {
|
||||
get {return file;}
|
||||
}
|
||||
private DateTime pubdate;
|
||||
public DateTime Pubdate {
|
||||
get {return pubdate;}
|
||||
}
|
||||
private string link;
|
||||
public string Link {
|
||||
get {return link;}
|
||||
}
|
||||
private string directLink;
|
||||
public string DirectLink {
|
||||
get {return directLink;}
|
||||
}
|
||||
private Version version;
|
||||
public Version Version {
|
||||
get {return version;}
|
||||
set {
|
||||
version = value;
|
||||
}
|
||||
}
|
||||
private string language;
|
||||
public string Language {
|
||||
get {return language;}
|
||||
set {language = value;}
|
||||
}
|
||||
|
||||
public SourceforgeFile(string file, string pubdate, string link, string directLink) {
|
||||
this.file = file;
|
||||
this.pubdate = DateTime.Parse(pubdate);
|
||||
this.link = link;
|
||||
this.directLink = directLink;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Description of SourceForgeHelper.
|
||||
/// </summary>
|
||||
public class SourceForgeHelper {
|
||||
private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(SourceForgeHelper));
|
||||
private const String RSSFEED = "http://sourceforge.net/api/file/index/project-id/191585/mtime/desc/rss";
|
||||
|
||||
/// <summary>
|
||||
/// Read the Greenshot RSS feed, so we can use this information to check for updates
|
||||
/// </summary>
|
||||
/// <returns>Dictionary<string, Dictionary<string, RssFile>> with files and their RssFile "description"</returns>
|
||||
public static Dictionary<string, Dictionary<string, SourceforgeFile>> readRSS() {
|
||||
HttpWebRequest webRequest;
|
||||
XmlDocument rssDoc = new XmlDocument();
|
||||
try {
|
||||
webRequest = (HttpWebRequest)GreenshotPlugin.Core.NetworkHelper.CreatedWebRequest(RSSFEED);
|
||||
XmlTextReader rssReader = new XmlTextReader(webRequest.GetResponse().GetResponseStream());
|
||||
|
||||
// Load the XML content into a XmlDocument
|
||||
rssDoc.Load(rssReader);
|
||||
} catch (Exception wE) {
|
||||
LOG.WarnFormat("Problem reading RSS from {0}", RSSFEED);
|
||||
LOG.Warn(wE.Message);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Loop for the <rss> tag
|
||||
XmlNode nodeRss = null;
|
||||
for (int i = 0; i < rssDoc.ChildNodes.Count; i++) {
|
||||
// If it is the rss tag
|
||||
if (rssDoc.ChildNodes[i].Name == "rss") {
|
||||
// <rss> tag found
|
||||
nodeRss = rssDoc.ChildNodes[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (nodeRss == null) {
|
||||
LOG.Debug("No RSS Feed!");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Loop for the <channel> tag
|
||||
XmlNode nodeChannel = null;
|
||||
for (int i = 0; i < nodeRss.ChildNodes.Count; i++) {
|
||||
// If it is the channel tag
|
||||
if (nodeRss.ChildNodes[i].Name == "channel") {
|
||||
// <channel> tag found
|
||||
nodeChannel = nodeRss.ChildNodes[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (nodeChannel == null) {
|
||||
LOG.Debug("No channel in RSS feed!");
|
||||
return null;
|
||||
}
|
||||
|
||||
Dictionary<string, Dictionary<string, SourceforgeFile>> rssFiles = new Dictionary<string, Dictionary<string, SourceforgeFile>>();
|
||||
|
||||
// Loop for the <title>, <link>, <description> and all the other tags
|
||||
for (int i = 0; i < nodeChannel.ChildNodes.Count; i++) {
|
||||
// If it is the item tag, then it has children tags which we will add as items to the ListView
|
||||
|
||||
if (nodeChannel.ChildNodes[i].Name == "item") {
|
||||
XmlNode nodeItem = nodeChannel.ChildNodes[i];
|
||||
string sfLink = nodeItem["link"].InnerText;
|
||||
string pubdate = nodeItem["pubDate"].InnerText;
|
||||
try {
|
||||
Match match= Regex.Match(Uri.UnescapeDataString(sfLink), @"^http.*sourceforge.*\/projects\/([^\/]+)\/files\/([^\/]+)\/([^\/]+)\/(.+)\/download$");
|
||||
if (match.Success) {
|
||||
string project = match.Groups[1].Value;
|
||||
string subdir = match.Groups[2].Value;
|
||||
string type = match.Groups[3].Value;
|
||||
string file = match.Groups[4].Value;
|
||||
// !!! Change this to the mirror !!!
|
||||
string mirror = "kent";
|
||||
string directLink = Uri.EscapeUriString("http://"+mirror+".dl.sourceforge.net/project/"+project+"/"+subdir+"/"+type+"/"+file);
|
||||
Dictionary<string, SourceforgeFile> filesForType;
|
||||
if (rssFiles.ContainsKey(type)) {
|
||||
filesForType = rssFiles[type];
|
||||
} else {
|
||||
filesForType = new Dictionary<string, SourceforgeFile>();
|
||||
rssFiles.Add(type, filesForType);
|
||||
}
|
||||
SourceforgeFile rssFile = new SourceforgeFile(file, pubdate, sfLink, directLink);
|
||||
if (file.EndsWith(".exe") ||file.EndsWith(".zip")) {
|
||||
string version = Regex.Replace(file, ".*[a-zA-Z]-", "");
|
||||
version = version.Replace("-[a-zA-Z]+.*","");
|
||||
version = Regex.Replace(version, ".exe$", "");
|
||||
version = Regex.Replace(version, ".zip$", "");
|
||||
if (version.Trim().Length > 0) {
|
||||
version = version.Replace('-','.');
|
||||
version = version.Replace(',','.');
|
||||
try {
|
||||
rssFile.Version = new Version(version);
|
||||
} catch (Exception) {
|
||||
LOG.DebugFormat("Found invalid version {0} in file {1}", version, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type.Equals("Translations")) {
|
||||
string culture = Regex.Replace(file, @"[a-zA-Z]+-(..-..)\.(xml|html)", "$1");
|
||||
CultureInfo cultureInfo = new CultureInfo(culture);
|
||||
rssFile.Language = cultureInfo.NativeName;
|
||||
}
|
||||
filesForType.Add(file, rssFile);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOG.WarnFormat("Couldn't read RSS entry for: {0}", nodeChannel["title"].InnerText);
|
||||
LOG.Warn("Reason: ", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rssFiles;
|
||||
}
|
||||
}
|
||||
}
|
676
GreenshotPlugin/Core/WindowCapture.cs
Normal file
676
GreenshotPlugin/Core/WindowCapture.cs
Normal file
|
@ -0,0 +1,676 @@
|
|||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
* For more information see: http://getgreenshot.org/
|
||||
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using Greenshot.Plugin;
|
||||
using GreenshotPlugin.UnmanagedHelpers;
|
||||
|
||||
namespace GreenshotPlugin.Core {
|
||||
/// <summary>
|
||||
/// This Class is used to pass details about the capture around.
|
||||
/// The time the Capture was taken and the Title of the window (or a region of) that is captured
|
||||
/// </summary>
|
||||
public class CaptureDetails : ICaptureDetails {
|
||||
private string title;
|
||||
public string Title {
|
||||
get {return title;}
|
||||
set {title = value;}
|
||||
}
|
||||
|
||||
private string filename;
|
||||
public string Filename {
|
||||
get {return filename;}
|
||||
set {filename = value;}
|
||||
}
|
||||
|
||||
private DateTime dateTime;
|
||||
public DateTime DateTime {
|
||||
get {return dateTime;}
|
||||
set {dateTime = value;}
|
||||
}
|
||||
|
||||
private float dpiX;
|
||||
public float DpiX {
|
||||
get {
|
||||
return dpiX;
|
||||
}
|
||||
set {
|
||||
dpiX = value;
|
||||
}
|
||||
}
|
||||
|
||||
private float dpiY;
|
||||
public float DpiY {
|
||||
get {
|
||||
return dpiY;
|
||||
}
|
||||
set {
|
||||
dpiY = value;
|
||||
}
|
||||
}
|
||||
private Dictionary<string, string> metaData = new Dictionary<string, string>();
|
||||
public Dictionary<string, string> MetaData {
|
||||
get {return metaData;}
|
||||
}
|
||||
|
||||
public void AddMetaData(string key, string value) {
|
||||
if (metaData.ContainsKey(key)) {
|
||||
metaData[key] = value;
|
||||
} else {
|
||||
metaData.Add(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private CaptureMode captureMode;
|
||||
public CaptureMode CaptureMode {
|
||||
get {return captureMode;}
|
||||
set {captureMode = value;}
|
||||
}
|
||||
|
||||
private List<IDestination> captureDestinations = new List<IDestination>();
|
||||
public List<IDestination> CaptureDestinations {
|
||||
get {return captureDestinations;}
|
||||
set {captureDestinations = value;}
|
||||
}
|
||||
|
||||
public void ClearDestinations() {
|
||||
captureDestinations.Clear();
|
||||
}
|
||||
|
||||
public void RemoveDestination(IDestination destination) {
|
||||
if (captureDestinations.Contains(destination)) {
|
||||
captureDestinations.Remove(destination);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddDestination(IDestination captureDestination) {
|
||||
if (!captureDestinations.Contains(captureDestination)) {
|
||||
captureDestinations.Add(captureDestination);
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasDestination(string designation) {
|
||||
foreach(IDestination destination in captureDestinations) {
|
||||
if (designation.Equals(destination.Designation)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public CaptureDetails() {
|
||||
dateTime = DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class is used to pass an instance of the "Capture" around
|
||||
/// Having the Bitmap, eventually the Windows Title and cursor all together.
|
||||
/// </summary>
|
||||
public class Capture : IDisposable, ICapture {
|
||||
private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(Capture));
|
||||
private List<ICaptureElement> elements = new List<ICaptureElement>();
|
||||
|
||||
private Rectangle screenBounds;
|
||||
/// <summary>
|
||||
/// Get/Set the Screenbounds
|
||||
/// </summary>
|
||||
public Rectangle ScreenBounds {
|
||||
get {
|
||||
if (screenBounds == null) {
|
||||
screenBounds = WindowCapture.GetScreenBounds();
|
||||
}
|
||||
return screenBounds;
|
||||
}
|
||||
set {screenBounds = value;}
|
||||
}
|
||||
|
||||
private Image image;
|
||||
/// <summary>
|
||||
/// Get/Set the Image
|
||||
/// </summary>
|
||||
public Image Image {
|
||||
get {return image;}
|
||||
set {
|
||||
if (image != null) {
|
||||
image.Dispose();
|
||||
}
|
||||
image = value;
|
||||
if (value != null) {
|
||||
if (value.PixelFormat.Equals(PixelFormat.Format8bppIndexed) || value.PixelFormat.Equals(PixelFormat.Format1bppIndexed) || value.PixelFormat.Equals(PixelFormat.Format4bppIndexed)) {
|
||||
LOG.Debug("Converting Bitmap to PixelFormat.Format32bppArgb as we don't support: " + value.PixelFormat);
|
||||
try {
|
||||
// Default Bitmap PixelFormat is Format32bppArgb
|
||||
this.image = new Bitmap(value);
|
||||
} finally {
|
||||
// Always dispose, even when a exception occured
|
||||
value.Dispose();
|
||||
}
|
||||
}
|
||||
LOG.Debug("Image is set with the following specifications: " + image.Width + "," + image.Height + " - " + image.PixelFormat);
|
||||
} else {
|
||||
LOG.Debug("Image is removed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void NullImage() {
|
||||
image = null;
|
||||
}
|
||||
|
||||
private Icon cursor;
|
||||
/// <summary>
|
||||
/// Get/Set the image for the Cursor
|
||||
/// </summary>
|
||||
public Icon Cursor {
|
||||
get {return cursor;}
|
||||
set {
|
||||
if (cursor != null) {
|
||||
cursor.Dispose();
|
||||
}
|
||||
cursor = (Icon)value.Clone();
|
||||
}
|
||||
}
|
||||
|
||||
private bool cursorVisible = false;
|
||||
/// <summary>
|
||||
/// Set if the cursor is visible
|
||||
/// </summary>
|
||||
public bool CursorVisible {
|
||||
get {return cursorVisible;}
|
||||
set {cursorVisible = value;}
|
||||
}
|
||||
|
||||
private Point cursorLocation = Point.Empty;
|
||||
/// <summary>
|
||||
/// Get/Set the CursorLocation
|
||||
/// </summary>
|
||||
public Point CursorLocation {
|
||||
get {return cursorLocation;}
|
||||
set {cursorLocation = value;}
|
||||
}
|
||||
|
||||
private Point location = Point.Empty;
|
||||
/// <summary>
|
||||
/// Get/set the Location
|
||||
/// </summary>
|
||||
public Point Location {
|
||||
get {return location;}
|
||||
set {location = value;}
|
||||
}
|
||||
|
||||
private CaptureDetails captureDetails;
|
||||
/// <summary>
|
||||
/// Get/set the CaptureDetails
|
||||
/// </summary>
|
||||
public ICaptureDetails CaptureDetails {
|
||||
get {return captureDetails;}
|
||||
set {captureDetails = (CaptureDetails)value;}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default Constructor
|
||||
/// </summary>
|
||||
public Capture() {
|
||||
screenBounds = WindowCapture.GetScreenBounds();
|
||||
captureDetails = new CaptureDetails();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with Image
|
||||
/// Note: the supplied bitmap can be disposed immediately or when constructor is called.
|
||||
/// </summary>
|
||||
/// <param name="newImage">Image</param>
|
||||
public Capture(Image newImage) : this() {
|
||||
this.Image = newImage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~Capture() {
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The public accessible Dispose
|
||||
/// Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice
|
||||
/// </summary>
|
||||
public void Dispose() {
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This Dispose is called from the Dispose and the Destructor.
|
||||
/// When disposing==true all non-managed resources should be freed too!
|
||||
/// </summary>
|
||||
/// <param name="disposing"></param>
|
||||
protected virtual void Dispose(bool disposing) {
|
||||
if (disposing) {
|
||||
if (image != null) {
|
||||
image.Dispose();
|
||||
}
|
||||
if (cursor != null) {
|
||||
cursor.Dispose();
|
||||
}
|
||||
}
|
||||
image = null;
|
||||
cursor = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Crops the capture to the specified rectangle (with Bitmap coordinates!)
|
||||
/// </summary>
|
||||
/// <param name="cropRectangle">Rectangle with bitmap coordinates</param>
|
||||
public bool Crop(Rectangle cropRectangle) {
|
||||
LOG.Debug("Cropping to: " + cropRectangle.ToString());
|
||||
if (ImageHelper.Crop(ref image, ref cropRectangle)) {
|
||||
location = cropRectangle.Location;
|
||||
// Change mouse location according to the cropRegtangle (including screenbounds) offset
|
||||
MoveMouseLocation(-cropRectangle.Location.X, -cropRectangle.Location.Y);
|
||||
// Move all the elements
|
||||
MoveElements(-cropRectangle.Location.X, -cropRectangle.Location.Y);
|
||||
|
||||
// Remove invisible elements
|
||||
List <ICaptureElement> newElements = new List<ICaptureElement>();
|
||||
foreach(ICaptureElement captureElement in elements) {
|
||||
if (captureElement.Bounds.IntersectsWith(cropRectangle)) {
|
||||
newElements.Add(captureElement);
|
||||
}
|
||||
}
|
||||
elements = newElements;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply a translate to the mouse location.
|
||||
/// e.g. needed for crop
|
||||
/// </summary>
|
||||
/// <param name="x">x coordinates to move the mouse</param>
|
||||
/// <param name="y">y coordinates to move the mouse</param>
|
||||
public void MoveMouseLocation(int x, int y) {
|
||||
cursorLocation.Offset(x, y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply a translate to the elements
|
||||
/// e.g. needed for crop
|
||||
/// </summary>
|
||||
/// <param name="x">x coordinates to move the elements</param>
|
||||
/// <param name="y">y coordinates to move the elements</param>
|
||||
public void MoveElements(int x, int y) {
|
||||
MoveElements(elements, x, y);
|
||||
}
|
||||
|
||||
private void MoveElements(List<ICaptureElement> listOfElements, int x, int y) {
|
||||
foreach(ICaptureElement childElement in listOfElements) {
|
||||
Rectangle bounds = childElement.Bounds;
|
||||
bounds.Offset(x, y);
|
||||
childElement.Bounds = bounds;
|
||||
MoveElements(childElement.Children, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a new element to the capture
|
||||
/// </summary>
|
||||
/// <param name="element">CaptureElement</param>
|
||||
public void AddElement(ICaptureElement element) {
|
||||
int match = elements.IndexOf(element);
|
||||
if (match >= 0) {
|
||||
if (elements[match].Children.Count < element.Children.Count) {
|
||||
elements.RemoveAt(match);
|
||||
elements.Add(element);
|
||||
}
|
||||
} else {
|
||||
elements.Add(element);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of rectangles which represent object that are on the capture
|
||||
/// </summary>
|
||||
public List<ICaptureElement> Elements {
|
||||
get {
|
||||
return elements;
|
||||
}
|
||||
set {
|
||||
elements = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ICaptureElement FindElementUnderPoint(Point p) {
|
||||
foreach(ICaptureElement element in elements) {
|
||||
if (element.Bounds.Contains(p)) {
|
||||
return element.FindElementUnderPoint(p);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A class representing an element in the capture
|
||||
/// </summary>
|
||||
public class CaptureElement : ICaptureElement {
|
||||
public CaptureElement(Rectangle bounds) {
|
||||
Bounds = bounds;
|
||||
}
|
||||
public CaptureElement(string name) {
|
||||
Name = name;
|
||||
}
|
||||
public CaptureElement(string name, Rectangle bounds) {
|
||||
Name = name;
|
||||
Bounds = bounds;
|
||||
}
|
||||
|
||||
private List<ICaptureElement> children = new List<ICaptureElement>();
|
||||
public List<ICaptureElement> Children {
|
||||
get {
|
||||
return children;
|
||||
}
|
||||
set {
|
||||
children = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string Name {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public Rectangle Bounds {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public ICaptureElement FindElementUnderPoint(Point point) {
|
||||
if (!Bounds.Contains(point)) {
|
||||
return null;
|
||||
}
|
||||
foreach(CaptureElement childElement in children) {
|
||||
if (childElement.Bounds.Contains(point)) {
|
||||
ICaptureElement selectedElement = childElement.FindElementUnderPoint(point);
|
||||
return selectedElement;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// CaptureElements are regarded equal if their bounds are equal. this should be sufficient.
|
||||
public override bool Equals(object obj) {
|
||||
bool ret = false;
|
||||
if (obj != null && GetType().Equals(obj.GetType())) {
|
||||
CaptureElement other = obj as CaptureElement;
|
||||
if (Bounds.Equals(other.Bounds)) {
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
return Bounds.GetHashCode();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The Window Capture code
|
||||
/// </summary>
|
||||
public class WindowCapture {
|
||||
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(WindowCapture));
|
||||
|
||||
private WindowCapture() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the bounds of all screens combined.
|
||||
/// </summary>
|
||||
/// <returns>A Rectangle of the bounds of the entire display area.</returns>
|
||||
public static Rectangle GetScreenBounds() {
|
||||
int left = 0, top = 0, bottom = 0, right = 0;
|
||||
foreach (Screen screen in Screen.AllScreens) {
|
||||
left = Math.Min(left, screen.Bounds.X);
|
||||
top = Math.Min(top, screen.Bounds.Y);
|
||||
int screenAbsRight = screen.Bounds.X + screen.Bounds.Width;
|
||||
int screenAbsBottom = screen.Bounds.Y + screen.Bounds.Height;
|
||||
right = Math.Max(right, screenAbsRight);
|
||||
bottom = Math.Max(bottom, screenAbsBottom);
|
||||
}
|
||||
return new Rectangle(left, top, (right + Math.Abs(left)), (bottom + Math.Abs(top)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the cursor location safely, accounting for DPI settings in Vista/Windows 7
|
||||
/// <returns>Point with cursor location</returns>
|
||||
public static Point GetCursorLocation() {
|
||||
if (Environment.OSVersion.Version.Major >= 6) {
|
||||
POINT cursorLocation;
|
||||
if (User32.GetPhysicalCursorPos(out cursorLocation)) {
|
||||
return new Point(cursorLocation.X, cursorLocation.Y);
|
||||
} else {
|
||||
Win32Error error = Win32.GetLastErrorCode();
|
||||
LOG.ErrorFormat("Error retrieving PhysicalCursorPos : {0}", Win32.GetMessage(error));
|
||||
}
|
||||
}
|
||||
return new Point(Cursor.Position.X, Cursor.Position.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method will capture the current Cursor by using User32 Code
|
||||
/// </summary>
|
||||
/// <returns>A Capture Object with the Mouse Cursor information in it.</returns>
|
||||
public static ICapture CaptureCursor(ICapture capture) {
|
||||
LOG.Debug("Capturing the mouse cursor.");
|
||||
if (capture == null) {
|
||||
capture = new Capture();
|
||||
}
|
||||
int x,y;
|
||||
IntPtr hicon;
|
||||
CursorInfo cursorInfo = new CursorInfo();
|
||||
IconInfo iconInfo;
|
||||
cursorInfo.cbSize = Marshal.SizeOf(cursorInfo);
|
||||
if (User32.GetCursorInfo(out cursorInfo)) {
|
||||
if (cursorInfo.flags == User32.CURSOR_SHOWING) {
|
||||
hicon = User32.CopyIcon(cursorInfo.hCursor);
|
||||
if (User32.GetIconInfo(hicon, out iconInfo)) {
|
||||
Point cursorLocation = GetCursorLocation();
|
||||
// Allign cursor location to Bitmap coordinates (instead of Screen coordinates)
|
||||
x = cursorLocation.X - iconInfo.xHotspot - capture.ScreenBounds.X;
|
||||
y = cursorLocation.Y - iconInfo.yHotspot - capture.ScreenBounds.Y;
|
||||
// Set the location
|
||||
capture.CursorLocation = new Point(x, y);
|
||||
|
||||
using (Icon icon = Icon.FromHandle(hicon)) {
|
||||
capture.Cursor = icon;
|
||||
}
|
||||
|
||||
if (iconInfo.hbmMask != IntPtr.Zero) {
|
||||
GDI32.DeleteObject(iconInfo.hbmMask);
|
||||
}
|
||||
if (iconInfo.hbmColor != IntPtr.Zero) {
|
||||
GDI32.DeleteObject(iconInfo.hbmColor);
|
||||
}
|
||||
}
|
||||
User32.DestroyIcon(hicon);
|
||||
}
|
||||
}
|
||||
return capture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method will call the CaptureRectangle with the screenbounds, therefor Capturing the whole screen.
|
||||
/// </summary>
|
||||
/// <returns>A Capture Object with the Screen as an Image</returns>
|
||||
public static ICapture CaptureScreen(ICapture capture) {
|
||||
if (capture == null) {
|
||||
capture = new Capture();
|
||||
}
|
||||
return CaptureRectangle(capture, capture.ScreenBounds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create an exception that might explain what is wrong while capturing
|
||||
/// </summary>
|
||||
/// <param name="method">string with current method</param>
|
||||
/// <param name="capture">ICapture</param>
|
||||
/// <param name="captureBounds">Rectangle of what we want to capture</param>
|
||||
/// <returns></returns>
|
||||
private static Exception CreateCaptureException(string method, Rectangle captureBounds) {
|
||||
Exception exceptionToThrow = User32.CreateWin32Exception(method);
|
||||
if (!captureBounds.IsEmpty) {
|
||||
exceptionToThrow.Data.Add("Height", captureBounds.Height);
|
||||
exceptionToThrow.Data.Add("Width", captureBounds.Width);
|
||||
}
|
||||
return exceptionToThrow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method will use User32 code to capture the specified captureBounds from the screen
|
||||
/// </summary>
|
||||
/// <param name="capture">ICapture where the captured Bitmap will be stored</param>
|
||||
/// <param name="captureBounds">Rectangle with the bounds to capture</param>
|
||||
/// <returns>A Capture Object with a part of the Screen as an Image</returns>
|
||||
public static ICapture CaptureRectangle(ICapture capture, Rectangle captureBounds) {
|
||||
if (capture == null) {
|
||||
capture = new Capture();
|
||||
}
|
||||
capture.Image = CaptureRectangle(captureBounds);
|
||||
capture.Location = captureBounds.Location;
|
||||
((Bitmap)capture.Image).SetResolution(capture.CaptureDetails.DpiX, capture.CaptureDetails.DpiY);
|
||||
if (capture.Image == null) {
|
||||
return null;
|
||||
}
|
||||
return capture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method will use User32 code to capture the specified captureBounds from the screen
|
||||
/// </summary>
|
||||
/// <param name="captureBounds">Rectangle with the bounds to capture</param>
|
||||
/// <returns>Bitmap which is captured from the screen at the location specified by the captureBounds</returns>
|
||||
public static Bitmap CaptureRectangle(Rectangle captureBounds) {
|
||||
Bitmap returnBitmap = null;
|
||||
if (captureBounds.Height <= 0 || captureBounds.Width <= 0) {
|
||||
LOG.Warn("Nothing to capture, ignoring!");
|
||||
return null;
|
||||
} else {
|
||||
LOG.Debug("CaptureRectangle Called!");
|
||||
}
|
||||
|
||||
// .NET GDI+ Solution, according to some post this has a GDI+ leak...
|
||||
// See http://connect.microsoft.com/VisualStudio/feedback/details/344752/gdi-object-leak-when-calling-graphics-copyfromscreen
|
||||
// Bitmap capturedBitmap = new Bitmap(captureBounds.Width, captureBounds.Height);
|
||||
// using (Graphics graphics = Graphics.FromImage(capturedBitmap)) {
|
||||
// graphics.CopyFromScreen(captureBounds.Location, Point.Empty, captureBounds.Size, CopyPixelOperation.CaptureBlt);
|
||||
// }
|
||||
// capture.Image = capturedBitmap;
|
||||
// capture.Location = captureBounds.Location;
|
||||
|
||||
// "P/Invoke" Solution for capturing the screen
|
||||
IntPtr hWndDesktop = User32.GetDesktopWindow();
|
||||
// get te hDC of the target window
|
||||
IntPtr hDCDesktop = User32.GetWindowDC(hWndDesktop);
|
||||
|
||||
// Make sure the last error is set to 0
|
||||
Win32.SetLastError(0);
|
||||
|
||||
// create a device context we can copy to
|
||||
IntPtr hDCDest = GDI32.CreateCompatibleDC(hDCDesktop);
|
||||
// Check if the device context is there, if not throw an error with as much info as possible!
|
||||
if (hDCDest == IntPtr.Zero) {
|
||||
// Get Exception before the error is lost
|
||||
Exception exceptionToThrow = CreateCaptureException("CreateCompatibleDC", captureBounds);
|
||||
// Cleanup
|
||||
User32.ReleaseDC(hWndDesktop, hDCDesktop);
|
||||
// throw exception
|
||||
throw exceptionToThrow;
|
||||
}
|
||||
|
||||
// Create BitmapInfoHeader for CreateDIBSection
|
||||
BitmapInfoHeader bmi = new BitmapInfoHeader(captureBounds.Width, captureBounds.Height, 24);
|
||||
|
||||
// Make sure the last error is set to 0
|
||||
Win32.SetLastError(0);
|
||||
|
||||
// create a bitmap we can copy it to, using GetDeviceCaps to get the width/height
|
||||
IntPtr bits0; // not used for our purposes. It returns a pointer to the raw bits that make up the bitmap.
|
||||
IntPtr hDIBSection = GDI32.CreateDIBSection(hDCDesktop, ref bmi, BitmapInfoHeader.DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0);
|
||||
|
||||
if (hDIBSection == IntPtr.Zero) {
|
||||
// Get Exception before the error is lost
|
||||
Exception exceptionToThrow = CreateCaptureException("CreateDIBSection", captureBounds);
|
||||
exceptionToThrow.Data.Add("hdcDest", hDCDest.ToInt32());
|
||||
exceptionToThrow.Data.Add("hdcSrc", hDCDesktop.ToInt32());
|
||||
|
||||
// clean up
|
||||
GDI32.DeleteDC(hDCDest);
|
||||
User32.ReleaseDC(hWndDesktop, hDCDesktop);
|
||||
|
||||
// Throw so people can report the problem
|
||||
throw exceptionToThrow;
|
||||
} else {
|
||||
// select the bitmap object and store the old handle
|
||||
IntPtr hOldObject = GDI32.SelectObject(hDCDest, hDIBSection);
|
||||
|
||||
// bitblt over (make copy)
|
||||
GDI32.BitBlt(hDCDest, 0, 0, captureBounds.Width, captureBounds.Height, hDCDesktop, captureBounds.X, captureBounds.Y, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);
|
||||
|
||||
// restore selection (old handle)
|
||||
GDI32.SelectObject(hDCDest, hOldObject);
|
||||
// clean up
|
||||
GDI32.DeleteDC(hDCDest);
|
||||
User32.ReleaseDC(hWndDesktop, hDCDesktop);
|
||||
|
||||
// get a .NET image object for it
|
||||
// A suggestion for the "A generic error occurred in GDI+." E_FAIL/0×80004005 error is to re-try...
|
||||
bool success = false;
|
||||
ExternalException exception = null;
|
||||
for(int i = 0; i < 3; i++) {
|
||||
try {
|
||||
// assign image to Capture, the image will be disposed there..
|
||||
returnBitmap = Bitmap.FromHbitmap(hDIBSection);
|
||||
success = true;
|
||||
break;
|
||||
} catch (ExternalException ee) {
|
||||
LOG.Warn("Problem getting bitmap at try " + i + " : ", ee);
|
||||
exception = ee;
|
||||
}
|
||||
}
|
||||
if (!success) {
|
||||
LOG.Error("Still couldn't create Bitmap!");
|
||||
throw exception;
|
||||
}
|
||||
// free up the Bitmap object
|
||||
GDI32.DeleteObject(hDIBSection);
|
||||
|
||||
}
|
||||
return returnBitmap;
|
||||
}
|
||||
}
|
||||
}
|
1458
GreenshotPlugin/Core/WindowsHelper.cs
Normal file
1458
GreenshotPlugin/Core/WindowsHelper.cs
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue