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