"Merged" 0.8 with HEAD, so we can continue developing

git-svn-id: http://svn.code.sf.net/p/greenshot/code/trunk@1282 7dccd23d-a4a3-4e1f-8c07-b4c1b4018ab4
This commit is contained in:
RKrom 2011-07-17 12:05:59 +00:00
commit f3b0878b02
539 changed files with 86855 additions and 0 deletions

View file

@ -0,0 +1,726 @@
/*
* 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);
}
}
}

View file

@ -0,0 +1,196 @@
/*
* 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;
namespace GreenshotPlugin.Core {
public enum Destination {
Editor, FileDefault, FileWithDialog, Clipboard, Printer, EMail
}
public enum OutputFormat {
bmp, gif, jpg, png, tiff
}
public enum WindowCaptureMode {
Screen, GDI, Aero, AeroTransparent, Auto
}
public enum EmailFormat {
TXT, HTML
}
public enum UpdateCheckInterval {
Never,
Daily,
Weekly,
Monthly
}
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")]
public string Language;
[IniProperty("RegionHotkey", Description="Hotkey for starting the region capture", DefaultValue="PrintScreen")]
public string RegionHotkey;
[IniProperty("WindowHotkey", Description="Hotkey for starting the window capture", DefaultValue="Alt + PrintScreen")]
public string WindowHotkey;
[IniProperty("FullscreenHotkey", Description="Hotkey for starting the fullscreen capture", DefaultValue="Ctrl + PrintScreen")]
public string FullscreenHotkey;
[IniProperty("LastregionHotkey", Description="Hotkey for starting the last region capture", DefaultValue="Shift + PrintScreen")]
public string LastregionHotkey;
[IniProperty("IEHotkey", Description="Hotkey for starting the IE capture", DefaultValue="Shift + Ctrl + PrintScreen")]
public string IEHotkey;
[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>();
[IniProperty("CaptureMousepointer", Description="Should the mouse be captured?", DefaultValue="true")]
public bool CaptureMousepointer;
[IniProperty("CaptureWindowsInteractive", Description="Use interactive window selection to capture? (false=Capture active window)", DefaultValue="false")]
public bool CaptureWindowsInteractive;
[IniProperty("CaptureDelay", Description="Capture delay in millseconds.", DefaultValue="100")]
public int CaptureDelay;
[IniProperty("WindowCaptureMode", Description="The capture mode used to capture a Window.", DefaultValue="Auto")]
public WindowCaptureMode WindowCaptureMode;
[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")]
public bool PlayCameraSound = false;
[IniProperty("OutputFilePath", Description="Output file path.")]
public string OutputFilePath;
[IniProperty("OutputFileFilenamePattern", Description="Filename pattern for screenshot.", DefaultValue="${capturetime}_${title}")]
public string OutputFileFilenamePattern;
[IniProperty("OutputFileFormat", Description="Default file type for writing screenshots. (bmp, gif, jpg, png, tiff)", DefaultValue="png")]
public OutputFormat OutputFileFormat = OutputFormat.png;
[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("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;
[IniProperty("OutputFileCopyPathToClipboard", Description="When saving a screenshot, copy the path to the clipboard?", DefaultValue="true")]
public bool OutputFileCopyPathToClipboard;
[IniProperty("OutputFileAsFullpath", Description="SaveAs Full path?")]
public string OutputFileAsFullpath;
[IniProperty("OutputFileJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")]
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")]
public uint OutputFileIncrementingNumber;
[IniProperty("OutputPrintPromptOptions", 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")]
public bool OutputPrintAllowRotate;
[IniProperty("OutputPrintAllowEnlarge", 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")]
public bool OutputPrintAllowShrink;
[IniProperty("OutputPrintCenter", Description="Center image when printing?", DefaultValue="true")]
public bool OutputPrintCenter;
[IniProperty("OutputPrintInverted", Description="Print image inverted (use e.g. for console captures)", DefaultValue="false")]
public bool OutputPrintInverted;
[IniProperty("OutputPrintTimestamp", 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("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;
[IniProperty("ExcludePlugins", Description="Comma separated list of Plugins which are NOT allowed.")]
public List<string> ExcludePlugins;
[IniProperty("UpdateCheckInterval", Description="How many days between every update check? (0=no checks)", DefaultValue="1")]
public int UpdateCheckInterval;
[IniProperty("LastUpdateCheck", Description="Last update check")]
public DateTime LastUpdateCheck;
// change to false for releases
public bool CheckUnstable = true;
/// <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 override object GetDefault(string property) {
switch(property) {
case "PluginWhitelist":
case "PluginBacklist":
return new List<string>();
case "OutputFileAsFullpath":
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop),"dummy.png");
case "OutputFilePath":
return Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
case "DWMBackgroundColor":
return Color.White;
}
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 override string PreCheckValue(string propertyName, string propertyValue) {
// Changed the separator, now we need to correct this
if ("Destinations".Equals(propertyName)) {
if (propertyValue != null) {
return propertyValue.Replace('|',',');
}
}
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() {
if (OutputDestinations == null) {
OutputDestinations = new List<Destination>();
}
if (OutputDestinations.Count == 0) {
OutputDestinations.Add(Destination.Editor);
}
}
}
}

View file

@ -0,0 +1,608 @@
/*
* 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.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;
/// <summary>
/// The following code comes from: http://www.developerfusion.com/code/4693/using-the-credential-management-api/
/// and is slightly modified so it works for us.
/// As the "Stored usernames and passwords" which can be accessed by: Start-> Run and type "Control keymgr.dll"
/// doesn't show all credentials use the tool here: http://www.microsoft.com/indonesia/msdn/credmgmt.aspx
/// The following code is an example for a login, it will call the Authenticate with user/password
/// which should return true if the login worked, false if not.
/// private static bool Login(string system, string name) {
/// try {
/// CredentialsDialog dialog = new CredentialsDialog(system);
/// dialog.Name = name;
/// while (dialog.Show(dialog.Name) == DialogResult.OK) {
/// if (Authenticate(dialog.Name, dialog.Password)) {
/// if (dialog.SaveChecked) dialog.Confirm(true);
/// return true;
/// } else {
/// try {
/// dialog.Confirm(false);
/// } catch (ApplicationException) {
/// // exception handling ...
/// }
/// dialog.IncorrectPassword = true;
/// }
/// }
/// } catch (ApplicationException) {
/// // exception handling ...
/// }
/// return false;
/// }
/// </summary>
namespace GreenshotPlugin.Core {
/// <summary>Encapsulates dialog functionality from the Credential Management API.</summary>
public sealed class CredentialsDialog {
[DllImport("gdi32.dll", SetLastError=true)]
private static extern bool DeleteObject(IntPtr hObject);
/// <summary>The only valid bitmap height (in pixels) of a user-defined banner.</summary>
private const int ValidBannerHeight = 60;
/// <summary>The only valid bitmap width (in pixels) of a user-defined banner.</summary>
private const int ValidBannerWidth = 320;
/// <summary>Initializes a new instance of the <see cref="T:SecureCredentialsLibrary.CredentialsDialog"/> class
/// with the specified target.</summary>
/// <param name="target">The name of the target for the credentials, typically a server name.</param>
public CredentialsDialog(string target) : this(target, null) {
}
/// <summary>Initializes a new instance of the <see cref="T:SecureCredentialsLibrary.CredentialsDialog"/> class
/// with the specified target and caption.</summary>
/// <param name="target">The name of the target for the credentials, typically a server name.</param>
/// <param name="caption">The caption of the dialog (null will cause a system default title to be used).</param>
public CredentialsDialog(string target, string caption) : this(target, caption, null) {
}
/// <summary>Initializes a new instance of the <see cref="T:SecureCredentialsLibrary.CredentialsDialog"/> class
/// with the specified target, caption and message.</summary>
/// <param name="target">The name of the target for the credentials, typically a server name.</param>
/// <param name="caption">The caption of the dialog (null will cause a system default title to be used).</param>
/// <param name="message">The message of the dialog (null will cause a system default message to be used).</param>
public CredentialsDialog(string target, string caption, string message) : this(target, caption, message, null) {
}
/// <summary>Initializes a new instance of the <see cref="T:SecureCredentialsLibrary.CredentialsDialog"/> class
/// with the specified target, caption, message and banner.</summary>
/// <param name="target">The name of the target for the credentials, typically a server name.</param>
/// <param name="caption">The caption of the dialog (null will cause a system default title to be used).</param>
/// <param name="message">The message of the dialog (null will cause a system default message to be used).</param>
/// <param name="banner">The image to display on the dialog (null will cause a system default image to be used).</param>
public CredentialsDialog(string target, string caption, string message, Image banner) {
this.Target = target;
this.Caption = caption;
this.Message = message;
this.Banner = banner;
}
private bool _alwaysDisplay = false;
/// <summary>
/// Gets or sets if the dialog will be shown even if the credentials
/// can be returned from an existing credential in the credential manager.
/// </summary>
public bool AlwaysDisplay {
get {
return _alwaysDisplay;
}
set {
_alwaysDisplay = value;
}
}
private bool _excludeCertificates = true;
/// <summary>Gets or sets if the dialog is populated with name/password only.</summary>
public bool ExcludeCertificates {
get {
return _excludeCertificates;
}
set {
_excludeCertificates = value;
}
}
private bool _persist = true;
/// <summary>Gets or sets if the credentials are to be persisted in the credential manager.</summary>
public bool Persist {
get {
return _persist;
}
set {
_persist = value;
}
}
private bool _incorrectPassword = false;
/// <summary>Gets or sets if the incorrect password balloontip needs to be shown. Introduced AFTER Windows XP</summary>Gets></summary>
public bool IncorrectPassword {
get {
return _incorrectPassword;
}
set {
_incorrectPassword = value;
}
}
private bool _keepName = false;
/// <summary>Gets or sets if the name is read-only.</summary>
public bool KeepName {
get {
return _keepName;
}
set {
_keepName = value;
}
}
private string _name = String.Empty;
/// <summary>Gets or sets the name for the credentials.</summary>
public string Name {
get {
return _name;
}
set {
if (value != null) {
if (value.Length > CREDUI.MAX_USERNAME_LENGTH) {
string message = String.Format(
Thread.CurrentThread.CurrentUICulture,
"The name has a maximum length of {0} characters.",
CREDUI.MAX_USERNAME_LENGTH);
throw new ArgumentException(message, "Name");
}
}
_name = value;
}
}
private string _password = String.Empty;
/// <summary>Gets or sets the password for the credentials.</summary>
public string Password {
get {
return _password;
}
set {
if (value != null) {
if (value.Length > CREDUI.MAX_PASSWORD_LENGTH) {
string message = String.Format(
Thread.CurrentThread.CurrentUICulture,
"The password has a maximum length of {0} characters.",
CREDUI.MAX_PASSWORD_LENGTH);
throw new ArgumentException(message, "Password");
}
}
_password = value;
}
}
private bool _saveChecked = false;
/// <summary>Gets or sets if the save checkbox status.</summary>
public bool SaveChecked {
get {
return _saveChecked;
}
set {
_saveChecked = value;
}
}
private bool _saveDisplayed = true;
/// <summary>Gets or sets if the save checkbox is displayed.</summary>
/// <remarks>This value only has effect if Persist is true.</remarks>
public bool SaveDisplayed {
get {
return _saveDisplayed;
}
set {
_saveDisplayed = value;
}
}
private string _target = String.Empty;
/// <summary>Gets or sets the name of the target for the credentials, typically a server name.</summary>
public string Target {
get {
return _target;
}
set {
if (value == null) {
throw new ArgumentException("The target cannot be a null value.", "Target");
} else if (value.Length > CREDUI.MAX_GENERIC_TARGET_LENGTH) {
string message = String.Format(
Thread.CurrentThread.CurrentUICulture,
"The target has a maximum length of {0} characters.",
CREDUI.MAX_GENERIC_TARGET_LENGTH);
throw new ArgumentException(message, "Target");
}
_target = value;
}
}
private string _caption = String.Empty;
/// <summary>Gets or sets the caption of the dialog.</summary>
/// <remarks>A null value will cause a system default caption to be used.</remarks>
public string Caption {
get {
return _caption;
}
set {
if (value != null) {
if (value.Length > CREDUI.MAX_CAPTION_LENGTH) {
string message = String.Format(
Thread.CurrentThread.CurrentUICulture,
"The caption has a maximum length of {0} characters.",
CREDUI.MAX_CAPTION_LENGTH);
throw new ArgumentException(message, "Caption");
}
}
_caption = value;
}
}
private string _message = String.Empty;
/// <summary>Gets or sets the message of the dialog.</summary>
/// <remarks>A null value will cause a system default message to be used.</remarks>
public string Message {
get {
return _message;
}
set {
if (value != null) {
if (value.Length > CREDUI.MAX_MESSAGE_LENGTH) {
string message = String.Format(
Thread.CurrentThread.CurrentUICulture,
"The message has a maximum length of {0} characters.",
CREDUI.MAX_MESSAGE_LENGTH);
throw new ArgumentException(message, "Message");
}
}
_message = value;
}
}
private Image _banner = null;
/// <summary>Gets or sets the image to display on the dialog.</summary>
/// <remarks>A null value will cause a system default image to be used.</remarks>
public Image Banner {
get {
return _banner;
}
set {
if (value != null) {
if (value.Width != ValidBannerWidth) {
throw new ArgumentException("The banner image width must be 320 pixels.", "Banner");
}
if (value.Height != ValidBannerHeight) {
throw new ArgumentException("The banner image height must be 60 pixels.", "Banner");
}
}
_banner = value;
}
}
/// <summary>Shows the credentials dialog.</summary>
/// <returns>Returns a DialogResult indicating the user action.</returns>
public DialogResult Show() {
return Show(null, this.Name, this.Password, this.SaveChecked);
}
/// <summary>Shows the credentials dialog with the specified save checkbox status.</summary>
/// <param name="saveChecked">True if the save checkbox is checked.</param>
/// <returns>Returns a DialogResult indicating the user action.</returns>
public DialogResult Show(bool saveChecked) {
return Show(null, this.Name, this.Password, saveChecked);
}
/// <summary>Shows the credentials dialog with the specified name.</summary>
/// <param name="name">The name for the credentials.</param>
/// <returns>Returns a DialogResult indicating the user action.</returns>
public DialogResult Show(string name) {
return Show(null, name, this.Password, this.SaveChecked);
}
/// <summary>Shows the credentials dialog with the specified name and password.</summary>
/// <param name="name">The name for the credentials.</param>
/// <param name="password">The password for the credentials.</param>
/// <returns>Returns a DialogResult indicating the user action.</returns>
public DialogResult Show(string name, string password) {
return Show(null, name, password, this.SaveChecked);
}
/// <summary>Shows the credentials dialog with the specified name, password and save checkbox status.</summary>
/// <param name="name">The name for the credentials.</param>
/// <param name="password">The password for the credentials.</param>
/// <param name="saveChecked">True if the save checkbox is checked.</param>
/// <returns>Returns a DialogResult indicating the user action.</returns>
public DialogResult Show(string name, string password, bool saveChecked) {
return Show(null, name, password, saveChecked);
}
/// <summary>Shows the credentials dialog with the specified owner.</summary>
/// <param name="owner">The System.Windows.Forms.IWin32Window the dialog will display in front of.</param>
/// <returns>Returns a DialogResult indicating the user action.</returns>
public DialogResult Show(IWin32Window owner) {
return Show(owner, this.Name, this.Password, this.SaveChecked);
}
/// <summary>Shows the credentials dialog with the specified owner and save checkbox status.</summary>
/// <param name="owner">The System.Windows.Forms.IWin32Window the dialog will display in front of.</param>
/// <param name="saveChecked">True if the save checkbox is checked.</param>
/// <returns>Returns a DialogResult indicating the user action.</returns>
public DialogResult Show(IWin32Window owner, bool saveChecked) {
return Show(owner, this.Name, this.Password, saveChecked);
}
/// <summary>Shows the credentials dialog with the specified owner, name and password.</summary>
/// <param name="owner">The System.Windows.Forms.IWin32Window the dialog will display in front of.</param>
/// <param name="name">The name for the credentials.</param>
/// <param name="password">The password for the credentials.</param>
/// <returns>Returns a DialogResult indicating the user action.</returns>
public DialogResult Show(IWin32Window owner, string name, string password) {
return Show(owner, name, password, this.SaveChecked);
}
/// <summary>Shows the credentials dialog with the specified owner, name, password and save checkbox status.</summary>
/// <param name="owner">The System.Windows.Forms.IWin32Window the dialog will display in front of.</param>
/// <param name="name">The name for the credentials.</param>
/// <param name="password">The password for the credentials.</param>
/// <param name="saveChecked">True if the save checkbox is checked.</param>
/// <returns>Returns a DialogResult indicating the user action.</returns>
public DialogResult Show(IWin32Window owner, string name, string password, bool saveChecked) {
if ((Environment.OSVersion.Version.Major < 5) || ((Environment.OSVersion.Version.Major == 5) && (Environment.OSVersion.Version.Minor < 1))) {
throw new ApplicationException("The Credential Management API requires Windows XP / Windows Server 2003 or later.");
}
this.Name = name;
this.Password = password;
this.SaveChecked = saveChecked;
return ShowDialog(owner);
}
/// <summary>Confirmation action to be applied.</summary>
/// <param name="value">True if the credentials should be persisted.</param>
public void Confirm(bool value) {
switch (CREDUI.ConfirmCredentials(this.Target, value)) {
case CREDUI.ReturnCodes.NO_ERROR:
break;
case CREDUI.ReturnCodes.ERROR_INVALID_PARAMETER:
// for some reason, this is encountered when credentials are overwritten
break;
default:
throw new ApplicationException("Credential confirmation failed.");
}
}
/// <summary>Returns a DialogResult indicating the user action.</summary>
/// <param name="owner">The System.Windows.Forms.IWin32Window the dialog will display in front of.</param>
/// <remarks>
/// Sets the name, password and SaveChecked accessors to the state of the dialog as it was dismissed by the user.
/// </remarks>
private DialogResult ShowDialog(IWin32Window owner) {
// set the api call parameters
StringBuilder name = new StringBuilder(CREDUI.MAX_USERNAME_LENGTH);
name.Append(this.Name);
StringBuilder password = new StringBuilder(CREDUI.MAX_PASSWORD_LENGTH);
password.Append(this.Password);
int saveChecked = Convert.ToInt32(this.SaveChecked);
CREDUI.INFO info = GetInfo(owner);
CREDUI.FLAGS flags = GetFlags();
// make the api call
CREDUI.ReturnCodes code = CREDUI.PromptForCredentials(
ref info,
this.Target,
IntPtr.Zero, 0,
name, CREDUI.MAX_USERNAME_LENGTH,
password, CREDUI.MAX_PASSWORD_LENGTH,
ref saveChecked,
flags
);
// clean up resources
if (this.Banner != null) {
DeleteObject(info.hbmBanner);
}
// set the accessors from the api call parameters
this.Name = name.ToString();
this.Password = password.ToString();
this.SaveChecked = Convert.ToBoolean(saveChecked);
return GetDialogResult(code);
}
/// <summary>Returns the info structure for dialog display settings.</summary>
/// <param name="owner">The System.Windows.Forms.IWin32Window the dialog will display in front of.</param>
private CREDUI.INFO GetInfo(IWin32Window owner) {
CREDUI.INFO info = new CREDUI.INFO();
if (owner != null) info.hwndParent = owner.Handle;
info.pszCaptionText = this.Caption;
info.pszMessageText = this.Message;
if (this.Banner != null) {
info.hbmBanner = new Bitmap(this.Banner, ValidBannerWidth, ValidBannerHeight).GetHbitmap();
}
info.cbSize = Marshal.SizeOf(info);
return info;
}
/// <summary>Returns the flags for dialog display options.</summary>
private CREDUI.FLAGS GetFlags() {
CREDUI.FLAGS flags = CREDUI.FLAGS.GENERIC_CREDENTIALS;
if (this.IncorrectPassword) {
flags = flags | CREDUI.FLAGS.INCORRECT_PASSWORD;
}
if (this.AlwaysDisplay) {
flags = flags | CREDUI.FLAGS.ALWAYS_SHOW_UI;
}
if (this.ExcludeCertificates) {
flags = flags | CREDUI.FLAGS.EXCLUDE_CERTIFICATES;
}
if (this.Persist) {
flags = flags | CREDUI.FLAGS.EXPECT_CONFIRMATION;
if (this.SaveDisplayed) {
flags = flags | CREDUI.FLAGS.SHOW_SAVE_CHECK_BOX;
} else {
flags = flags | CREDUI.FLAGS.PERSIST;
}
} else {
flags = flags | CREDUI.FLAGS.DO_NOT_PERSIST;
}
if (this.KeepName) {
flags = flags | CREDUI.FLAGS.KEEP_USERNAME;
}
return flags;
}
/// <summary>Returns a DialogResult from the specified code.</summary>
/// <param name="code">The credential return code.</param>
private DialogResult GetDialogResult(CREDUI.ReturnCodes code) {
DialogResult result;
switch (code) {
case CREDUI.ReturnCodes.NO_ERROR:
result = DialogResult.OK;
break;
case CREDUI.ReturnCodes.ERROR_CANCELLED:
result = DialogResult.Cancel;
break;
case CREDUI.ReturnCodes.ERROR_NO_SUCH_LOGON_SESSION:
throw new ApplicationException("No such logon session.");
case CREDUI.ReturnCodes.ERROR_NOT_FOUND:
throw new ApplicationException("Not found.");
case CREDUI.ReturnCodes.ERROR_INVALID_ACCOUNT_NAME:
throw new ApplicationException("Invalid account name.");
case CREDUI.ReturnCodes.ERROR_INSUFFICIENT_BUFFER:
throw new ApplicationException("Insufficient buffer.");
case CREDUI.ReturnCodes.ERROR_INVALID_PARAMETER:
throw new ApplicationException("Invalid parameter.");
case CREDUI.ReturnCodes.ERROR_INVALID_FLAGS:
throw new ApplicationException("Invalid flags.");
default:
throw new ApplicationException("Unknown credential result encountered.");
}
return result;
}
}
public sealed class CREDUI {
private CREDUI() {
}
/// <summary>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/authentication_constants.asp</summary>
public const int MAX_MESSAGE_LENGTH = 100;
public const int MAX_CAPTION_LENGTH = 100;
public const int MAX_GENERIC_TARGET_LENGTH = 100;
public const int MAX_DOMAIN_TARGET_LENGTH = 100;
public const int MAX_USERNAME_LENGTH = 100;
public const int MAX_PASSWORD_LENGTH = 100;
/// <summary>
/// http://www.pinvoke.net/default.aspx/Enums.CREDUI_FLAGS
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/dpapiusercredentials.asp
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/creduipromptforcredentials.asp
/// </summary>
[Flags] public enum FLAGS {
INCORRECT_PASSWORD = 0x1,
DO_NOT_PERSIST = 0x2,
REQUEST_ADMINISTRATOR = 0x4,
EXCLUDE_CERTIFICATES = 0x8,
REQUIRE_CERTIFICATE = 0x10,
SHOW_SAVE_CHECK_BOX = 0x40,
ALWAYS_SHOW_UI = 0x80,
REQUIRE_SMARTCARD = 0x100,
PASSWORD_ONLY_OK = 0x200,
VALIDATE_USERNAME = 0x400,
COMPLETE_USERNAME = 0x800,
PERSIST = 0x1000,
SERVER_CREDENTIAL = 0x4000,
EXPECT_CONFIRMATION = 0x20000,
GENERIC_CREDENTIALS = 0x40000,
USERNAME_TARGET_CREDENTIALS = 0x80000,
KEEP_USERNAME = 0x100000,
}
/// <summary>http://www.pinvoke.net/default.aspx/Enums.CredUIReturnCodes</summary>
public enum ReturnCodes {
NO_ERROR = 0,
ERROR_INVALID_PARAMETER = 87,
ERROR_INSUFFICIENT_BUFFER = 122,
ERROR_INVALID_FLAGS = 1004,
ERROR_NOT_FOUND = 1168,
ERROR_CANCELLED = 1223,
ERROR_NO_SUCH_LOGON_SESSION = 1312,
ERROR_INVALID_ACCOUNT_NAME = 1315
}
/// <summary>
/// http://www.pinvoke.net/default.aspx/Structures.CREDUI_INFO
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/credui_info.asp
/// </summary>
public struct INFO {
public int cbSize;
public IntPtr hwndParent;
[MarshalAs(UnmanagedType.LPWStr)] public string pszMessageText;
[MarshalAs(UnmanagedType.LPWStr)] public string pszCaptionText;
public IntPtr hbmBanner;
}
/// <summary>
/// http://www.pinvoke.net/default.aspx/credui.CredUIPromptForCredentialsW
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/creduipromptforcredentials.asp
/// </summary>
[DllImport("credui", EntryPoint="CredUIPromptForCredentialsW", CharSet=CharSet.Unicode)]
public static extern ReturnCodes PromptForCredentials(
ref INFO creditUR,
string targetName,
IntPtr reserved1,
int iError,
StringBuilder userName,
int maxUserName,
StringBuilder password,
int maxPassword,
ref int iSave,
FLAGS flags
);
/// <summary>
/// http://www.pinvoke.net/default.aspx/credui.CredUIConfirmCredentials
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/creduiconfirmcredentials.asp
/// </summary>
[DllImport("credui.dll", EntryPoint="CredUIConfirmCredentialsW", CharSet=CharSet.Unicode)]
public static extern ReturnCodes ConfirmCredentials(string targetName, bool confirm);
}
}

View file

@ -0,0 +1,47 @@
/*
* 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.ComponentModel;
using System.Drawing;
namespace GreenshotPlugin.Core {
/// <summary>
/// Centralized storage of the icons & bitmaps
/// </summary>
public class GreenshotResources {
private static ComponentResourceManager greenshotResources = new ComponentResourceManager(typeof(GreenshotResources));
public static Image getImage(string imageName) {
return (Image)greenshotResources.GetObject(imageName);
}
public static Icon getIcon(string imageName) {
return (Icon)greenshotResources.GetObject(imageName);
}
public static Icon getGreenshotIcon() {
return getIcon("Greenshot.Icon");
}
public static Image getGreenshotImage() {
return getImage("Greenshot.Image");
}
}
}

View file

@ -0,0 +1,459 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="Greenshot.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
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
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPSkpKSisCAAAAAAAAAQAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFTg9JgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAgAAAgABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAEAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIA
AAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAA
AKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIA
AAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAA
AKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIA
AAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAA
AKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCKAAAACAAAABA
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
</value>
</data>
<data name="Greenshot.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
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
</value>
</data>
</root>

View file

@ -0,0 +1,52 @@
/*
* 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 Microsoft.Win32;
namespace GreenshotPlugin.Core {
/// <summary>
/// Description of IEHelper.
/// </summary>
public class IEHelper {
// Internet explorer Registry key
private const string IE_KEY = @"Software\Microsoft\Internet Explorer";
/// <summary>
/// Helper method to get the IE version
/// </summary>
/// <returns></returns>
public static int IEVersion() {
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);
}
}
}
return version;
}
private IEHelper() {
}
}
}

View file

@ -0,0 +1,496 @@
/*
* 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.IO;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Xml;
namespace GreenshotPlugin.Core {
public interface ILanguage {
void Load();
string GetString(Enum id);
string GetFormattedString(Enum id, object param);
string GetHelpFilePath();
/// <summary>
/// Set language
/// </summary>
/// <param name="wantedIETF">wanted IETF</param>
/// <returns>Actuall IETF </returns>
string SetLanguage(string cultureInfo);
void SynchronizeLanguageToCulture();
string CurrentLanguage {
get;
}
List<LanguageConfiguration> SupportedLanguages {
get;
}
String LanguageFilePattern {
get;
set;
}
LanguageConfiguration CurrentLanguageConfiguration {
get;
}
}
/// <summary>
/// Description of Language.
/// </summary>
public class LanguageContainer : ILanguage {
private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(LanguageContainer));
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 const string HELP_FILENAME_PATTERN = @"help-*.html";
private Dictionary<string, string> strings = new Dictionary<string, string>();
private List<LanguageConfiguration> languages = new List<LanguageConfiguration>();
private string currentIETF = null;
private string languageFilePattern;
public LanguageContainer() {
}
public String LanguageFilePattern {
get {
return languageFilePattern;
}
set {
languageFilePattern = value;
}
}
public void Load() {
languages = LoadFiles(languageFilePattern);
}
public string CurrentLanguage {
get {
return currentIETF;
}
}
public List<LanguageConfiguration> SupportedLanguages {
get {
return languages;
}
}
public LanguageConfiguration CurrentLanguageConfiguration {
get {
foreach(LanguageConfiguration languageConfiguration in SupportedLanguages) {
if (currentIETF.Equals(languageConfiguration.Ietf)) {
return languageConfiguration;
}
}
return null;
}
}
public void SynchronizeLanguageToCulture() {
if (CurrentLanguage == null || !CurrentLanguage.Equals(Thread.CurrentThread.CurrentUICulture.Name)) {
SetLanguage(Thread.CurrentThread.CurrentUICulture.Name);
}
}
/// <summary>
/// Set language
/// </summary>
/// <param name="wantedIETF">wanted IETF</param>
/// <returns>Actuall IETF </returns>
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!");
}
// Find selected languages in available languages
foreach(LanguageConfiguration language in languages) {
LOG.Debug("Found language: " + language.Ietf);
identifiedLanguages.Add(language.Ietf, language);
}
LanguageConfiguration selectedLanguage = null;
try {
selectedLanguage = identifiedLanguages[wantedIETF];
} catch (KeyNotFoundException) {
LOG.Warn("Selecteded language " + wantedIETF + " not found.");
}
// Make best match for language (e.g. en -> "en-US")
if (selectedLanguage == null) {
foreach(string ietf in identifiedLanguages.Keys) {
if (ietf.StartsWith(wantedIETF)) {
try {
selectedLanguage = identifiedLanguages[ietf];
LOG.Info("Selecteded language " + ietf + " by near match for: " + wantedIETF);
wantedIETF = ietf;
break;
} catch (KeyNotFoundException) {
LOG.Warn("Selecteded language " + wantedIETF + " not found.");
}
}
}
}
if (selectedLanguage == null && !DEFAULT_LANGUAGE.Equals(wantedIETF)) {
try {
selectedLanguage = identifiedLanguages[DEFAULT_LANGUAGE];
} catch (KeyNotFoundException) {
LOG.Warn("No english language file found!!");
}
}
if (selectedLanguage == null) {
// Select first (maybe only) language!
selectedLanguage = languages[0];
LOG.Warn("Selected " + selectedLanguage.Ietf + " as fallback language!");
}
// build directionary for the strings
strings.Clear();
foreach(Resource resource in selectedLanguage.Resources) {
AddResource(resource);
}
currentIETF = selectedLanguage.Ietf;
Thread.CurrentThread.CurrentUICulture = new CultureInfo(currentIETF);
return currentIETF;
}
private void AddResource(Resource resource) {
try {
if (resource.Text != null) {
strings.Add(resource.Name, resource.Text.Trim(TRIMCHARS));
} else {
LOG.Warn("Resource is null: " + resource.Name);
strings.Add(resource.Name, "");
}
} catch (ArgumentException ae) {
LOG.Error("Problem adding " + resource.Name, ae);
throw ae;
}
}
private List<LanguageConfiguration> LoadFiles(string languageFilePattern) {
List<LanguageConfiguration> loadedLanguages = new List<LanguageConfiguration>();
List<string> languageDirectories = new List<string>();
languageDirectories.Add(STARTUP_LANGUAGE_PATH);
languageDirectories.Add(APPLICATIONDATA_LANGUAGE_PATH);
foreach(string path in languageDirectories) {
// Search in executable directory
if (Directory.Exists(path)) {
foreach(string languageFile in Directory.GetFiles(path, languageFilePattern, SearchOption.AllDirectories)) {
LanguageConfiguration languageConfig = LanguageConfiguration.Load(languageFile);
if (languageConfig != null) {
LOG.Info("Loaded language: " + languageConfig.Description);
loadedLanguages.Add(languageConfig);
}
}
}
}
return loadedLanguages;
}
public void Validate(Enum languageKeys) {
Dictionary<string, List<string>> keysPerLanguage = new Dictionary<string, List<string>>();
foreach(LanguageConfiguration languageToValidate in languages) {
List<string> keys = new List<string>();
foreach(Resource resource in languageToValidate.Resources) {
keys.Add(resource.Name);
}
keys.Sort();
keysPerLanguage.Add(languageToValidate.Ietf, keys);
}
// Make list of values in the enum
List<string> fixedKeys = new List<string>();
foreach(Enum langKey in Enum.GetValues(languageKeys.GetType())) {
fixedKeys.Add(langKey.ToString());
}
foreach(string ietf in keysPerLanguage.Keys) {
List<string> keys = keysPerLanguage[ietf];
foreach(string key in fixedKeys) {
if (!keys.Contains(key)) {
LOG.Warn(ietf + " is missing resource with name [" + key + "]");
}
}
foreach(string key in keys) {
if (!fixedKeys.Contains(key)) {
LOG.Warn(ietf + " has additional resource with name [" + key + "]");
}
}
}
}
private void ToEnum() {
if (!LOG.IsDebugEnabled) {
return;
}
StringBuilder EnumClass = new StringBuilder();
EnumClass.AppendLine("/*");
EnumClass.AppendLine(" * Auto generated");
EnumClass.AppendLine(" */");
EnumClass.AppendLine("using System;");
EnumClass.AppendLine();
EnumClass.AppendLine("namespace Greenshot.Configuration {");
EnumClass.AppendLine(" public enum LangKey {");
List<string> keys = new List<string>();
foreach(LanguageConfiguration foundLanguage in languages) {
if (foundLanguage.Ietf.Equals(DEFAULT_LANGUAGE)) {
foreach(Resource resource in foundLanguage.Resources) {
keys.Add(resource.Name);
}
}
}
keys.Sort();
bool added = false;
foreach(string key in keys) {
if (added) {
EnumClass.AppendLine(",");
}
EnumClass.Append(" " + key);
added = true;
}
EnumClass.AppendLine();
EnumClass.AppendLine(" }");
EnumClass.AppendLine("}");
LOG.Debug("LangKeys should be: \r\n" + EnumClass.ToString());
}
public string GetString(Enum id) {
if(!strings.ContainsKey(id.ToString())) {
AdoptMissingResourcesFromDefaultLanguage();
}
try {
return strings[id.ToString()];
} catch (KeyNotFoundException) {
return "string ###"+id+"### not found";
}
}
public string GetFormattedString(Enum id, object param) {
if(!strings.ContainsKey(id.ToString())) {
AdoptMissingResourcesFromDefaultLanguage();
}
try {
return String.Format(strings[id.ToString()], param);
} catch (KeyNotFoundException) {
return "string ###"+id+"### not found";
}
}
private void AdoptMissingResourcesFromDefaultLanguage() {
LanguageConfiguration defaultLanguageConfiguration = GetDefaultLanguageConfiguration();
if (defaultLanguageConfiguration != null) {
foreach(Resource resource in defaultLanguageConfiguration.Resources) {
if(resource != null && !strings.ContainsKey(resource.Name)) {
AddResource(resource);
if(LOG.IsWarnEnabled) {
LOG.Warn("Adopted missing string resource from default language: "+resource.Name);
}
}
}
} else {
LOG.Warn("Default language file is missing! The default language file is: " + DEFAULT_LANGUAGE);
}
}
private LanguageConfiguration GetDefaultLanguageConfiguration() {
foreach(LanguageConfiguration language in languages) {
if(language.Ietf == DEFAULT_LANGUAGE) return language;
}
return null;
}
/// finds a returns the path of the best matching help file.
/// 1st tries to find file for currentLanguage, 2nd for defaultLanguage.
/// if neither is found, the first help file found is returned
public string GetHelpFilePath() {
List<string> helpFiles = new List<string>();
// Search in executable directory
if (Directory.Exists(STARTUP_LANGUAGE_PATH)) {
helpFiles.AddRange(Directory.GetFiles(STARTUP_LANGUAGE_PATH, HELP_FILENAME_PATTERN, SearchOption.AllDirectories));
}
// Search in ApplicationData directory
if (Directory.Exists(APPLICATIONDATA_LANGUAGE_PATH)) {
helpFiles.AddRange(Directory.GetFiles(APPLICATIONDATA_LANGUAGE_PATH, HELP_FILENAME_PATTERN, SearchOption.AllDirectories));
}
foreach(string helpFile in helpFiles) {
if(helpFile.EndsWith(currentIETF+".html")) {
return helpFile;
}
}
foreach(string helpFile in helpFiles) {
if(helpFile.EndsWith(DEFAULT_LANGUAGE+".html")) {
return helpFile;
}
}
LOG.Warn("Help file not found for default language, will load "+helpFiles[0]);
return helpFiles[0];
}
}
public class LanguageConfiguration {
private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(LanguageConfiguration));
public string description;
public string Description {
get {
return description;
}
set {
description = value;
}
}
public string ietf;
public string Ietf {
get {
return ietf;
}
set {
ietf = value;
}
}
public string version;
public string Version {
get {
return version;
}
set {
version = value;
}
}
public string file;
public string File {
get {
return file;
}
set {
file = value;
}
}
public List<Resource> Resources;
public LanguageConfiguration() {
Resources = new List<Resource>();
}
/// <summary>
/// loads a language configuration from a file path
/// </summary>
public static LanguageConfiguration Load(string path) {
LanguageConfiguration ret = null;
try {
XmlDocument doc = new XmlDocument();
doc.Load(path);
XmlNodeList nodes = doc.GetElementsByTagName("language");
if(nodes.Count > 0) {
ret = new LanguageConfiguration();
ret.File = path;
XmlNode node = nodes.Item(0);
ret.Description = node.Attributes["description"].Value;
ret.Ietf = node.Attributes["ietf"].Value;
ret.Version = node.Attributes["version"].Value;
XmlNodeList resourceNodes = doc.GetElementsByTagName("resource");
ret.Resources = new List<Resource>(resourceNodes.Count);
foreach(XmlNode resourceNode in resourceNodes) {
Resource res = new Resource();
res.Name = resourceNode.Attributes["name"].Value;
res.Text = resourceNode.InnerText;
ret.Resources.Add(res);
}
} else {
throw new XmlException("Root element <language> is missing");
}
} catch(Exception e) {
LOG.Error("Could not load language file "+path, e);
}
return ret;
}
}
public class Resource {
public string name;
public string Name {get; set;}
public string text;
public string Text {get; set;}
public override int GetHashCode() {
int hash = 7;
if (Text != null) {
hash = hash ^ Text.GetHashCode();
}
if (Name != null) {
hash = hash ^ Name.GetHashCode();
}
return hash;
}
public override bool Equals(object obj) {
if (obj == null) {
return false;
}
if (obj is Resource) {
Resource other = (Resource) obj;
if(Name == null) {
if (other.Name != null) {
return false;
}
return true;
}
if(Text == null) {
if (other.Text != null) {
return false;
}
return true;
}
return (Name.Equals(other.Name) && Text.Equals(other.Text));
}
return false;
}
}
}

