mirror of
https://github.com/greenshot/greenshot
synced 2025-07-16 10:03:44 -07:00
* Improving the DPI handling for most forms, there are still issues with: * the AboutForm.Designer.cs where the title with the version scales differently. * the destination picker doesn't seem to scale the font correctly. Some parts are not tested yet... * Solved the issue with the destination picker font, and some other small issues. There still is an issue when using Powertoys (the feature which is experimental), that the capture is somehow skipping.
526 lines
18 KiB
C#
526 lines
18 KiB
C#
/*
|
|
* Greenshot - a free and open source screenshot tool
|
|
* Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
|
|
*
|
|
* For more information see: http://getgreenshot.org/
|
|
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 1 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Windows.Forms;
|
|
using System.Reflection;
|
|
using GreenshotPlugin.Core;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel.Design;
|
|
using System.IO;
|
|
using GreenshotPlugin.IniFile;
|
|
using log4net;
|
|
|
|
namespace GreenshotPlugin.Controls {
|
|
/// <summary>
|
|
/// This form is used for automatically binding the elements of the form to the language
|
|
/// </summary>
|
|
public class GreenshotForm : Form, IGreenshotLanguageBindable {
|
|
private static readonly ILog LOG = LogManager.GetLogger(typeof(GreenshotForm));
|
|
protected static CoreConfiguration coreConfiguration;
|
|
private static readonly IDictionary<Type, FieldInfo[]> reflectionCache = new Dictionary<Type, FieldInfo[]>();
|
|
private IComponentChangeService m_changeService;
|
|
private bool _isDesignModeLanguageSet;
|
|
private bool _applyLanguageManually;
|
|
private bool _storeFieldsManually;
|
|
private IDictionary<string, Control> _designTimeControls;
|
|
private IDictionary<string, ToolStripItem> _designTimeToolStripItems;
|
|
|
|
static GreenshotForm() {
|
|
if (!IsInDesignMode) {
|
|
coreConfiguration = IniConfig.GetIniSection<CoreConfiguration>();
|
|
}
|
|
}
|
|
|
|
[Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
|
|
public string LanguageKey {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to check the designmode during a constructor
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
protected static bool IsInDesignMode {
|
|
get {
|
|
return (Application.ExecutablePath.IndexOf("devenv.exe", StringComparison.OrdinalIgnoreCase) > -1) || (Application.ExecutablePath.IndexOf("sharpdevelop.exe", StringComparison.OrdinalIgnoreCase) > -1 || (Application.ExecutablePath.IndexOf("wdexpress.exe", StringComparison.OrdinalIgnoreCase) > -1));
|
|
}
|
|
}
|
|
|
|
protected bool ManualLanguageApply {
|
|
get {
|
|
return _applyLanguageManually;
|
|
}
|
|
set {
|
|
_applyLanguageManually = value;
|
|
}
|
|
}
|
|
|
|
protected bool ManualStoreFields {
|
|
get {
|
|
return _storeFieldsManually;
|
|
}
|
|
set {
|
|
_storeFieldsManually = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// When this is set, the form will be brought to the foreground as soon as it is shown.
|
|
/// </summary>
|
|
protected bool ToFront {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Code to initialize the language etc during design time
|
|
/// </summary>
|
|
protected void InitializeForDesigner() {
|
|
if (!DesignMode) return;
|
|
_designTimeControls = new Dictionary<string, Control>();
|
|
_designTimeToolStripItems = new Dictionary<string, ToolStripItem>();
|
|
try {
|
|
ITypeResolutionService typeResService = GetService(typeof(ITypeResolutionService)) as ITypeResolutionService;
|
|
|
|
// Add a hard-path if you are using SharpDevelop
|
|
// Language.AddLanguageFilePath(@"C:\Greenshot\Greenshot\Languages");
|
|
|
|
// this "type"
|
|
Assembly currentAssembly = GetType().Assembly;
|
|
if (typeResService != null)
|
|
{
|
|
string assemblyPath = typeResService.GetPathOfAssembly(currentAssembly.GetName());
|
|
string assemblyDirectory = Path.GetDirectoryName(assemblyPath);
|
|
if (assemblyDirectory != null && !Language.AddLanguageFilePath(Path.Combine(assemblyDirectory, @"..\..\Greenshot\Languages\"))) {
|
|
Language.AddLanguageFilePath(Path.Combine(assemblyDirectory, @"..\..\..\Greenshot\Languages\"));
|
|
}
|
|
if (assemblyDirectory != null && !Language.AddLanguageFilePath(Path.Combine(assemblyDirectory, @"..\..\Languages\"))) {
|
|
Language.AddLanguageFilePath(Path.Combine(assemblyDirectory, @"..\..\..\Languages\"));
|
|
}
|
|
}
|
|
} catch (Exception ex) {
|
|
MessageBox.Show(ex.Message);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This override is only for the design-time of the form
|
|
/// </summary>
|
|
/// <param name="e"></param>
|
|
protected override void OnPaint(PaintEventArgs e) {
|
|
if (DesignMode) {
|
|
if (!_isDesignModeLanguageSet) {
|
|
_isDesignModeLanguageSet = true;
|
|
try {
|
|
ApplyLanguage();
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// ignored
|
|
}
|
|
}
|
|
}
|
|
base.OnPaint(e);
|
|
}
|
|
|
|
protected override void OnLoad(EventArgs e) {
|
|
// Every GreenshotForm should have it's default icon
|
|
// And it might not ne needed for a Tool Window, but still for the task manager / switcher it's important
|
|
Icon = GreenshotResources.GetGreenshotIcon();
|
|
if (!DesignMode) {
|
|
if (!_applyLanguageManually) {
|
|
ApplyLanguage();
|
|
}
|
|
FillFields();
|
|
base.OnLoad(e);
|
|
} else {
|
|
LOG.Info("OnLoad called from designer.");
|
|
InitializeForDesigner();
|
|
base.OnLoad(e);
|
|
ApplyLanguage();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Make sure the form is visible, if this is wanted
|
|
/// </summary>
|
|
/// <param name="e">EventArgs</param>
|
|
protected override void OnShown(EventArgs e) {
|
|
base.OnShown(e);
|
|
if (ToFront) {
|
|
WindowDetails.ToForeground(Handle);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// check if the form was closed with an OK, if so store the values in the GreenshotControls
|
|
/// </summary>
|
|
/// <param name="e"></param>
|
|
protected override void OnClosed(EventArgs e) {
|
|
if (!DesignMode && !_storeFieldsManually) {
|
|
if (DialogResult == DialogResult.OK) {
|
|
LOG.Info("Form was closed with OK: storing field values.");
|
|
StoreFields();
|
|
}
|
|
}
|
|
base.OnClosed(e);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This override allows the control to register event handlers for IComponentChangeService events
|
|
/// at the time the control is sited, which happens only in design mode.
|
|
/// </summary>
|
|
public override ISite Site {
|
|
get {
|
|
return base.Site;
|
|
}
|
|
set {
|
|
// Clear any component change event handlers.
|
|
ClearChangeNotifications();
|
|
|
|
// Set the new Site value.
|
|
base.Site = value;
|
|
|
|
m_changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService));
|
|
|
|
// Register event handlers for component change events.
|
|
RegisterChangeNotifications();
|
|
}
|
|
}
|
|
|
|
private void ClearChangeNotifications() {
|
|
// The m_changeService value is null when not in design mode,
|
|
// as the IComponentChangeService is only available at design time.
|
|
m_changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService));
|
|
|
|
// Clear our the component change events to prepare for re-siting.
|
|
if (m_changeService != null) {
|
|
m_changeService.ComponentChanged -= OnComponentChanged;
|
|
m_changeService.ComponentAdded -= OnComponentAdded;
|
|
}
|
|
}
|
|
|
|
private void RegisterChangeNotifications() {
|
|
// Register the event handlers for the IComponentChangeService events
|
|
if (m_changeService != null) {
|
|
m_changeService.ComponentChanged += OnComponentChanged;
|
|
m_changeService.ComponentAdded += OnComponentAdded;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method handles the OnComponentChanged event to display a notification.
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="ce"></param>
|
|
private void OnComponentChanged(object sender, ComponentChangedEventArgs ce)
|
|
{
|
|
if (((IComponent) ce.Component)?.Site == null || ce.Member == null) return;
|
|
if (!"LanguageKey".Equals(ce.Member.Name)) return;
|
|
if (ce.Component is Control control) {
|
|
LOG.InfoFormat("Changing LanguageKey for {0} to {1}", control.Name, ce.NewValue);
|
|
ApplyLanguage(control, (string)ce.NewValue);
|
|
} else {
|
|
if (ce.Component is ToolStripItem item) {
|
|
LOG.InfoFormat("Changing LanguageKey for {0} to {1}", item.Name, ce.NewValue);
|
|
ApplyLanguage(item, (string)ce.NewValue);
|
|
} else {
|
|
LOG.InfoFormat("Not possible to changing LanguageKey for {0} to {1}", ce.Component.GetType(), ce.NewValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnComponentAdded(object sender, ComponentEventArgs ce) {
|
|
if (ce.Component?.Site == null) return;
|
|
if (ce.Component is Control control) {
|
|
if (!_designTimeControls.ContainsKey(control.Name)) {
|
|
_designTimeControls.Add(control.Name, control);
|
|
} else {
|
|
_designTimeControls[control.Name] = control;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ce.Component is ToolStripItem stripItem) {
|
|
ToolStripItem item = stripItem;
|
|
if (!_designTimeControls.ContainsKey(item.Name)) {
|
|
_designTimeToolStripItems.Add(item.Name, item);
|
|
} else {
|
|
_designTimeToolStripItems[item.Name] = item;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clean up any resources being used.
|
|
protected override void Dispose(bool disposing) {
|
|
if (disposing) {
|
|
ClearChangeNotifications();
|
|
}
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
protected void ApplyLanguage(ToolStripItem applyTo, string languageKey) {
|
|
string langString;
|
|
if (!string.IsNullOrEmpty(languageKey)) {
|
|
if (!Language.TryGetString(languageKey, out langString)) {
|
|
LOG.WarnFormat("Unknown language key '{0}' configured for control '{1}', this might be okay.", languageKey, applyTo.Name);
|
|
return;
|
|
}
|
|
applyTo.Text = langString;
|
|
} else {
|
|
// Fallback to control name!
|
|
if (Language.TryGetString(applyTo.Name, out langString)) {
|
|
applyTo.Text = langString;
|
|
return;
|
|
}
|
|
if (!DesignMode) {
|
|
LOG.DebugFormat("Greenshot control without language key: {0}", applyTo.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void ApplyLanguage(ToolStripItem applyTo) {
|
|
if (applyTo is IGreenshotLanguageBindable languageBindable) {
|
|
ApplyLanguage(applyTo, languageBindable.LanguageKey);
|
|
}
|
|
}
|
|
|
|
protected void ApplyLanguage(Control applyTo) {
|
|
if (!(applyTo is IGreenshotLanguageBindable languageBindable)) {
|
|
// check if it's a menu!
|
|
if (!(applyTo is ToolStrip toolStrip))
|
|
{
|
|
return;
|
|
}
|
|
foreach (ToolStripItem item in toolStrip.Items) {
|
|
ApplyLanguage(item);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Apply language text to the control
|
|
ApplyLanguage(applyTo, languageBindable.LanguageKey);
|
|
|
|
// Repopulate the combox boxes
|
|
if (applyTo is IGreenshotConfigBindable configBindable && applyTo is GreenshotComboBox comboxBox) {
|
|
if (!string.IsNullOrEmpty(configBindable.SectionName) && !string.IsNullOrEmpty(configBindable.PropertyName)) {
|
|
IniSection section = IniConfig.GetIniSection(configBindable.SectionName);
|
|
if (section != null) {
|
|
// Only update the language, so get the actual value and than repopulate
|
|
Enum currentValue = comboxBox.GetSelectedEnum();
|
|
comboxBox.Populate(section.Values[configBindable.PropertyName].ValueType);
|
|
comboxBox.SetValue(currentValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method to cache the fieldinfo values, so we don't need to reflect all the time!
|
|
/// </summary>
|
|
/// <param name="typeToGetFieldsFor"></param>
|
|
/// <returns></returns>
|
|
private static FieldInfo[] GetCachedFields(Type typeToGetFieldsFor) {
|
|
if (!reflectionCache.TryGetValue(typeToGetFieldsFor, out var fields)) {
|
|
fields = typeToGetFieldsFor.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
|
reflectionCache.Add(typeToGetFieldsFor, fields);
|
|
}
|
|
return fields;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Apply all the language settings to the "Greenshot" Controls on this form
|
|
/// </summary>
|
|
protected void ApplyLanguage() {
|
|
SuspendLayout();
|
|
try {
|
|
// Set title of the form
|
|
if (!string.IsNullOrEmpty(LanguageKey) && Language.TryGetString(LanguageKey, out var langString)) {
|
|
Text = langString;
|
|
}
|
|
|
|
// Reset the text values for all GreenshotControls
|
|
foreach (FieldInfo field in GetCachedFields(GetType())) {
|
|
object controlObject = field.GetValue(this);
|
|
if (controlObject == null) {
|
|
LOG.DebugFormat("No value: {0}", field.Name);
|
|
continue;
|
|
}
|
|
|
|
if (!(controlObject is Control applyToControl)) {
|
|
ToolStripItem applyToItem = controlObject as ToolStripItem;
|
|
if (applyToItem == null) {
|
|
LOG.DebugFormat("No Control or ToolStripItem: {0}", field.Name);
|
|
continue;
|
|
}
|
|
ApplyLanguage(applyToItem);
|
|
} else {
|
|
ApplyLanguage(applyToControl);
|
|
}
|
|
}
|
|
|
|
if (DesignMode) {
|
|
foreach (Control designControl in _designTimeControls.Values) {
|
|
ApplyLanguage(designControl);
|
|
}
|
|
foreach (ToolStripItem designToolStripItem in _designTimeToolStripItems.Values) {
|
|
ApplyLanguage(designToolStripItem);
|
|
}
|
|
}
|
|
} finally {
|
|
ResumeLayout();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Apply the language text to supplied control
|
|
/// </summary>
|
|
protected void ApplyLanguage(Control applyTo, string languageKey) {
|
|
string langString;
|
|
if (!string.IsNullOrEmpty(languageKey)) {
|
|
if (!Language.TryGetString(languageKey, out langString)) {
|
|
LOG.WarnFormat("Wrong language key '{0}' configured for control '{1}'", languageKey, applyTo.Name);
|
|
return;
|
|
}
|
|
applyTo.Text = langString;
|
|
} else {
|
|
// Fallback to control name!
|
|
if (Language.TryGetString(applyTo.Name, out langString)) {
|
|
applyTo.Text = langString;
|
|
return;
|
|
}
|
|
if (!DesignMode) {
|
|
LOG.DebugFormat("Greenshot control without language key: {0}", applyTo.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fill all GreenshotControls with the values from the configuration
|
|
/// </summary>
|
|
protected void FillFields() {
|
|
foreach (FieldInfo field in GetCachedFields(GetType())) {
|
|
var controlObject = field.GetValue(this);
|
|
IGreenshotConfigBindable configBindable = controlObject as IGreenshotConfigBindable;
|
|
if (!string.IsNullOrEmpty(configBindable?.SectionName) && !string.IsNullOrEmpty(configBindable.PropertyName)) {
|
|
IniSection section = IniConfig.GetIniSection(configBindable.SectionName);
|
|
if (section != null) {
|
|
if (!section.Values.TryGetValue(configBindable.PropertyName, out var iniValue)) {
|
|
LOG.DebugFormat("Wrong property '{0}' configured for field '{1}'",configBindable.PropertyName,field.Name);
|
|
continue;
|
|
}
|
|
|
|
if (controlObject is CheckBox checkBox) {
|
|
checkBox.Checked = (bool)iniValue.Value;
|
|
checkBox.Enabled = !iniValue.IsFixed;
|
|
continue;
|
|
}
|
|
|
|
if (controlObject is RadioButton radíoButton) {
|
|
radíoButton.Checked = (bool)iniValue.Value;
|
|
radíoButton.Enabled = !iniValue.IsFixed;
|
|
continue;
|
|
}
|
|
|
|
if (controlObject is TextBox textBox) {
|
|
if (controlObject is HotkeyControl hotkeyControl) {
|
|
string hotkeyValue = (string)iniValue.Value;
|
|
if (!string.IsNullOrEmpty(hotkeyValue)) {
|
|
hotkeyControl.SetHotkey(hotkeyValue);
|
|
hotkeyControl.Enabled = !iniValue.IsFixed;
|
|
}
|
|
continue;
|
|
}
|
|
textBox.Text = iniValue.ToString();
|
|
textBox.Enabled = !iniValue.IsFixed;
|
|
continue;
|
|
}
|
|
|
|
if (controlObject is GreenshotComboBox comboxBox) {
|
|
comboxBox.Populate(iniValue.ValueType);
|
|
comboxBox.SetValue((Enum)iniValue.Value);
|
|
comboxBox.Enabled = !iniValue.IsFixed;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
OnFieldsFilled();
|
|
}
|
|
|
|
protected virtual void OnFieldsFilled() {
|
|
}
|
|
|
|
/// <summary>
|
|
/// Store all GreenshotControl values to the configuration
|
|
/// </summary>
|
|
protected void StoreFields() {
|
|
bool iniDirty = false;
|
|
foreach (FieldInfo field in GetCachedFields(GetType())) {
|
|
var controlObject = field.GetValue(this);
|
|
IGreenshotConfigBindable configBindable = controlObject as IGreenshotConfigBindable;
|
|
|
|
if (!string.IsNullOrEmpty(configBindable?.SectionName) && !string.IsNullOrEmpty(configBindable.PropertyName)) {
|
|
IniSection section = IniConfig.GetIniSection(configBindable.SectionName);
|
|
if (section != null) {
|
|
if (!section.Values.TryGetValue(configBindable.PropertyName, out var iniValue)) {
|
|
continue;
|
|
}
|
|
|
|
if (controlObject is CheckBox checkBox) {
|
|
iniValue.Value = checkBox.Checked;
|
|
iniDirty = true;
|
|
continue;
|
|
}
|
|
|
|
if (controlObject is RadioButton radioButton) {
|
|
iniValue.Value = radioButton.Checked;
|
|
iniDirty = true;
|
|
continue;
|
|
}
|
|
|
|
if (controlObject is TextBox textBox) {
|
|
if (controlObject is HotkeyControl hotkeyControl) {
|
|
iniValue.Value = hotkeyControl.ToString();
|
|
iniDirty = true;
|
|
continue;
|
|
}
|
|
iniValue.UseValueOrDefault(textBox.Text);
|
|
iniDirty = true;
|
|
continue;
|
|
}
|
|
|
|
if (controlObject is GreenshotComboBox comboxBox) {
|
|
iniValue.Value = comboxBox.GetSelectedEnum();
|
|
iniDirty = true;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
if (iniDirty) {
|
|
IniConfig.Save();
|
|
}
|
|
}
|
|
}
|
|
}
|