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.Diagnostics;
23 using System.Drawing;
24 using System.Text;
25 using System.Windows.Forms;
26 
27 using KeePass.Resources;
28 
29 using KeePassLib;
30 using KeePassLib.Native;
31 using KeePassLib.Utility;
32 
33 namespace KeePass.UI.ToolStripRendering
34 {
35 	internal sealed class ProExtTsrFactory : TsrFactory
36 	{
37 		private PwUuid m_uuid = new PwUuid(new byte[] {
38 			0x21, 0xED, 0x54, 0x1A, 0xE2, 0xEB, 0xCB, 0x0C,
39 			0x57, 0x18, 0x41, 0x32, 0x70, 0xD8, 0xE0, 0xE9
40 		});
41 
42 		public override PwUuid Uuid
43 		{
44 			get { return m_uuid; }
45 		}
46 
47 		public override string Name
48 		{
49 			get { return (".NET/Office - " + KPRes.Professional); }
50 		}
51 
CreateInstance()52 		public override ToolStripRenderer CreateInstance()
53 		{
54 			return new ProExtTsr();
55 		}
56 	}
57 
58 	public class ProExtTsr : ToolStripProfessionalRenderer
59 	{
60 		private bool m_bCustomColorTable = false;
61 
62 		protected bool IsDarkStyle
63 		{
64 			get
65 			{
66 				ProfessionalColorTable ct = this.ColorTable;
67 				if(ct == null) { Debug.Assert(false); return false; }
68 
69 				return UIUtil.IsDarkColor(ct.ToolStripDropDownBackground);
70 			}
71 		}
72 
73 		protected virtual bool EnsureTextContrast
74 		{
75 			get { return true; }
76 		}
77 
ProExtTsr()78 		public ProExtTsr() : base()
79 		{
80 		}
81 
ProExtTsr(ProfessionalColorTable ct)82 		public ProExtTsr(ProfessionalColorTable ct) : base(ct)
83 		{
84 			m_bCustomColorTable = true;
85 		}
86 
OnRenderButtonBackground(ToolStripItemRenderEventArgs e)87 		protected override void OnRenderButtonBackground(ToolStripItemRenderEventArgs e)
88 		{
89 			base.OnRenderButtonBackground(e);
90 
91 			// .NET incorrectly draws the border using
92 			// this.ColorTable.ButtonSelectedBorder even when the button
93 			// is pressed; thus in this case we draw it again using the
94 			// correct color
95 			ToolStripItem tsi = ((e != null) ? e.Item : null);
96 			if((tsi != null) && tsi.Pressed && !NativeLib.IsUnix())
97 			{
98 				using(Pen p = new Pen(this.ColorTable.ButtonPressedBorder))
99 				{
100 					e.Graphics.DrawRectangle(p, 0, 0, tsi.Width - 1, tsi.Height - 1);
101 				}
102 			}
103 		}
104 
OnRenderItemCheck(ToolStripItemImageRenderEventArgs e)105 		protected override void OnRenderItemCheck(ToolStripItemImageRenderEventArgs e)
106 		{
107 			if(MonoWorkarounds.IsRequired())
108 			{
109 				base.OnRenderItemCheck(e);
110 				return;
111 			}
112 
113 			Image imgToDispose = null;
114 			try
115 			{
116 				Graphics g = e.Graphics;
117 				Image imgOrg = e.Image;
118 				Rectangle rOrg = e.ImageRectangle;
119 				ToolStripItem tsi = e.Item;
120 
121 				Image img = imgOrg;
122 				Rectangle r = rOrg;
123 				Debug.Assert(r.Width == r.Height);
124 				Debug.Assert(DpiUtil.ScalingRequired || (r.Size ==
125 					((img != null) ? img.Size : new Size(3, 5))));
126 
127 				// Override the .NET checkmark bitmap
128 				ToolStripMenuItem tsmi = (tsi as ToolStripMenuItem);
129 				if((tsmi != null) && tsmi.Checked && (tsmi.Image == null))
130 					img = Properties.Resources.B16x16_MenuCheck;
131 
132 				if(tsi != null)
133 				{
134 					Rectangle rContent = tsi.ContentRectangle;
135 					Debug.Assert(rContent.Contains(r) || DpiUtil.ScalingRequired);
136 					r.Intersect(rContent);
137 					if(r.Height < r.Width) r.Width = r.Height;
138 				}
139 				else { Debug.Assert(false); }
140 
141 				if((img != null) && (r.Size != img.Size))
142 				{
143 					img = GfxUtil.ScaleImage(img, r.Width, r.Height,
144 						ScaleTransformFlags.UIIcon);
145 					imgToDispose = img;
146 				}
147 
148 				if((img != imgOrg) || (r != rOrg))
149 				{
150 					ToolStripItemImageRenderEventArgs eNew =
151 						new ToolStripItemImageRenderEventArgs(g, tsi, img, r);
152 					base.OnRenderItemCheck(eNew);
153 					return;
154 				}
155 
156 				/* ToolStripMenuItem tsmi = (tsi as ToolStripMenuItem);
157 				if((tsmi != null) && tsmi.Checked && (r.Width > 0) &&
158 					(r.Height > 0) && (img != null) &&
159 					((img.Width != r.Width) || (img.Height != r.Height)))
160 				{
161 					Rectangle rContent = tsmi.ContentRectangle;
162 					r.Intersect(rContent);
163 					if(r.Height < r.Width)
164 						r.Width = r.Height;
165 
166 					ProfessionalColorTable ct = this.ColorTable;
167 
168 					Color clrBorder = ct.ButtonSelectedBorder;
169 
170 					Color clrBG = ct.CheckBackground;
171 					if(tsmi.Selected) clrBG = ct.CheckSelectedBackground;
172 					if(tsmi.Pressed) clrBG = ct.CheckPressedBackground;
173 
174 					Color clrFG = ((UIUtil.ColorToGrayscale(clrBG).R >= 128) ?
175 						Color.Black : Color.White);
176 
177 					using(SolidBrush sb = new SolidBrush(clrBG))
178 					{
179 						g.FillRectangle(sb, r);
180 					}
181 					using(Pen p = new Pen(clrBorder))
182 					{
183 						g.DrawRectangle(p, r.X, r.Y, r.Width - 1, r.Height - 1);
184 					}
185 
186 					ControlPaint.DrawMenuGlyph(g, r, MenuGlyph.Checkmark,
187 						clrFG, Color.Transparent);
188 
189 					// if((img.Width == r.Width) && (img.Height == r.Height))
190 					//	g.DrawImage(img, r);
191 					// else
192 					// {
193 					//	Image imgScaled = GfxUtil.ScaleImage(img,
194 					//		r.Width, r.Height, ScaleTransformFlags.UIIcon);
195 					//	g.DrawImage(imgScaled, r);
196 					//	imgScaled.Dispose();
197 					// }
198 
199 					return;
200 				} */
201 
202 				/* if((img != null) && (r.Width > 0) && (r.Height > 0) &&
203 					((img.Width != r.Width) || (img.Height != r.Height)) &&
204 					(tsi != null))
205 				{
206 					// This should only happen on high DPI
207 					Debug.Assert(DpiUtil.ScalingRequired);
208 
209 					Image imgScaled = GfxUtil.ScaleImage(img,
210 						r.Width, r.Height, ScaleTransformFlags.UIIcon);
211 					// Image imgScaled = new Bitmap(r.Width, r.Height,
212 					//	PixelFormat.Format32bppArgb);
213 					// using(Graphics gScaled = Graphics.FromImage(imgScaled))
214 					// {
215 					//	gScaled.Clear(Color.Transparent);
216 
217 					//	Color clrFG = ((UIUtil.ColorToGrayscale(
218 					//		this.ColorTable.CheckBackground).R >= 128) ?
219 					//		Color.FromArgb(18, 24, 163) : Color.White);
220 
221 					//	Rectangle rGlyph = new Rectangle(0, 0, r.Width, r.Height);
222 					//	// rGlyph.Inflate(-r.Width / 12, -r.Height / 12);
223 
224 					//	ControlPaint.DrawMenuGlyph(gScaled, rGlyph,
225 					//		MenuGlyph.Bullet, clrFG, Color.Transparent);
226 					// }
227 
228 					ToolStripItemImageRenderEventArgs eMod =
229 						new ToolStripItemImageRenderEventArgs(g, e.Item,
230 							imgScaled, r);
231 					base.OnRenderItemCheck(eMod);
232 
233 					imgScaled.Dispose();
234 					return;
235 				} */
236 			}
237 			catch(Exception) { Debug.Assert(false); }
238 			finally
239 			{
240 				if(imgToDispose != null) imgToDispose.Dispose();
241 			}
242 
243 			base.OnRenderItemCheck(e); // Not in 'finally', see 'eNew'
244 		}
245 
OnRenderItemText(ToolStripItemTextRenderEventArgs e)246 		protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e)
247 		{
248 			if(e != null)
249 			{
250 				ToolStripItem tsi = e.Item;
251 
252 				// In high contrast mode, various colors of the default
253 				// color table are incorrect, thus check m_bCustomColorTable
254 				if((tsi != null) && this.EnsureTextContrast && m_bCustomColorTable)
255 				{
256 					bool bDarkBack = this.IsDarkStyle;
257 					if(tsi.Selected || tsi.Pressed)
258 					{
259 						if((tsi.Owner is ContextMenuStrip) || (tsi.OwnerItem != null))
260 							bDarkBack = UIUtil.IsDarkColor(this.ColorTable.MenuItemSelected);
261 						else // Top menu item
262 						{
263 							if(tsi.Pressed)
264 								bDarkBack = UIUtil.IsDarkColor(
265 									this.ColorTable.MenuItemPressedGradientMiddle);
266 							else
267 								bDarkBack = UIUtil.IsDarkColor(UIUtil.ColorMiddle(
268 									this.ColorTable.MenuItemSelectedGradientBegin,
269 									this.ColorTable.MenuItemSelectedGradientEnd));
270 						}
271 					}
272 
273 					// e.TextColor might be incorrect, thus use tsi.ForeColor
274 					bool bDarkText = UIUtil.IsDarkColor(tsi.ForeColor);
275 
276 					if(bDarkBack && bDarkText)
277 					{
278 						Debug.Assert(false);
279 						e.TextColor = Color.White;
280 					}
281 					else if(!bDarkBack && !bDarkText)
282 					{
283 						Debug.Assert(false);
284 						e.TextColor = Color.Black;
285 					}
286 				}
287 			}
288 			else { Debug.Assert(false); }
289 
290 			base.OnRenderItemText(e);
291 		}
292 	}
293 }
294