View file

@ -0,0 +1,133 @@
/*
* 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.IO;
using System.Net;
using System.Text;
namespace GreenshotPlugin.Core {
/// <summary>
/// Description of NetworkHelper.
/// </summary>
public class NetworkHelper {
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(NetworkHelper));
private static CoreConfiguration config = IniConfig.GetIniSection<CoreConfiguration>();
/// <summary>
/// Download a file as string
/// </summary>
/// <param name=url">An Uri to specify the download location</param>
/// <param name=encoding">The encoding to use</param>
/// <returns>string with the file content</returns>
public static string DownloadFileAsString(Uri url, Encoding encoding) {
try {
HttpWebRequest request = (HttpWebRequest)CreatedWebRequest(url);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
if (request.HaveResponse) {
StreamReader reader = new StreamReader(response.GetResponseStream(), encoding);
return reader.ReadToEnd();
}
} catch (Exception e) {
LOG.Error("Problem downloading from: " + url.ToString(), e);
}
return null;
}
/// <summary>
/// Download the FavIcon as a Bitmap
/// </summary>
/// <param name="baseUri"></param>
/// <returns>Bitmap with the FavIcon</returns>
public static Bitmap DownloadFavIcon(Uri baseUri) {
Uri url = new Uri(baseUri, new Uri("favicon.ico"));
try {
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);
}
} catch (Exception e) {
LOG.Error("Problem downloading the FavIcon from: " + baseUri.ToString(), e);
}
return null;
}
/// <summary>
/// Helper method to create a web request, eventually with proxy
/// </summary>
/// <param name="uri">string with uri to connect to</param>
/// <returns>WebRequest</returns>
public static WebRequest CreatedWebRequest(string uri) {
return CreatedWebRequest(new Uri(uri));
}
/// <summary>
/// Helper method to create a web request, eventually with proxy
/// </summary>
/// <param name="uri">Uri with uri to connect to</param>
/// <returns>WebRequest</returns>
public static WebRequest CreatedWebRequest(Uri uri) {
WebRequest webRequest = WebRequest.Create(uri);
if (config.UseProxy) {
webRequest.Proxy = GreenshotPlugin.Core.NetworkHelper.CreateProxy(uri);
//webRequest.Proxy.Credentials = CredentialCache.DefaultCredentials;
}
return webRequest;
}
/// <summary>
/// Create a IWebProxy Object which can be used to access the Internet
/// This method will check the configuration if the proxy is allowed to be used.
/// Usages can be found in the DownloadFavIcon or Jira and Confluence plugins
/// </summary>
/// <param name="url"></param>
/// <returns>IWebProxy filled with all the proxy details or null if none is set/wanted</returns>
public static IWebProxy CreateProxy(Uri uri) {
IWebProxy proxyToUse = null;
if (config.UseProxy) {
proxyToUse = WebRequest.DefaultWebProxy;
if (proxyToUse != null) {
proxyToUse.Credentials = CredentialCache.DefaultCredentials;
if (LOG.IsDebugEnabled) {
// check the proxy for the Uri
if (!proxyToUse.IsBypassed(uri)) {
Uri proxyUri = proxyToUse.GetProxy(uri);
if (proxyUri != null) {
LOG.Debug("Using proxy: " + proxyUri.ToString() + " for " + uri.ToString());
} else {
LOG.Debug("No proxy found!");
}
} else {
LOG.Debug("Proxy bypass for: " + uri.ToString());
}
}
} else {
LOG.Debug("No proxy found!");
}
}
return proxyToUse;
}
}
}

View 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.Windows.Forms;
using Greenshot.Plugin;
namespace GreenshotPlugin.Core {
/// <summary>
/// Description of PluginUtils.
/// </summary>
public class PluginUtils {
private PluginUtils() {
}
/// <summary>
/// Helper method to add a MenuItem to the File MenuItem of an ImageEditor
/// </summary>
/// <param name="imageEditor"></param>
/// <param name="item"></param>
public static void AddToFileMenu(IImageEditor imageEditor, ToolStripMenuItem item) {
ToolStripMenuItem toolStripMenuItem = imageEditor.GetFileMenuItem();
bool added = false;
for(int i = 0; i< toolStripMenuItem.DropDownItems.Count; i++) {
if (toolStripMenuItem.DropDownItems[i].GetType() == typeof(ToolStripSeparator)) {
toolStripMenuItem.DropDownItems.Insert(i, item);
added = true;
break;
}
}
if (!added) {
toolStripMenuItem.DropDownItems.Add(item);
}
}
/// <summary>
/// Helper method to add a MenuItem to the Plugin MenuItem of an ImageEditor
/// </summary>
/// <param name="imageEditor"></param>
/// <param name="item"></param>
public static void AddToPluginMenu(IImageEditor imageEditor, ToolStripMenuItem item) {
ToolStripMenuItem toolStripMenuItem = imageEditor.GetPluginMenuItem();
bool added = false;
for(int i = 0; i< toolStripMenuItem.DropDownItems.Count; i++) {
if (toolStripMenuItem.DropDownItems[i].GetType() == typeof(ToolStripSeparator)) {
toolStripMenuItem.DropDownItems.Insert(i, item);
added = true;
break;
}
}
if (!added) {
toolStripMenuItem.DropDownItems.Add(item);
}
}
}
}

View file

@ -0,0 +1,124 @@
/*
* 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;
}
}
}