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.Drawing.Drawing2D;
25 using System.Drawing.Imaging;
26 using System.IO;
27 using System.Media;
28 using System.Runtime.InteropServices;
29 using System.Text;
30 using System.Text.RegularExpressions;
31 using System.Threading;
32 using System.Windows.Forms;
33 using System.Windows.Forms.VisualStyles;
34 
35 using Microsoft.Win32;
36 
37 using KeePass.App;
38 using KeePass.App.Configuration;
39 using KeePass.Native;
40 using KeePass.Resources;
41 using KeePass.UI.ToolStripRendering;
42 using KeePass.Util;
43 using KeePass.Util.MultipleValues;
44 using KeePass.Util.Spr;
45 
46 using KeePassLib;
47 using KeePassLib.Collections;
48 using KeePassLib.Delegates;
49 using KeePassLib.Interfaces;
50 using KeePassLib.Utility;
51 
52 using NativeLib = KeePassLib.Native.NativeLib;
53 
54 namespace KeePass.UI
55 {
56 	public sealed class UIScrollInfo
57 	{
58 		private readonly int m_dx;
59 		public int ScrollX { get { return m_dx; } }
60 
61 		private readonly int m_dy;
62 		public int ScrollY { get { return m_dy; } }
63 
64 		private readonly int m_idxTop;
65 		public int TopIndex { get { return m_idxTop; } }
66 
UIScrollInfo(int iScrollX, int iScrollY, int iTopIndex)67 		public UIScrollInfo(int iScrollX, int iScrollY, int iTopIndex)
68 		{
69 			m_dx = iScrollX;
70 			m_dy = iScrollY;
71 			m_idxTop = iTopIndex;
72 		}
73 	}
74 
75 	public static class UIUtil
76 	{
77 		private const int FwsNormal = 0;
78 		private const int FwsMaximized = 2; // Compatible with FormWindowState
79 
80 		private static bool g_bScreenReaderActive = false;
81 
82 		private static bool g_bVistaStyleLists = false;
83 		public static bool VistaStyleListsSupported
84 		{
85 			get { return g_bVistaStyleLists; }
86 		}
87 
88 		public static bool IsDarkTheme
89 		{
90 			get
91 			{
92 				return !IsDarkColor(SystemColors.ControlText);
93 			}
94 		}
95 
96 		public static bool IsHighContrast
97 		{
98 			get
99 			{
100 				try { return SystemInformation.HighContrast; }
101 				catch(Exception) { Debug.Assert(false); }
102 				return false;
103 			}
104 		}
105 
Initialize(bool bReinitialize)106 		public static void Initialize(bool bReinitialize)
107 		{
108 			// bReinitialize is currently not used, but not removed
109 			// for plugin backward compatibility
110 
111 			if(Program.DesignMode) return;
112 
113 			string strUuid = Program.Config.UI.ToolStripRenderer;
114 			ToolStripRenderer tsr = TsrPool.GetBestRenderer(strUuid);
115 			if(tsr == null) { Debug.Assert(false); tsr = new ToolStripProfessionalRenderer(); }
116 			ToolStripManager.Renderer = tsr;
117 
118 			g_bVistaStyleLists = (WinUtil.IsAtLeastWindowsVista &&
119 				(Environment.Version.Major >= 2));
120 
121 			OnSystemSettingChange();
122 		}
123 
OnSystemSettingChange()124 		internal static void OnSystemSettingChange()
125 		{
126 			try
127 			{
128 				if(NativeLib.IsUnix()) return;
129 
130 				Debug.Assert(Marshal.SizeOf(typeof(bool)) == 4);
131 				Debug.Assert(Marshal.SizeOf(typeof(int)) == 4);
132 				int i = 0;
133 				if(NativeMethods.SystemParametersInfoI32(
134 					NativeMethods.SPI_GETSCREENREADER, 0, ref i, 0))
135 				{
136 					bool b = (i != 0);
137 #if DEBUG
138 					if(b != g_bScreenReaderActive)
139 						Trace.WriteLine("Screen reader is " + (b ?
140 							string.Empty : "in") + "active.");
141 #endif
142 					g_bScreenReaderActive = b;
143 				}
144 				else { Debug.Assert(false); }
145 			}
146 			catch(Exception) { Debug.Assert(false); }
147 		}
148 
RtfGetCharFormat(RichTextBox rtb)149 		internal static NativeMethods.CHARFORMAT2 RtfGetCharFormat(RichTextBox rtb)
150 		{
151 			NativeMethods.CHARFORMAT2 cf = new NativeMethods.CHARFORMAT2();
152 			cf.cbSize = (uint)Marshal.SizeOf(typeof(NativeMethods.CHARFORMAT2));
153 
154 			if(NativeLib.IsUnix()) return cf;
155 
156 			IntPtr pCF = IntPtr.Zero;
157 			try
158 			{
159 				pCF = Marshal.AllocCoTaskMem((int)cf.cbSize);
160 				Marshal.StructureToPtr(cf, pCF, false);
161 
162 				IntPtr wParam = (IntPtr)NativeMethods.SCF_SELECTION;
163 				NativeMethods.SendMessage(rtb.Handle,
164 					NativeMethods.EM_GETCHARFORMAT, wParam, pCF);
165 
166 				cf = (NativeMethods.CHARFORMAT2)Marshal.PtrToStructure(pCF,
167 					typeof(NativeMethods.CHARFORMAT2));
168 			}
169 			catch(Exception) { Debug.Assert(false); }
170 			finally { if(pCF != IntPtr.Zero) Marshal.FreeCoTaskMem(pCF); }
171 
172 			return cf;
173 		}
174 
RtfSetCharFormat(RichTextBox rtb, ref NativeMethods.CHARFORMAT2 cf)175 		internal static void RtfSetCharFormat(RichTextBox rtb, ref NativeMethods.CHARFORMAT2 cf)
176 		{
177 			if(NativeLib.IsUnix()) return;
178 
179 			uint cb = (uint)Marshal.SizeOf(typeof(NativeMethods.CHARFORMAT2));
180 			if(cf.cbSize != cb) { Debug.Assert(false); cf.cbSize = cb; }
181 
182 			IntPtr pCF = IntPtr.Zero;
183 			try
184 			{
185 				pCF = Marshal.AllocCoTaskMem((int)cb);
186 				Marshal.StructureToPtr(cf, pCF, false);
187 
188 				IntPtr wParam = (IntPtr)NativeMethods.SCF_SELECTION;
189 				NativeMethods.SendMessage(rtb.Handle,
190 					NativeMethods.EM_SETCHARFORMAT, wParam, pCF);
191 			}
192 			catch(Exception) { Debug.Assert(false); }
193 			finally { if(pCF != IntPtr.Zero) Marshal.FreeCoTaskMem(pCF); }
194 		}
195 
RtfSetSelectionLink(RichTextBox rtb)196 		public static void RtfSetSelectionLink(RichTextBox rtb)
197 		{
198 			if(rtb == null) { Debug.Assert(false); return; }
199 
200 			// Rich Edit 4.1 and higher do not support automatic URL
201 			// detection and manual CFE_LINK assignments at once
202 			// (Rich Edit 3.0 supported this); see EM_AUTOURLDETECT
203 			Debug.Assert(!rtb.DetectUrls);
204 
205 			Debug.Assert(rtb.HideSelection); // Flicker opt.
206 
207 			NativeMethods.CHARFORMAT2 cf = new NativeMethods.CHARFORMAT2();
208 			cf.cbSize = (uint)Marshal.SizeOf(typeof(NativeMethods.CHARFORMAT2));
209 
210 			cf.dwMask = NativeMethods.CFM_LINK;
211 			cf.dwEffects = NativeMethods.CFE_LINK;
212 
213 			RtfSetCharFormat(rtb, ref cf);
214 		}
215 
RtfIsFirstCharLink(RichTextBox rtb)216 		public static bool RtfIsFirstCharLink(RichTextBox rtb)
217 		{
218 			NativeMethods.CHARFORMAT2 cf = RtfGetCharFormat(rtb);
219 			return ((cf.dwEffects & NativeMethods.CFE_LINK) != 0);
220 		}
221 
RtfLinkifyText(RichTextBox rtb, string strLinkText, bool bResetSelection)222 		public static void RtfLinkifyText(RichTextBox rtb, string strLinkText,
223 			bool bResetSelection)
224 		{
225 			RtfLinkifyText(rtb, strLinkText, bResetSelection, false);
226 		}
227 
228 		// Cf. RtfLinkifyTexts
RtfLinkifyText(RichTextBox rtb, string strLinkText, bool bResetSelection, bool bAll)229 		public static void RtfLinkifyText(RichTextBox rtb, string strLinkText,
230 			bool bResetSelection, bool bAll)
231 		{
232 			if(rtb == null) { Debug.Assert(false); return; }
233 
234 			try
235 			{
236 				string strFind = StrUtil.RtfFilterText(strLinkText);
237 				if(string.IsNullOrEmpty(strFind)) return;
238 				if(strFind.Trim().Length == 0) return;
239 
240 				string strText = (rtb.Text ?? string.Empty);
241 				int iOffset = 0;
242 
243 				while(iOffset < strText.Length)
244 				{
245 					int i = strText.IndexOf(strFind, iOffset);
246 					if(i < iOffset) break;
247 
248 					rtb.Select(i, strFind.Length);
249 					RtfSetSelectionLink(rtb);
250 
251 					if(!bAll) break;
252 					iOffset = i + strFind.Length;
253 				}
254 			}
255 			catch(Exception) { Debug.Assert(false); }
256 			finally
257 			{
258 				try { if(bResetSelection) rtb.Select(0, 0); }
259 				catch(Exception) { Debug.Assert(false); }
260 			}
261 		}
262 
263 		// Cf. RtfLinkifyText
RtfLinkifyTexts(RichTextBox rtb, List<KeyValuePair<string, bool>> lTexts, bool bSequentially)264 		internal static void RtfLinkifyTexts(RichTextBox rtb,
265 			List<KeyValuePair<string, bool>> lTexts, bool bSequentially)
266 		{
267 			if(rtb == null) { Debug.Assert(false); return; }
268 			if(lTexts == null) { Debug.Assert(false); return; }
269 
270 			try
271 			{
272 				string strText = (rtb.Text ?? string.Empty);
273 				int iOffset = 0;
274 
275 				foreach(KeyValuePair<string, bool> kvp in lTexts)
276 				{
277 					if(iOffset >= strText.Length) break;
278 
279 					string strFind = StrUtil.RtfFilterText(kvp.Key);
280 					if(string.IsNullOrEmpty(strFind)) continue;
281 					if(strFind.Trim().Length == 0) continue;
282 
283 					int i = strText.IndexOf(strFind, iOffset);
284 					if(i < iOffset) { Debug.Assert(!bSequentially); continue; }
285 
286 					if(kvp.Value)
287 					{
288 						rtb.Select(i, strFind.Length);
289 						RtfSetSelectionLink(rtb);
290 					}
291 
292 					if(bSequentially) iOffset = i + strFind.Length;
293 				}
294 			}
295 			catch(Exception) { Debug.Assert(false); }
296 		}
297 
RtfLinkifyReferences(RichTextBox rtb, bool bResetSelection)298 		public static void RtfLinkifyReferences(RichTextBox rtb, bool bResetSelection)
299 		{
300 			if(rtb == null) { Debug.Assert(false); return; }
301 
302 			try
303 			{
304 				string str = (rtb.Text ?? string.Empty);
305 				int iOffset = 0;
306 
307 				while(iOffset < str.Length)
308 				{
309 					int iStart = str.IndexOf(SprEngine.StrRefStart, iOffset,
310 						StrUtil.CaseIgnoreCmp);
311 					if(iStart < iOffset) break;
312 
313 					int iEnd = str.IndexOf(SprEngine.StrRefEnd, iStart + 1,
314 						StrUtil.CaseIgnoreCmp);
315 					if(iEnd <= iStart) break;
316 
317 					int cc = iEnd - iStart + SprEngine.StrRefEnd.Length;
318 
319 					rtb.Select(iStart, cc);
320 					RtfSetSelectionLink(rtb);
321 
322 					iOffset = iStart + cc;
323 				}
324 
325 				if(bResetSelection) rtb.Select(0, 0);
326 			}
327 			catch(Exception) { Debug.Assert(false); }
328 		}
329 
RtfLinkifyUrls(RichTextBox rtb)330 		internal static void RtfLinkifyUrls(RichTextBox rtb)
331 		{
332 			if(rtb == null) { Debug.Assert(false); return; }
333 
334 			try
335 			{
336 				string str = (rtb.Text ?? string.Empty);
337 				int iOffset = 0;
338 
339 				while(iOffset < str.Length)
340 				{
341 					int iSep = str.IndexOf(':', iOffset);
342 					if(iSep < iOffset) break;
343 
344 					int iStart = iSep;
345 					while(iStart >= 1)
346 					{
347 						if(!UrlUtil.IsUriSchemeChar(str[iStart - 1])) break;
348 						--iStart;
349 					}
350 
351 					string strScheme = str.Substring(iStart, iSep - iStart);
352 					if(!UrlUtil.IsKnownScheme(strScheme))
353 					{
354 						iOffset = iSep + 1;
355 						continue;
356 					}
357 
358 					int ccUrl = UrlUtil.GetUrlLength(str, iStart, true);
359 
360 					rtb.Select(iStart, ccUrl);
361 					RtfSetSelectionLink(rtb);
362 
363 					int iOffsetNew = iStart + ccUrl;
364 					if(iOffsetNew > iOffset) iOffset = iOffsetNew;
365 					else { Debug.Assert(false); iOffset = iSep + 1; }
366 				}
367 			}
368 			catch(Exception) { Debug.Assert(false); }
369 		}
370 
RtfSetFontSize(RichTextBox rtb, float fSizeInPt)371 		internal static void RtfSetFontSize(RichTextBox rtb, float fSizeInPt)
372 		{
373 			NativeMethods.CHARFORMAT2 cf = new NativeMethods.CHARFORMAT2();
374 			cf.cbSize = (uint)Marshal.SizeOf(typeof(NativeMethods.CHARFORMAT2));
375 
376 			cf.dwMask = NativeMethods.CFM_SIZE;
377 			cf.yHeight = (int)(fSizeInPt * 20.0f);
378 
379 			RtfSetCharFormat(rtb, ref cf);
380 		}
381 
RtfToggleSelectionFormat(RichTextBox rtb, FontStyle fs)382 		internal static void RtfToggleSelectionFormat(RichTextBox rtb, FontStyle fs)
383 		{
384 			if(rtb == null) { Debug.Assert(false); return; }
385 
386 			try
387 			{
388 				Font f = rtb.SelectionFont;
389 				if(f != null)
390 					rtb.SelectionFont = new Font(f, f.Style ^ fs);
391 				else
392 				{
393 					NativeMethods.CHARFORMAT2 cf = RtfGetCharFormat(rtb);
394 					cf.dwMask = 0;
395 
396 					if((fs & FontStyle.Bold) == FontStyle.Bold)
397 					{
398 						cf.dwMask |= NativeMethods.CFM_BOLD;
399 						cf.dwEffects ^= NativeMethods.CFE_BOLD;
400 					}
401 					if((fs & FontStyle.Italic) == FontStyle.Italic)
402 					{
403 						cf.dwMask |= NativeMethods.CFM_ITALIC;
404 						cf.dwEffects ^= NativeMethods.CFE_ITALIC;
405 					}
406 					if((fs & FontStyle.Underline) == FontStyle.Underline)
407 					{
408 						cf.dwMask |= NativeMethods.CFM_UNDERLINE;
409 						cf.dwEffects ^= NativeMethods.CFE_UNDERLINE;
410 					}
411 					if((fs & FontStyle.Strikeout) == FontStyle.Strikeout)
412 					{
413 						cf.dwMask |= NativeMethods.CFM_STRIKEOUT;
414 						cf.dwEffects ^= NativeMethods.CFE_STRIKEOUT;
415 					}
416 
417 					RtfSetCharFormat(rtb, ref cf);
418 				}
419 			}
420 			catch(Exception) { Debug.Assert(false); }
421 		}
422 
423 		[Obsolete("Use GfxUtil.LoadImage instead.")]
LoadImage(byte[] pb)424 		public static Image LoadImage(byte[] pb)
425 		{
426 			return GfxUtil.LoadImage(pb);
427 		}
428 
CreateColorBitmap24(int nWidth, int nHeight, Color color)429 		public static Image CreateColorBitmap24(int nWidth, int nHeight, Color color)
430 		{
431 			Bitmap bmp = new Bitmap(nWidth, nHeight, PixelFormat.Format24bppRgb);
432 
433 			using(Graphics g = Graphics.FromImage(bmp))
434 			{
435 				g.Clear(color);
436 				// g.DrawRectangle(Pens.Black, 0, 0, nWidth - 1, nHeight - 1);
437 			}
438 
439 			return bmp;
440 		}
441 
CreateColorBitmap24(Button btnTarget, Color clr)442 		public static Image CreateColorBitmap24(Button btnTarget, Color clr)
443 		{
444 			if(btnTarget == null) { Debug.Assert(false); return null; }
445 
446 			Rectangle rect = btnTarget.ClientRectangle;
447 			return CreateColorBitmap24(rect.Width - 8, rect.Height - 8, clr);
448 		}
449 
BuildImageListUnscaled(List<Image> lImages, int nWidth, int nHeight)450 		public static ImageList BuildImageListUnscaled(List<Image> lImages,
451 			int nWidth, int nHeight)
452 		{
453 			ImageList il = new ImageList();
454 			il.ImageSize = new Size(nWidth, nHeight);
455 			il.ColorDepth = ColorDepth.Depth32Bit;
456 
457 			if((lImages != null) && (lImages.Count > 0))
458 				il.Images.AddRange(lImages.ToArray());
459 
460 			return il;
461 		}
462 
BuildImageList(List<PwCustomIcon> lIcons, int nWidth, int nHeight)463 		public static ImageList BuildImageList(List<PwCustomIcon> lIcons,
464 			int nWidth, int nHeight)
465 		{
466 			ImageList il = new ImageList();
467 			il.ImageSize = new Size(nWidth, nHeight);
468 			il.ColorDepth = ColorDepth.Depth32Bit;
469 
470 			List<Image> lImages = BuildImageListEx(lIcons, nWidth, nHeight);
471 			if((lImages != null) && (lImages.Count > 0))
472 				il.Images.AddRange(lImages.ToArray());
473 
474 			return il;
475 		}
476 
BuildImageListEx(List<PwCustomIcon> lIcons, int nWidth, int nHeight)477 		public static List<Image> BuildImageListEx(List<PwCustomIcon> lIcons,
478 			int nWidth, int nHeight)
479 		{
480 			List<Image> lImages = new List<Image>();
481 
482 			foreach(PwCustomIcon pwci in lIcons)
483 			{
484 				Image img = pwci.GetImage(nWidth, nHeight);
485 				if(img == null)
486 				{
487 					Debug.Assert(false);
488 					img = UIUtil.CreateColorBitmap24(nWidth, nHeight, Color.White);
489 				}
490 
491 				if((img.Width != nWidth) || (img.Height != nHeight))
492 				{
493 					Debug.Assert(false);
494 					img = new Bitmap(img, new Size(nWidth, nHeight));
495 				}
496 
497 				lImages.Add(img);
498 			}
499 
500 			return lImages;
501 		}
502 
ConvertImageList24(List<Image> lImages, int nWidth, int nHeight, Color clrBack)503 		public static ImageList ConvertImageList24(List<Image> lImages,
504 			int nWidth, int nHeight, Color clrBack)
505 		{
506 			ImageList il = new ImageList();
507 			il.ImageSize = new Size(nWidth, nHeight);
508 			il.ColorDepth = ColorDepth.Depth24Bit;
509 
510 			List<Image> lNew = new List<Image>();
511 			foreach(Image img in lImages)
512 			{
513 				Bitmap bmpNew = new Bitmap(nWidth, nHeight, PixelFormat.Format24bppRgb);
514 
515 				using(Graphics g = Graphics.FromImage(bmpNew))
516 				{
517 					g.Clear(clrBack);
518 
519 					if((img.Width == nWidth) && (img.Height == nHeight))
520 						g.DrawImageUnscaled(img, 0, 0);
521 					else
522 					{
523 						GfxUtil.SetHighQuality(g);
524 						g.DrawImage(img, 0, 0, nWidth, nHeight);
525 					}
526 				}
527 
528 				lNew.Add(bmpNew);
529 			}
530 			il.Images.AddRange(lNew.ToArray());
531 
532 			return il;
533 		}
534 
CloneImageList(ImageList ilSource, bool bCloneImages)535 		public static ImageList CloneImageList(ImageList ilSource, bool bCloneImages)
536 		{
537 			Debug.Assert(ilSource != null); if(ilSource == null) throw new ArgumentNullException("ilSource");
538 
539 			ImageList ilNew = new ImageList();
540 			ilNew.ColorDepth = ilSource.ColorDepth;
541 			ilNew.ImageSize = ilSource.ImageSize;
542 
543 			foreach(Image img in ilSource.Images)
544 			{
545 				if(bCloneImages) ilNew.Images.Add(new Bitmap(img));
546 				else ilNew.Images.Add(img);
547 			}
548 
549 			return ilNew;
550 		}
551 
DrawAnimatedRects(Rectangle rectFrom, Rectangle rectTo)552 		public static bool DrawAnimatedRects(Rectangle rectFrom, Rectangle rectTo)
553 		{
554 			bool bResult;
555 
556 			try
557 			{
558 				NativeMethods.RECT rnFrom = new NativeMethods.RECT(rectFrom);
559 				NativeMethods.RECT rnTo = new NativeMethods.RECT(rectTo);
560 
561 				bResult = NativeMethods.DrawAnimatedRects(IntPtr.Zero,
562 					NativeMethods.IDANI_CAPTION, ref rnFrom, ref rnTo);
563 			}
564 			catch(Exception) { Debug.Assert(false); bResult = false; }
565 
566 			return bResult;
567 		}
568 
SetCueBanner(IntPtr hWnd, string strText)569 		private static void SetCueBanner(IntPtr hWnd, string strText)
570 		{
571 			if(hWnd == IntPtr.Zero) { Debug.Assert(false); return; }
572 			if(strText == null) { Debug.Assert(false); strText = string.Empty; }
573 
574 			IntPtr p = IntPtr.Zero;
575 			try
576 			{
577 				p = Marshal.StringToCoTaskMemUni(strText);
578 				NativeMethods.SendMessage(hWnd, NativeMethods.EM_SETCUEBANNER,
579 					IntPtr.Zero, p);
580 			}
581 			catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
582 			finally { if(p != IntPtr.Zero) Marshal.FreeCoTaskMem(p); }
583 		}
584 
SetCueBanner(TextBox tb, string strText)585 		public static void SetCueBanner(TextBox tb, string strText)
586 		{
587 			if(tb == null) { Debug.Assert(false); return; }
588 
589 			SetCueBanner(tb.Handle, strText);
590 		}
591 
SetCueBanner(ToolStripTextBox tb, string strText)592 		public static void SetCueBanner(ToolStripTextBox tb, string strText)
593 		{
594 			if(tb == null) { Debug.Assert(false); return; }
595 
596 			SetCueBanner(tb.TextBox, strText);
597 		}
598 
SetCueBanner(ToolStripComboBox tb, string strText)599 		public static void SetCueBanner(ToolStripComboBox tb, string strText)
600 		{
601 			try
602 			{
603 				NativeMethods.COMBOBOXINFO cbi = new NativeMethods.COMBOBOXINFO();
604 				cbi.cbSize = Marshal.SizeOf(typeof(NativeMethods.COMBOBOXINFO));
605 
606 				NativeMethods.GetComboBoxInfo(tb.ComboBox.Handle, ref cbi);
607 
608 				SetCueBanner(cbi.hwndEdit, strText);
609 			}
610 			catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
611 		}
612 
CreateScreenshot()613 		public static Bitmap CreateScreenshot()
614 		{
615 			return CreateScreenshot(null);
616 		}
617 
CreateScreenshot(Screen sc)618 		public static Bitmap CreateScreenshot(Screen sc)
619 		{
620 			try
621 			{
622 				Screen s = (sc ?? Screen.PrimaryScreen);
623 				Bitmap bmp = new Bitmap(s.Bounds.Width, s.Bounds.Height);
624 
625 				using(Graphics g = Graphics.FromImage(bmp))
626 				{
627 					g.CopyFromScreen(s.Bounds.Location, new Point(0, 0),
628 						s.Bounds.Size);
629 				}
630 
631 				return bmp;
632 			}
633 			catch(Exception) { } // Throws on Cocoa and Quartz
634 
635 			return null;
636 		}
637 
DimImage(Image bmp)638 		public static void DimImage(Image bmp)
639 		{
640 			if(bmp == null) { Debug.Assert(false); return; }
641 
642 			using(Brush b = new SolidBrush(Color.FromArgb(192, Color.Black)))
643 			{
644 				using(Graphics g = Graphics.FromImage(bmp))
645 				{
646 					g.FillRectangle(b, 0, 0, bmp.Width, bmp.Height);
647 				}
648 			}
649 		}
650 
PrepareStandardMultilineControl(RichTextBox rtb, bool bSimpleTextOnly, bool bCtrlEnterAccepts)651 		public static void PrepareStandardMultilineControl(RichTextBox rtb,
652 			bool bSimpleTextOnly, bool bCtrlEnterAccepts)
653 		{
654 			if(rtb == null) { Debug.Assert(false); return; }
655 
656 			// See CustomRichTextBoxEx.CreateParams
657 			// try
658 			// {
659 			//	int nStyle = NativeMethods.GetWindowStyle(rtb.Handle);
660 			//	if((nStyle & NativeMethods.ES_WANTRETURN) == 0)
661 			//	{
662 			//		NativeMethods.SetWindowLong(rtb.Handle, NativeMethods.GWL_STYLE,
663 			//			nStyle | NativeMethods.ES_WANTRETURN);
664 			//		Debug.Assert((NativeMethods.GetWindowStyle(rtb.Handle) &
665 			//			NativeMethods.ES_WANTRETURN) != 0);
666 			//	}
667 			// }
668 			// catch(Exception) { }
669 
670 			CustomRichTextBoxEx crtb = (rtb as CustomRichTextBoxEx);
671 			if(crtb != null)
672 			{
673 				crtb.SimpleTextOnly = bSimpleTextOnly;
674 				crtb.CtrlEnterAccepts = bCtrlEnterAccepts;
675 			}
676 			else { Debug.Assert(!bSimpleTextOnly && !bCtrlEnterAccepts); }
677 		}
678 
SetMultilineText(TextBox tb, string str)679 		public static void SetMultilineText(TextBox tb, string str)
680 		{
681 			if(tb == null) { Debug.Assert(false); return; }
682 			if(str == null) str = string.Empty;
683 
684 			if(!NativeLib.IsUnix())
685 				str = StrUtil.NormalizeNewLines(str, true);
686 
687 			tb.Text = str;
688 		}
689 
690 		/// <summary>
691 		/// Fill a <c>ListView</c> with password entries.
692 		/// </summary>
693 		/// <param name="lv"><c>ListView</c> to fill.</param>
694 		/// <param name="vEntries">Entries.</param>
695 		/// <param name="vColumns">Columns of the <c>ListView</c>. The first
696 		/// parameter of the key-value pair is the internal string field name,
697 		/// and the second one the text displayed in the column header.</param>
CreateEntryList(ListView lv, IEnumerable<PwEntry> vEntries, List<KeyValuePair<string, string>> vColumns, ImageList ilIcons)698 		public static void CreateEntryList(ListView lv, IEnumerable<PwEntry> vEntries,
699 			List<KeyValuePair<string, string>> vColumns, ImageList ilIcons)
700 		{
701 			if(lv == null) throw new ArgumentNullException("lv");
702 			if(vEntries == null) throw new ArgumentNullException("vEntries");
703 			if(vColumns == null) throw new ArgumentNullException("vColumns");
704 			if(vColumns.Count == 0) throw new ArgumentException();
705 			// ilIcons may be null
706 
707 			lv.BeginUpdate();
708 
709 			lv.Items.Clear();
710 			lv.Columns.Clear();
711 			lv.ShowGroups = true;
712 			lv.SmallImageList = ilIcons;
713 
714 			foreach(KeyValuePair<string, string> kvp in vColumns)
715 			{
716 				lv.Columns.Add(kvp.Value);
717 			}
718 
719 			DocumentManagerEx dm = Program.MainForm.DocumentManager;
720 			ListViewGroup lvg = new ListViewGroup(Guid.NewGuid().ToString());
721 			DateTime dtNow = DateTime.UtcNow;
722 			bool bFirstEntry = true;
723 
724 			foreach(PwEntry pe in vEntries)
725 			{
726 				if(pe == null) { Debug.Assert(false); continue; }
727 
728 				if(pe.ParentGroup != null)
729 				{
730 					string strGroup = pe.ParentGroup.GetFullPath(" - ", false);
731 					if(strGroup != lvg.Header)
732 					{
733 						lvg = new ListViewGroup(strGroup, HorizontalAlignment.Left);
734 						lv.Groups.Add(lvg);
735 					}
736 				}
737 
738 				ListViewItem lvi = new ListViewItem(AppDefs.GetEntryField(pe, vColumns[0].Key));
739 
740 				if(ilIcons != null)
741 				{
742 					if(pe.Expires && (pe.ExpiryTime <= dtNow))
743 						lvi.ImageIndex = (int)PwIcon.Expired;
744 					else if(pe.CustomIconUuid == PwUuid.Zero)
745 						lvi.ImageIndex = (int)pe.IconId;
746 					else
747 					{
748 						lvi.ImageIndex = (int)pe.IconId;
749 
750 						foreach(PwDocument ds in dm.Documents)
751 						{
752 							int nInx = ds.Database.GetCustomIconIndex(pe.CustomIconUuid);
753 							if(nInx >= 0)
754 							{
755 								ilIcons.Images.Add(new Bitmap(DpiUtil.GetIcon(
756 									ds.Database, pe.CustomIconUuid)));
757 								lvi.ImageIndex = ilIcons.Images.Count - 1;
758 								break;
759 							}
760 						}
761 					}
762 				}
763 
764 				for(int iCol = 1; iCol < vColumns.Count; ++iCol)
765 					lvi.SubItems.Add(AppDefs.GetEntryField(pe, vColumns[iCol].Key));
766 
767 				if(!UIUtil.ColorsEqual(pe.ForegroundColor, Color.Empty))
768 					lvi.ForeColor = pe.ForegroundColor;
769 				if(!UIUtil.ColorsEqual(pe.BackgroundColor, Color.Empty))
770 					lvi.BackColor = pe.BackgroundColor;
771 
772 				lvi.Tag = pe;
773 
774 				lv.Items.Add(lvi);
775 				lvg.Items.Add(lvi);
776 
777 				if(bFirstEntry)
778 				{
779 					UIUtil.SetFocusedItem(lv, lvi, true);
780 					bFirstEntry = false;
781 				}
782 			}
783 
784 			int nColWidth = (lv.ClientRectangle.Width - GetVScrollBarWidth()) /
785 				vColumns.Count;
786 			foreach(ColumnHeader ch in lv.Columns)
787 			{
788 				ch.Width = nColWidth;
789 			}
790 
791 			lv.EndUpdate();
792 		}
793 
794 		/// <summary>
795 		/// Fill a <c>ListView</c> with password entries.
796 		/// </summary>
CreateEntryList(ListView lv, List<AutoTypeCtx> lCtxs, AceAutoTypeCtxFlags f, ImageList ilIcons)797 		public static void CreateEntryList(ListView lv, List<AutoTypeCtx> lCtxs,
798 			AceAutoTypeCtxFlags f, ImageList ilIcons)
799 		{
800 			if(lv == null) throw new ArgumentNullException("lv");
801 			if(lCtxs == null) throw new ArgumentNullException("lCtxs");
802 
803 			lv.BeginUpdate();
804 
805 			lv.Items.Clear();
806 			lv.Columns.Clear();
807 			lv.ShowGroups = true;
808 			lv.SmallImageList = ilIcons;
809 
810 			Debug.Assert((f & AceAutoTypeCtxFlags.ColTitle) != AceAutoTypeCtxFlags.None);
811 			f |= AceAutoTypeCtxFlags.ColTitle; // Enforce title
812 
813 			lv.Columns.Add(KPRes.Title);
814 			if((f & AceAutoTypeCtxFlags.ColUserName) != AceAutoTypeCtxFlags.None)
815 				lv.Columns.Add(KPRes.UserName);
816 			if((f & AceAutoTypeCtxFlags.ColPassword) != AceAutoTypeCtxFlags.None)
817 				lv.Columns.Add(KPRes.Password);
818 			if((f & AceAutoTypeCtxFlags.ColUrl) != AceAutoTypeCtxFlags.None)
819 				lv.Columns.Add(KPRes.Url);
820 			if((f & AceAutoTypeCtxFlags.ColNotes) != AceAutoTypeCtxFlags.None)
821 				lv.Columns.Add(KPRes.Notes);
822 			if((f & AceAutoTypeCtxFlags.ColSequenceComments) != AceAutoTypeCtxFlags.None)
823 				lv.Columns.Add(KPRes.Sequence + " - " + KPRes.Comments);
824 			if((f & AceAutoTypeCtxFlags.ColSequence) != AceAutoTypeCtxFlags.None)
825 				lv.Columns.Add(KPRes.Sequence);
826 
827 			ListViewGroup lvg = new ListViewGroup(Guid.NewGuid().ToString());
828 			DateTime dtNow = DateTime.UtcNow;
829 			Regex rxSeqCmt = null;
830 			bool bFirstEntry = true;
831 
832 			foreach(AutoTypeCtx ctx in lCtxs)
833 			{
834 				if(ctx == null) { Debug.Assert(false); continue; }
835 				PwEntry pe = ctx.Entry;
836 				if(pe == null) { Debug.Assert(false); continue; }
837 				PwDatabase pd = ctx.Database;
838 				if(pd == null) { Debug.Assert(false); continue; }
839 
840 				if(pe.ParentGroup != null)
841 				{
842 					string strGroup = pe.ParentGroup.GetFullPath(" - ", true);
843 					if(strGroup != lvg.Header)
844 					{
845 						lvg = new ListViewGroup(strGroup, HorizontalAlignment.Left);
846 						lv.Groups.Add(lvg);
847 					}
848 				}
849 
850 				SprContext sprCtx = new SprContext(pe, pd, SprCompileFlags.Deref);
851 				sprCtx.ForcePlainTextPasswords = false;
852 
853 				ListViewItem lvi = new ListViewItem(SprEngine.Compile(
854 					pe.Strings.ReadSafe(PwDefs.TitleField), sprCtx));
855 
856 				if(pe.Expires && (pe.ExpiryTime <= dtNow))
857 					lvi.ImageIndex = (int)PwIcon.Expired;
858 				else if(pe.CustomIconUuid == PwUuid.Zero)
859 					lvi.ImageIndex = (int)pe.IconId;
860 				else
861 				{
862 					int nInx = pd.GetCustomIconIndex(pe.CustomIconUuid);
863 					if(nInx > -1)
864 					{
865 						ilIcons.Images.Add(new Bitmap(DpiUtil.GetIcon(
866 							pd, pe.CustomIconUuid)));
867 						lvi.ImageIndex = ilIcons.Images.Count - 1;
868 					}
869 					else { Debug.Assert(false); lvi.ImageIndex = (int)pe.IconId; }
870 				}
871 
872 				if((f & AceAutoTypeCtxFlags.ColUserName) != AceAutoTypeCtxFlags.None)
873 					lvi.SubItems.Add(SprEngine.Compile(pe.Strings.ReadSafe(
874 						PwDefs.UserNameField), sprCtx));
875 				if((f & AceAutoTypeCtxFlags.ColPassword) != AceAutoTypeCtxFlags.None)
876 					lvi.SubItems.Add(SprEngine.Compile(pe.Strings.ReadSafe(
877 						PwDefs.PasswordField), sprCtx));
878 				if((f & AceAutoTypeCtxFlags.ColUrl) != AceAutoTypeCtxFlags.None)
879 					lvi.SubItems.Add(SprEngine.Compile(pe.Strings.ReadSafe(
880 						PwDefs.UrlField), sprCtx));
881 				if((f & AceAutoTypeCtxFlags.ColNotes) != AceAutoTypeCtxFlags.None)
882 					lvi.SubItems.Add(StrUtil.MultiToSingleLine(SprEngine.Compile(
883 						pe.Strings.ReadSafe(PwDefs.NotesField), sprCtx)));
884 				if((f & AceAutoTypeCtxFlags.ColSequenceComments) != AceAutoTypeCtxFlags.None)
885 				{
886 					if(rxSeqCmt == null)
887 						rxSeqCmt = new Regex("\\{[Cc]:[^\\}]*\\}");
888 
889 					string strSeqCmt = string.Empty, strImpSeqCmt = string.Empty;
890 					foreach(Match m in rxSeqCmt.Matches(ctx.Sequence))
891 					{
892 						string strPart = m.Value;
893 						if(strPart == null) { Debug.Assert(false); continue; }
894 						if(strPart.Length < 4) { Debug.Assert(false); continue; }
895 
896 						strPart = strPart.Substring(3, strPart.Length - 4).Trim();
897 						bool bImp = strPart.StartsWith("!"); // Important comment
898 						if(bImp) strPart = strPart.Substring(1);
899 						if(strPart.Length == 0) continue;
900 
901 						if(bImp)
902 						{
903 							if(strImpSeqCmt.Length > 0) strImpSeqCmt += " - ";
904 							strImpSeqCmt += strPart;
905 						}
906 						else
907 						{
908 							if(strSeqCmt.Length > 0) strSeqCmt += " - ";
909 							strSeqCmt += strPart;
910 						}
911 					}
912 
913 					lvi.SubItems.Add((strImpSeqCmt.Length > 0) ? strImpSeqCmt :
914 						strSeqCmt);
915 				}
916 				if((f & AceAutoTypeCtxFlags.ColSequence) != AceAutoTypeCtxFlags.None)
917 					lvi.SubItems.Add(ctx.Sequence);
918 				Debug.Assert(lvi.SubItems.Count == lv.Columns.Count);
919 
920 				if(!UIUtil.ColorsEqual(pe.ForegroundColor, Color.Empty))
921 					lvi.ForeColor = pe.ForegroundColor;
922 				if(!UIUtil.ColorsEqual(pe.BackgroundColor, Color.Empty))
923 					lvi.BackColor = pe.BackgroundColor;
924 
925 				lvi.Tag = ctx;
926 
927 				lv.Items.Add(lvi);
928 				lvg.Items.Add(lvi);
929 
930 				if(bFirstEntry)
931 				{
932 					UIUtil.SetFocusedItem(lv, lvi, true);
933 					bFirstEntry = false;
934 				}
935 			}
936 
937 			lv.EndUpdate();
938 
939 			// Resize columns *after* EndUpdate, otherwise sizing problem
940 			// caused by the scrollbar
941 			UIUtil.ResizeColumns(lv, true);
942 		}
943 
GetVScrollBarWidth()944 		public static int GetVScrollBarWidth()
945 		{
946 			try { return SystemInformation.VerticalScrollBarWidth; }
947 			catch(Exception) { Debug.Assert(false); }
948 
949 			return 18; // Default theme on Windows Vista
950 		}
951 
952 		/// <summary>
953 		/// Create a file type filter specification string.
954 		/// </summary>
955 		/// <param name="strExtension">Default extension(s), without leading
956 		/// dot. Multiple extensions must be separated by a '|' (e.g.
957 		/// "html|htm", having the same description "HTML Files").</param>
CreateFileTypeFilter(string strExtension, string strDescription, bool bIncludeAllFiles)958 		public static string CreateFileTypeFilter(string strExtension, string strDescription,
959 			bool bIncludeAllFiles)
960 		{
961 			StringBuilder sb = new StringBuilder();
962 
963 			if(!string.IsNullOrEmpty(strExtension) && !string.IsNullOrEmpty(
964 				strDescription))
965 			{
966 				// str += strDescription + @" (*." + strExtension +
967 				//	@")|*." + strExtension;
968 
969 				string[] vExts = strExtension.Split(new char[] { '|' },
970 					StringSplitOptions.RemoveEmptyEntries);
971 				if(vExts.Length > 0)
972 				{
973 					sb.Append(strDescription);
974 					sb.Append(@" (*.");
975 
976 					for(int i = 0; i < vExts.Length; ++i)
977 					{
978 						if(i > 0) sb.Append(@", *.");
979 						sb.Append(vExts[i]);
980 					}
981 
982 					sb.Append(@")|*.");
983 
984 					for(int i = 0; i < vExts.Length; ++i)
985 					{
986 						if(i > 0) sb.Append(@";*.");
987 						sb.Append(vExts[i]);
988 					}
989 				}
990 			}
991 
992 			if(bIncludeAllFiles)
993 			{
994 				if(sb.Length > 0) sb.Append(@"|");
995 				sb.Append(KPRes.AllFiles);
996 				sb.Append(@" (*.*)|*.*");
997 			}
998 
999 			return sb.ToString();
1000 		}
1001 
GetPrimaryFileTypeExt(string strExtensions)1002 		public static string GetPrimaryFileTypeExt(string strExtensions)
1003 		{
1004 			if(strExtensions == null) { Debug.Assert(false); return string.Empty; }
1005 
1006 			int i = strExtensions.IndexOf('|');
1007 			if(i >= 0) return strExtensions.Substring(0, i);
1008 
1009 			return strExtensions; // Single extension
1010 		}
1011 
1012 		[Obsolete("Use the overload with the strContext parameter.")]
CreateOpenFileDialog(string strTitle, string strFilter, int iFilterIndex, string strDefaultExt, bool bMultiSelect, bool bRestoreDirectory)1013 		public static OpenFileDialog CreateOpenFileDialog(string strTitle, string strFilter,
1014 			int iFilterIndex, string strDefaultExt, bool bMultiSelect, bool bRestoreDirectory)
1015 		{
1016 			return (OpenFileDialog)(CreateOpenFileDialog(strTitle, strFilter,
1017 				iFilterIndex, strDefaultExt, bMultiSelect, string.Empty).FileDialog);
1018 		}
1019 
CreateOpenFileDialog(string strTitle, string strFilter, int iFilterIndex, string strDefaultExt, bool bMultiSelect, string strContext)1020 		public static OpenFileDialogEx CreateOpenFileDialog(string strTitle, string strFilter,
1021 			int iFilterIndex, string strDefaultExt, bool bMultiSelect, string strContext)
1022 		{
1023 			OpenFileDialogEx ofd = new OpenFileDialogEx(strContext);
1024 
1025 			if(!string.IsNullOrEmpty(strDefaultExt))
1026 				ofd.DefaultExt = strDefaultExt;
1027 
1028 			if(!string.IsNullOrEmpty(strFilter))
1029 			{
1030 				ofd.Filter = strFilter;
1031 
1032 				if(iFilterIndex > 0) ofd.FilterIndex = iFilterIndex;
1033 			}
1034 
1035 			ofd.Multiselect = bMultiSelect;
1036 
1037 			if(!string.IsNullOrEmpty(strTitle))
1038 				ofd.Title = strTitle;
1039 
1040 			return ofd;
1041 		}
1042 
1043 		[Obsolete("Use the overload with the strContext parameter.")]
CreateSaveFileDialog(string strTitle, string strSuggestedFileName, string strFilter, int iFilterIndex, string strDefaultExt, bool bRestoreDirectory)1044 		public static SaveFileDialog CreateSaveFileDialog(string strTitle,
1045 			string strSuggestedFileName, string strFilter, int iFilterIndex,
1046 			string strDefaultExt, bool bRestoreDirectory)
1047 		{
1048 			return (SaveFileDialog)(CreateSaveFileDialog(strTitle, strSuggestedFileName,
1049 				strFilter, iFilterIndex, strDefaultExt, string.Empty).FileDialog);
1050 		}
1051 
1052 		[Obsolete("Use the overload with the strContext parameter.")]
CreateSaveFileDialog(string strTitle, string strSuggestedFileName, string strFilter, int iFilterIndex, string strDefaultExt, bool bRestoreDirectory, bool bIsDatabaseFile)1053 		public static SaveFileDialog CreateSaveFileDialog(string strTitle,
1054 			string strSuggestedFileName, string strFilter, int iFilterIndex,
1055 			string strDefaultExt, bool bRestoreDirectory, bool bIsDatabaseFile)
1056 		{
1057 			return (SaveFileDialog)(CreateSaveFileDialog(strTitle, strSuggestedFileName,
1058 				strFilter, iFilterIndex, strDefaultExt, (bIsDatabaseFile ?
1059 				AppDefs.FileDialogContext.Database : string.Empty)).FileDialog);
1060 		}
1061 
CreateSaveFileDialog(string strTitle, string strSuggestedFileName, string strFilter, int iFilterIndex, string strDefaultExt, string strContext)1062 		public static SaveFileDialogEx CreateSaveFileDialog(string strTitle,
1063 			string strSuggestedFileName, string strFilter, int iFilterIndex,
1064 			string strDefaultExt, string strContext)
1065 		{
1066 			SaveFileDialogEx sfd = new SaveFileDialogEx(strContext);
1067 
1068 			if(!string.IsNullOrEmpty(strDefaultExt))
1069 				sfd.DefaultExt = strDefaultExt;
1070 
1071 			if(!string.IsNullOrEmpty(strSuggestedFileName))
1072 				sfd.FileName = strSuggestedFileName;
1073 
1074 			if(!string.IsNullOrEmpty(strFilter))
1075 			{
1076 				sfd.Filter = strFilter;
1077 
1078 				if(iFilterIndex > 0) sfd.FilterIndex = iFilterIndex;
1079 			}
1080 
1081 			if(!string.IsNullOrEmpty(strTitle))
1082 				sfd.Title = strTitle;
1083 
1084 			if(strContext != null)
1085 			{
1086 				if((strContext == AppDefs.FileDialogContext.Database) &&
1087 					(Program.Config.Defaults.FileSaveAsDirectory.Length > 0))
1088 					sfd.InitialDirectory = Program.Config.Defaults.FileSaveAsDirectory;
1089 			}
1090 
1091 			return sfd;
1092 		}
1093 
CreateFolderBrowserDialog(string strDescription)1094 		public static FolderBrowserDialog CreateFolderBrowserDialog(string strDescription)
1095 		{
1096 			FolderBrowserDialog fbd = new FolderBrowserDialog();
1097 
1098 			if(!string.IsNullOrEmpty(strDescription))
1099 				fbd.Description = strDescription;
1100 
1101 			fbd.ShowNewFolderButton = true;
1102 
1103 			return fbd;
1104 		}
1105 
CreateColorDialog(Color clrDefault)1106 		private static ColorDialog CreateColorDialog(Color clrDefault)
1107 		{
1108 			ColorDialog dlg = new ColorDialog();
1109 
1110 			dlg.AllowFullOpen = true;
1111 			dlg.AnyColor = true;
1112 			if(!UIUtil.ColorsEqual(clrDefault, Color.Empty)) dlg.Color = clrDefault;
1113 			dlg.FullOpen = true;
1114 			dlg.ShowHelp = false;
1115 			// dlg.SolidColorOnly = false;
1116 
1117 			try
1118 			{
1119 				string strColors = Program.Config.Defaults.CustomColors;
1120 				if(!string.IsNullOrEmpty(strColors))
1121 				{
1122 					int[] vColors = StrUtil.DeserializeIntArray(strColors);
1123 					if((vColors != null) && (vColors.Length > 0))
1124 						dlg.CustomColors = vColors;
1125 				}
1126 			}
1127 			catch(Exception) { Debug.Assert(false); }
1128 
1129 			return dlg;
1130 		}
1131 
SaveCustomColors(ColorDialog dlg)1132 		private static void SaveCustomColors(ColorDialog dlg)
1133 		{
1134 			if(dlg == null) { Debug.Assert(false); return; }
1135 
1136 			try
1137 			{
1138 				int[] vColors = dlg.CustomColors;
1139 				if((vColors == null) || (vColors.Length == 0))
1140 					Program.Config.Defaults.CustomColors = string.Empty;
1141 				else
1142 					Program.Config.Defaults.CustomColors =
1143 						StrUtil.SerializeIntArray(vColors);
1144 			}
1145 			catch(Exception) { Debug.Assert(false); }
1146 		}
1147 
ShowColorDialog(Color clrDefault)1148 		public static Color? ShowColorDialog(Color clrDefault)
1149 		{
1150 			ColorDialog dlg = CreateColorDialog(clrDefault);
1151 
1152 			GlobalWindowManager.AddDialog(dlg);
1153 			DialogResult dr = dlg.ShowDialog();
1154 			GlobalWindowManager.RemoveDialog(dlg);
1155 
1156 			SaveCustomColors(dlg);
1157 
1158 			Color? clrResult = null;
1159 			if(dr == DialogResult.OK) clrResult = dlg.Color;
1160 
1161 			dlg.Dispose();
1162 			return clrResult;
1163 		}
1164 
CreateFontDialog(bool bEffects)1165 		public static FontDialog CreateFontDialog(bool bEffects)
1166 		{
1167 			FontDialog dlg = new FontDialog();
1168 
1169 			dlg.FontMustExist = true;
1170 			dlg.ShowEffects = bEffects;
1171 
1172 			return dlg;
1173 		}
1174 
SetGroupNodeToolTip(TreeNode tn, PwGroup pg)1175 		public static void SetGroupNodeToolTip(TreeNode tn, PwGroup pg)
1176 		{
1177 			if((tn == null) || (pg == null)) { Debug.Assert(false); return; }
1178 
1179 			string str = GetPwGroupToolTip(pg);
1180 			if(str == null) return;
1181 
1182 			try { tn.ToolTipText = str; }
1183 			catch(Exception) { Debug.Assert(false); }
1184 		}
1185 
GetPwGroupToolTip(PwGroup pg)1186 		public static string GetPwGroupToolTip(PwGroup pg)
1187 		{
1188 			if(pg == null) { Debug.Assert(false); return null; }
1189 
1190 			string strNotes = pg.Notes.Trim();
1191 			// string strTags = StrUtil.TagsToString(pg.Tags, true);
1192 
1193 			if(strNotes.Length == 0) // && (strTags.Length == 0))
1194 				return null;
1195 
1196 			StringBuilder sb = new StringBuilder();
1197 			sb.Append(pg.Name);
1198 
1199 			// if(strNotes.Length != 0)
1200 			// {
1201 			sb.Append(MessageService.NewParagraph);
1202 			sb.Append(strNotes);
1203 			// }
1204 
1205 			// if(strTags.Length != 0)
1206 			// {
1207 			//	sb.Append(MessageService.NewParagraph);
1208 			//	sb.Append(strTags);
1209 			//	sb.Append('.');
1210 			// }
1211 
1212 			// uint uSubGroups, uEntries;
1213 			// pg.GetCounts(true, out uSubGroups, out uEntries);
1214 			// sb.Append(MessageService.NewParagraph);
1215 			// sb.Append(KPRes.Subgroups); sb.Append(": "); sb.Append(uSubGroups);
1216 			// sb.Append(MessageService.NewLine);
1217 			// sb.Append(KPRes.Entries); sb.Append(": "); sb.Append(uEntries);
1218 
1219 			return sb.ToString();
1220 		}
1221 
1222 		// public static string GetPwGroupToolTipTN(TreeNode tn)
1223 		// {
1224 		//	if(tn == null) { Debug.Assert(false); return null; }
1225 		//	PwGroup pg = (tn.Tag as PwGroup);
1226 		//	if(pg == null) { Debug.Assert(false); return null; }
1227 		//	return GetPwGroupToolTip(pg);
1228 		// }
1229 
LightenColor(Color clrBase, double dblFactor)1230 		public static Color LightenColor(Color clrBase, double dblFactor)
1231 		{
1232 			if((dblFactor <= 0.0) || (dblFactor > 1.0)) return clrBase;
1233 
1234 			unchecked
1235 			{
1236 				byte r = (byte)((double)(255 - clrBase.R) * dblFactor + clrBase.R);
1237 				byte g = (byte)((double)(255 - clrBase.G) * dblFactor + clrBase.G);
1238 				byte b = (byte)((double)(255 - clrBase.B) * dblFactor + clrBase.B);
1239 				return Color.FromArgb((int)r, (int)g, (int)b);
1240 			}
1241 		}
1242 
DarkenColor(Color clrBase, double dblFactor)1243 		public static Color DarkenColor(Color clrBase, double dblFactor)
1244 		{
1245 			if((dblFactor <= 0.0) || (dblFactor > 1.0)) return clrBase;
1246 
1247 			unchecked
1248 			{
1249 				byte r = (byte)((double)clrBase.R - ((double)clrBase.R * dblFactor));
1250 				byte g = (byte)((double)clrBase.G - ((double)clrBase.G * dblFactor));
1251 				byte b = (byte)((double)clrBase.B - ((double)clrBase.B * dblFactor));
1252 				return Color.FromArgb((int)r, (int)g, (int)b);
1253 			}
1254 		}
1255 
1256 		public static void MoveSelectedItemsInternalOne<T>(ListView lv,
1257 			PwObjectList<T> v, bool bUp)
1258 			where T : class, IDeepCloneable<T>
1259 		{
1260 			if(lv == null) throw new ArgumentNullException("lv");
1261 			if(v == null) throw new ArgumentNullException("v");
ArgumentException()1262 			if(lv.Items.Count != (int)v.UCount) throw new ArgumentException();
1263 
1264 			ListView.SelectedListViewItemCollection lvsic = lv.SelectedItems;
1265 			if(lvsic.Count == 0) return;
1266 
1267 			int nStart = (bUp ? 0 : (lvsic.Count - 1));
1268 			int nEnd = (bUp ? lvsic.Count : -1);
1269 			int iStep = (bUp ? 1 : -1);
1270 			for(int i = nStart; i != nEnd; i += iStep)
v.MoveOne(lvsic[i].Tag as T), bUp1271 				v.MoveOne((lvsic[i].Tag as T), bUp);
1272 		}
1273 
1274 		public static void DeleteSelectedItems<T>(ListView lv, PwObjectList<T>
1275 			vInternalList)
1276 			where T : class, IDeepCloneable<T>
1277 		{
1278 			if(lv == null) throw new ArgumentNullException("lv");
1279 			if(vInternalList == null) throw new ArgumentNullException("vInternalList");
1280 
1281 			ListView.SelectedIndexCollection lvsic = lv.SelectedIndices;
1282 			int n = lvsic.Count; // Getting Count sends a message
1283 			if(n == 0) return;
1284 
1285 			// LVSIC: one access by index requires O(n) time, thus copy
1286 			// all to an array (which requires O(1) for each element)
1287 			int[] v = new int[n];
lvsic.CopyTo(v, 0)1288 			lvsic.CopyTo(v, 0);
1289 
1290 			for(int i = 0; i < n; ++i)
1291 			{
1292 				int nIndex = v[n - i - 1];
1293 				if(vInternalList.Remove(lv.Items[nIndex].Tag as T))
1294 					lv.Items.RemoveAt(nIndex);
1295 				else { Debug.Assert(false); }
1296 			}
1297 		}
1298 
GetSelectedItemTags(ListView lv)1299 		public static object[] GetSelectedItemTags(ListView lv)
1300 		{
1301 			if(lv == null) throw new ArgumentNullException("lv");
1302 
1303 			ListView.SelectedListViewItemCollection lvsc = lv.SelectedItems;
1304 			if(lvsc == null) { Debug.Assert(false); return new object[0]; }
1305 			int n = lvsc.Count; // Getting Count sends a message
1306 
1307 			object[] p = new object[n];
1308 			int i = 0;
1309 			// LVSLVIC: one access by index requires O(n) time, thus use
1310 			// enumerator instead (which requires O(1) for each element)
1311 			foreach(ListViewItem lvi in lvsc)
1312 			{
1313 				if(i >= n) { Debug.Assert(false); break; }
1314 
1315 				p[i] = lvi.Tag;
1316 				++i;
1317 			}
1318 			Debug.Assert(i == n);
1319 
1320 			return p;
1321 		}
1322 
SelectItems(ListView lv, object[] vItemTags)1323 		public static void SelectItems(ListView lv, object[] vItemTags)
1324 		{
1325 			if(lv == null) throw new ArgumentNullException("lv");
1326 			if(vItemTags == null) throw new ArgumentNullException("vItemTags");
1327 
1328 			for(int i = 0; i < lv.Items.Count; ++i)
1329 			{
1330 				if(Array.IndexOf<object>(vItemTags, lv.Items[i].Tag) >= 0)
1331 					lv.Items[i].Selected = true;
1332 			}
1333 		}
1334 
DeselectAllItems(ListView lv)1335 		internal static void DeselectAllItems(ListView lv)
1336 		{
1337 			if(lv == null) { Debug.Assert(false); return; }
1338 
1339 			ListView.SelectedIndexCollection lvsic = lv.SelectedIndices;
1340 			int n = lvsic.Count;
1341 			if(n == 0) return;
1342 
1343 			int[] v = new int[n];
1344 			lvsic.CopyTo(v, 0);
1345 
1346 			for(int i = n - 1; i >= 0; --i)
1347 				lv.Items[v[i]].Selected = false;
1348 		}
1349 
SetWebBrowserDocument(WebBrowser wb, string strDocumentText)1350 		public static void SetWebBrowserDocument(WebBrowser wb, string strDocumentText)
1351 		{
1352 			string strContent = (strDocumentText ?? string.Empty);
1353 
1354 			wb.AllowNavigation = true;
1355 			wb.DocumentText = strContent;
1356 
1357 			// Wait for document being loaded
1358 			for(int i = 0; i < 50; ++i)
1359 			{
1360 				if(wb.DocumentText == strContent) break;
1361 
1362 				Thread.Sleep(20);
1363 				Application.DoEvents();
1364 			}
1365 		}
1366 
GetWebBrowserDocument(WebBrowser wb)1367 		public static string GetWebBrowserDocument(WebBrowser wb)
1368 		{
1369 			return wb.DocumentText;
1370 		}
1371 
SetExplorerTheme(IntPtr hWnd)1372 		public static void SetExplorerTheme(IntPtr hWnd)
1373 		{
1374 			if(hWnd == IntPtr.Zero) { Debug.Assert(false); return; }
1375 
1376 			try { NativeMethods.SetWindowTheme(hWnd, "explorer", null); }
1377 			catch(Exception) { } // Not supported on older operating systems
1378 		}
1379 
SetExplorerTheme(Control c, bool bUseListFont)1380 		public static void SetExplorerTheme(Control c, bool bUseListFont)
1381 		{
1382 			if(c == null) { Debug.Assert(false); return; }
1383 
1384 			SetExplorerTheme(c.Handle);
1385 
1386 			if(bUseListFont)
1387 			{
1388 				if(UISystemFonts.ListFont != null)
1389 					c.Font = UISystemFonts.ListFont;
1390 			}
1391 		}
1392 
SetShield(Button btn, bool bSetShield)1393 		public static void SetShield(Button btn, bool bSetShield)
1394 		{
1395 			if(btn == null) throw new ArgumentNullException("btn");
1396 
1397 			try
1398 			{
1399 				if(btn.FlatStyle != FlatStyle.System)
1400 				{
1401 					Debug.Assert(false);
1402 					btn.FlatStyle = FlatStyle.System;
1403 				}
1404 
1405 				IntPtr h = btn.Handle;
1406 				if(h == IntPtr.Zero) { Debug.Assert(false); return; }
1407 
1408 				NativeMethods.SendMessage(h, NativeMethods.BCM_SETSHIELD,
1409 					IntPtr.Zero, (IntPtr)(bSetShield ? 1 : 0));
1410 			}
1411 			catch(Exception) { Debug.Assert(false); }
1412 		}
1413 
Configure(ToolStrip ts)1414 		public static void Configure(ToolStrip ts)
1415 		{
1416 			if(Program.DesignMode) return;
1417 			if(ts == null) { Debug.Assert(false); return; }
1418 
1419 			// if(Program.Translation.Properties.RightToLeft)
1420 			//	ts.RightToLeft = RightToLeft.Yes;
1421 
1422 			DpiUtil.Configure(ts);
1423 		}
1424 
1425 		// internal static void ConfigureToolStripItem(ToolStripItem ts)
1426 		// {
1427 		//	if(ts == null) { Debug.Assert(false); return; }
1428 		//	if(Program.DesignMode) return;
1429 		//	// Disable separators such that clicking on them
1430 		//	// does not close the menu
1431 		//	ToolStripSeparator tsSep = (ts as ToolStripSeparator);
1432 		//	if(tsSep != null) tsSep.Enabled = false;
1433 		// }
1434 
ConfigureTbButton(ToolStripItem tb, string strText, string strTooltip)1435 		public static void ConfigureTbButton(ToolStripItem tb, string strText,
1436 			string strTooltip)
1437 		{
1438 			ConfigureTbButton(tb, strText, strTooltip, null);
1439 		}
1440 
ConfigureTbButton(ToolStripItem tb, string strText, string strToolTip, ToolStripMenuItem tsmiEquiv)1441 		public static void ConfigureTbButton(ToolStripItem tb, string strText,
1442 			string strToolTip, ToolStripMenuItem tsmiEquiv)
1443 		{
1444 			if(tb == null) { Debug.Assert(false); return; }
1445 
1446 			string strT = (strText ?? string.Empty);
1447 			string strTT = (strToolTip ?? strT);
1448 
1449 			strT = StrUtil.TrimDots(StrUtil.RemoveAccelerator(strT), true);
1450 			strTT = StrUtil.TrimDots(StrUtil.RemoveAccelerator(strTT), true);
1451 
1452 			Debug.Assert((strT.Length >= 1) || (tb.Text.Length == 0));
1453 			tb.Text = strT;
1454 
1455 			if((tsmiEquiv != null) && (strTT.Length != 0))
1456 			{
1457 				string strShortcut = tsmiEquiv.ShortcutKeyDisplayString;
1458 				if(!string.IsNullOrEmpty(strShortcut))
1459 					strTT += " (" + strShortcut + ")";
1460 			}
1461 
1462 			Debug.Assert((strTT.Length >= 1) || (tb.ToolTipText.Length == 0));
1463 			tb.ToolTipText = strTT;
1464 
1465 			if(strT.Length == 0)
1466 			{
1467 				ToolStripControlHost tsch = (tb as ToolStripControlHost);
1468 				if(tsch != null) AccSetName(tsch.Control, strTT);
1469 				else { Debug.Assert(false); }
1470 			}
1471 		}
1472 
ConfigureToolTip(ToolTip tt)1473 		public static void ConfigureToolTip(ToolTip tt)
1474 		{
1475 			if(tt == null) { Debug.Assert(false); return; }
1476 
1477 			try
1478 			{
1479 				tt.AutoPopDelay = 32000; // Cf. method below
1480 				tt.InitialDelay = 250;
1481 				tt.ReshowDelay = 50;
1482 
1483 				Debug.Assert(tt.AutoPopDelay == 32000);
1484 			}
1485 			catch(Exception) { Debug.Assert(false); }
1486 		}
1487 
ConfigureToolTip(IntPtr hToolTip)1488 		private static void ConfigureToolTip(IntPtr hToolTip)
1489 		{
1490 			if(hToolTip == IntPtr.Zero) { Debug.Assert(false); return; }
1491 			if(NativeLib.IsUnix()) return;
1492 
1493 			try
1494 			{
1495 				// Apparently the maximum value is 2^15 - 1 = 32767
1496 				// (signed short maximum); any larger value results
1497 				// in a truncated value or is ignored
1498 				NativeMethods.SendMessage(hToolTip, NativeMethods.TTM_SETDELAYTIME,
1499 					(IntPtr)NativeMethods.TTDT_AUTOPOP, (IntPtr)32000); // Cf. method above
1500 				NativeMethods.SendMessage(hToolTip, NativeMethods.TTM_SETDELAYTIME,
1501 					(IntPtr)NativeMethods.TTDT_INITIAL, (IntPtr)250);
1502 				NativeMethods.SendMessage(hToolTip, NativeMethods.TTM_SETDELAYTIME,
1503 					(IntPtr)NativeMethods.TTDT_RESHOW, (IntPtr)50);
1504 
1505 				Debug.Assert(NativeMethods.SendMessage(hToolTip, NativeMethods.TTM_GETDELAYTIME,
1506 					(IntPtr)NativeMethods.TTDT_AUTOPOP, IntPtr.Zero) == (IntPtr)32000);
1507 			}
1508 			catch(Exception) { Debug.Assert(false); }
1509 		}
1510 
ConfigureToolTip(Control c, int msgGetToolTip)1511 		private static void ConfigureToolTip(Control c, int msgGetToolTip)
1512 		{
1513 			if(c == null) { Debug.Assert(false); return; }
1514 			if(NativeLib.IsUnix()) return;
1515 
1516 			try
1517 			{
1518 				IntPtr hControl = c.Handle;
1519 				if(hControl == IntPtr.Zero) { Debug.Assert(false); return; }
1520 
1521 				IntPtr hToolTip = NativeMethods.SendMessage(hControl,
1522 					msgGetToolTip, IntPtr.Zero, IntPtr.Zero);
1523 				if(hToolTip != IntPtr.Zero) ConfigureToolTip(hToolTip);
1524 			}
1525 			catch(Exception) { Debug.Assert(false); }
1526 		}
1527 
ConfigureToolTip(TreeView tv)1528 		internal static void ConfigureToolTip(TreeView tv)
1529 		{
1530 			ConfigureToolTip(tv, NativeMethods.TVM_GETTOOLTIPS);
1531 		}
1532 
ConfigureToolTip(ListView lv)1533 		internal static void ConfigureToolTip(ListView lv)
1534 		{
1535 			ConfigureToolTip(lv, NativeMethods.LVM_GETTOOLTIPS);
1536 		}
1537 
SetToolTip(ToolTip tt, Control c, string strText, bool bAcc)1538 		internal static void SetToolTip(ToolTip tt, Control c, string strText,
1539 			bool bAcc)
1540 		{
1541 			if(c == null) { Debug.Assert(false); return; }
1542 
1543 			try
1544 			{
1545 				if(tt != null) tt.SetToolTip(c, strText);
1546 				else { Debug.Assert(false); }
1547 			}
1548 			catch(Exception) { Debug.Assert(false); }
1549 
1550 			if(bAcc) AccSetName(c, strText);
1551 		}
1552 
SetToolTip(ToolStripControlHost tsch, string strText, bool bAcc)1553 		internal static void SetToolTip(ToolStripControlHost tsch, string strText,
1554 			bool bAcc)
1555 		{
1556 			if(tsch == null) { Debug.Assert(false); return; }
1557 
1558 			try { tsch.ToolTipText = strText; }
1559 			catch(Exception) { Debug.Assert(false); }
1560 
1561 			if(bAcc) AccSetName(tsch.Control, strText);
1562 		}
1563 
CreateGroupList(PwGroup pgContainer, ComboBox cmb, Dictionary<int, PwUuid> outCreatedItems, PwUuid uuidToSelect, out int iSelectIndex)1564 		public static void CreateGroupList(PwGroup pgContainer, ComboBox cmb,
1565 			Dictionary<int, PwUuid> outCreatedItems, PwUuid uuidToSelect,
1566 			out int iSelectIndex)
1567 		{
1568 			iSelectIndex = -1;
1569 
1570 			if(pgContainer == null) { Debug.Assert(false); return; }
1571 			if(cmb == null) { Debug.Assert(false); return; }
1572 			// Do not clear the combobox!
1573 
1574 			int iSelectInner = -1;
1575 			GroupHandler gh = delegate(PwGroup pg)
1576 			{
1577 				string str = new string(' ', Math.Abs(8 * ((int)pg.GetDepth() - 1)));
1578 				str += pg.Name;
1579 
1580 				if((uuidToSelect != null) && pg.Uuid.Equals(uuidToSelect))
1581 					iSelectInner = cmb.Items.Count;
1582 
1583 				if(outCreatedItems != null)
1584 					outCreatedItems[cmb.Items.Count] = pg.Uuid;
1585 
1586 				cmb.Items.Add(str);
1587 				return true;
1588 			};
1589 
1590 			pgContainer.TraverseTree(TraversalMethod.PreOrder, gh, null);
1591 			iSelectIndex = iSelectInner;
1592 		}
1593 
MakeInheritableBoolComboBox(ComboBox cmb, bool? bSelect, bool bInheritedState)1594 		public static void MakeInheritableBoolComboBox(ComboBox cmb, bool? bSelect,
1595 			bool bInheritedState)
1596 		{
1597 			if(cmb == null) { Debug.Assert(false); return; }
1598 
1599 			cmb.Items.Clear();
1600 			cmb.Items.Add(KPRes.InheritSettingFromParent + " (" + (bInheritedState ?
1601 				KPRes.Enabled : KPRes.Disabled) + ")");
1602 			cmb.Items.Add(KPRes.Enabled);
1603 			cmb.Items.Add(KPRes.Disabled);
1604 
1605 			if(bSelect.HasValue) cmb.SelectedIndex = (bSelect.Value ? 1 : 2);
1606 			else cmb.SelectedIndex = 0;
1607 		}
1608 
GetInheritableBoolComboBoxValue(ComboBox cmb)1609 		public static bool? GetInheritableBoolComboBoxValue(ComboBox cmb)
1610 		{
1611 			if(cmb == null) { Debug.Assert(false); return null; }
1612 
1613 			if(cmb.SelectedIndex == 1) return true;
1614 			if(cmb.SelectedIndex == 2) return false;
1615 			return null;
1616 		}
1617 
SetEnabled(Control c, bool bEnabled)1618 		public static void SetEnabled(Control c, bool bEnabled)
1619 		{
1620 			if(c == null) { Debug.Assert(false); return; }
1621 
1622 			if(c.Enabled != bEnabled) c.Enabled = bEnabled;
1623 		}
1624 
SetEnabledFast(bool bEnabled, params Control[] v)1625 		internal static void SetEnabledFast(bool bEnabled, params Control[] v)
1626 		{
1627 			if(v == null) { Debug.Assert(false); return; }
1628 
1629 			foreach(Control c in v)
1630 			{
1631 				if(c == null) { Debug.Assert(false); continue; }
1632 
1633 				c.Enabled = bEnabled;
1634 			}
1635 		}
1636 
SetEnabledFast(bool bEnabled, params ToolStripItem[] v)1637 		internal static void SetEnabledFast(bool bEnabled, params ToolStripItem[] v)
1638 		{
1639 			if(v == null) { Debug.Assert(false); return; }
1640 
1641 			foreach(ToolStripItem tsi in v)
1642 			{
1643 				if(tsi == null) { Debug.Assert(false); continue; }
1644 
1645 				tsi.Enabled = bEnabled;
1646 			}
1647 		}
1648 
SetChecked(CheckBox cb, bool bChecked)1649 		public static void SetChecked(CheckBox cb, bool bChecked)
1650 		{
1651 			if(cb == null) { Debug.Assert(false); return; }
1652 
1653 			// If the state is indeterminate, setting the Checked
1654 			// property to true does not change the state to checked,
1655 			// thus we use the CheckState property instead of Checked
1656 			cb.CheckState = (bChecked ? CheckState.Checked : CheckState.Unchecked);
1657 		}
1658 
GetGlyphBitmap(MenuGlyph mg, Color clrFG)1659 		private static Bitmap GetGlyphBitmap(MenuGlyph mg, Color clrFG)
1660 		{
1661 			try
1662 			{
1663 				Size sz = new Size(DpiUtil.ScaleIntX(16), DpiUtil.ScaleIntY(16));
1664 
1665 				Bitmap bmp = new Bitmap(sz.Width, sz.Height,
1666 					PixelFormat.Format32bppArgb);
1667 				using(Graphics g = Graphics.FromImage(bmp))
1668 				{
1669 					g.Clear(Color.Transparent);
1670 					ControlPaint.DrawMenuGlyph(g, new Rectangle(Point.Empty,
1671 						sz), mg, clrFG, Color.Transparent);
1672 				}
1673 
1674 				return bmp;
1675 			}
1676 			catch(Exception) { Debug.Assert(false); }
1677 
1678 			return null;
1679 		}
1680 
1681 		private static Bitmap g_bmpCheck = null;
1682 		private static Bitmap g_bmpCheckLight = null;
1683 		private static Bitmap g_bmpTrans = null;
SetChecked(ToolStripMenuItem tsmi, bool bChecked)1684 		public static void SetChecked(ToolStripMenuItem tsmi, bool bChecked)
1685 		{
1686 			if(tsmi == null) { Debug.Assert(false); return; }
1687 
1688 			string strIDCheck = "guid:5EAEA440-02AA-4E62-B57E-724A6F89B1EE";
1689 			string strIDTrans = "guid:38DDF11D-F101-468A-A006-9810A95F34F4";
1690 
1691 			// The image references may change, thus use the Tag instead;
1692 			// https://sourceforge.net/p/keepass/discussion/329220/thread/e1950e60/
1693 			bool bSetImage = false;
1694 			Image imgCur = tsmi.Image;
1695 			if(imgCur == null) bSetImage = true;
1696 			else
1697 			{
1698 				string strID = (imgCur.Tag as string);
1699 				if(strID == null) { } // Unknown image, don't overwrite
1700 				else if((strID == strIDCheck) || (strID == strIDTrans))
1701 					bSetImage = true;
1702 			}
1703 
1704 			if(bSetImage)
1705 			{
1706 				Image img = null;
1707 
1708 				if(bChecked)
1709 				{
1710 					if(g_bmpCheck == null)
1711 					{
1712 						g_bmpCheck = new Bitmap(Properties.Resources.B16x16_MenuCheck);
1713 						g_bmpCheck.Tag = strIDCheck;
1714 					}
1715 
1716 					img = g_bmpCheck;
1717 
1718 					Color clrFG = tsmi.ForeColor;
1719 					if(!UIUtil.ColorsEqual(clrFG, Color.Empty) &&
1720 						(ColorToGrayscale(clrFG).R >= 128))
1721 					{
1722 						if(g_bmpCheckLight == null)
1723 						{
1724 							g_bmpCheckLight = GetGlyphBitmap(MenuGlyph.Checkmark,
1725 								Color.White); // Not clrFG, for consistency
1726 
1727 							if(g_bmpCheckLight != null)
1728 							{
1729 								Debug.Assert(g_bmpCheckLight.Tag == null);
1730 								g_bmpCheckLight.Tag = strIDCheck;
1731 							}
1732 						}
1733 
1734 						if(g_bmpCheckLight != null) img = g_bmpCheckLight;
1735 					}
1736 					else { Debug.Assert(g_bmpCheckLight == null); } // Always or never
1737 				}
1738 				else
1739 				{
1740 					if(g_bmpTrans == null)
1741 					{
1742 						g_bmpTrans = new Bitmap(Properties.Resources.B16x16_Transparent);
1743 						g_bmpTrans.Tag = strIDTrans;
1744 					}
1745 
1746 					// Assign transparent image instead of null in order to
1747 					// prevent incorrect menu item heights
1748 					img = g_bmpTrans;
1749 				}
1750 
1751 				tsmi.Image = img;
1752 			}
1753 
1754 			tsmi.Checked = bChecked;
1755 		}
1756 
1757 		private static Bitmap g_bmpRadioLight = null;
SetRadioChecked(ToolStripMenuItem tsmi, bool bChecked)1758 		public static void SetRadioChecked(ToolStripMenuItem tsmi, bool bChecked)
1759 		{
1760 			if(tsmi == null) { Debug.Assert(false); return; }
1761 
1762 			Debug.Assert(!tsmi.CheckOnClick); // Potential to break image
1763 
1764 			if(bChecked)
1765 			{
1766 				Image imgCheck = Properties.Resources.B16x16_MenuRadio;
1767 
1768 				Color clrFG = tsmi.ForeColor;
1769 				if(!UIUtil.ColorsEqual(clrFG, Color.Empty) &&
1770 					(ColorToGrayscale(clrFG).R >= 128))
1771 				{
1772 					if(g_bmpRadioLight == null)
1773 						g_bmpRadioLight = GetGlyphBitmap(MenuGlyph.Bullet,
1774 							Color.White); // Not clrFG, for consistency
1775 
1776 					if(g_bmpRadioLight != null) imgCheck = g_bmpRadioLight;
1777 				}
1778 				else { Debug.Assert(g_bmpRadioLight == null); } // Always or never
1779 
1780 				tsmi.Image = imgCheck;
1781 				tsmi.CheckState = CheckState.Checked;
1782 			}
1783 			else
1784 			{
1785 				// Transparent: see SetChecked method
1786 				tsmi.Image = Properties.Resources.B16x16_Transparent;
1787 				tsmi.CheckState = CheckState.Unchecked;
1788 			}
1789 		}
1790 
ResizeColumns(ListView lv, bool bBlockUIUpdate)1791 		public static void ResizeColumns(ListView lv, bool bBlockUIUpdate)
1792 		{
1793 			ResizeColumns(lv, null, bBlockUIUpdate);
1794 		}
1795 
ResizeColumns(ListView lv, int[] vRelWidths, bool bBlockUIUpdate)1796 		public static void ResizeColumns(ListView lv, int[] vRelWidths,
1797 			bool bBlockUIUpdate)
1798 		{
1799 			if(lv == null) { Debug.Assert(false); return; }
1800 			// vRelWidths may be null
1801 
1802 			List<int> lCurWidths = new List<int>();
1803 			foreach(ColumnHeader ch in lv.Columns) { lCurWidths.Add(ch.Width); }
1804 
1805 			int n = lCurWidths.Count;
1806 			if(n == 0) return;
1807 
1808 			int[] vRels = new int[n];
1809 			int nRelSum = 0;
1810 			for(int i = 0; i < n; ++i)
1811 			{
1812 				if((vRelWidths != null) && (i < vRelWidths.Length))
1813 				{
1814 					if(vRelWidths[i] >= 0) vRels[i] = vRelWidths[i];
1815 					else { Debug.Assert(false); vRels[i] = 0; }
1816 				}
1817 				else vRels[i] = 1; // Unit width 1 is default
1818 
1819 				nRelSum += vRels[i];
1820 			}
1821 			if(nRelSum == 0) { Debug.Assert(false); return; }
1822 
1823 			int w = lv.ClientSize.Width - 1;
1824 			if(w <= 0) { Debug.Assert(false); return; }
1825 
1826 			// The client width might include the width of a vertical
1827 			// scrollbar or not (unreliable; for example the scrollbar
1828 			// width is not subtracted during a Form.Load even though
1829 			// a scrollbar is required); try to detect this situation
1830 			int cwScroll = UIUtil.GetVScrollBarWidth();
1831 			if((lv.Width - w) < cwScroll) // Scrollbar not already subtracted
1832 			{
1833 				int nItems = lv.Items.Count;
1834 				if(nItems > 0)
1835 				{
1836 #if DEBUG
1837 					foreach(ListViewItem lvi in lv.Items)
1838 					{
1839 						Debug.Assert(lvi.Bounds.Height == lv.Items[0].Bounds.Height);
1840 					}
1841 #endif
1842 
1843 					if((((long)nItems * (long)lv.Items[0].Bounds.Height) >
1844 						(long)lv.ClientSize.Height) && (w > cwScroll))
1845 						w -= cwScroll;
1846 				}
1847 			}
1848 
1849 			double dx = (double)w / (double)nRelSum;
1850 
1851 			int[] vNewWidths = new int[n];
1852 			int iFirstVisible = -1, wSum = 0;
1853 			for(int i = 0; i < n; ++i)
1854 			{
1855 				int cw = (int)Math.Floor(dx * (double)vRels[i]);
1856 				vNewWidths[i] = cw;
1857 
1858 				wSum += cw;
1859 				if((iFirstVisible < 0) && (cw > 0)) iFirstVisible = i;
1860 			}
1861 
1862 			if((iFirstVisible >= 0) && (wSum < w))
1863 				vNewWidths[iFirstVisible] += (w - wSum);
1864 
1865 			if(bBlockUIUpdate) lv.BeginUpdate();
1866 
1867 			int iCol = 0;
1868 			foreach(ColumnHeader ch in lv.Columns)
1869 			{
1870 				if(iCol >= n) { Debug.Assert(false); break; }
1871 
1872 				if(vNewWidths[iCol] != lCurWidths[iCol])
1873 					ch.Width = vNewWidths[iCol];
1874 
1875 				++iCol;
1876 			}
1877 			Debug.Assert(iCol == n);
1878 
1879 			if(bBlockUIUpdate) lv.EndUpdate();
1880 		}
1881 
ColorsEqual(Color c1, Color c2)1882 		public static bool ColorsEqual(Color c1, Color c2)
1883 		{
1884 			// return ((c1.R == c2.R) && (c1.G == c2.G) && (c1.B == c2.B) &&
1885 			//	(c1.A == c2.A));
1886 			return (c1.ToArgb() == c2.ToArgb());
1887 		}
1888 
GetAlternateColor(Color clrBase)1889 		public static Color GetAlternateColor(Color clrBase)
1890 		{
1891 			if(ColorsEqual(clrBase, Color.White)) return Color.FromArgb(238, 238, 255);
1892 
1893 			float b = clrBase.GetBrightness();
1894 			if(b >= 0.5) return UIUtil.DarkenColor(clrBase, 0.1);
1895 			return UIUtil.LightenColor(clrBase, 0.25);
1896 		}
1897 
GetAlternateColorEx(Color clrBase)1898 		public static Color GetAlternateColorEx(Color clrBase)
1899 		{
1900 			int c = Program.Config.MainWindow.EntryListAlternatingBgColor;
1901 			if(c != 0) return Color.FromArgb(c);
1902 
1903 			return GetAlternateColor(clrBase);
1904 		}
1905 
SetAlternatingBgColors(ListView lv, Color clrAlternate, bool bAlternate)1906 		public static void SetAlternatingBgColors(ListView lv, Color clrAlternate,
1907 			bool bAlternate)
1908 		{
1909 			if(lv == null) throw new ArgumentNullException("lv");
1910 
1911 			Color clrBg = lv.BackColor;
1912 
1913 			if(!UIUtil.GetGroupsEnabled(lv) || !bAlternate)
1914 			{
1915 				for(int i = 0; i < lv.Items.Count; ++i)
1916 				{
1917 					ListViewItem lvi = lv.Items[i];
1918 					Debug.Assert(lvi.Index == i);
1919 					Debug.Assert(lvi.UseItemStyleForSubItems);
1920 
1921 					if(!bAlternate)
1922 					{
1923 						if(ColorsEqual(lvi.BackColor, clrAlternate))
1924 							lvi.BackColor = clrBg;
1925 					}
1926 					else if(((i & 1) == 0) && ColorsEqual(lvi.BackColor, clrAlternate))
1927 						lvi.BackColor = clrBg;
1928 					else if(((i & 1) == 1) && ColorsEqual(lvi.BackColor, clrBg))
1929 						lvi.BackColor = clrAlternate;
1930 				}
1931 			}
1932 			else // Groups && alternating
1933 			{
1934 				foreach(ListViewGroup lvg in lv.Groups)
1935 				{
1936 					// Within the group the items are not in display order,
1937 					// but the order can be derived from the item indices
1938 					List<ListViewItem> lItems = new List<ListViewItem>();
1939 					foreach(ListViewItem lviEnum in lvg.Items)
1940 						lItems.Add(lviEnum);
1941 					lItems.Sort(UIUtil.LviCompareByIndex);
1942 
1943 					for(int i = 0; i < lItems.Count; ++i)
1944 					{
1945 						ListViewItem lvi = lItems[i];
1946 						Debug.Assert(lvi.UseItemStyleForSubItems);
1947 
1948 						if(((i & 1) == 0) && ColorsEqual(lvi.BackColor, clrAlternate))
1949 							lvi.BackColor = clrBg;
1950 						else if(((i & 1) == 1) && ColorsEqual(lvi.BackColor, clrBg))
1951 							lvi.BackColor = clrAlternate;
1952 					}
1953 				}
1954 			}
1955 		}
1956 
LviCompareByIndex(ListViewItem a, ListViewItem b)1957 		private static int LviCompareByIndex(ListViewItem a, ListViewItem b)
1958 		{
1959 			return a.Index.CompareTo(b.Index);
1960 		}
1961 
SetSortIcon(ListView lv, int iColumn, SortOrder so)1962 		public static bool SetSortIcon(ListView lv, int iColumn, SortOrder so)
1963 		{
1964 			if(lv == null) { Debug.Assert(false); return false; }
1965 			if(NativeLib.IsUnix()) return false;
1966 
1967 			try
1968 			{
1969 				IntPtr hHeader = NativeMethods.SendMessage(lv.Handle,
1970 					NativeMethods.LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
1971 
1972 				bool bUnicode = (Marshal.SystemDefaultCharSize >= 2);
1973 				int nGetMsg = (bUnicode ? NativeMethods.HDM_GETITEMW :
1974 					NativeMethods.HDM_GETITEMA);
1975 				int nSetMsg = (bUnicode ? NativeMethods.HDM_SETITEMW :
1976 					NativeMethods.HDM_SETITEMA);
1977 
1978 				for(int i = 0; i < lv.Columns.Count; ++i)
1979 				{
1980 					IntPtr pColIndex = new IntPtr(i);
1981 
1982 					NativeMethods.HDITEM hdItem = new NativeMethods.HDITEM();
1983 					hdItem.mask = NativeMethods.HDI_FORMAT;
1984 
1985 					if(NativeMethods.SendMessageHDItem(hHeader, nGetMsg, pColIndex,
1986 						ref hdItem) == IntPtr.Zero) { Debug.Assert(false); }
1987 
1988 					if((i != iColumn) || (so == SortOrder.None))
1989 						hdItem.fmt &= (~NativeMethods.HDF_SORTUP &
1990 							~NativeMethods.HDF_SORTDOWN);
1991 					else
1992 					{
1993 						if(so == SortOrder.Ascending)
1994 						{
1995 							hdItem.fmt &= ~NativeMethods.HDF_SORTDOWN;
1996 							hdItem.fmt |= NativeMethods.HDF_SORTUP;
1997 						}
1998 						else // SortOrder.Descending
1999 						{
2000 							hdItem.fmt &= ~NativeMethods.HDF_SORTUP;
2001 							hdItem.fmt |= NativeMethods.HDF_SORTDOWN;
2002 						}
2003 					}
2004 
2005 					Debug.Assert(hdItem.mask == NativeMethods.HDI_FORMAT);
2006 					if(NativeMethods.SendMessageHDItem(hHeader, nSetMsg, pColIndex,
2007 						ref hdItem) == IntPtr.Zero) { Debug.Assert(false); }
2008 				}
2009 			}
2010 			catch(Exception) { Debug.Assert(false); return false; }
2011 
2012 			return true;
2013 		}
2014 
SetDisplayIndices(ListView lv, int[] v)2015 		public static void SetDisplayIndices(ListView lv, int[] v)
2016 		{
2017 			// Display indices must be assigned in an ordered way (with
2018 			// respect to the display indices, not the column indices),
2019 			// otherwise .NET's automatic adjustments result in
2020 			// different display indices;
2021 			// https://sourceforge.net/p/keepass/discussion/329221/thread/5e00cffe/
2022 
2023 			if(lv == null) { Debug.Assert(false); return; }
2024 			if(v == null) { Debug.Assert(false); return; }
2025 
2026 			int nCols = lv.Columns.Count;
2027 			int nMin = Math.Min(nCols, v.Length);
2028 
2029 			SortedDictionary<int, int> d = new SortedDictionary<int, int>();
2030 			for(int i = 0; i < nMin; ++i)
2031 			{
2032 				int nIdx = v[i];
2033 				if((nIdx >= 0) && (nIdx < nCols)) d[nIdx] = i;
2034 			}
2035 
2036 			foreach(KeyValuePair<int, int> kvp in d)
2037 				lv.Columns[kvp.Value].DisplayIndex = kvp.Key;
2038 
2039 #if DEBUG
2040 			int[] vNew = new int[nMin];
2041 			for(int i = 0; i < nMin; ++i)
2042 				vNew[i] = lv.Columns[i].DisplayIndex;
2043 			Debug.Assert(StrUtil.SerializeIntArray(vNew) ==
2044 				StrUtil.SerializeIntArray(MemUtil.Mid(v, 0, nMin)));
2045 #endif
2046 		}
2047 
ColorToGrayscale(Color clr)2048 		public static Color ColorToGrayscale(Color clr)
2049 		{
2050 			int l = (int)((0.3f * clr.R) + (0.59f * clr.G) + (0.11f * clr.B));
2051 			if(l >= 256) l = 255;
2052 			return Color.FromArgb(l, l, l);
2053 		}
2054 
ColorTowards(Color clr, Color clrBase, double dblFactor)2055 		public static Color ColorTowards(Color clr, Color clrBase, double dblFactor)
2056 		{
2057 			int l = (int)((0.3f * clrBase.R) + (0.59f * clrBase.G) + (0.11f * clrBase.B));
2058 
2059 			if(l < 128) return DarkenColor(clr, dblFactor);
2060 			return LightenColor(clr, dblFactor);
2061 		}
2062 
ColorTowardsGrayscale(Color clr, Color clrBase, double dblFactor)2063 		public static Color ColorTowardsGrayscale(Color clr, Color clrBase, double dblFactor)
2064 		{
2065 			return ColorToGrayscale(ColorTowards(clr, clrBase, dblFactor));
2066 		}
2067 
IsDarkColor(Color clr)2068 		public static bool IsDarkColor(Color clr)
2069 		{
2070 			Color clrLvl = ColorToGrayscale(clr);
2071 			return (clrLvl.R < 128);
2072 		}
2073 
ColorMiddle(Color clrA, Color clrB)2074 		public static Color ColorMiddle(Color clrA, Color clrB)
2075 		{
2076 			return Color.FromArgb(((int)clrA.A + (int)clrB.A) / 2,
2077 				((int)clrA.R + (int)clrB.R) / 2,
2078 				((int)clrA.G + (int)clrB.G) / 2,
2079 				((int)clrA.B + (int)clrB.B) / 2);
2080 		}
2081 
CreateRoundedRectangle(int x, int y, int dx, int dy, int r)2082 		public static GraphicsPath CreateRoundedRectangle(int x, int y, int dx, int dy,
2083 			int r)
2084 		{
2085 			try
2086 			{
2087 				GraphicsPath gp = new GraphicsPath();
2088 
2089 				gp.AddLine(x + r, y, x + dx - (r * 2), y);
2090 				gp.AddArc(x + dx - (r * 2), y, r * 2, r * 2, 270.0f, 90.0f);
2091 				gp.AddLine(x + dx, y + r, x + dx, y + dy - (r * 2));
2092 				gp.AddArc(x + dx - (r * 2), y + dy - (r * 2), r * 2, r * 2, 0.0f, 90.0f);
2093 				gp.AddLine(x + dx - (r * 2), y + dy, x + r, y + dy);
2094 				gp.AddArc(x, y + dy - (r * 2), r * 2, r * 2, 90.0f, 90.0f);
2095 				gp.AddLine(x, y + dy - (r * 2), x, y + r);
2096 				gp.AddArc(x, y, r * 2, r * 2, 180.0f, 90.0f);
2097 
2098 				gp.CloseFigure();
2099 				return gp;
2100 			}
2101 			catch(Exception) { Debug.Assert(false); }
2102 
2103 			return null;
2104 		}
2105 
GetActiveControl(ContainerControl cc)2106 		public static Control GetActiveControl(ContainerControl cc)
2107 		{
2108 			if(cc == null) { Debug.Assert(false); return null; }
2109 
2110 			try
2111 			{
2112 				Control c = cc.ActiveControl;
2113 				if(c == cc) return c;
2114 
2115 				ContainerControl ccSub = (c as ContainerControl);
2116 				if(ccSub != null) return GetActiveControl(ccSub);
2117 				return c;
2118 			}
2119 			catch(Exception) { Debug.Assert(false); }
2120 
2121 			return null;
2122 		}
2123 
ApplyKeyUIFlags(ulong aceUIFlags, CheckBox cbPassword, CheckBox cbKeyFile, CheckBox cbUserAccount, CheckBox cbHidePassword)2124 		public static void ApplyKeyUIFlags(ulong aceUIFlags, CheckBox cbPassword,
2125 			CheckBox cbKeyFile, CheckBox cbUserAccount, CheckBox cbHidePassword)
2126 		{
2127 			if((aceUIFlags & (ulong)AceKeyUIFlags.EnablePassword) != 0)
2128 				UIUtil.SetEnabled(cbPassword, true);
2129 			if((aceUIFlags & (ulong)AceKeyUIFlags.EnableKeyFile) != 0)
2130 				UIUtil.SetEnabled(cbKeyFile, true);
2131 			if((aceUIFlags & (ulong)AceKeyUIFlags.EnableUserAccount) != 0)
2132 				UIUtil.SetEnabled(cbUserAccount, true);
2133 			if((aceUIFlags & (ulong)AceKeyUIFlags.EnableHidePassword) != 0)
2134 				UIUtil.SetEnabled(cbHidePassword, true);
2135 
2136 			if((aceUIFlags & (ulong)AceKeyUIFlags.CheckPassword) != 0)
2137 				UIUtil.SetChecked(cbPassword, true);
2138 			if((aceUIFlags & (ulong)AceKeyUIFlags.CheckKeyFile) != 0)
2139 				UIUtil.SetChecked(cbKeyFile, true);
2140 			if((aceUIFlags & (ulong)AceKeyUIFlags.CheckUserAccount) != 0)
2141 				UIUtil.SetChecked(cbUserAccount, true);
2142 			if((aceUIFlags & (ulong)AceKeyUIFlags.CheckHidePassword) != 0)
2143 				UIUtil.SetChecked(cbHidePassword, true);
2144 
2145 			if((aceUIFlags & (ulong)AceKeyUIFlags.UncheckPassword) != 0)
2146 				UIUtil.SetChecked(cbPassword, false);
2147 			if((aceUIFlags & (ulong)AceKeyUIFlags.UncheckKeyFile) != 0)
2148 				UIUtil.SetChecked(cbKeyFile, false);
2149 			if((aceUIFlags & (ulong)AceKeyUIFlags.UncheckUserAccount) != 0)
2150 				UIUtil.SetChecked(cbUserAccount, false);
2151 			if((aceUIFlags & (ulong)AceKeyUIFlags.UncheckHidePassword) != 0)
2152 				UIUtil.SetChecked(cbHidePassword, false);
2153 
2154 			if((aceUIFlags & (ulong)AceKeyUIFlags.DisablePassword) != 0)
2155 				UIUtil.SetEnabled(cbPassword, false);
2156 			if((aceUIFlags & (ulong)AceKeyUIFlags.DisableKeyFile) != 0)
2157 				UIUtil.SetEnabled(cbKeyFile, false);
2158 			if((aceUIFlags & (ulong)AceKeyUIFlags.DisableUserAccount) != 0)
2159 				UIUtil.SetEnabled(cbUserAccount, false);
2160 			if((aceUIFlags & (ulong)AceKeyUIFlags.DisableHidePassword) != 0)
2161 				UIUtil.SetEnabled(cbHidePassword, false);
2162 		}
2163 
GetGroupsEnabled(ListView lv)2164 		public static bool GetGroupsEnabled(ListView lv)
2165 		{
2166 			if(lv == null) { Debug.Assert(false); return false; }
2167 
2168 			// Corresponds almost with the internal GroupsEnabled property
2169 			return (lv.ShowGroups && (lv.Groups.Count > 0) && !lv.VirtualMode);
2170 		}
2171 
GetMaxVisibleItemCount(ListView lv)2172 		public static int GetMaxVisibleItemCount(ListView lv)
2173 		{
2174 			if(lv == null) { Debug.Assert(false); return 0; }
2175 
2176 			int hClient = lv.ClientSize.Height;
2177 			int hHeader = NativeMethods.GetHeaderHeight(lv);
2178 			if(hHeader > 0)
2179 			{
2180 				if(((lv.Height - hClient) < hHeader) && (hClient > hHeader))
2181 					hClient -= hHeader;
2182 			}
2183 
2184 			int dy = lv.Items[0].Bounds.Height;
2185 			if(dy <= 1) { Debug.Assert(false); dy = DpiUtil.ScaleIntY(16) + 1; }
2186 
2187 			return (hClient / dy);
2188 		}
2189 
GetTopVisibleItem(ListView lv)2190 		public static int GetTopVisibleItem(ListView lv)
2191 		{
2192 			if(lv == null) { Debug.Assert(false); return -1; }
2193 
2194 			// The returned value must be an existing index or -1
2195 			int nRes = -1;
2196 			try
2197 			{
2198 				if(lv.Items.Count == 0) return nRes;
2199 
2200 				ListViewItem lvi = null;
2201 				if(!UIUtil.GetGroupsEnabled(lv)) lvi = lv.TopItem;
2202 				else
2203 				{
2204 					// In grouped mode, the TopItem property does not work;
2205 					// https://connect.microsoft.com/VisualStudio/feedback/details/642188/listview-control-bug-topitem-property-doesnt-work-with-groups
2206 					// https://msdn.microsoft.com/en-us/library/windows/desktop/bb761087.aspx
2207 
2208 					int dyHeader = NativeMethods.GetHeaderHeight(lv);
2209 
2210 					int yMin = int.MaxValue;
2211 					foreach(ListViewItem lviEnum in lv.Items)
2212 					{
2213 						int yEnum = Math.Abs(lviEnum.Position.Y - dyHeader);
2214 						if(yEnum < yMin)
2215 						{
2216 							yMin = yEnum;
2217 							lvi = lviEnum;
2218 						}
2219 					}
2220 				}
2221 
2222 				if(lvi != null) nRes = lvi.Index;
2223 			}
2224 			catch(Exception) { Debug.Assert(false); }
2225 
2226 			return nRes;
2227 		}
2228 
SetTopVisibleItem(ListView lv, int iIndex)2229 		public static void SetTopVisibleItem(ListView lv, int iIndex)
2230 		{
2231 			SetTopVisibleItem(lv, iIndex, false);
2232 		}
2233 
SetTopVisibleItem(ListView lv, int iIndex, bool bEnsureSelectedVisible)2234 		public static void SetTopVisibleItem(ListView lv, int iIndex,
2235 			bool bEnsureSelectedVisible)
2236 		{
2237 			if(lv == null) { Debug.Assert(false); return; }
2238 			if(iIndex < 0) return; // No assert
2239 
2240 			int n = lv.Items.Count;
2241 			if(n <= 0) return;
2242 
2243 			if(iIndex >= n) iIndex = n - 1; // No assert
2244 
2245 			int iOrgTop = GetTopVisibleItem(lv);
2246 
2247 			lv.BeginUpdate(); // Might reduce flicker
2248 
2249 			if(iIndex != iOrgTop) // Prevent unnecessary flicker
2250 			{
2251 				// Setting lv.TopItem doesn't work properly
2252 
2253 				// lv.EnsureVisible(n - 1);
2254 				// if(iIndex != (n - 1)) lv.EnsureVisible(iIndex);
2255 
2256 #if DEBUG
2257 				foreach(ListViewItem lvi in lv.Items)
2258 				{
2259 					Debug.Assert(lvi.Bounds.Height == lv.Items[0].Bounds.Height);
2260 				}
2261 #endif
2262 
2263 				if(iIndex < iOrgTop)
2264 					lv.EnsureVisible(iIndex);
2265 				else
2266 				{
2267 					int nVisItems = GetMaxVisibleItemCount(lv);
2268 
2269 					int iNewLast = nVisItems + iIndex - 1;
2270 					if(iNewLast < 0) { Debug.Assert(false); iNewLast = 0; }
2271 					iNewLast = Math.Min(iNewLast, n - 1);
2272 
2273 					lv.EnsureVisible(iNewLast);
2274 
2275 					int iNewTop = GetTopVisibleItem(lv);
2276 					if(iNewTop > iIndex) // Scrolled too far
2277 					{
2278 						Debug.Assert(false);
2279 						lv.EnsureVisible(iIndex); // Scroll back
2280 					}
2281 					else if(iNewTop == iIndex) { } // Perfect
2282 					else { Debug.Assert(iNewLast == (n - 1)); }
2283 
2284 					// int hItem = lv.Items[0].Bounds.Height;
2285 					// if(hItem <= 0) { Debug.Assert(false); hItem = DpiUtil.ScaleIntY(16) + 1; }
2286 					// int hToScroll = (iIndex - iOrgTop) * hItem;
2287 					// NativeMethods.Scroll(lv, 0, hToScroll);
2288 					// int iNewTop = GetTopVisibleItem(lv);
2289 					// if(iNewTop > iIndex) // Scrolled too far
2290 					// {
2291 					//	Debug.Assert(false);
2292 					//	lv.EnsureVisible(iIndex); // Scroll back
2293 					// }
2294 					// else if(iNewTop == iIndex) { } // Perfect
2295 					// else
2296 					// {
2297 					//	lv.EnsureVisible(n - 1);
2298 					//	if(iIndex != (n - 1)) lv.EnsureVisible(iIndex);
2299 					// }
2300 				}
2301 			}
2302 
2303 			if(bEnsureSelectedVisible)
2304 			{
2305 				ListView.SelectedIndexCollection lvsic = lv.SelectedIndices;
2306 				int nSel = lvsic.Count;
2307 				if(nSel > 0)
2308 				{
2309 					int[] vSel = new int[nSel];
2310 					lvsic.CopyTo(vSel, 0);
2311 
2312 					lv.EnsureVisible(vSel[nSel - 1]);
2313 					if(nSel >= 2) lv.EnsureVisible(vSel[0]);
2314 				}
2315 			}
2316 
2317 			lv.EndUpdate();
2318 		}
2319 
GetScrollInfo(ListView lv, bool bForRestoreOnly)2320 		public static UIScrollInfo GetScrollInfo(ListView lv,
2321 			bool bForRestoreOnly)
2322 		{
2323 			if(lv == null) { Debug.Assert(false); return null; }
2324 
2325 			int scrY = NativeMethods.GetScrollPosY(lv.Handle);
2326 			int idxTop = GetTopVisibleItem(lv);
2327 
2328 			// Fix index-based scroll position
2329 			if((scrY == idxTop) && (idxTop > 0))
2330 			{
2331 				// Groups imply pixel position
2332 				Debug.Assert(!UIUtil.GetGroupsEnabled(lv));
2333 
2334 				if(!bForRestoreOnly)
2335 				{
2336 					int hSum = 0;
2337 					foreach(ListViewItem lvi in lv.Items)
2338 					{
2339 						if(scrY <= 0) break;
2340 
2341 						int hItem = lvi.Bounds.Height;
2342 						if(hItem > 1) hSum += hItem;
2343 						else { Debug.Assert(false); }
2344 
2345 						--scrY;
2346 					}
2347 
2348 					scrY = hSum;
2349 				}
2350 				else scrY = 0; // Pixels not required for restoration
2351 			}
2352 
2353 			return new UIScrollInfo(0, scrY, idxTop);
2354 		}
2355 
Scroll(ListView lv, UIScrollInfo s, bool bEnsureSelectedVisible)2356 		public static void Scroll(ListView lv, UIScrollInfo s,
2357 			bool bEnsureSelectedVisible)
2358 		{
2359 			if(lv == null) { Debug.Assert(false); return; }
2360 			if(s == null) { Debug.Assert(false); return; }
2361 
2362 			if(UIUtil.GetGroupsEnabled(lv) && !NativeLib.IsUnix())
2363 			{
2364 				// Only works correctly when groups are present
2365 				// (lv.ShowGroups is not sufficient)
2366 				NativeMethods.Scroll(lv, s.ScrollX, s.ScrollY);
2367 
2368 				if(bEnsureSelectedVisible)
2369 				{
2370 					Debug.Assert(false); // Unsupported mode combination
2371 					SetTopVisibleItem(lv, GetTopVisibleItem(lv), true);
2372 				}
2373 			}
2374 			else SetTopVisibleItem(lv, s.TopIndex, bEnsureSelectedVisible);
2375 		}
2376 
2377 		/// <summary>
2378 		/// Test whether a screen area is at least partially visible.
2379 		/// </summary>
2380 		/// <param name="rect">Area to test.</param>
2381 		/// <returns>Returns <c>true</c>, if the area is at least partially
2382 		/// visible. Otherwise, <c>false</c> is returned.</returns>
IsScreenAreaVisible(Rectangle rect)2383 		public static bool IsScreenAreaVisible(Rectangle rect)
2384 		{
2385 			try
2386 			{
2387 				foreach(Screen scr in Screen.AllScreens)
2388 				{
2389 					Rectangle scrBounds = scr.Bounds;
2390 
2391 					if((rect.Left > scrBounds.Right) || (rect.Right < scrBounds.Left) ||
2392 						(rect.Top > scrBounds.Bottom) || (rect.Bottom < scrBounds.Top)) { }
2393 					else return true;
2394 				}
2395 			}
2396 			catch(Exception) { Debug.Assert(false); return true; }
2397 
2398 			return false;
2399 		}
2400 
EnsureInsideScreen(Form f)2401 		public static void EnsureInsideScreen(Form f)
2402 		{
2403 			if(f == null) { Debug.Assert(false); return; }
2404 
2405 			try
2406 			{
2407 				if(!f.Visible) return; // No assert
2408 				if(f.WindowState != FormWindowState.Normal) return;
2409 
2410 				int x = f.Location.X;
2411 				int y = f.Location.Y;
2412 				int w = f.Size.Width;
2413 				int h = f.Size.Height;
2414 
2415 				Debug.Assert((x != -32000) && (x != -64000));
2416 				Debug.Assert(x != AppDefs.InvalidWindowValue);
2417 				Debug.Assert((y != -32000) && (y != -64000));
2418 				Debug.Assert(y != AppDefs.InvalidWindowValue);
2419 				Debug.Assert(w != AppDefs.InvalidWindowValue);
2420 				Debug.Assert(h != AppDefs.InvalidWindowValue);
2421 
2422 				Rectangle rect = new Rectangle(x, y, w, h);
2423 				if(IsScreenAreaVisible(rect)) return;
2424 
2425 				Screen scr = Screen.PrimaryScreen;
2426 				Rectangle rectScr = scr.Bounds;
2427 				BoundsSpecified bs = BoundsSpecified.Location;
2428 
2429 				if((w > rectScr.Width) || (h > rectScr.Height))
2430 				{
2431 					w = Math.Min(w, rectScr.Width);
2432 					h = Math.Min(h, rectScr.Height);
2433 					bs |= BoundsSpecified.Size;
2434 				}
2435 
2436 				x = rectScr.X + ((rectScr.Width - w) / 2);
2437 				y = rectScr.Y + ((rectScr.Height - h) / 2);
2438 
2439 				f.SetBounds(x, y, w, h, bs);
2440 			}
2441 			catch(Exception) { Debug.Assert(false); }
2442 		}
2443 
GetWindowScreenRect(Form f)2444 		public static string GetWindowScreenRect(Form f)
2445 		{
2446 			if(f == null) { Debug.Assert(false); return string.Empty; }
2447 
2448 			List<int> l = new List<int>();
2449 
2450 			Point pt = f.Location;
2451 			l.Add(pt.X);
2452 			l.Add(pt.Y);
2453 
2454 			FormBorderStyle s = f.FormBorderStyle;
2455 			if((s == FormBorderStyle.Sizable) || (s == FormBorderStyle.SizableToolWindow))
2456 			{
2457 				Size sz = f.Size;
2458 				l.Add(sz.Width);
2459 				l.Add(sz.Height);
2460 
2461 				if(f.WindowState == FormWindowState.Maximized) l.Add(FwsMaximized);
2462 			}
2463 
2464 			return StrUtil.SerializeIntArray(l.ToArray());
2465 		}
2466 
SetWindowScreenRect(Form f, string strRect)2467 		public static void SetWindowScreenRect(Form f, string strRect)
2468 		{
2469 			if((f == null) || (strRect == null)) { Debug.Assert(false); return; }
2470 
2471 			try
2472 			{
2473 				// Backward compatibility (", " as separator)
2474 				Debug.Assert(StrUtil.SerializeIntArray(new int[] {
2475 					12, 34, 56 }) == "12 34 56"); // Should not use ','
2476 				string str = strRect.Replace(",", string.Empty);
2477 				if(str.Length == 0) return; // No assert
2478 
2479 				int[] v = StrUtil.DeserializeIntArray(str);
2480 				if((v == null) || (v.Length < 2)) { Debug.Assert(false); return; }
2481 
2482 				FormBorderStyle s = f.FormBorderStyle;
2483 				bool bSizable = ((s == FormBorderStyle.Sizable) ||
2484 					(s == FormBorderStyle.SizableToolWindow));
2485 
2486 				int ws = ((v.Length <= 4) ? FwsNormal : v[4]);
2487 				if(ws == FwsMaximized)
2488 				{
2489 					if(bSizable && f.MaximizeBox)
2490 						f.WindowState = FormWindowState.Maximized;
2491 					else { Debug.Assert(false); }
2492 
2493 					return; // Ignore the saved size; restore to default
2494 				}
2495 				else if(ws != FwsNormal) { Debug.Assert(false); return; }
2496 
2497 				bool bSize = ((v.Length >= 4) && (v[2] > 0) && (v[3] > 0) && bSizable);
2498 
2499 				Rectangle rect = new Rectangle();
2500 				rect.X = v[0];
2501 				rect.Y = v[1];
2502 				if(bSize) rect.Size = new Size(v[2], v[3]);
2503 				else rect.Size = f.Size;
2504 
2505 				if(UIUtil.IsScreenAreaVisible(rect))
2506 				{
2507 					f.Location = rect.Location;
2508 					if(bSize) f.Size = rect.Size;
2509 				}
2510 			}
2511 			catch(Exception) { Debug.Assert(false); }
2512 		}
2513 
SetWindowScreenRectEx(Form f, string strRect)2514 		public static string SetWindowScreenRectEx(Form f, string strRect)
2515 		{
2516 			SetWindowScreenRect(f, strRect);
2517 			return GetWindowScreenRect(f);
2518 		}
2519 
ScaleWindowScreenRect(string strRect, double sX, double sY)2520 		internal static string ScaleWindowScreenRect(string strRect, double sX, double sY)
2521 		{
2522 			if(string.IsNullOrEmpty(strRect)) return strRect;
2523 
2524 			try
2525 			{
2526 				string str = strRect.Replace(",", string.Empty); // Backward compat.
2527 
2528 				int[] v = StrUtil.DeserializeIntArray(str);
2529 				if((v == null) || (v.Length < 2)) { Debug.Assert(false); return strRect; }
2530 
2531 				v[0] = (int)Math.Round((double)v[0] * sX); // X
2532 				v[1] = (int)Math.Round((double)v[1] * sY); // Y
2533 
2534 				if(v.Length >= 4)
2535 				{
2536 					v[2] = (int)Math.Round((double)v[2] * sX); // Width
2537 					v[3] = (int)Math.Round((double)v[3] * sY); // Height
2538 				}
2539 
2540 				return StrUtil.SerializeIntArray(v);
2541 			}
2542 			catch(Exception) { Debug.Assert(false); }
2543 
2544 			return strRect;
2545 		}
2546 
GetColumnWidths(ListView lv)2547 		public static string GetColumnWidths(ListView lv)
2548 		{
2549 			if(lv == null) { Debug.Assert(false); return string.Empty; }
2550 
2551 			int n = lv.Columns.Count;
2552 			int[] vSizes = new int[n];
2553 			for(int i = 0; i < n; ++i) vSizes[i] = lv.Columns[i].Width;
2554 
2555 			return StrUtil.SerializeIntArray(vSizes);
2556 		}
2557 
SetColumnWidths(ListView lv, string strSizes)2558 		public static void SetColumnWidths(ListView lv, string strSizes)
2559 		{
2560 			if(string.IsNullOrEmpty(strSizes)) return; // No assert
2561 
2562 			int[] vSizes = StrUtil.DeserializeIntArray(strSizes);
2563 
2564 			int n = lv.Columns.Count;
2565 			Debug.Assert(n == vSizes.Length);
2566 
2567 			for(int i = 0; i < Math.Min(n, vSizes.Length); ++i)
2568 				lv.Columns[i].Width = vSizes[i];
2569 		}
2570 
SetButtonImage(Button btn, Image img, bool b16To15)2571 		public static Image SetButtonImage(Button btn, Image img, bool b16To15)
2572 		{
2573 			if(btn == null) { Debug.Assert(false); return null; }
2574 			if(img == null) { Debug.Assert(false); return null; }
2575 
2576 			Image imgNew = img;
2577 			if(b16To15 && (btn.Height == 23) && (imgNew.Height == 16))
2578 				imgNew = GfxUtil.ScaleImage(imgNew, imgNew.Width, 15,
2579 					ScaleTransformFlags.UIIcon);
2580 
2581 			// if(btn.RightToLeft == RightToLeft.Yes)
2582 			// {
2583 			//	// Dispose scaled image only
2584 			//	Image imgToDispose = ((imgNew != img) ? imgNew : null);
2585 			//	imgNew = (Image)imgNew.Clone();
2586 			//	imgNew.RotateFlip(RotateFlipType.RotateNoneFlipX);
2587 			//	if(imgToDispose != null) imgToDispose.Dispose();
2588 			// }
2589 
2590 			btn.Image = imgNew;
2591 			return imgNew;
2592 		}
2593 
OverwriteButtonImage(Button btn, ref Image imgCur, Image imgNew)2594 		public static void OverwriteButtonImage(Button btn, ref Image imgCur,
2595 			Image imgNew)
2596 		{
2597 			if(btn == null) { Debug.Assert(false); return; }
2598 			// imgNew may be null
2599 
2600 			Debug.Assert(object.ReferenceEquals(btn.Image, imgCur));
2601 
2602 			Image imgPrev = imgCur;
2603 
2604 			btn.Image = imgNew;
2605 			imgCur = imgNew;
2606 
2607 			if(imgPrev != null) imgPrev.Dispose();
2608 		}
2609 
DisposeButtonImage(Button btn, ref Image imgCur)2610 		public static void DisposeButtonImage(Button btn, ref Image imgCur)
2611 		{
2612 			if(btn == null) { Debug.Assert(false); return; }
2613 
2614 			Debug.Assert(object.ReferenceEquals(btn.Image, imgCur));
2615 
2616 			if(imgCur != null)
2617 			{
2618 				btn.Image = null;
2619 				imgCur.Dispose();
2620 				imgCur = null;
2621 			}
2622 		}
2623 
OverwriteIfNotEqual(ref Image imgCur, Image imgNew)2624 		internal static void OverwriteIfNotEqual(ref Image imgCur, Image imgNew)
2625 		{
2626 			if(object.ReferenceEquals(imgCur, imgNew)) return;
2627 
2628 			if(imgCur != null) imgCur.Dispose();
2629 			imgCur = imgNew;
2630 		}
2631 
EnableAutoCompletion(ComboBox cb, bool bAlsoAutoAppend)2632 		public static void EnableAutoCompletion(ComboBox cb, bool bAlsoAutoAppend)
2633 		{
2634 			if(cb == null) { Debug.Assert(false); return; }
2635 
2636 			Debug.Assert(cb.DropDownStyle != ComboBoxStyle.DropDownList);
2637 
2638 			cb.AutoCompleteMode = (bAlsoAutoAppend ? AutoCompleteMode.SuggestAppend :
2639 				AutoCompleteMode.Suggest);
2640 			cb.AutoCompleteSource = AutoCompleteSource.ListItems;
2641 		}
2642 
EnableAutoCompletion(ToolStripComboBox cb, bool bAlsoAutoAppend)2643 		public static void EnableAutoCompletion(ToolStripComboBox cb, bool bAlsoAutoAppend)
2644 		{
2645 			if(cb == null) { Debug.Assert(false); return; }
2646 
2647 			Debug.Assert(cb.DropDownStyle != ComboBoxStyle.DropDownList);
2648 
2649 			cb.AutoCompleteMode = (bAlsoAutoAppend ? AutoCompleteMode.SuggestAppend :
2650 				AutoCompleteMode.Suggest);
2651 			cb.AutoCompleteSource = AutoCompleteSource.ListItems;
2652 		}
2653 
EnableAutoCompletion(TextBox tb, bool bAlsoAutoAppend, string[] vItems)2654 		public static void EnableAutoCompletion(TextBox tb, bool bAlsoAutoAppend,
2655 			string[] vItems)
2656 		{
2657 			if((tb == null) || (vItems == null)) { Debug.Assert(false); return; }
2658 			if(vItems.Length == 0) return;
2659 
2660 			try
2661 			{
2662 				foreach(string str in vItems)
2663 				{
2664 					if(str == null) { Debug.Assert(false); return; }
2665 				}
2666 
2667 				// The system/framework sorts the auto-completion list
2668 
2669 				VoidDelegate f = delegate()
2670 				{
2671 					try
2672 					{
2673 						AutoCompleteStringCollection c = new AutoCompleteStringCollection();
2674 						c.AddRange(vItems);
2675 
2676 						tb.AutoCompleteCustomSource = c;
2677 						tb.AutoCompleteSource = AutoCompleteSource.CustomSource;
2678 						tb.AutoCompleteMode = (bAlsoAutoAppend ?
2679 							AutoCompleteMode.SuggestAppend : AutoCompleteMode.Suggest);
2680 					}
2681 					catch(Exception) { Debug.Assert(false); }
2682 				};
2683 
2684 				if(tb.InvokeRequired || MonoWorkarounds.IsRequired(373134))
2685 					tb.Invoke(f);
2686 				else f();
2687 			}
2688 			catch(Exception) { Debug.Assert(false); }
2689 		}
2690 
SetFocus(Control c, Form fParent)2691 		public static void SetFocus(Control c, Form fParent)
2692 		{
2693 			SetFocus(c, fParent, false);
2694 		}
2695 
SetFocus(Control c, Form fParent, bool bToForegroundAndFocus)2696 		public static void SetFocus(Control c, Form fParent, bool bToForegroundAndFocus)
2697 		{
2698 			if(c == null) { Debug.Assert(false); return; }
2699 			// fParent may be null
2700 
2701 			try
2702 			{
2703 				Debug.Assert(c.Visible && c.Enabled);
2704 				if(fParent != null) fParent.ActiveControl = c;
2705 			}
2706 			catch(Exception) { Debug.Assert(false); }
2707 
2708 			try
2709 			{
2710 				if(c.CanSelect) c.Select();
2711 
2712 				// https://sourceforge.net/p/keepass/discussion/329220/thread/045940bf/
2713 				// https://sourceforge.net/p/keepass/discussion/329220/thread/6834e222/
2714 				if(bToForegroundAndFocus && c.CanFocus) c.Focus();
2715 			}
2716 			catch(Exception) { Debug.Assert(false); }
2717 		}
2718 
ResetFocus(Control c, Form fParent)2719 		public static void ResetFocus(Control c, Form fParent)
2720 		{
2721 			ResetFocus(c, fParent, false);
2722 		}
2723 
ResetFocus(Control c, Form fParent, bool bToForegroundAndFocus)2724 		public static void ResetFocus(Control c, Form fParent, bool bToForegroundAndFocus)
2725 		{
2726 			if(c == null) { Debug.Assert(false); return; }
2727 			// fParent may be null
2728 
2729 			try
2730 			{
2731 				Control cPre = null;
2732 				if(fParent != null) cPre = GetActiveControl(fParent);
2733 
2734 				bool bStdSetFocus = true;
2735 				if(c == cPre)
2736 				{
2737 					// Special reset for password text boxes that
2738 					// can show a Caps Lock balloon tip;
2739 					// https://sourceforge.net/p/keepass/feature-requests/1905/
2740 					TextBox tb = (c as TextBox);
2741 					if((tb != null) && !NativeLib.IsUnix())
2742 					{
2743 						IntPtr h = tb.Handle;
2744 						bool bCapsLock = ((NativeMethods.GetKeyState(
2745 							NativeMethods.VK_CAPITAL) & 1) != 0);
2746 
2747 						if((h != IntPtr.Zero) && tb.UseSystemPasswordChar &&
2748 							!tb.ReadOnly && bCapsLock)
2749 						{
2750 							NativeMethods.SendMessage(h, NativeMethods.WM_KILLFOCUS,
2751 								IntPtr.Zero, IntPtr.Zero);
2752 							NativeMethods.SendMessage(h, NativeMethods.WM_SETFOCUS,
2753 								IntPtr.Zero, IntPtr.Zero);
2754 
2755 							bStdSetFocus = false;
2756 						}
2757 					}
2758 				}
2759 
2760 				if(bStdSetFocus) UIUtil.SetFocus(c, fParent, bToForegroundAndFocus);
2761 			}
2762 			catch(Exception) { Debug.Assert(false); }
2763 		}
2764 
2765 		/// <summary>
2766 		/// Show a modal dialog and destroy it afterwards.
2767 		/// </summary>
2768 		/// <param name="f">Form to show and destroy.</param>
2769 		/// <returns>Result from <c>ShowDialog</c>.</returns>
ShowDialogAndDestroy(Form f)2770 		public static DialogResult ShowDialogAndDestroy(Form f)
2771 		{
2772 			if(f == null) { Debug.Assert(false); return DialogResult.None; }
2773 
2774 			DialogResult dr = f.ShowDialog();
2775 			UIUtil.DestroyForm(f);
2776 			return dr;
2777 		}
2778 
ShowDialogAndDestroy(Form f, Form fParent)2779 		internal static DialogResult ShowDialogAndDestroy(Form f, Form fParent)
2780 		{
2781 			if(f == null) { Debug.Assert(false); return DialogResult.None; }
2782 			if(fParent == null) return ShowDialogAndDestroy(f);
2783 
2784 			DialogResult dr = f.ShowDialog(fParent);
2785 			UIUtil.DestroyForm(f);
2786 			return dr;
2787 		}
2788 
2789 		/// <summary>
2790 		/// Show a modal dialog. If the result isn't the specified value, the
2791 		/// dialog is disposed and <c>true</c> is returned. Otherwise, <c>false</c>
2792 		/// is returned (without disposing the dialog).
2793 		/// </summary>
2794 		/// <param name="f">Dialog to show.</param>
2795 		/// <param name="drNotValue">Comparison value.</param>
2796 		/// <returns>See description.</returns>
ShowDialogNotValue(Form f, DialogResult drNotValue)2797 		public static bool ShowDialogNotValue(Form f, DialogResult drNotValue)
2798 		{
2799 			if(f == null) { Debug.Assert(false); return true; }
2800 
2801 			if(f.ShowDialog() != drNotValue)
2802 			{
2803 				UIUtil.DestroyForm(f);
2804 				return true;
2805 			}
2806 
2807 			return false;
2808 		}
2809 
DestroyForm(Form f)2810 		public static void DestroyForm(Form f)
2811 		{
2812 			if(f == null) { Debug.Assert(false); return; }
2813 
2814 			try
2815 			{
2816 				// f.Close(); // Don't trigger closing events
2817 				f.Dispose();
2818 			}
2819 			catch(Exception) { Debug.Assert(false); }
2820 		}
2821 
ExtractVistaIcon(Icon ico)2822 		public static Image ExtractVistaIcon(Icon ico)
2823 		{
2824 			if(ico == null) { Debug.Assert(false); return null; }
2825 
2826 			MemoryStream ms = new MemoryStream();
2827 			try
2828 			{
2829 				ico.Save(ms);
2830 				byte[] pb = ms.ToArray();
2831 
2832 				return GfxUtil.LoadImage(pb); // Extracts best image from ICO
2833 			}
2834 			catch { Debug.Assert(false); }
2835 			finally { ms.Close(); }
2836 
2837 			return null;
2838 		}
2839 
ColorToHsv(Color clr, out float fHue, out float fSaturation, out float fValue)2840 		public static void ColorToHsv(Color clr, out float fHue,
2841 			out float fSaturation, out float fValue)
2842 		{
2843 			int nMax = Math.Max(clr.R, Math.Max(clr.G, clr.B));
2844 			int nMin = Math.Min(clr.R, Math.Min(clr.G, clr.B));
2845 
2846 			fHue = clr.GetHue(); // In degrees
2847 			fSaturation = ((nMax == 0) ? 0.0f : (1.0f - ((float)nMin / nMax)));
2848 			fValue = (float)nMax / 255.0f;
2849 		}
2850 
ColorFromHsv(float fHue, float fSaturation, float fValue)2851 		public static Color ColorFromHsv(float fHue, float fSaturation, float fValue)
2852 		{
2853 			float d = fHue / 60.0f;
2854 			float fl = (float)Math.Floor(d);
2855 			float f = d - fl;
2856 
2857 			fValue *= 255.0f;
2858 			int v = (int)fValue;
2859 			int p = (int)(fValue * (1.0f - fSaturation));
2860 			int q = (int)(fValue * (1.0f - (fSaturation * f)));
2861 			int t = (int)(fValue * (1.0f - (fSaturation * (1.0f - f))));
2862 
2863 			try
2864 			{
2865 				int hi = (int)fl % 6;
2866 				if(hi == 0) return Color.FromArgb(255, v, t, p);
2867 				if(hi == 1) return Color.FromArgb(255, q, v, p);
2868 				if(hi == 2) return Color.FromArgb(255, p, v, t);
2869 				if(hi == 3) return Color.FromArgb(255, p, q, v);
2870 				if(hi == 4) return Color.FromArgb(255, t, p, v);
2871 
2872 				return Color.FromArgb(255, v, p, q);
2873 			}
2874 			catch(Exception) { Debug.Assert(false); }
2875 
2876 			return Color.Empty;
2877 		}
2878 
IconToBitmap(Icon ico, int w, int h)2879 		public static Bitmap IconToBitmap(Icon ico, int w, int h)
2880 		{
2881 			if(ico == null) { Debug.Assert(false); return null; }
2882 
2883 			if(w < 0) w = ico.Width;
2884 			if(h < 0) h = ico.Height;
2885 
2886 			Bitmap bmpR = null;
2887 			Icon icoToDispose = null;
2888 			try
2889 			{
2890 				Icon icoBest = ico;
2891 				if((ico.Width != w) || (ico.Height != h))
2892 				{
2893 					icoBest = new Icon(ico, w, h);
2894 					icoToDispose = icoBest;
2895 				}
2896 
2897 				Bitmap bmp = icoBest.ToBitmap();
2898 				if((bmp.Width == w) && (bmp.Height == h))
2899 					bmpR = bmp;
2900 				else
2901 				{
2902 					Image imgSc = GfxUtil.ScaleImage(bmp, w, h, ScaleTransformFlags.UIIcon);
2903 					if(imgSc != null)
2904 					{
2905 						bmpR = (imgSc as Bitmap);
2906 						if(bmpR == null)
2907 						{
2908 							Debug.Assert(false); // Should be a Bitmap for performance
2909 							bmpR = new Bitmap(imgSc);
2910 							imgSc.Dispose();
2911 						}
2912 					}
2913 					else { Debug.Assert(false); }
2914 
2915 					bmp.Dispose();
2916 				}
2917 			}
2918 			catch(Exception) { Debug.Assert(false); }
2919 			finally { if(icoToDispose != null) icoToDispose.Dispose(); }
2920 
2921 			if(bmpR == null)
2922 			{
2923 				bmpR = new Bitmap(w, h, PixelFormat.Format32bppArgb);
2924 				using(Graphics g = Graphics.FromImage(bmpR))
2925 				{
2926 					g.Clear(Color.Transparent);
2927 					GfxUtil.SetHighQuality(g);
2928 					g.DrawIcon(ico, new Rectangle(0, 0, w, h));
2929 				}
2930 			}
2931 
2932 			return bmpR;
2933 		}
2934 
BitmapToIcon(Bitmap bmp)2935 		public static Icon BitmapToIcon(Bitmap bmp)
2936 		{
2937 			if(bmp == null) { Debug.Assert(false); return null; }
2938 
2939 			Icon ico = null;
2940 			IntPtr hIcon = IntPtr.Zero;
2941 			try
2942 			{
2943 				hIcon = bmp.GetHicon();
2944 				using(Icon icoNoOwn = Icon.FromHandle(hIcon))
2945 				{
2946 					ico = (Icon)icoNoOwn.Clone();
2947 				}
2948 			}
2949 			catch(Exception) { Debug.Assert(false); }
2950 			finally
2951 			{
2952 				if(hIcon != IntPtr.Zero)
2953 				{
2954 					try { NativeMethods.DestroyIcon(hIcon); }
2955 					catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
2956 				}
2957 			}
2958 
2959 			return ico;
2960 		}
2961 
CloneWithColorMatrix(Image img, ColorMatrix cm)2962 		private static Bitmap CloneWithColorMatrix(Image img, ColorMatrix cm)
2963 		{
2964 			if(img == null) { Debug.Assert(false); return null; }
2965 			if(cm == null) { Debug.Assert(false); return null; }
2966 
2967 			try
2968 			{
2969 				int w = img.Width, h = img.Height;
2970 
2971 				Bitmap bmp = new Bitmap(w, h, PixelFormat.Format32bppArgb);
2972 				using(Graphics g = Graphics.FromImage(bmp))
2973 				{
2974 					g.Clear(Color.Transparent);
2975 
2976 					g.InterpolationMode = InterpolationMode.NearestNeighbor;
2977 					g.SmoothingMode = SmoothingMode.None;
2978 
2979 					ImageAttributes a = new ImageAttributes();
2980 					a.SetColorMatrix(cm);
2981 
2982 					g.DrawImage(img, new Rectangle(0, 0, w, h), 0, 0, w, h,
2983 						GraphicsUnit.Pixel, a);
2984 				}
2985 
2986 				return bmp;
2987 			}
2988 			catch(Exception) { Debug.Assert(false); }
2989 
2990 			return null;
2991 		}
2992 
InvertImage(Image img)2993 		public static Bitmap InvertImage(Image img)
2994 		{
2995 			ColorMatrix cm = new ColorMatrix(new float[][] {
2996 				new float[] { -1, 0, 0, 0, 0 },
2997 				new float[] { 0, -1, 0, 0, 0 },
2998 				new float[] { 0, 0, -1, 0, 0 },
2999 				new float[] { 0, 0, 0, 1, 0 },
3000 				new float[] { 1, 1, 1, 0, 1 }
3001 			});
3002 
3003 			return CloneWithColorMatrix(img, cm);
3004 		}
3005 
CreateGrayImage(Image img)3006 		public static Bitmap CreateGrayImage(Image img)
3007 		{
3008 			ColorMatrix cm = new ColorMatrix(new float[][] {
3009 				new float[] { 0.30f, 0.30f, 0.30f, 0, 0 },
3010 				new float[] { 0.59f, 0.59f, 0.59f, 0, 0 },
3011 				new float[] { 0.11f, 0.11f, 0.11f, 0, 0 },
3012 				new float[] { 0, 0, 0, 1, 0 },
3013 				new float[] { 0, 0, 0, 0, 1 }
3014 			});
3015 
3016 			return CloneWithColorMatrix(img, cm);
3017 		}
3018 
CreateTabColorImage(Color clr, TabControl cTab)3019 		public static Image CreateTabColorImage(Color clr, TabControl cTab)
3020 		{
3021 			if(cTab == null) { Debug.Assert(false); return null; }
3022 
3023 			int qSize = cTab.ItemSize.Height - 3;
3024 			if(MonoWorkarounds.IsRequired()) qSize -= 1;
3025 			if(qSize < 4) { Debug.Assert(false); return null; }
3026 
3027 			const int dyTrans = 3;
3028 			int yCenter = (qSize - dyTrans) / 2 + dyTrans;
3029 			Rectangle rectTop = new Rectangle(0, dyTrans, qSize, yCenter - dyTrans);
3030 			Rectangle rectBottom = new Rectangle(0, yCenter, qSize, qSize - yCenter);
3031 
3032 			Color clrLight = UIUtil.LightenColor(clr, 0.5);
3033 			Color clrDark = UIUtil.DarkenColor(clr, 0.1);
3034 
3035 			Bitmap bmp = new Bitmap(qSize, qSize, PixelFormat.Format32bppArgb);
3036 			using(Graphics g = Graphics.FromImage(bmp))
3037 			{
3038 				g.Clear(Color.Transparent);
3039 
3040 				using(LinearGradientBrush brLight = new LinearGradientBrush(
3041 					rectTop, clrLight, clr, LinearGradientMode.Vertical))
3042 				{
3043 					g.FillRectangle(brLight, rectTop);
3044 				}
3045 
3046 				using(LinearGradientBrush brDark = new LinearGradientBrush(
3047 					rectBottom, clr, clrDark, LinearGradientMode.Vertical))
3048 				{
3049 					g.FillRectangle(brDark, rectBottom);
3050 				}
3051 			}
3052 
3053 			return bmp;
3054 		}
3055 
PlayUacSound()3056 		public static bool PlayUacSound()
3057 		{
3058 			try
3059 			{
3060 				string strRoot = "HKEY_CURRENT_USER\\AppEvents\\Schemes\\Apps\\.Default\\WindowsUAC\\";
3061 
3062 				string strWav = (Registry.GetValue(strRoot + ".Current",
3063 					string.Empty, string.Empty) as string);
3064 				if(string.IsNullOrEmpty(strWav))
3065 					strWav = (Registry.GetValue(strRoot + ".Default",
3066 						string.Empty, string.Empty) as string);
3067 				if(string.IsNullOrEmpty(strWav))
3068 					strWav = @"%SystemRoot%\Media\Windows User Account Control.wav";
3069 
3070 				strWav = SprEngine.Compile(strWav, null);
3071 
3072 				if(!File.Exists(strWav)) throw new FileNotFoundException();
3073 
3074 				NativeMethods.PlaySound(strWav, IntPtr.Zero, NativeMethods.SND_FILENAME |
3075 					NativeMethods.SND_ASYNC | NativeMethods.SND_NODEFAULT);
3076 				return true;
3077 			}
3078 			catch(Exception) { }
3079 
3080 			Debug.Assert(NativeLib.IsUnix() || !WinUtil.IsAtLeastWindowsVista);
3081 			// Do not play a standard sound here
3082 			return false;
3083 		}
3084 
GetWindowImage(IntPtr hWnd, bool bPrefSmall)3085 		public static Image GetWindowImage(IntPtr hWnd, bool bPrefSmall)
3086 		{
3087 			try
3088 			{
3089 				IntPtr hIcon;
3090 				if(bPrefSmall)
3091 				{
3092 					hIcon = NativeMethods.SendMessage(hWnd, NativeMethods.WM_GETICON,
3093 						new IntPtr(NativeMethods.ICON_SMALL2), IntPtr.Zero);
3094 					if(hIcon != IntPtr.Zero) return Icon.FromHandle(hIcon).ToBitmap();
3095 				}
3096 
3097 				hIcon = NativeMethods.SendMessage(hWnd, NativeMethods.WM_GETICON,
3098 					new IntPtr(bPrefSmall ? NativeMethods.ICON_SMALL :
3099 					NativeMethods.ICON_BIG), IntPtr.Zero);
3100 				if(hIcon != IntPtr.Zero) return Icon.FromHandle(hIcon).ToBitmap();
3101 
3102 				hIcon = NativeMethods.GetClassLongPtrEx(hWnd, bPrefSmall ?
3103 					NativeMethods.GCLP_HICONSM : NativeMethods.GCLP_HICON);
3104 				if(hIcon != IntPtr.Zero) return Icon.FromHandle(hIcon).ToBitmap();
3105 
3106 				hIcon = NativeMethods.SendMessage(hWnd, NativeMethods.WM_GETICON,
3107 					new IntPtr(bPrefSmall ? NativeMethods.ICON_BIG : NativeMethods.ICON_SMALL),
3108 					IntPtr.Zero);
3109 				if(hIcon != IntPtr.Zero) return Icon.FromHandle(hIcon).ToBitmap();
3110 
3111 				hIcon = NativeMethods.GetClassLongPtrEx(hWnd, bPrefSmall ?
3112 					NativeMethods.GCLP_HICON : NativeMethods.GCLP_HICONSM);
3113 				if(hIcon != IntPtr.Zero) return Icon.FromHandle(hIcon).ToBitmap();
3114 
3115 				if(!bPrefSmall)
3116 				{
3117 					hIcon = NativeMethods.SendMessage(hWnd, NativeMethods.WM_GETICON,
3118 						new IntPtr(NativeMethods.ICON_SMALL2), IntPtr.Zero);
3119 					if(hIcon != IntPtr.Zero) return Icon.FromHandle(hIcon).ToBitmap();
3120 				}
3121 			}
3122 			catch(Exception) { Debug.Assert(false); }
3123 
3124 			return null;
3125 		}
3126 
3127 		/// <summary>
3128 		/// Set the state of a window. This is a workaround for
3129 		/// https://sourceforge.net/projects/keepass/forums/forum/329221/topic/4610118
3130 		/// </summary>
SetWindowState(Form f, FormWindowState fws)3131 		public static void SetWindowState(Form f, FormWindowState fws)
3132 		{
3133 			if(f == null) { Debug.Assert(false); return; }
3134 
3135 			f.WindowState = fws;
3136 
3137 			// If the window state change / resize handler changes
3138 			// the window state again, the property gets out of sync
3139 			// with the real state. Therefore, we now make sure that
3140 			// the property is synchronized with the actual window
3141 			// state.
3142 			try
3143 			{
3144 				// Get the value that .NET currently caches; note
3145 				// this isn't necessarily the real window state
3146 				// due to the bug
3147 				FormWindowState fwsCached = f.WindowState;
3148 
3149 				IntPtr hWnd = f.Handle;
3150 				if(hWnd == IntPtr.Zero) { Debug.Assert(false); return; }
3151 
3152 				// Get the real state using Windows API functions
3153 				bool bIsRealMin = NativeMethods.IsIconic(hWnd);
3154 				bool bIsRealMax = NativeMethods.IsZoomed(hWnd);
3155 
3156 				FormWindowState? fwsFix = null;
3157 				if(bIsRealMin && (fwsCached != FormWindowState.Minimized))
3158 					fwsFix = FormWindowState.Minimized;
3159 				else if(bIsRealMax && (fwsCached != FormWindowState.Maximized) &&
3160 					!bIsRealMin)
3161 					fwsFix = FormWindowState.Maximized;
3162 				else if(!bIsRealMin && !bIsRealMax &&
3163 					(fwsCached != FormWindowState.Normal))
3164 					fwsFix = FormWindowState.Normal;
3165 
3166 				if(fwsFix.HasValue)
3167 				{
3168 					// If the window is invisible, no state
3169 					// change / resize handlers are called
3170 					bool bVisible = f.Visible;
3171 					if(bVisible) f.Visible = false;
3172 					f.WindowState = fwsFix.Value;
3173 					if(bVisible) f.Visible = true;
3174 				}
3175 			}
3176 			catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
3177 		}
3178 
3179 		private static KeysConverter g_convKeys = null;
GetKeysName(Keys k)3180 		public static string GetKeysName(Keys k)
3181 		{
3182 			StringBuilder sb = new StringBuilder();
3183 
3184 			if((k & Keys.Control) != Keys.None)
3185 			{
3186 				sb.Append(KPRes.KeyboardKeyCtrl);
3187 				sb.Append('+');
3188 			}
3189 			if((k & Keys.Alt) != Keys.None)
3190 			{
3191 				sb.Append(KPRes.KeyboardKeyAlt);
3192 				sb.Append('+');
3193 			}
3194 			if((k & Keys.Shift) != Keys.None)
3195 			{
3196 				sb.Append(KPRes.KeyboardKeyShift);
3197 				sb.Append('+');
3198 			}
3199 
3200 			Keys kCode = (k & Keys.KeyCode);
3201 			switch(kCode)
3202 			{
3203 				case Keys.None:
3204 					if((sb.Length != 0) && (sb[sb.Length - 1] == '+'))
3205 						sb.Remove(sb.Length - 1, 1);
3206 					break;
3207 
3208 				// .NET's German translation is "Eingabetaste",
3209 				// but the shorter "Eingabe" is more common
3210 				case Keys.Return: sb.Append(KPRes.KeyboardKeyReturn); break;
3211 
3212 				// "Esc" is more common than "Escape"
3213 				case Keys.Escape: sb.Append(KPRes.KeyboardKeyEsc); break;
3214 
3215 				case Keys.Up: sb.Append('\u2191'); break;
3216 				case Keys.Right: sb.Append('\u2192'); break;
3217 				case Keys.Down: sb.Append('\u2193'); break;
3218 				case Keys.Left: sb.Append('\u2190'); break;
3219 
3220 				case Keys.Add: sb.Append('+'); break;
3221 				case Keys.Subtract: sb.Append('-'); break;
3222 				case Keys.Multiply: sb.Append('*'); break;
3223 				case Keys.Divide: sb.Append('/'); break;
3224 
3225 				default:
3226 					if(g_convKeys == null) g_convKeys = new KeysConverter();
3227 					sb.Append(g_convKeys.ConvertToString(kCode));
3228 					break;
3229 			}
3230 
3231 			return sb.ToString();
3232 		}
3233 
AssignShortcut(ToolStripMenuItem tsmi, Keys k)3234 		public static void AssignShortcut(ToolStripMenuItem tsmi, Keys k)
3235 		{
3236 			AssignShortcut(tsmi, k, null, false);
3237 		}
3238 
AssignShortcut(ToolStripMenuItem tsmi, Keys k, ToolStripMenuItem tsmiSecondary, bool bTextOnly)3239 		internal static void AssignShortcut(ToolStripMenuItem tsmi, Keys k,
3240 			ToolStripMenuItem tsmiSecondary, bool bTextOnly)
3241 		{
3242 			if(tsmi == null) { Debug.Assert(false); return; }
3243 
3244 			if(!bTextOnly)
3245 			{
3246 #if DEBUG
3247 				bool bCheckGlobal = false;
3248 
3249 				ToolStrip ts1 = GetTopLevelOwner(tsmi);
3250 				if(ts1 != null) bCheckGlobal = !(ts1 is ContextMenuStrip);
3251 				else { Debug.Assert(false); }
3252 
3253 				if(tsmiSecondary != null)
3254 				{
3255 					ToolStrip ts2 = GetTopLevelOwner(tsmiSecondary);
3256 					if(ts2 != null) bCheckGlobal |= !(ts2 is ContextMenuStrip);
3257 					else { Debug.Assert(false); }
3258 				}
3259 
3260 				if(bCheckGlobal)
3261 				{
3262 					// Control-dependent shortcuts shouldn't be registered as global ones
3263 					Debug.Assert(((k & Keys.Modifiers) != Keys.None) || (k == Keys.F1));
3264 					Debug.Assert((k & Keys.KeyCode) != Keys.C);
3265 					Debug.Assert((k & Keys.KeyCode) != Keys.V);
3266 					Debug.Assert((k & Keys.KeyCode) != Keys.X);
3267 					Debug.Assert((k & Keys.KeyCode) != Keys.Y);
3268 					Debug.Assert((k & Keys.KeyCode) != Keys.Z);
3269 					Debug.Assert((k & Keys.KeyCode) != Keys.Escape);
3270 					Debug.Assert((k & Keys.KeyCode) != Keys.Return);
3271 					Debug.Assert((k & Keys.KeyCode) != Keys.Insert);
3272 					Debug.Assert((k & Keys.KeyCode) != Keys.Delete);
3273 					Debug.Assert((k & Keys.KeyCode) != Keys.F10);
3274 				}
3275 #endif
3276 
3277 				tsmi.ShortcutKeys = k;
3278 			}
3279 
3280 			string str = GetKeysName(k);
3281 			tsmi.ShortcutKeyDisplayString = str;
3282 
3283 			if(tsmiSecondary != null)
3284 				tsmiSecondary.ShortcutKeyDisplayString = str;
3285 		}
3286 
GetTopLevelOwner(ToolStripItem tsi)3287 		internal static ToolStrip GetTopLevelOwner(ToolStripItem tsi)
3288 		{
3289 			if(tsi == null) { Debug.Assert(false); return null; }
3290 
3291 			// Items have intermediate Owners of type ToolStripDropDownMenu
3292 
3293 			while(true)
3294 			{
3295 				ToolStripItem tsiOwner = tsi.OwnerItem;
3296 				if(tsiOwner == null) break;
3297 				if(tsiOwner == tsi) { Debug.Assert(false); break; }
3298 				tsi = tsiOwner;
3299 			}
3300 
3301 			ToolStrip ts = tsi.Owner;
3302 			Debug.Assert((ts is CustomMenuStripEx) || (ts is CustomToolStripEx) ||
3303 				(ts is CustomContextMenuStripEx));
3304 			return ts;
3305 		}
3306 
GetSelectedItem(ToolStripItemCollection tsic)3307 		internal static ToolStripItem GetSelectedItem(ToolStripItemCollection tsic)
3308 		{
3309 			if(tsic == null) { Debug.Assert(false); return null; }
3310 
3311 			foreach(ToolStripItem tsi in tsic)
3312 			{
3313 				if(tsi == null) { Debug.Assert(false); continue; }
3314 				if(tsi.Selected) return tsi;
3315 			}
3316 
3317 			return null;
3318 		}
3319 
SetFocusedItem(ListView lv, ListViewItem lvi, bool bAlsoSelect)3320 		public static void SetFocusedItem(ListView lv, ListViewItem lvi,
3321 			bool bAlsoSelect)
3322 		{
3323 			if((lv == null) || (lvi == null)) { Debug.Assert(false); return; }
3324 
3325 			if(bAlsoSelect) lvi.Selected = true;
3326 
3327 			try { lv.FocusedItem = lvi; } // .NET
3328 			catch(Exception)
3329 			{
3330 				try { lvi.Focused = true; } // Mono
3331 				catch(Exception) { Debug.Assert(false); }
3332 			}
3333 		}
3334 
CreateDropDownImage(Image imgBase)3335 		public static Image CreateDropDownImage(Image imgBase)
3336 		{
3337 			if(imgBase == null) { Debug.Assert(false); return null; }
3338 
3339 			// Height of the arrow without the glow effect
3340 			// int hArrow = (int)Math.Ceiling((double)DpiUtil.ScaleIntY(20) / 8.0);
3341 			// int hArrow = (int)Math.Ceiling((double)DpiUtil.ScaleIntY(28) / 8.0);
3342 			int hArrow = DpiUtil.ScaleIntY(3);
3343 
3344 			int dx = imgBase.Width, dy = imgBase.Height;
3345 			if((dx < ((hArrow * 2) + 2)) || (dy < (hArrow + 2)))
3346 				return new Bitmap(imgBase);
3347 
3348 			bool bRtl = Program.Translation.Properties.RightToLeft;
3349 			bool bStdClr = !UIUtil.IsDarkTheme;
3350 
3351 			Bitmap bmp = new Bitmap(dx, dy, PixelFormat.Format32bppArgb);
3352 			using(Graphics g = Graphics.FromImage(bmp))
3353 			{
3354 				g.Clear(Color.Transparent);
3355 				g.DrawImageUnscaled(imgBase, 0, 0);
3356 
3357 				// Pen penDark = Pens.Black;
3358 				// g.DrawLine(penDark, dx - 5, dy - 3, dx - 1, dy - 3);
3359 				// g.DrawLine(penDark, dx - 4, dy - 2, dx - 2, dy - 2);
3360 				// // g.DrawLine(penDark, dx - 7, dy - 4, dx - 1, dy - 4);
3361 				// // g.DrawLine(penDark, dx - 6, dy - 3, dx - 2, dy - 3);
3362 				// // g.DrawLine(penDark, dx - 5, dy - 2, dx - 3, dy - 2);
3363 				// using(Pen penLight = new Pen(Color.FromArgb(
3364 				//	160, 255, 255, 255), 1.0f))
3365 				// {
3366 				//	g.DrawLine(penLight, dx - 5, dy - 4, dx - 1, dy - 4);
3367 				//	g.DrawLine(penLight, dx - 6, dy - 3, dx - 4, dy - 1);
3368 				//	g.DrawLine(penLight, dx - 2, dy - 1, dx - 1, dy - 2);
3369 				//	// g.DrawLine(penLight, dx - 7, dy - 5, dx - 1, dy - 5);
3370 				//	// g.DrawLine(penLight, dx - 8, dy - 4, dx - 5, dy - 1);
3371 				//	// g.DrawLine(penLight, dx - 3, dy - 1, dx - 1, dy - 3);
3372 				// }
3373 
3374 				g.SmoothingMode = SmoothingMode.None;
3375 
3376 				if(bRtl)
3377 				{
3378 					g.ScaleTransform(-1, 1);
3379 					g.TranslateTransform(-dx + 1, 0);
3380 				}
3381 
3382 				Pen penDark = (bStdClr ? Pens.Black : Pens.White);
3383 				for(int i = 1; i < hArrow; ++i)
3384 					g.DrawLine(penDark, dx - hArrow - i, dy - 1 - i,
3385 						dx - hArrow + i, dy - 1 - i);
3386 
3387 				int c = (bStdClr ? 255 : 0);
3388 				using(Pen penLight = new Pen(Color.FromArgb(160, c, c, c), 1.0f))
3389 				{
3390 					g.DrawLine(penLight, dx - (hArrow * 2) + 1,
3391 						dy - hArrow - 1, dx - 1, dy - hArrow - 1);
3392 					g.DrawLine(penLight, dx - (hArrow * 2),
3393 						dy - hArrow, dx - hArrow - 1, dy - 1);
3394 					g.DrawLine(penLight, dx - hArrow + 1, dy - 1,
3395 						dx - 1, dy - hArrow + 1);
3396 				}
3397 			}
3398 
3399 			// bmp.SetPixel(dx - 3, dy - 1, Color.Black);
3400 			// // bmp.SetPixel(dx - 4, dy - 1, Color.Black);
3401 			bmp.SetPixel((bRtl ? (hArrow - 1) : (dx - hArrow)), dy - 1,
3402 				(bStdClr ? Color.Black : Color.White));
3403 
3404 			return bmp;
3405 		}
3406 
3407 		[Obsolete("Use GfxUtil.ScaleImage instead.")]
CreateScaledImage(Image img, int w, int h)3408 		public static Bitmap CreateScaledImage(Image img, int w, int h)
3409 		{
3410 			if(img == null) { Debug.Assert(false); return null; }
3411 
3412 			Image imgSc = GfxUtil.ScaleImage(img, w, h);
3413 			if(imgSc == null) { Debug.Assert(false); return null; }
3414 
3415 			Bitmap bmpSc = (imgSc as Bitmap);
3416 			if(bmpSc == null)
3417 			{
3418 				Debug.Assert(false); // Should be a Bitmap for performance
3419 				bmpSc = new Bitmap(imgSc);
3420 				imgSc.Dispose();
3421 			}
3422 
3423 			return bmpSc;
3424 		}
3425 
3426 		/* public static T DgvGetComboBoxValue<T>(DataGridViewComboBoxCell c,
3427 			List<KeyValuePair<T, string>> lItems)
3428 		{
3429 			if((c == null) || (lItems == null)) { Debug.Assert(false); return default(T); }
3430 
3431 			string strValue = ((c.Value as string) ?? string.Empty);
3432 			foreach(KeyValuePair<T, string> kvp in lItems)
3433 			{
3434 				if(kvp.Value == strValue) return kvp.Key;
3435 			}
3436 
3437 			Debug.Assert(false);
3438 			return default(T);
3439 		}
3440 
3441 		public static void DgvSetComboBoxValue<T>(DataGridViewComboBoxCell c,
3442 			T tValue, List<KeyValuePair<T, string>> lItems)
3443 		{
3444 			if((c == null) || (lItems == null)) { Debug.Assert(false); return; }
3445 
3446 			foreach(KeyValuePair<T, string> kvp in lItems)
3447 			{
3448 				if(kvp.Key.Equals(tValue))
3449 				{
3450 					c.Value = kvp.Value;
3451 					return;
3452 				}
3453 			}
3454 
3455 			Debug.Assert(false);
3456 		}
3457 
3458 		public static bool GetChecked(DataGridViewCheckBoxCell c)
3459 		{
3460 			if(c == null) { Debug.Assert(false); return false; }
3461 
3462 			object o = c.Value;
3463 			if(o == null) { Debug.Assert(false); return false; }
3464 
3465 			return StrUtil.StringToBool(o.ToString());
3466 		}
3467 
3468 		public static void SetChecked(DataGridViewCheckBoxCell c, bool bChecked)
3469 		{
3470 			if(c == null) { Debug.Assert(false); return; }
3471 
3472 			c.Value = bChecked;
3473 		} */
3474 
GetFileIcon(string strFilePath, int w, int h)3475 		public static Image GetFileIcon(string strFilePath, int w, int h)
3476 		{
3477 			try
3478 			{
3479 				using(Icon ico = Icon.ExtractAssociatedIcon(strFilePath))
3480 				{
3481 					return IconToBitmap(ico, w, h);
3482 				}
3483 			}
3484 			catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
3485 
3486 			return null;
3487 		}
3488 
SetHandled(KeyEventArgs e, bool bHandled)3489 		public static void SetHandled(KeyEventArgs e, bool bHandled)
3490 		{
3491 			if(e == null) { Debug.Assert(false); return; }
3492 
3493 			e.Handled = bHandled;
3494 			e.SuppressKeyPress = bHandled;
3495 		}
3496 
HandleCommonKeyEvent(KeyEventArgs e, bool bDown, Control cCtx)3497 		public static bool HandleCommonKeyEvent(KeyEventArgs e, bool bDown,
3498 			Control cCtx)
3499 		{
3500 			if(e == null) { Debug.Assert(false); return false; }
3501 			if(cCtx == null) { Debug.Assert(false); return false; }
3502 
3503 			// On Windows, all of the following is already supported by .NET
3504 			if(!NativeLib.IsUnix()) return false;
3505 
3506 			Keys k = e.KeyCode;
3507 			bool bC = e.Control, bA = e.Alt, bS = e.Shift;
3508 			bool bMac = (NativeLib.GetPlatformID() == PlatformID.MacOSX);
3509 
3510 			if(((k == Keys.Apps) && !bA) || // Shift and Control irrelevant
3511 				((k == Keys.F10) && bS && !bA) || // Control irrelevant
3512 				(bMac && (k == Keys.D5) && bC && bA) ||
3513 				(bMac && (k == Keys.NumPad5) && bC))
3514 			{
3515 				bool bOp = bDown;
3516 				if(k == Keys.Apps) bOp = !bDown;
3517 
3518 				if(bOp)
3519 				{
3520 					ContextMenu cm = cCtx.ContextMenu;
3521 					ContextMenuStrip cms = cCtx.ContextMenuStrip;
3522 
3523 					if(cms != null) cms.Show(Cursor.Position);
3524 					else if(cm != null)
3525 					{
3526 						Point pt = cCtx.PointToClient(Cursor.Position);
3527 						cm.Show(cCtx, pt);
3528 					}
3529 				}
3530 
3531 				UIUtil.SetHandled(e, true);
3532 				return true;
3533 			}
3534 
3535 			return false;
3536 		}
3537 
GetIconSize()3538 		public static Size GetIconSize()
3539 		{
3540 #if DEBUG
3541 			if(!NativeLib.IsUnix())
3542 			{
3543 				Debug.Assert(SystemInformation.IconSize.Width == DpiUtil.ScaleIntX(32));
3544 				Debug.Assert(SystemInformation.IconSize.Height == DpiUtil.ScaleIntY(32));
3545 			}
3546 #endif
3547 
3548 			try { return SystemInformation.IconSize; }
3549 			catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
3550 
3551 			return new Size(DpiUtil.ScaleIntX(32), DpiUtil.ScaleIntY(32));
3552 		}
3553 
GetSmallIconSize()3554 		public static Size GetSmallIconSize()
3555 		{
3556 #if DEBUG
3557 			if(!NativeLib.IsUnix())
3558 			{
3559 				Debug.Assert(SystemInformation.SmallIconSize.Width == DpiUtil.ScaleIntX(16));
3560 				Debug.Assert(SystemInformation.SmallIconSize.Height == DpiUtil.ScaleIntY(16));
3561 			}
3562 #endif
3563 
3564 			// Throws under Mono 4.2.1 on Mac OS X;
3565 			// https://sourceforge.net/p/keepass/discussion/329221/thread/7c096cfc/
3566 			try { return SystemInformation.SmallIconSize; }
3567 			catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
3568 
3569 			return new Size(DpiUtil.ScaleIntX(16), DpiUtil.ScaleIntY(16));
3570 		}
3571 
3572 		/* internal static bool HasClickedSeparator(ToolStripItemClickedEventArgs e)
3573 		{
3574 			if(e == null) { Debug.Assert(false); return false; }
3575 
3576 			ToolStripSeparator ts = (e.ClickedItem as ToolStripSeparator);
3577 			if(ts == null) return false;
3578 
3579 			Debug.Assert(!(e.ClickedItem is ToolStripMenuItem));
3580 			return true;
3581 		} */
3582 
RemoveBannerIfNecessary(Form f)3583 		public static void RemoveBannerIfNecessary(Form f)
3584 		{
3585 			if(f == null) { Debug.Assert(false); return; }
3586 
3587 			try
3588 			{
3589 				Screen s = Screen.FromControl(f);
3590 				if(s == null) { Debug.Assert(false); return; }
3591 
3592 				int hForm = f.Height;
3593 				if(s.WorkingArea.Height >= hForm) return;
3594 
3595 				PictureBox pbBanner = null;
3596 				foreach(Control c in f.Controls)
3597 				{
3598 					if(c == null) { Debug.Assert(false); continue; }
3599 
3600 #if DEBUG
3601 					if(c.Name == "m_bannerImage")
3602 					{
3603 						Debug.Assert(c is PictureBox);
3604 						Debug.Assert(c.Dock == DockStyle.Top);
3605 						Debug.Assert(c.Visible);
3606 					}
3607 #endif
3608 
3609 					PictureBox pb = (c as PictureBox);
3610 					if(pb != null)
3611 					{
3612 						if(pb.Dock != DockStyle.Top) continue;
3613 
3614 						// Check whether there are multiple picture
3615 						// boxes that could be the dialog banner
3616 						if(pbBanner != null) { Debug.Assert(false); return; }
3617 
3618 						pbBanner = pb;
3619 						// No break
3620 					}
3621 				}
3622 				if(pbBanner == null) return; // No assert
3623 				Debug.Assert(pbBanner.Name == "m_bannerImage");
3624 
3625 				int dy = pbBanner.Height;
3626 				if((dy <= 0) || (dy >= hForm)) { Debug.Assert(false); return; }
3627 
3628 				f.SuspendLayout();
3629 				try
3630 				{
3631 					pbBanner.Visible = false;
3632 
3633 					foreach(Control c in f.Controls)
3634 					{
3635 						if(c == null) { Debug.Assert(false); }
3636 						else if(c != pbBanner)
3637 						{
3638 							Point pt = c.Location;
3639 							c.Location = new Point(pt.X, pt.Y - dy);
3640 						}
3641 					}
3642 
3643 					f.Height = hForm - dy;
3644 				}
3645 				catch(Exception) { Debug.Assert(false); }
3646 				finally { f.ResumeLayout(); }
3647 			}
3648 			catch(Exception) { Debug.Assert(false); }
3649 		}
3650 
StrDictListInit(ListView lv)3651 		internal static void StrDictListInit(ListView lv)
3652 		{
3653 			if(lv == null) { Debug.Assert(false); return; }
3654 
3655 			int w = (lv.ClientSize.Width - GetVScrollBarWidth()) / 2;
3656 			lv.Columns.Add(KPRes.Name, w);
3657 			lv.Columns.Add(KPRes.Value, w);
3658 		}
3659 
StrDictListUpdate(ListView lv, StringDictionaryEx sd, bool bMultipleValues)3660 		internal static void StrDictListUpdate(ListView lv, StringDictionaryEx sd,
3661 			bool bMultipleValues)
3662 		{
3663 			if((lv == null) || (sd == null)) { Debug.Assert(false); return; }
3664 
3665 			string strCue = MultipleValuesEx.CueString;
3666 
3667 			UIScrollInfo si = GetScrollInfo(lv, true);
3668 			lv.BeginUpdate();
3669 			lv.Items.Clear();
3670 
3671 			foreach(KeyValuePair<string, string> kvp in sd)
3672 			{
3673 				if(kvp.Key == null) { Debug.Assert(false); continue; }
3674 
3675 				string strValue = StrUtil.MultiToSingleLine(StrUtil.CompactString3Dots(
3676 					(kvp.Value ?? string.Empty), 1024));
3677 
3678 				ListViewItem lvi = lv.Items.Add(kvp.Key);
3679 				lvi.SubItems.Add(strValue);
3680 
3681 				if(bMultipleValues && (strValue == strCue))
3682 					MultipleValuesEx.ConfigureText(lvi, 1);
3683 			}
3684 
3685 			Scroll(lv, si, true);
3686 			lv.EndUpdate();
3687 		}
3688 
StrDictListDeleteSel(ListView lv, StringDictionaryEx sd, bool bMultipleValues)3689 		internal static void StrDictListDeleteSel(ListView lv, StringDictionaryEx sd,
3690 			bool bMultipleValues)
3691 		{
3692 			if((lv == null) || (sd == null)) { Debug.Assert(false); return; }
3693 
3694 			ListView.SelectedListViewItemCollection lvsic = lv.SelectedItems;
3695 			if((lvsic == null) || (lvsic.Count <= 0)) return;
3696 
3697 			foreach(ListViewItem lvi in lvsic)
3698 			{
3699 				if(lvi == null) { Debug.Assert(false); continue; }
3700 
3701 				string strName = lvi.Text;
3702 				if(strName == null) { Debug.Assert(false); continue; }
3703 
3704 				if(!sd.Remove(strName)) { Debug.Assert(false); }
3705 			}
3706 
3707 			StrDictListUpdate(lv, sd, bMultipleValues);
3708 		}
3709 
SetText(Control c, string strText)3710 		public static void SetText(Control c, string strText)
3711 		{
3712 			if(c == null) { Debug.Assert(false); return; }
3713 			if(strText == null) { Debug.Assert(false); strText = string.Empty; }
3714 
3715 			using(RtlAwareResizeScope r = new RtlAwareResizeScope(c))
3716 			{
3717 				c.Text = strText;
3718 			}
3719 		}
3720 
GetEntryIconIndex(PwDatabase pd, PwEntry pe, DateTime dtNow)3721 		internal static int GetEntryIconIndex(PwDatabase pd, PwEntry pe,
3722 			DateTime dtNow)
3723 		{
3724 			if(pe == null) { Debug.Assert(false); return (int)PwIcon.Key; }
3725 
3726 			if(pe.Expires && (pe.ExpiryTime <= dtNow))
3727 				return (int)PwIcon.Expired;
3728 
3729 			if(pe.CustomIconUuid == PwUuid.Zero)
3730 				return (int)pe.IconId;
3731 
3732 			int i = -1;
3733 			if(pd != null) i = pd.GetCustomIconIndex(pe.CustomIconUuid);
3734 			else { Debug.Assert(false); }
3735 			if(i >= 0) return ((int)PwIcon.Count + i);
3736 			Debug.Assert(false);
3737 			return (int)pe.IconId;
3738 		}
3739 
SetView(ListView lv, View v)3740 		internal static void SetView(ListView lv, View v)
3741 		{
3742 			if(lv == null) { Debug.Assert(false); return; }
3743 
3744 			if(lv.View != v) lv.View = v;
3745 		}
3746 
3747 		public static void PerformOverride<T>(ref T o)
3748 			where T : Control, new()
3749 		{
3750 			if(!TypeOverridePool.IsRegistered(typeof(T))) return;
3751 
3752 			T d = TypeOverridePool.CreateInstance<T>();
3753 
3754 			if((o != null) && (d != null))
3755 			{
3756 				d.Dock = o.Dock;
3757 				d.Location = o.Location;
3758 				d.Name = o.Name;
3759 				d.Size = o.Size;
3760 				d.TabIndex = o.TabIndex;
3761 				d.Text = o.Text;
3762 
3763 				TextBox tbO = (o as TextBox), tbD = (d as TextBox);
3764 				if(tbO != null)
3765 					tbD.UseSystemPasswordChar = tbO.UseSystemPasswordChar;
3766 
3767 				Control p = o.Parent;
3768 				if(p != null)
3769 				{
3770 					int i = p.Controls.IndexOf(o);
3771 					if(i >= 0)
3772 					{
3773 						p.SuspendLayout();
3774 						p.Controls.RemoveAt(i);
3775 						p.Controls.Add(d);
3776 						p.Controls.SetChildIndex(d, i);
3777 						p.ResumeLayout();
3778 					}
3779 					else { Debug.Assert(false); }
3780 				}
3781 				else { Debug.Assert(false); }
3782 			}
3783 
3784 			o = d;
3785 		}
3786 
3787 		private static int g_tLastDoEvents = 0;
DoEventsByTime(bool bForce)3788 		internal static void DoEventsByTime(bool bForce)
3789 		{
3790 			int t = Environment.TickCount, tLast = g_tLastDoEvents;
3791 			int d = t - tLast;
3792 
3793 			if((d >= PwDefs.UIUpdateDelay) || bForce || (tLast == 0))
3794 			{
3795 				g_tLastDoEvents = t;
3796 				Application.DoEvents();
3797 			}
3798 		}
3799 
AccIsEnabled()3800 		internal static bool AccIsEnabled()
3801 		{
3802 			return (Program.Config.UI.OptimizeForScreenReader || g_bScreenReaderActive);
3803 		}
3804 
AccSetName(Control c, string strName)3805 		internal static void AccSetName(Control c, string strName)
3806 		{
3807 			AccSetName(c, strName, null);
3808 		}
3809 
AccSetName(Control c, string strName, string strNameSub)3810 		internal static void AccSetName(Control c, string strName, string strNameSub)
3811 		{
3812 			if(Program.DesignMode) return; // For quality progress bar, ...
3813 			if(c == null) { Debug.Assert(false); return; }
3814 
3815 			try
3816 			{
3817 				if(!AccIsEnabled()) return;
3818 
3819 				string str;
3820 				if(!string.IsNullOrEmpty(strName))
3821 				{
3822 					if(!string.IsNullOrEmpty(strNameSub))
3823 						str = strName + " \u2013 " + strNameSub;
3824 					else str = strName;
3825 				}
3826 				else str = strNameSub;
3827 
3828 				c.AccessibleName = str; // Null is allowed
3829 
3830 				if((c is PictureBox) || (c is QualityProgressBar))
3831 					c.AccessibleRole = AccessibleRole.StaticText;
3832 			}
3833 			catch(Exception) { Debug.Assert(false); }
3834 		}
3835 
AccSetName(Control c, Control cNameSource)3836 		internal static void AccSetName(Control c, Control cNameSource)
3837 		{
3838 			if(cNameSource == null) { Debug.Assert(false); return; }
3839 
3840 			try
3841 			{
3842 				Debug.Assert((c != null) && (c.TabIndex == (cNameSource.TabIndex + 1)));
3843 				Debug.Assert(c != cNameSource);
3844 
3845 				string str = StrUtil.RemoveAccelerator(cNameSource.Text ?? string.Empty);
3846 				AccSetName(c, str, null);
3847 			}
3848 			catch(Exception) { Debug.Assert(false); }
3849 		}
3850 	}
3851 }
3852