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