1 /*
2   KeePass Password Safe - The Open-Source Password Manager
3   Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
4 
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19 
20 using System;
21 using System.Collections.Generic;
22 using System.ComponentModel;
23 using System.Diagnostics;
24 using System.Drawing;
25 using System.Drawing.Drawing2D;
26 using System.Text;
27 using System.Windows.Forms;
28 using System.Windows.Forms.VisualStyles;
29 
30 using KeePass.App;
31 using KeePass.Util;
32 
33 using KeePassLib.Native;
34 
35 namespace KeePass.UI
36 {
37 	public sealed class QualityProgressBar : Control
38 	{
QualityProgressBar()39 		public QualityProgressBar() : base()
40 		{
41 			if(Program.DesignMode) return;
42 
43 			this.DoubleBuffered = true;
44 		}
45 
46 		private int m_nMinimum = 0;
47 		[DefaultValue(0)]
48 		public int Minimum
49 		{
50 			get { return m_nMinimum; }
51 			set { m_nMinimum = value; Invalidate(); }
52 		}
53 
54 		private int m_nMaximum = 100;
55 		[DefaultValue(100)]
56 		public int Maximum
57 		{
58 			get { return m_nMaximum; }
59 			set { m_nMaximum = value; Invalidate(); }
60 		}
61 
62 		private int m_nPosition = 0;
63 		[DefaultValue(0)]
64 		public int Value
65 		{
66 			get { return m_nPosition; }
67 			set { m_nPosition = value; Invalidate(); }
68 		}
69 
70 		private ProgressBarStyle m_pbsStyle = ProgressBarStyle.Continuous;
71 		[Browsable(false)]
72 		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
73 		public ProgressBarStyle Style
74 		{
75 			get { return m_pbsStyle; }
76 			set { m_pbsStyle = value; Invalidate(); }
77 		}
ShouldSerializeStyle()78 		public bool ShouldSerializeStyle() { return false; }
79 
80 		private string m_strText = string.Empty;
81 		[DefaultValue("")]
82 		public string ProgressText
83 		{
84 			get { return m_strText; }
85 			set
86 			{
87 				m_strText = value;
88 				UIUtil.AccSetName(this, value ?? string.Empty);
89 				Invalidate();
90 			}
91 		}
92 
OnPaint(PaintEventArgs e)93 		protected override void OnPaint(PaintEventArgs e)
94 		{
95 			try { PaintPriv(e); }
96 			catch(Exception) { Debug.Assert(false); }
97 		}
98 
PaintPriv(PaintEventArgs e)99 		private void PaintPriv(PaintEventArgs e)
100 		{
101 			Graphics g = e.Graphics;
102 			if(g == null) { base.OnPaint(e); return; }
103 
104 			int nNormPos = m_nPosition - m_nMinimum;
105 			int nNormMax = m_nMaximum - m_nMinimum;
106 			if(nNormMax <= 0) { Debug.Assert(false); nNormMax = 100; }
107 			if(nNormPos < 0) { Debug.Assert(false); nNormPos = 0; }
108 			if(nNormPos > nNormMax) { Debug.Assert(false); nNormPos = nNormMax; }
109 
110 			Rectangle rectClient = this.ClientRectangle;
111 			Rectangle rectDraw;
112 			VisualStyleElement vse = VisualStyleElement.ProgressBar.Bar.Normal;
113 			if(VisualStyleRenderer.IsSupported &&
114 				VisualStyleRenderer.IsElementDefined(vse))
115 			{
116 				VisualStyleRenderer vsr = new VisualStyleRenderer(vse);
117 
118 				if(vsr.IsBackgroundPartiallyTransparent())
119 					vsr.DrawParentBackground(g, rectClient, this);
120 
121 				vsr.DrawBackground(g, rectClient);
122 
123 				rectDraw = vsr.GetBackgroundContentRectangle(g, rectClient);
124 			}
125 			else
126 			{
127 				g.FillRectangle(SystemBrushes.Control, rectClient);
128 
129 				Pen penGray = SystemPens.ControlDark;
130 				Pen penWhite = SystemPens.ControlLight;
131 				g.DrawLine(penGray, 0, 0, rectClient.Width - 1, 0);
132 				g.DrawLine(penGray, 0, 0, 0, rectClient.Height - 1);
133 				g.DrawLine(penWhite, rectClient.Width - 1, 0,
134 					rectClient.Width - 1, rectClient.Height - 1);
135 				g.DrawLine(penWhite, 0, rectClient.Height - 1,
136 					rectClient.Width - 1, rectClient.Height - 1);
137 
138 				rectDraw = new Rectangle(rectClient.X + 1, rectClient.Y + 1,
139 					rectClient.Width - 2, rectClient.Height - 2);
140 			}
141 
142 			int nDrawWidth = (int)((float)rectDraw.Width * (float)nNormPos /
143 				(float)nNormMax);
144 
145 			Color clrStart = AppDefs.ColorQualityLow;
146 			Color clrEnd = AppDefs.ColorQualityHigh;
147 			Color clrMid = AppDefs.ColorQualityMid;
148 			if(!this.Enabled)
149 			{
150 				clrStart = UIUtil.ColorToGrayscale(SystemColors.ControlDark);
151 				clrEnd = UIUtil.ColorToGrayscale(SystemColors.ControlLight);
152 				clrMid = UIUtil.ColorMiddle(clrStart, clrEnd);
153 			}
154 
155 			bool bRtl = (this.RightToLeft == RightToLeft.Yes);
156 			if(bRtl)
157 			{
158 				Color clrTemp = clrStart;
159 				clrStart = clrEnd;
160 				clrEnd = clrTemp;
161 			}
162 
163 			// Workaround for Windows <= XP
164 			Rectangle rectGrad = new Rectangle(rectDraw.X, rectDraw.Y,
165 				rectDraw.Width, rectDraw.Height);
166 			if(!WinUtil.IsAtLeastWindowsVista && !NativeLib.IsUnix())
167 				rectGrad.Inflate(1, 0);
168 
169 			using(LinearGradientBrush brush = new LinearGradientBrush(rectGrad,
170 				clrStart, clrEnd, LinearGradientMode.Horizontal))
171 			{
172 				ColorBlend cb = new ColorBlend();
173 				cb.Colors = new Color[3] { clrStart, clrMid, clrEnd };
174 				cb.Positions = new float[3] { 0.0f, 0.5f, 1.0f };
175 				brush.InterpolationColors = cb;
176 
177 				g.FillRectangle(brush, (bRtl ? (rectDraw.Width - nDrawWidth + 1) :
178 					rectDraw.Left), rectDraw.Top, nDrawWidth, rectDraw.Height);
179 			}
180 
181 			PaintText(g, rectDraw, bRtl);
182 		}
183 
PaintText(Graphics g, Rectangle rectDraw, bool bRtl)184 		private void PaintText(Graphics g, Rectangle rectDraw, bool bRtl)
185 		{
186 			if(string.IsNullOrEmpty(m_strText)) return;
187 
188 			Font f = (FontUtil.DefaultFont ?? this.Font);
189 			Color clrFG = UIUtil.ColorToGrayscale(this.ForeColor);
190 			Color clrBG = Color.FromArgb(clrFG.ToArgb() ^ 0x20FFFFFF);
191 
192 			int dx = rectDraw.X;
193 			int dy = rectDraw.Y;
194 			int dw = rectDraw.Width;
195 			int dh = rectDraw.Height;
196 
197 			if(!NativeLib.IsUnix() || !UIUtil.IsDarkColor(clrFG))
198 			{
199 				Rectangle rectGlow = rectDraw;
200 				rectGlow.Width = TextRenderer.MeasureText(g, m_strText, f).Width;
201 				rectGlow.X = ((dw - rectGlow.Width) / 2) + dx;
202 
203 				// Instead of an ellipse, Mono draws a circle
204 				if(NativeLib.IsUnix())
205 					rectGlow.Inflate(rectGlow.Width * 2, rectGlow.Height * 2);
206 				else
207 					rectGlow.Inflate(rectGlow.Width / 2, rectGlow.Height / 2);
208 
209 				using(GraphicsPath gpGlow = new GraphicsPath())
210 				{
211 					gpGlow.AddEllipse(rectGlow);
212 
213 					using(PathGradientBrush pgbGlow = new PathGradientBrush(gpGlow))
214 					{
215 						pgbGlow.CenterPoint = new PointF((dw / 2.0f) + dx,
216 							(dh / 2.0f) + dy);
217 						pgbGlow.CenterColor = clrBG;
218 						pgbGlow.SurroundColors = new Color[] { Color.Transparent };
219 
220 						Region rgOrgClip = g.Clip;
221 						g.SetClip(rectDraw);
222 						g.FillPath(pgbGlow, gpGlow);
223 						g.Clip = rgOrgClip;
224 					}
225 				}
226 			}
227 
228 			// With ClearType on, text drawn using Graphics.DrawString
229 			// looks better than TextRenderer.DrawText;
230 			// https://sourceforge.net/p/keepass/discussion/329220/thread/06ef4466/
231 			// TextFormatFlags tff = (TextFormatFlags.HorizontalCenter | TextFormatFlags.SingleLine |
232 			//	TextFormatFlags.VerticalCenter);
233 			// TextRenderer.DrawText(g, m_strText, f, rectDraw, clrFG, tff);
234 
235 			using(SolidBrush br = new SolidBrush(clrFG))
236 			{
237 				StringFormatFlags sff = (StringFormatFlags.FitBlackBox |
238 					StringFormatFlags.NoClip);
239 				if(bRtl) sff |= StringFormatFlags.DirectionRightToLeft;
240 
241 				using(StringFormat sf = new StringFormat(sff))
242 				{
243 					sf.Alignment = StringAlignment.Center;
244 					sf.LineAlignment = StringAlignment.Center;
245 
246 					RectangleF rf = new RectangleF(dx, dy, dw, dh);
247 					g.DrawString(m_strText, f, br, rf, sf);
248 				}
249 			}
250 		}
251 
OnPaintBackground(PaintEventArgs pevent)252 		protected override void OnPaintBackground(PaintEventArgs pevent)
253 		{
254 			// base.OnPaintBackground(pevent);
255 		}
256 	}
257 }
258