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.IO; 25 using System.Text; 26 using System.Text.RegularExpressions; 27 using System.Threading; 28 using System.Windows.Forms; 29 using System.Xml; 30 using System.Xml.Serialization; 31 32 using KeePass.App; 33 using KeePass.App.Configuration; 34 using KeePass.DataExchange; 35 using KeePass.Ecas; 36 using KeePass.Native; 37 using KeePass.Plugins; 38 using KeePass.Resources; 39 using KeePass.UI; 40 using KeePass.Util; 41 using KeePass.Util.Archive; 42 using KeePass.Util.MultipleValues; 43 using KeePass.Util.Spr; 44 45 using KeePassLib; 46 using KeePassLib.Collections; 47 using KeePassLib.Cryptography; 48 using KeePassLib.Cryptography.PasswordGenerator; 49 using KeePassLib.Delegates; 50 using KeePassLib.Interfaces; 51 using KeePassLib.Keys; 52 using KeePassLib.Security; 53 using KeePassLib.Serialization; 54 using KeePassLib.Utility; 55 56 using NativeLib = KeePassLib.Native.NativeLib; 57 58 namespace KeePass.Forms 59 { 60 public partial class MainForm : Form 61 { 62 private DocumentManagerEx m_docMgr = new DocumentManagerEx(); 63 64 private ListViewGroup m_lvgLastEntryGroup = null; 65 private bool m_bEntryGrouping = false; 66 private DateTime m_dtCachedNow = DateTime.UtcNow; 67 private bool m_bOnlyTans = false; 68 private string m_strLastEntryViewRtf = null; 69 private Font m_fontExpired = null; 70 private Font m_fontBoldUI = null; 71 private Font m_fontBoldTree = null; 72 private Font m_fontItalicTree = null; 73 private Color m_clrAlternateItemBgColor = Color.Empty; 74 private Point m_ptLastEntriesMouseClick = new Point(0, 0); 75 private RichTextBoxContextMenu m_ctxEntryPreviewContextMenu = new RichTextBoxContextMenu(); 76 private DynamicMenu m_dynStringsMenu; 77 private DynamicMenu m_dynStringsCtx; 78 private DynamicMenu m_dynBinariesMenu; 79 private DynamicMenu m_dynBinariesCtx; 80 private DynamicMenu m_dynFindTagsMenu; 81 private DynamicMenu m_dynFindTagsToolBar; 82 private DynamicMenu m_dynTagAddMenu; 83 private DynamicMenu m_dynTagAddCtx; 84 private DynamicMenu m_dynTagRemoveMenu; 85 private DynamicMenu m_dynTagRemoveCtx; 86 private DynamicMenu m_dynMoveToGroupMenu; 87 private DynamicMenu m_dynMoveToGroupCtx; 88 private DynamicMenu m_dynAutoTypeAdvMenu; 89 private DynamicMenu m_dynAutoTypeAdvCtx; 90 private OpenWithMenu m_dynOpenUrlMenu; 91 private OpenWithMenu m_dynOpenUrlCtx; 92 private OpenWithMenu m_dynOpenUrlToolBar; 93 private MenuItemLinks m_milMain = new MenuItemLinks(); 94 95 private readonly AsyncPwListUpdate m_asyncListUpdate; 96 97 private MruList m_mruList = new MruList(); 98 99 private SessionLockNotifier m_sessionLockNotifier = new SessionLockNotifier(); 100 101 private DefaultPluginHost m_pluginDefaultHost = new DefaultPluginHost(); 102 private PluginManager m_pluginManager = new PluginManager(); 103 104 private CriticalSectionEx m_csLockTimer = new CriticalSectionEx(); 105 private int m_nLockTimerMax = 0; 106 // private volatile int m_nLockTimerCur = 0; 107 private long m_lLockAtTicks = long.MaxValue; 108 private uint m_uLastInputTime = uint.MaxValue; 109 private long m_lLockAtGlobalTicks = long.MaxValue; 110 111 private uint m_uBlockQuickFind = 0; 112 private object m_objQuickFindSync = new object(); 113 private int m_iLastQuickFindTicks = Environment.TickCount - 1500; 114 private string m_strLastQuickSearch = string.Empty; 115 116 private ToolStripSeparator m_tsSepCustomToolBar = null; 117 private List<ToolStripButton> m_vCustomToolBarButtons = new List<ToolStripButton>(); 118 119 private int m_nClipClearMax = 0; 120 private int m_nClipClearCur = -1; 121 122 private readonly string m_strNeverExpires = KPRes.NeverExpires; 123 private readonly string m_strStatusReady = KPRes.Ready; 124 private readonly string m_strNoneP = "(" + KPRes.None + ")"; 125 126 private bool m_bSimpleTanView = true; 127 private bool m_bShowTanIndices = true; 128 129 private Image m_imgFileSaveEnabled = null; 130 private Image m_imgFileSaveDisabled = null; 131 // private Image m_imgFileSaveAllEnabled = null; 132 // private Image m_imgFileSaveAllDisabled = null; 133 private List<Image> m_lStdClientImages = null; 134 private ImageList m_ilCurrentIcons = null; 135 136 // private KeyValuePair<Color, Icon> m_kvpIcoMain = 137 // new KeyValuePair<Color, Icon>(Color.Empty, null); 138 // private KeyValuePair<Color, Icon> m_kvpIcoTrayNormal = 139 // new KeyValuePair<Color, Icon>(Color.Empty, null); 140 // private KeyValuePair<Color, Icon> m_kvpIcoTrayLocked = 141 // new KeyValuePair<Color, Icon>(Color.Empty, null); 142 143 private List<Image> m_lTabImages = new List<Image>(); 144 private ImageList m_ilTabImages = null; 145 146 private bool m_bIsAutoTyping = false; 147 private uint m_uTabChangeBlocked = 0; 148 private uint m_uForceSave = 0; 149 private uint m_uUIBlocked = 0; 150 private uint m_uUnlockAutoBlocked = 0; 151 private MouseButtons m_mbLastTrayMouseButtons = MouseButtons.None; 152 private uint m_uWindowStateAutoBlocked = 0; 153 private uint m_uMainTimerBlocked = 0; 154 155 private bool m_bUpdateUIStateOnce = false; 156 private int m_nLastSelChUpdateUIStateTicks = 0; 157 158 private readonly int m_nAppMessage = Program.ApplicationMessage; 159 private readonly int m_nTaskbarButtonMessage; 160 private bool m_bTaskbarButtonMessage; 161 162 private FormWindowState m_fwsLast = FormWindowState.Normal; 163 private Control m_cLastActive = null; 164 private PwGroup m_pgActiveAtDragStart = null; 165 private Keys m_kLastUnhandledGroupsKey = Keys.None; 166 private Stack<ShowWarningsLogger> m_sCancellable = new Stack<ShowWarningsLogger>(); 167 168 // private Stack<Form> m_vRedirectActivation = new Stack<Form>(); 169 170 private Size? m_oszClientLast = null; 171 internal Size LastClientSize 172 { 173 get 174 { 175 if(m_oszClientLast.HasValue) return m_oszClientLast.Value; 176 return this.ClientSize; 177 } 178 } 179 180 public DocumentManagerEx DocumentManager { get { return m_docMgr; } } 181 public PwDatabase ActiveDatabase { get { return m_docMgr.ActiveDatabase; } } 182 public ImageList ClientIcons { get { return m_ilCurrentIcons; } } 183 184 /// <summary> 185 /// Get a reference to the main menu. 186 /// </summary> 187 public MenuStrip MainMenu { get { return m_menuMain; } } 188 /// <summary> 189 /// Get a reference to the 'Tools' popup menu in the main menu. It is 190 /// recommended that you use this reference instead of searching the 191 /// main menu for the 'Tools' item. 192 /// </summary> 193 public ToolStripMenuItem ToolsMenu { get { return m_menuTools; } } 194 195 public ContextMenuStrip EntryContextMenu { get { return m_ctxPwList; } } 196 public ContextMenuStrip GroupContextMenu { get { return m_ctxGroupList; } } 197 public ContextMenuStrip TrayContextMenu { get { return m_ctxTray; } } 198 199 public ToolStripProgressBar MainProgressBar { get { return m_statusPartProgress; } } 200 public NotifyIcon MainNotifyIcon { get { return m_ntfTray.NotifyIcon; } } 201 202 public MruList FileMruList { get { return m_mruList; } } 203 204 internal PluginManager PluginManager { get { return m_pluginManager; } } 205 internal bool HasFormLoaded { get { return m_bFormLoaded; } } 206 207 internal sealed class MainAppState 208 { 209 public bool FileLocked; 210 public bool DatabaseOpened; 211 public int EntriesCount; 212 public int EntriesSelected; 213 public bool EnableLockCmd; 214 public bool NoWindowShown; 215 public PwEntry SelectedEntry; 216 public bool CanCopyUserName = false; 217 public bool CanCopyPassword = false; 218 public bool CanOpenUrl = false; 219 public bool CanPerformAutoType = false; 220 public bool IsOneTan = false; 221 public string LockUnlock; 222 } 223 224 internal enum AppCommandType 225 { 226 Window = 0, 227 Lock = 1 228 } 229 230 /// <summary> 231 /// Check if the main window is trayed (i.e. only the tray icon is visible). 232 /// </summary> 233 /// <returns>Returns <c>true</c>, if the window is trayed.</returns> IsTrayed()234 public bool IsTrayed() 235 { 236 return !this.Visible; 237 } 238 IsFileLocked(PwDocument ds)239 public bool IsFileLocked(PwDocument ds) 240 { 241 if(ds == null) ds = m_docMgr.ActiveDocument; 242 243 return (ds.LockedIoc.Path.Length != 0); 244 } 245 IsAtLeastOneFileOpen()246 public bool IsAtLeastOneFileOpen() 247 { 248 foreach(PwDocument ds in m_docMgr.Documents) 249 { 250 if(ds.Database.IsOpen) return true; 251 } 252 253 return false; 254 } 255 CleanUpEx()256 private void CleanUpEx() 257 { 258 m_asyncListUpdate.CancelPendingUpdatesAsync(); 259 260 Program.TriggerSystem.RaiseEvent(EcasEventIDs.AppExit); 261 262 MonoWorkarounds.Release(this); 263 GlobalWindowManager.CustomizeFormHandleCreated(this, false, false); 264 265 m_nClipClearCur = -1; 266 if(Program.Config.Security.ClipboardClearOnExit) 267 ClipboardUtil.ClearIfOwner(); 268 269 m_pluginManager.UnloadAllPlugins(); // Before saving the configuration 270 271 // Just unregister the events; no need to remove the buttons 272 foreach(ToolStripButton tbCustom in m_vCustomToolBarButtons) 273 tbCustom.Click -= OnCustomToolBarButtonClicked; 274 m_vCustomToolBarButtons.Clear(); 275 276 SaveConfig(); // After unloading plugins 277 278 m_sessionLockNotifier.Uninstall(); 279 HotKeyManager.UnregisterAll(); 280 281 EntryTemplates.Release(); 282 283 m_dynStringsMenu.MenuClick -= this.OnCopyCustomString; 284 m_dynStringsCtx.MenuClick -= this.OnCopyCustomString; 285 m_dynBinariesMenu.MenuClick -= this.OnEntryBinaryOpen; 286 m_dynBinariesCtx.MenuClick -= this.OnEntryBinaryOpen; 287 m_dynFindTagsMenu.MenuClick -= this.OnShowEntriesByTag; 288 m_dynFindTagsToolBar.MenuClick -= this.OnShowEntriesByTag; 289 m_dynTagAddMenu.MenuClick -= this.OnAddEntryTag; 290 m_dynTagAddCtx.MenuClick -= this.OnAddEntryTag; 291 m_dynTagRemoveMenu.MenuClick -= this.OnRemoveEntryTag; 292 m_dynTagRemoveCtx.MenuClick -= this.OnRemoveEntryTag; 293 m_dynMoveToGroupMenu.MenuClick -= this.OnEntryMoveToGroup; 294 m_dynMoveToGroupCtx.MenuClick -= this.OnEntryMoveToGroup; 295 m_dynAutoTypeAdvMenu.MenuClick -= this.OnEntryPerformAutoTypeAdv; 296 m_dynAutoTypeAdvCtx.MenuClick -= this.OnEntryPerformAutoTypeAdv; 297 m_dynOpenUrlMenu.Destroy(); 298 m_dynOpenUrlCtx.Destroy(); 299 m_dynOpenUrlToolBar.Destroy(); 300 301 m_ctxEntryPreviewContextMenu.Detach(); 302 303 m_lvsmMenu.Release(); 304 m_lvgmMenu.Release(); 305 306 m_mruList.Release(); 307 308 m_ntfTray.Visible = false; 309 310 // Debug.Assert(m_vRedirectActivation.Count == 0); 311 Debug.Assert(m_uUIBlocked == 0); 312 Debug.Assert(m_uUnlockAutoBlocked == 0); 313 Debug.Assert(m_sCancellable.Count == 0); 314 this.Visible = false; 315 316 if(m_fontBoldUI != null) { m_fontBoldUI.Dispose(); m_fontBoldUI = null; } 317 if(m_fontBoldTree != null) { m_fontBoldTree.Dispose(); m_fontBoldTree = null; } 318 if(m_fontItalicTree != null) { m_fontItalicTree.Dispose(); m_fontItalicTree = null; } 319 320 m_asyncListUpdate.WaitAll(); 321 m_bCleanedUp = true; 322 } 323 324 /// <summary> 325 /// Save the current configuration. The configuration is saved using the 326 /// cascading configuration files mechanism and the default paths are used. 327 /// </summary> SaveConfig()328 public void SaveConfig() 329 { 330 SaveWindowPositionAndSize(); 331 332 AceMainWindow mw = Program.Config.MainWindow; 333 334 mw.Layout = ((m_splitHorizontal.Orientation != Orientation.Horizontal) ? 335 AceMainWindowLayout.SideBySide : AceMainWindowLayout.Default); 336 337 UpdateColumnsEx(true); 338 339 Debug.Assert(m_bSimpleTanView == m_menuViewTanSimpleList.Checked); 340 mw.TanView.UseSimpleView = m_bSimpleTanView; 341 Debug.Assert(m_bShowTanIndices == m_menuViewTanIndices.Checked); 342 mw.TanView.ShowIndices = m_bShowTanIndices; 343 344 Program.Config.MainWindow.ListSorting = m_pListSorter; 345 346 SerializeMruList(true); 347 348 // mw.ShowGridLines = m_lvEntries.GridLines; 349 350 AppConfigSerializer.Save(Program.Config); 351 } 352 SaveWindowPositionAndSize()353 private void SaveWindowPositionAndSize() 354 { 355 if(!m_bFormLoadCalled) { Debug.Assert(false); return; } 356 357 FormWindowState ws = this.WindowState; 358 359 if(ws == FormWindowState.Normal) 360 { 361 Program.Config.MainWindow.X = this.Location.X; 362 Program.Config.MainWindow.Y = this.Location.Y; 363 Program.Config.MainWindow.Width = this.Size.Width; 364 Program.Config.MainWindow.Height = this.Size.Height; 365 } 366 367 if((ws == FormWindowState.Normal) || (ws == FormWindowState.Maximized)) 368 { 369 Program.Config.MainWindow.SplitterHorizontalFrac = 370 m_splitHorizontal.SplitterDistanceFrac; 371 Program.Config.MainWindow.SplitterVerticalFrac = 372 m_splitVertical.SplitterDistanceFrac; 373 } 374 375 // Program.Config.MainWindow.Maximized = (ws == FormWindowState.Maximized); 376 } 377 378 /// <summary> 379 /// Update the UI state, i.e. enable/disable menu items depending on the state 380 /// of the database (open, closed, locked, modified) and the selected items in 381 /// the groups and entries list. You must call this function after all 382 /// state-changing operations. For example, if you add a new entry the state 383 /// needs to be updated (as the database has been modified) and you must call 384 /// this function. 385 /// </summary> 386 /// <param name="bSetModified">If this parameter is <c>true</c>, the 387 /// currently open database is marked as modified.</param> UpdateUIState(bool bSetModified)388 private void UpdateUIState(bool bSetModified) 389 { 390 UpdateUIState(bSetModified, null); 391 } 392 UpdateUIState(bool bSetModified, Control cOptFocus)393 private void UpdateUIState(bool bSetModified, Control cOptFocus) 394 { 395 // For instance when running "START /MIN KeePass.exe", 396 // we can get called before OnFormLoad has been called 397 if(!m_bFormLoadCalled) return; 398 399 NotifyUserActivity(); 400 m_bUpdateUIStateOnce = false; // We do it now 401 402 MainAppState s = GetMainAppState(); 403 PwDatabase pd = m_docMgr.ActiveDatabase; 404 PwEntry pe = s.SelectedEntry; 405 ulong uif = Program.Config.UI.UIFlags; 406 407 if(s.DatabaseOpened && bSetModified) pd.Modified = true; 408 409 bool bGroupsEnabled = m_tvGroups.Enabled; 410 if(bGroupsEnabled && !s.DatabaseOpened) 411 { 412 m_tvGroups.BackColor = AppDefs.ColorControlDisabled; 413 m_tvGroups.Enabled = false; 414 } 415 else if(!bGroupsEnabled && s.DatabaseOpened) 416 { 417 m_tvGroups.Enabled = true; 418 m_tvGroups.BackColor = AppDefs.ColorControlNormal; 419 } 420 421 if(Program.Config.MainWindow.EntrySelGroupSel && (pe != null)) 422 { 423 PwGroup pgInitial = GetSelectedGroup(); 424 PwGroup pgToSel = pe.ParentGroup; 425 if((pgToSel != null) && (pgToSel != pgInitial)) 426 SetSelectedGroup(pgToSel, true); 427 } 428 429 m_lvEntries.Enabled = s.DatabaseOpened; 430 431 m_statusPartSelected.Text = s.EntriesSelected.ToString() + 432 " " + KPRes.OfLower + " " + s.EntriesCount.ToString() + 433 " " + KPRes.SelectedLower; 434 SetStatusEx(null); 435 436 string strWindowText = PwDefs.ShortProductName; 437 string strNtfText = PwDefs.ShortProductName; 438 // int qSmall = UIUtil.GetSmallIconSize().Width; 439 440 const int cchNtf = NativeMethods.NOTIFYICONDATA_TIP_SIZE - 1; 441 442 if(s.FileLocked) 443 { 444 IOConnectionInfo iocLck = m_docMgr.ActiveDocument.LockedIoc; 445 446 string strWindowEnd = @" [" + KPRes.Locked + @"] - " + 447 PwDefs.ShortProductName; 448 string strFileDesc = iocLck.Path; 449 if(!Program.Config.MainWindow.ShowFullPathInTitle) 450 strFileDesc = UrlUtil.GetFileName(strFileDesc); 451 int iMaxChars = cchNtf - strWindowEnd.Length; 452 if(iMaxChars >= 0) 453 strFileDesc = WinUtil.CompactPath(strFileDesc, iMaxChars); 454 else { Debug.Assert(false); } 455 strWindowText = strFileDesc + strWindowEnd; 456 457 string strNtfPre = PwDefs.ShortProductName + " - " + 458 KPRes.WorkspaceLocked + MessageService.NewLine; 459 strFileDesc = iocLck.Path; 460 iMaxChars = cchNtf - strNtfPre.Length; 461 if(iMaxChars >= 0) 462 strFileDesc = WinUtil.CompactPath(strFileDesc, iMaxChars); 463 else { Debug.Assert(false); } 464 strNtfText = strNtfPre + strFileDesc; 465 466 // Icon icoDisposable, icoAssignable; 467 // CreateColorizedIcon(Properties.Resources.QuadLocked, qSmall, 468 // ref m_kvpIcoTrayLocked, out icoAssignable, out icoDisposable); 469 // m_ntfTray.Icon = icoAssignable; 470 // if(icoDisposable != null) icoDisposable.Dispose(); 471 m_ntfTray.Icon = CreateColorizedIcon(AppIconType.QuadLocked, true); 472 473 TaskbarList.SetOverlayIcon(this, Properties.Resources.LockOverlay, 474 KPRes.Locked); 475 } 476 else if(!s.DatabaseOpened) 477 { 478 // Icon icoDisposable, icoAssignable; 479 // CreateColorizedIcon(Properties.Resources.QuadNormal, qSmall, 480 // ref m_kvpIcoTrayNormal, out icoAssignable, out icoDisposable); 481 // m_ntfTray.Icon = icoAssignable; 482 // if(icoDisposable != null) icoDisposable.Dispose(); 483 m_ntfTray.Icon = CreateColorizedIcon(AppIconType.QuadNormal, true); 484 485 TaskbarList.SetOverlayIcon(this, null, string.Empty); 486 } 487 else // Database open and not locked 488 { 489 string strWindowEnd = (pd.Modified ? "* - " : " - ") + 490 PwDefs.ShortProductName; 491 string strFileDesc = pd.IOConnectionInfo.Path; 492 if(!Program.Config.MainWindow.ShowFullPathInTitle) 493 strFileDesc = UrlUtil.GetFileName(strFileDesc); 494 strFileDesc = WinUtil.CompactPath(strFileDesc, cchNtf - 495 strWindowEnd.Length); 496 strWindowText = strFileDesc + strWindowEnd; 497 498 string strNtfPre = PwDefs.ShortProductName + MessageService.NewLine; 499 strNtfText = strNtfPre + WinUtil.CompactPath( 500 pd.IOConnectionInfo.Path, cchNtf - strNtfPre.Length); 501 502 // Icon icoDisposable, icoAssignable; 503 // CreateColorizedIcon(Properties.Resources.QuadNormal, qSmall, 504 // ref m_kvpIcoTrayNormal, out icoAssignable, out icoDisposable); 505 // m_ntfTray.Icon = icoAssignable; 506 // if(icoDisposable != null) icoDisposable.Dispose(); 507 m_ntfTray.Icon = CreateColorizedIcon(AppIconType.QuadNormal, true); 508 509 TaskbarList.SetOverlayIcon(this, null, string.Empty); 510 } 511 512 DwmUtil.EnableWindowPeekPreview(this); 513 514 bool bFormShownRaised = false; 515 if(MonoWorkarounds.IsRequired(801414)) 516 bFormShownRaised = MonoWorkarounds.ExchangeFormShownRaised(this, false); 517 518 // Clip the strings again (it could be that a translator used 519 // a string in KPRes that is too long to be displayed) 520 this.Text = StrUtil.CompactString3Dots(strWindowText, cchNtf); 521 522 if(MonoWorkarounds.IsRequired(801414)) 523 MonoWorkarounds.ExchangeFormShownRaised(this, bFormShownRaised); 524 525 strNtfText = StrUtil.EncodeToolTipText(strNtfText); 526 m_ntfTray.Text = StrUtil.CompactString3Dots(strNtfText, cchNtf); 527 528 // Icon icoToDispose, icoToAssign; 529 // if(CreateColorizedIcon(Properties.Resources.KeePass, 0, 530 // ref m_kvpIcoMain, out icoToAssign, out icoToDispose)) 531 // this.Icon = icoToAssign; 532 // if(icoToDispose != null) icoToDispose.Dispose(); 533 this.Icon = CreateColorizedIcon(AppIconType.Main, false); 534 535 UIUtil.SetEnabledFast(s.DatabaseOpened || s.FileLocked, 536 m_menuFileClose, m_tbCloseTab); 537 UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuFileSaveAs, 538 m_menuFileSaveAsLocal, m_menuFileSaveAsUrl, m_menuFileSaveAsCopy, 539 m_menuFileChangeMasterKey, m_menuFilePrint, 540 m_menuFilePrintDatabase, m_menuFilePrintEmSheet, 541 m_menuFileImport, m_menuFileExport); 542 UIUtil.SetEnabledFast(s.DatabaseOpened && pd.MasterKey.ContainsType( 543 typeof(KcpKeyFile)), m_menuFilePrintKeyFile); 544 UIUtil.SetEnabledFast(s.DatabaseOpened && ((uif & 545 (ulong)AceUIFlags.DisableDbSettings) == 0), m_menuFileDbSettings); 546 UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuFileSync, 547 m_menuFileSyncFile, m_menuFileSyncUrl, m_menuFileSyncRecent); 548 UIUtil.SetEnabledFast(s.EnableLockCmd, m_menuFileLock, m_tbLockWorkspace); 549 550 // Enable/disable menu items for correct shortcut keys handling 551 UpdateUIGroupMenuState(s, false); 552 UpdateUIEntryMenuState(s, false); 553 554 UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuFindInDatabase, 555 m_menuFindInGroup, m_menuFindProfiles, m_menuFindTag, m_menuFindAll); 556 UIUtil.SetEnabledFast((s.EntriesSelected == 1), m_menuFindParentGroup); 557 UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuFindExp, m_menuFindExpIn, 558 m_menuFindExp1, m_menuFindExp2, m_menuFindExp3, m_menuFindExp7, 559 m_menuFindExp14, m_menuFindExp30, m_menuFindExp60, m_menuFindExpInF); 560 UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuFindLastMod, 561 m_menuFindLarge, m_menuFindDupPasswords, 562 m_menuFindSimPasswordsP, m_menuFindSimPasswordsC, 563 m_menuFindPwQuality); 564 565 UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuToolsGeneratePwList, 566 m_menuToolsTanWizard, m_menuToolsDbMaintenance, 567 m_menuToolsDbDelDupEntries, m_menuToolsDbDelEmptyGroups, 568 m_menuToolsDbDelUnusedIcons); 569 UIUtil.SetEnabledFast(s.DatabaseOpened && ((uif & 570 (ulong)AceUIFlags.DisableXmlReplace) == 0), m_menuToolsDbXmlRep); 571 572 UIUtil.SetEnabledFast(s.DatabaseOpened, m_tbAddEntry, m_tbFind, 573 m_tbEntryViewsDropDown, m_tbViewsShowAll, m_tbViewsShowExpired, 574 m_tbQuickFind); 575 576 Image imgSave; 577 bool bEnableSave; 578 if(Program.Config.MainWindow.DisableSaveIfNotModified) 579 { 580 imgSave = m_imgFileSaveEnabled; 581 bEnableSave = (s.DatabaseOpened && pd.Modified); 582 } 583 else 584 { 585 imgSave = ((!s.DatabaseOpened || pd.Modified) ? 586 m_imgFileSaveEnabled : m_imgFileSaveDisabled); 587 bEnableSave = s.DatabaseOpened; 588 } 589 m_menuFileSave.Image = imgSave; 590 m_tbSaveDatabase.Image = imgSave; 591 UIUtil.SetEnabledFast(bEnableSave, m_menuFileSave, m_tbSaveDatabase); 592 593 m_tbCopyUserName.Enabled = s.CanCopyUserName; 594 m_tbCopyPassword.Enabled = s.CanCopyPassword; 595 UIUtil.SetEnabledFast(s.CanOpenUrl, m_tbOpenUrl, 596 m_tbOpenUrlDefault, m_tbCopyUrl); 597 m_tbAutoType.Enabled = s.CanPerformAutoType; 598 599 m_menuFileLock.Text = s.LockUnlock; 600 string strLockText = StrUtil.RemoveAccelerator(s.LockUnlock); 601 m_tbLockWorkspace.Text = m_ctxTrayLock.Text = strLockText; 602 m_tbLockWorkspace.ToolTipText = strLockText + " (" + 603 m_menuFileLock.ShortcutKeyDisplayString + ")"; 604 605 bool bMultiDoc = (m_docMgr.DocumentCount > 1); 606 m_tabMain.Visible = bMultiDoc; 607 m_tbSaveAll.Visible = bMultiDoc; 608 m_tbCloseTab.Visible = (bMultiDoc || 609 !Program.Config.MainWindow.HideCloseDatabaseButton); 610 611 bool bAtLeastOneModified = false; 612 foreach(PwDocument pwDoc in m_docMgr.Documents) 613 { 614 if((pwDoc == null) || (pwDoc.Database == null)) { Debug.Assert(false); continue; } 615 bAtLeastOneModified |= pwDoc.Database.Modified; 616 } 617 // m_tbSaveAll.Image = (bAtLeastOneModified ? m_imgFileSaveAllEnabled : 618 // m_imgFileSaveAllDisabled); 619 m_tbSaveAll.Enabled = bAtLeastOneModified; 620 621 ShowEntryDetails(pe); 622 UpdateUITabs(); 623 624 if(cOptFocus != null) ResetDefaultFocus(cOptFocus); 625 626 if(this.UIStateUpdated != null) this.UIStateUpdated(this, EventArgs.Empty); 627 // Program.TriggerSystem.RaiseEvent(EcasEventIDs.UpdatedUIState); 628 } 629 UpdateUIGroupState(MainAppState sMain, bool bMenuVisible, bool bContextMenu)630 private void UpdateUIGroupState(MainAppState sMain, bool bMenuVisible, 631 bool bContextMenu) 632 { 633 MainAppState s = (sMain ?? GetMainAppState()); 634 635 PwGroup pg = GetSelectedGroup(); 636 PwGroup pgParent = ((pg != null) ? pg.ParentGroup : null); 637 PwDatabase pd = m_docMgr.ActiveDatabase; 638 if(!s.DatabaseOpened) pd = null; // Null if closed 639 PwGroup pgRoot = ((pd != null) ? pd.RootGroup : null); 640 641 bool bChildOps = (s.DatabaseOpened && (pg != pgRoot)); 642 bool bMoveOps = (bChildOps && (pgParent != null) && 643 (pgParent.Groups.UCount > 1)); 644 uint uSubGroups = 0, uSubEntries = 0; 645 if(pg != null) pg.GetCounts(true, out uSubGroups, out uSubEntries); 646 647 m_menuMain.SuspendLayout(); 648 m_ctxGroupList.SuspendLayout(); 649 650 UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuGroupAdd, m_menuGroupEdit); 651 UIUtil.SetEnabledFast(bChildOps, m_menuGroupDuplicate, m_menuGroupDelete); 652 653 UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuGroupRearrange); 654 UIUtil.SetEnabledFast((bMoveOps && (pgParent.Groups.IndexOf(pg) >= 1)), 655 m_menuGroupMoveToTop, m_menuGroupMoveOneUp); 656 UIUtil.SetEnabledFast((bMoveOps && (pgParent.Groups.IndexOf(pg) < 657 ((int)pgParent.Groups.UCount - 1))), m_menuGroupMoveOneDown, 658 m_menuGroupMoveToBottom); 659 660 if(bMenuVisible) 661 UpdateMoveToPreviousParentGroupUI(pg, null, m_menuGroupMoveToPreviousParent); 662 663 UIUtil.SetEnabledFast(((pg != null) && (pg.Groups.UCount > 1)), m_menuGroupSort); 664 UIUtil.SetEnabledFast((uSubGroups > 1), m_menuGroupSortRec); 665 666 UIUtil.SetEnabledFast((uSubGroups > 0), m_menuGroupExpand, m_menuGroupCollapse); 667 668 bool bShowEmpty = false, bEnableEmpty = false; 669 Debug.Assert(m_menuGroupEmptyRB.ShortcutKeys == Keys.None); // For bMenuVisible 670 if(bMenuVisible && s.DatabaseOpened && pd.RecycleBinEnabled) 671 { 672 PwGroup pgRB = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true); 673 674 bShowEmpty = (pgRB != null); 675 if(bContextMenu) bShowEmpty &= (pg == pgRB); 676 677 if(bShowEmpty) 678 bEnableEmpty = ((pgRB.Groups.UCount > 0) || (pgRB.Entries.UCount > 0)); 679 } 680 m_menuGroupEmptyRB.Enabled = bEnableEmpty; 681 m_milMain.SetCopyAvailable(m_menuGroupEmptyRB, bShowEmpty); 682 683 if(bContextMenu) 684 UIUtil.SetEnabledFast(s.DatabaseOpened, m_ctxGroupFind, m_ctxGroupFindProfiles); 685 686 // 'Paste Group' is updated in the menu opening handler 687 UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuGroupDX, m_menuGroupClipCopy, 688 m_menuGroupClipCopyPlain, m_menuGroupPrint, m_menuGroupExport); 689 690 m_ctxGroupList.ResumeLayout(true); 691 m_menuMain.ResumeLayout(true); 692 } 693 UpdateUIGroupMenuState(MainAppState sMain, bool bMenuVisible)694 private void UpdateUIGroupMenuState(MainAppState sMain, bool bMenuVisible) 695 { 696 UpdateUIGroupState(sMain, bMenuVisible, false); 697 } 698 UpdateUIGroupCtxState()699 private void UpdateUIGroupCtxState() 700 { 701 UpdateUIGroupState(null, true, true); 702 } 703 UpdateUIEntryState(MainAppState sMain, bool bMenuVisible, bool bContextMenu, DynamicMenu dynStrings, DynamicMenu dynBinaries)704 private void UpdateUIEntryState(MainAppState sMain, bool bMenuVisible, 705 bool bContextMenu, DynamicMenu dynStrings, DynamicMenu dynBinaries) 706 { 707 MainAppState s = (sMain ?? GetMainAppState()); 708 PwEntry pe = s.SelectedEntry; 709 710 m_menuMain.SuspendLayout(); 711 m_ctxPwList.SuspendLayout(); 712 713 m_menuEntryCopyUserName.Enabled = s.CanCopyUserName; 714 m_milMain.SetCopyAvailable(m_menuEntryCopyUserName, !s.IsOneTan); 715 716 m_menuEntryCopyPassword.Enabled = s.CanCopyPassword; 717 m_menuEntryCopyPassword.Text = (s.IsOneTan ? KPRes.CopyTanMenu : 718 KPRes.CopyPasswordMenu); 719 720 UIUtil.SetEnabledFast(s.CanOpenUrl, m_menuEntryUrl, m_menuEntryOpenUrl, 721 m_menuEntryCopyUrl); 722 723 if(bMenuVisible) 724 { 725 dynStrings.Clear(); 726 dynBinaries.Clear(); 727 728 uint uStrItems = 0, uBinItems = 0; 729 if((s.EntriesSelected == 1) && (pe != null)) 730 { 731 List<char> lAvailKeys = new List<char>(PwCharSet.MenuAccels); 732 foreach(KeyValuePair<string, ProtectedString> kvp in pe.Strings) 733 { 734 string strText = kvp.Key; 735 if(PwDefs.IsStandardField(strText)) continue; 736 737 strText = StrUtil.EncodeMenuText(strText); 738 strText = StrUtil.AddAccelerator(strText, lAvailKeys); 739 740 dynStrings.AddItem(strText, Properties.Resources.B16x16_KGPG_Info, 741 kvp.Key); 742 ++uStrItems; 743 } 744 745 lAvailKeys = new List<char>(PwCharSet.MenuAccels); 746 foreach(KeyValuePair<string, ProtectedBinary> kvp in pe.Binaries) 747 { 748 string strText = StrUtil.EncodeMenuText(kvp.Key); 749 strText = StrUtil.AddAccelerator(strText, lAvailKeys); 750 751 Image imgIcon = FileIcons.GetImageForName(kvp.Key, null); 752 753 EntryBinaryDataContext ctxBin = new EntryBinaryDataContext(); 754 ctxBin.Entry = pe; 755 ctxBin.Name = kvp.Key; 756 757 dynBinaries.AddItem(strText, imgIcon, ctxBin); 758 ++uBinItems; 759 } 760 } 761 762 if(uStrItems == 0) dynStrings.AddItem(m_strNoneP, null).Enabled = false; 763 if(uBinItems == 0) dynBinaries.AddItem(m_strNoneP, null).Enabled = false; 764 765 m_menuEntryCopyString.Enabled = (uStrItems != 0); 766 m_milMain.SetCopyAvailable(m_menuEntryCopyString, (uStrItems != 0)); 767 m_menuEntryAttachments.Enabled = (uBinItems != 0); 768 m_milMain.SetCopyAvailable(m_menuEntryAttachments, (uBinItems != 0)); 769 } 770 771 bool bAttach = ((s.EntriesSelected >= 2) || ((pe != null) && 772 (pe.Binaries.UCount > 0))); 773 m_menuEntrySaveAttachedFiles.Enabled = bAttach; 774 m_milMain.SetCopyAvailable(m_menuEntrySaveAttachedFiles, bAttach); 775 776 m_menuEntryPerformAutoType.Enabled = s.CanPerformAutoType; 777 778 m_menuEntryAutoTypeAdv.Enabled = s.CanPerformAutoType; 779 m_menuEntryAutoTypeAdv.Available = Program.Config.UI.ShowAdvAutoTypeCommands; 780 foreach(ToolStripItem tsiAdv in m_menuEntryAutoTypeAdv.DropDownItems) 781 { 782 tsiAdv.Enabled = s.CanPerformAutoType; 783 } 784 foreach(ToolStripItem tsiAdv in m_ctxEntryAutoTypeAdv.DropDownItems) 785 { 786 tsiAdv.Enabled = s.CanPerformAutoType; 787 } 788 789 UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuEntryAdd); 790 UIUtil.SetEnabledFast((s.EntriesSelected != 0), m_menuEntryEdit, 791 m_menuEntryEditQuick, m_menuEntryIcon, 792 m_menuEntryColor, m_menuEntryColorStandard, 793 m_menuEntryColorLightRed, m_menuEntryColorLightGreen, 794 m_menuEntryColorLightBlue, m_menuEntryColorLightYellow, 795 m_menuEntryColorCustom, m_menuEntryTagAdd, m_menuEntryTagRemove, 796 m_menuEntryTagNew, m_menuEntryExpiresNow, m_menuEntryExpiresNever, 797 m_menuEntryDuplicate, m_menuEntryDelete); 798 799 UIUtil.SetEnabledFast((s.DatabaseOpened && (s.EntriesCount > 0)), 800 m_menuEntrySelectAll); 801 802 UIUtil.SetEnabledFast((s.EntriesSelected != 0), m_menuEntryRearrange, 803 m_menuEntryMoveToGroup); 804 UIUtil.SetEnabledFast(((m_pListSorter.Column < 0) && (s.EntriesSelected > 0)), 805 m_menuEntryMoveToTop, m_menuEntryMoveOneUp, m_menuEntryMoveOneDown, 806 m_menuEntryMoveToBottom); 807 808 if(bMenuVisible) 809 { 810 PwEntry[] v = GetSelectedEntries(); 811 UpdateMoveToPreviousParentGroupUI(null, v, m_menuEntryMoveToPreviousParent); 812 } 813 814 UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuEntryDX); 815 // 'Paste Entry' is updated in the menu opening handler 816 // and its keyboard shortcut is handled manually (i.e. 817 // it's independent of the menu item state) 818 UIUtil.SetEnabledFast((s.EntriesSelected != 0), m_menuEntryClipCopy, 819 m_menuEntryClipCopyPlain, m_menuEntryPrint, m_menuEntryExport); 820 821 m_ctxPwList.ResumeLayout(true); 822 m_menuMain.ResumeLayout(true); 823 } 824 UpdateUIEntryMenuState(MainAppState sMain, bool bMenuVisible)825 private void UpdateUIEntryMenuState(MainAppState sMain, bool bMenuVisible) 826 { 827 UpdateUIEntryState(sMain, bMenuVisible, false, m_dynStringsMenu, 828 m_dynBinariesMenu); 829 } 830 UpdateUIEntryCtxState()831 private void UpdateUIEntryCtxState() 832 { 833 UpdateUIEntryState(null, true, true, m_dynStringsCtx, 834 m_dynBinariesCtx); 835 } 836 GetMainAppState()837 private MainAppState GetMainAppState() 838 { 839 PwDatabase pd = m_docMgr.ActiveDatabase; 840 PwEntry pe = GetSelectedEntry(true); 841 842 MainAppState s = new MainAppState(); 843 844 s.FileLocked = IsFileLocked(null); 845 s.DatabaseOpened = ((pd != null) && pd.IsOpen); 846 s.EntriesCount = (s.DatabaseOpened ? m_lvEntries.Items.Count : 0); 847 s.EntriesSelected = (s.DatabaseOpened ? m_lvEntries.SelectedIndices.Count : 0); 848 s.EnableLockCmd = (s.DatabaseOpened || s.FileLocked); 849 s.NoWindowShown = (GlobalWindowManager.WindowCount == 0); 850 s.SelectedEntry = pe; 851 852 if(pe != null) 853 { 854 if(s.EntriesSelected == 1) 855 { 856 s.CanCopyUserName = !pe.Strings.GetSafe(PwDefs.UserNameField).IsEmpty; 857 s.CanCopyPassword = !pe.Strings.GetSafe(PwDefs.PasswordField).IsEmpty; 858 s.CanPerformAutoType = pe.GetAutoTypeEnabled(); 859 s.IsOneTan = PwDefs.IsTanEntry(pe); 860 } 861 862 s.CanOpenUrl = ((s.EntriesSelected > 1) || 863 !pe.Strings.GetSafe(PwDefs.UrlField).IsEmpty); 864 } 865 866 s.LockUnlock = (s.FileLocked ? KPRes.LockMenuUnlock : KPRes.LockMenuLock); 867 868 return s; 869 } 870 871 /// <summary> 872 /// Set the main status bar text. 873 /// </summary> 874 /// <param name="strStatusText">New status bar text.</param> SetStatusEx(string strStatusText)875 public void SetStatusEx(string strStatusText) 876 { 877 if(strStatusText == null) m_statusPartInfo.Text = m_strStatusReady; 878 else m_statusPartInfo.Text = strStatusText; 879 } 880 UpdateClipboardStatus()881 private void UpdateClipboardStatus() 882 { 883 // Fix values in case the maximum time has been changed in the 884 // options while the countdown is running 885 if((m_nClipClearMax < 0) && (m_nClipClearCur > 0)) 886 m_nClipClearCur = 0; 887 else if((m_nClipClearCur > m_nClipClearMax) && (m_nClipClearMax >= 0)) 888 m_nClipClearCur = m_nClipClearMax; 889 890 if((m_nClipClearCur > 0) && (m_nClipClearMax > 0)) 891 m_statusClipboard.Value = ((m_nClipClearCur * 100) / m_nClipClearMax); 892 else if(m_nClipClearCur == 0) 893 m_statusClipboard.Visible = false; 894 } 895 896 /// <summary> 897 /// Start the clipboard countdown (set the current tick count to the 898 /// maximum value and decrease it each second -- at 0 the clipboard 899 /// is cleared automatically). This function is asynchronous. 900 /// </summary> StartClipboardCountdown()901 public void StartClipboardCountdown() 902 { 903 if(m_nClipClearMax >= 0) 904 { 905 m_nClipClearCur = m_nClipClearMax; 906 907 m_statusClipboard.Visible = true; 908 UpdateClipboardStatus(); 909 910 string strText = KPRes.ClipboardDataCopied + " " + 911 KPRes.ClipboardClearInSeconds + "."; 912 strText = strText.Replace(@"[PARAM]", m_nClipClearMax.ToString()); 913 914 SetStatusEx(strText); 915 916 // if(m_ntfTray.Visible) 917 // m_ntfTray.ShowBalloonTip(0, KPRes.ClipboardAutoClear, 918 // strText, ToolTipIcon.Info); 919 } 920 } 921 922 /// <summary> 923 /// Gets the focused or first selected entry. 924 /// </summary> 925 /// <returns>Matching entry or <c>null</c>.</returns> GetSelectedEntry(bool bRequireSelected)926 public PwEntry GetSelectedEntry(bool bRequireSelected) 927 { 928 return GetSelectedEntry(bRequireSelected, false); 929 } 930 GetSelectedEntry(bool bRequireSelected, bool bGetLastSelectedEntry)931 public PwEntry GetSelectedEntry(bool bRequireSelected, bool bGetLastSelectedEntry) 932 { 933 if(!m_docMgr.ActiveDatabase.IsOpen) return null; 934 935 if(!bRequireSelected) 936 { 937 ListViewItem lviFocused = m_lvEntries.FocusedItem; 938 if(lviFocused != null) return ((PwListItem)lviFocused.Tag).Entry; 939 } 940 941 ListView.SelectedListViewItemCollection coll = m_lvEntries.SelectedItems; 942 if(coll.Count > 0) 943 { 944 ListViewItem lvi = coll[bGetLastSelectedEntry ? (coll.Count - 1) : 0]; 945 if(lvi != null) return ((PwListItem)lvi.Tag).Entry; 946 } 947 948 return null; 949 } 950 951 /// <summary> 952 /// Get all selected entries. 953 /// </summary> 954 /// <returns>A list of all selected entries.</returns> GetSelectedEntries()955 public PwEntry[] GetSelectedEntries() 956 { 957 if(!m_docMgr.ActiveDatabase.IsOpen) return null; 958 959 ListView.SelectedListViewItemCollection coll = m_lvEntries.SelectedItems; 960 if(coll == null) { Debug.Assert(false); return null; } 961 int n = coll.Count; // Getting Count sends a message 962 if(n == 0) return null; 963 964 PwEntry[] vSelected = new PwEntry[n]; 965 int i = 0; 966 // LVSLVIC: one access by index requires O(n) time, thus use 967 // enumerator instead (which requires O(1) for each element) 968 foreach(ListViewItem lvi in coll) 969 { 970 if(i >= n) { Debug.Assert(false); break; } 971 972 vSelected[i] = ((PwListItem)lvi.Tag).Entry; 973 ++i; 974 } 975 Debug.Assert(i == n); 976 977 return vSelected; 978 } 979 GetSelectedEntriesCount()980 public uint GetSelectedEntriesCount() 981 { 982 if(!m_docMgr.ActiveDatabase.IsOpen) return 0; 983 984 return (uint)m_lvEntries.SelectedIndices.Count; 985 } 986 GetSelectedEntriesAsGroup()987 public PwGroup GetSelectedEntriesAsGroup() 988 { 989 PwGroup pg = new PwGroup(true, true); 990 pg.IsVirtual = true; 991 992 // Copying group properties would confuse users 993 // PwGroup pgSel = GetSelectedGroup(); 994 // if(pgSel != null) 995 // { 996 // pg.Name = pgSel.Name; 997 // pg.IconId = pgSel.IconId; 998 // pg.CustomIconUuid = pgSel.CustomIconUuid; 999 // } 1000 1001 PwEntry[] vSel = GetSelectedEntries(); 1002 if((vSel == null) || (vSel.Length == 0)) return pg; 1003 1004 foreach(PwEntry pe in vSel) pg.AddEntry(pe, false); 1005 1006 return pg; 1007 } 1008 1009 /// <summary> 1010 /// Get the currently selected group. The selected <c>TreeNode</c> is 1011 /// automatically translated to a <c>PwGroup</c>. 1012 /// </summary> 1013 /// <returns>Selected <c>PwGroup</c>.</returns> GetSelectedGroup()1014 public PwGroup GetSelectedGroup() 1015 { 1016 if(!m_docMgr.ActiveDatabase.IsOpen) return null; 1017 1018 TreeNode tn = m_tvGroups.SelectedNode; 1019 if(tn == null) return null; 1020 return (tn.Tag as PwGroup); 1021 } 1022 SetSelectedGroup(PwGroup pg, bool bEnsureVisible)1023 internal void SetSelectedGroup(PwGroup pg, bool bEnsureVisible) 1024 { 1025 if(pg == null) { Debug.Assert(false); return; } 1026 1027 TreeNode tn = GuiFindGroup(pg.Uuid, null); 1028 if(tn != null) 1029 { 1030 m_tvGroups.SelectedNode = tn; 1031 1032 if(bEnsureVisible) tn.EnsureVisible(); 1033 } 1034 else { Debug.Assert(false); } 1035 } 1036 1037 /// <summary> 1038 /// Create or set an entry list view item. 1039 /// </summary> 1040 /// <param name="pe">Entry.</param> 1041 /// <param name="lviTarget">If <c>null</c>, a new list view item is 1042 /// created and added to the list (a group is created if necessary). 1043 /// If not <c>null</c>, the properties are stored in this item (no 1044 /// list view group is created and the list view item is not added 1045 /// to the list).</param> 1046 /// <returns>Created or modified list view item.</returns> SetListEntry(PwEntry pe, ListViewItem lviTarget)1047 private ListViewItem SetListEntry(PwEntry pe, ListViewItem lviTarget) 1048 { 1049 if(pe == null) { Debug.Assert(false); return null; } 1050 1051 ListViewItem lvi = (lviTarget ?? new ListViewItem()); 1052 1053 PwListItem pli = new PwListItem(pe); 1054 if(lviTarget == null) lvi.Tag = pli; // Lock below (when adding it) 1055 else 1056 { 1057 lock(m_asyncListUpdate.ListEditSyncObject) { lvi.Tag = pli; } 1058 } 1059 1060 int iIndexHint = ((lviTarget != null) ? lviTarget.Index : 1061 m_lvEntries.Items.Count); 1062 1063 if(pe.Expires && (pe.ExpiryTime <= m_dtCachedNow)) 1064 { 1065 lvi.ImageIndex = (int)PwIcon.Expired; 1066 if(m_fontExpired != null) lvi.Font = m_fontExpired; 1067 } 1068 else // Not expired 1069 { 1070 // Reset font, if item was expired previously (i.e. has expired font) 1071 if((lviTarget != null) && (lvi.ImageIndex == (int)PwIcon.Expired)) 1072 lvi.Font = m_lvEntries.Font; 1073 1074 if(pe.CustomIconUuid.Equals(PwUuid.Zero)) 1075 lvi.ImageIndex = (int)pe.IconId; 1076 else 1077 lvi.ImageIndex = (int)PwIcon.Count + 1078 m_docMgr.ActiveDatabase.GetCustomIconIndex(pe.CustomIconUuid); 1079 } 1080 1081 if(m_bEntryGrouping && (lviTarget == null)) 1082 { 1083 PwGroup pgContainer = pe.ParentGroup; 1084 PwGroup pgLast = ((m_lvgLastEntryGroup != null) ? 1085 (PwGroup)m_lvgLastEntryGroup.Tag : null); 1086 1087 Debug.Assert(pgContainer != null); 1088 if(pgContainer != null) 1089 { 1090 if(pgContainer != pgLast) 1091 { 1092 m_lvgLastEntryGroup = new ListViewGroup( 1093 pgContainer.GetFullPath(" - ", false)); 1094 m_lvgLastEntryGroup.Tag = pgContainer; 1095 1096 m_lvEntries.Groups.Add(m_lvgLastEntryGroup); 1097 } 1098 1099 lvi.Group = m_lvgLastEntryGroup; 1100 } 1101 } 1102 1103 if(!UIUtil.ColorsEqual(pe.ForegroundColor, Color.Empty)) 1104 lvi.ForeColor = pe.ForegroundColor; 1105 else if(lviTarget != null) lvi.ForeColor = m_lvEntries.ForeColor; 1106 else { Debug.Assert(UIUtil.ColorsEqual(lvi.ForeColor, m_lvEntries.ForeColor)); } 1107 1108 if(!UIUtil.ColorsEqual(pe.BackgroundColor, Color.Empty)) 1109 lvi.BackColor = pe.BackgroundColor; 1110 // else if(Program.Config.MainWindow.EntryListAlternatingBgColors && 1111 // ((m_lvEntries.Items.Count & 1) == 1)) 1112 // lvi.BackColor = m_clrAlternateItemBgColor; 1113 else if(lviTarget != null) lvi.BackColor = m_lvEntries.BackColor; 1114 else { Debug.Assert(UIUtil.ColorsEqual(lvi.BackColor, m_lvEntries.BackColor)); } 1115 1116 bool bAsync; 1117 1118 // m_bOnlyTans &= PwDefs.IsTanEntry(pe); 1119 if(m_bShowTanIndices && m_bOnlyTans) 1120 { 1121 string strIndex = pe.Strings.ReadSafe(PwDefs.TanIndexField); 1122 1123 // KPF 1151 1124 if(Program.Config.MainWindow.EntryListShowDerefData && 1125 SprEngine.MightDeref(strIndex)) 1126 strIndex = AsyncPwListUpdate.SprCompileFn(strIndex, pli); 1127 1128 if(strIndex.Length != 0) lvi.Text = strIndex; 1129 else lvi.Text = PwDefs.TanTitle; 1130 } 1131 else 1132 { 1133 string strMain = GetEntryFieldEx(pe, 0, true, out bAsync); 1134 lvi.Text = strMain; 1135 if(bAsync) 1136 m_asyncListUpdate.Queue(strMain, pli, iIndexHint, 0, 1137 AsyncPwListUpdate.SprCompileFn); 1138 } 1139 1140 int nColumns = m_lvEntries.Columns.Count; 1141 if(lviTarget == null) 1142 { 1143 for(int iColumn = 1; iColumn < nColumns; ++iColumn) 1144 { 1145 string strSub = GetEntryFieldEx(pe, iColumn, true, out bAsync); 1146 lvi.SubItems.Add(strSub); 1147 if(bAsync) 1148 m_asyncListUpdate.Queue(strSub, pli, iIndexHint, iColumn, 1149 AsyncPwListUpdate.SprCompileFn); 1150 } 1151 } 1152 else 1153 { 1154 int nSubItems = lvi.SubItems.Count; 1155 for(int iColumn = 1; iColumn < nColumns; ++iColumn) 1156 { 1157 string strSub = GetEntryFieldEx(pe, iColumn, true, out bAsync); 1158 1159 if(iColumn < nSubItems) lvi.SubItems[iColumn].Text = strSub; 1160 else lvi.SubItems.Add(strSub); 1161 1162 if(bAsync) 1163 m_asyncListUpdate.Queue(strSub, pli, iIndexHint, iColumn, 1164 AsyncPwListUpdate.SprCompileFn); 1165 } 1166 1167 Debug.Assert(lvi.SubItems.Count == nColumns); 1168 } 1169 1170 if(lviTarget == null) 1171 { 1172 lock(m_asyncListUpdate.ListEditSyncObject) 1173 { 1174 m_lvEntries.Items.Add(lvi); 1175 } 1176 } 1177 return lvi; 1178 } 1179 AddEntriesToList(PwObjectList<PwEntry> vEntries)1180 private void AddEntriesToList(PwObjectList<PwEntry> vEntries) 1181 { 1182 if(vEntries == null) { Debug.Assert(false); return; } 1183 1184 m_bEntryGrouping = m_lvEntries.ShowGroups; 1185 1186 ListViewStateEx lvseCachedState = new ListViewStateEx(m_lvEntries); 1187 foreach(PwEntry pe in vEntries) 1188 { 1189 if(pe == null) { Debug.Assert(false); continue; } 1190 1191 if(m_bEntryGrouping) 1192 { 1193 PwGroup pg = pe.ParentGroup; 1194 1195 foreach(ListViewGroup lvg in m_lvEntries.Groups) 1196 { 1197 PwGroup pgList = (lvg.Tag as PwGroup); 1198 Debug.Assert(pgList != null); 1199 if((pgList != null) && (pg == pgList)) 1200 { 1201 m_lvgLastEntryGroup = lvg; 1202 break; 1203 } 1204 } 1205 } 1206 1207 SetListEntry(pe, null); 1208 } 1209 1210 Debug.Assert(lvseCachedState.CompareTo(m_lvEntries)); 1211 1212 UIUtil.SetAlternatingBgColors(m_lvEntries, m_clrAlternateItemBgColor, 1213 Program.Config.MainWindow.EntryListAlternatingBgColors); 1214 } 1215 UpdateGroupList(PwGroup pgToSelect)1216 private void UpdateGroupList(PwGroup pgToSelect) 1217 { 1218 NotifyUserActivity(); 1219 1220 PwDatabase pd = m_docMgr.ActiveDatabase; 1221 PwGroup pgSel = (pgToSelect ?? GetSelectedGroup()); 1222 1223 PwGroup pgTop = null; 1224 TreeNode tnTop = m_tvGroups.TopNode; 1225 if(tnTop != null) pgTop = (tnTop.Tag as PwGroup); 1226 tnTop = null; 1227 1228 m_tvGroups.BeginUpdate(); 1229 m_tvGroups.Nodes.Clear(); 1230 1231 UpdateImageLists(false); 1232 1233 m_dtCachedNow = DateTime.UtcNow; 1234 1235 TreeNode tnSel = null; 1236 TreeNode tnRoot = (pd.IsOpen ? GuiAddGroupRec(m_tvGroups.Nodes, 1237 pd.RootGroup, pgSel, ref tnSel, pgTop, ref tnTop) : null); 1238 1239 if(tnRoot != null) tnRoot.Expand(); 1240 1241 if(tnSel != null) 1242 { 1243 // Ensure all parent tree nodes are expanded 1244 List<TreeNode> lParents = new List<TreeNode>(); 1245 TreeNode tnUp = tnSel.Parent; 1246 while(tnUp != null) 1247 { 1248 lParents.Add(tnUp); 1249 tnUp = tnUp.Parent; 1250 } 1251 for(int i = (lParents.Count - 1); i >= 0; --i) 1252 lParents[i].Expand(); 1253 1254 m_tvGroups.SelectedNode = tnSel; 1255 } 1256 else if(tnRoot != null) m_tvGroups.SelectedNode = tnRoot; 1257 1258 // Restore view *after* changing the selection 1259 if(tnTop != null) m_tvGroups.TopNode = tnTop; 1260 1261 m_tvGroups.EndUpdate(); 1262 } 1263 GuiAddGroupRec(TreeNodeCollection tncContainer, PwGroup pg, PwGroup pgFind1, ref TreeNode tnFound1, PwGroup pgFind2, ref TreeNode tnFound2)1264 private TreeNode GuiAddGroupRec(TreeNodeCollection tncContainer, 1265 PwGroup pg, PwGroup pgFind1, ref TreeNode tnFound1, 1266 PwGroup pgFind2, ref TreeNode tnFound2) 1267 { 1268 if(tncContainer == null) { Debug.Assert(false); return null; } 1269 if(pg == null) { Debug.Assert(false); return null; } 1270 1271 PwDatabase pd = m_docMgr.ActiveDatabase; 1272 bool bExpired = (pg.Expires && (pg.ExpiryTime <= m_dtCachedNow)); 1273 string strName = pg.Name; // + GetGroupSuffixText(pg); 1274 1275 int nIconID = ((!pg.CustomIconUuid.Equals(PwUuid.Zero)) ? 1276 ((int)PwIcon.Count + pd.GetCustomIconIndex(pg.CustomIconUuid)) : 1277 (int)pg.IconId); 1278 if(bExpired) nIconID = (int)PwIcon.Expired; 1279 1280 TreeNode tn = new TreeNode(strName, nIconID, nIconID); 1281 tn.Tag = pg; 1282 UIUtil.SetGroupNodeToolTip(tn, pg); 1283 1284 if((pg == pd.RootGroup) && (m_fontBoldTree != null)) 1285 tn.NodeFont = m_fontBoldTree; 1286 else if(pd.RecycleBinEnabled && pg.Uuid.Equals(pd.RecycleBinUuid) && 1287 (m_fontItalicTree != null)) 1288 tn.NodeFont = m_fontItalicTree; 1289 else if(bExpired && (m_fontExpired != null)) 1290 tn.NodeFont = m_fontExpired; 1291 1292 tncContainer.Add(tn); 1293 1294 foreach(PwGroup pgSub in pg.Groups) 1295 GuiAddGroupRec(tn.Nodes, pgSub, pgFind1, ref tnFound1, 1296 pgFind2, ref tnFound2); 1297 1298 if(tn.Nodes.Count != 0) 1299 { 1300 bool bExpanded = tn.IsExpanded; 1301 if(bExpanded && !pg.IsExpanded) tn.Collapse(); 1302 else if(!bExpanded && pg.IsExpanded) tn.Expand(); 1303 } 1304 1305 if(pg == pgFind1) tnFound1 = tn; 1306 if(pg == pgFind2) tnFound2 = tn; 1307 return tn; 1308 } 1309 1310 /// <summary> 1311 /// Update the entries list. This function completely rebuilds the entries 1312 /// list. You must call this function after you've made any changes to 1313 /// the entries of the currently selected group. Note that if you only 1314 /// made small changes (like editing an existing entry), the 1315 /// <c>RefreshEntriesList</c> function could be a better choice, as it only 1316 /// updates currently listed items and doesn't rebuild the whole list as 1317 /// <c>UpdateEntryList</c>. 1318 /// </summary> 1319 /// <param name="pgSelected">Group whose entries should be shown. If this 1320 /// parameter is <c>null</c>, the entries of the currently selected group 1321 /// (groups view) are displayed, otherwise the entries of the <c>pgSelected</c> 1322 /// group are displayed.</param> UpdateEntryList(PwGroup pgSelected, bool bOnlyUpdateCurrentlyShown)1323 private void UpdateEntryList(PwGroup pgSelected, bool bOnlyUpdateCurrentlyShown) 1324 { 1325 NotifyUserActivity(); 1326 1327 UpdateImageLists(false); 1328 1329 PwEntry peTop = GetTopEntry(), peFocused = GetSelectedEntry(false); 1330 PwEntry[] vSelected = GetSelectedEntries(); 1331 int iScrollY = NativeMethods.GetScrollPosY(m_lvEntries.Handle); 1332 1333 bool bSubEntries = Program.Config.MainWindow.ShowEntriesOfSubGroups; 1334 1335 PwGroup pg = (pgSelected ?? GetSelectedGroup()); 1336 1337 if(bOnlyUpdateCurrentlyShown) 1338 { 1339 Debug.Assert(pgSelected == null); 1340 pg = GetCurrentEntries(); 1341 } 1342 1343 PwObjectList<PwEntry> pwlSource = ((pg != null) ? 1344 pg.GetEntries(bSubEntries) : new PwObjectList<PwEntry>()); 1345 1346 m_bOnlyTans = ListContainsOnlyTans(pwlSource); 1347 1348 m_asyncListUpdate.CancelPendingUpdatesAsync(); 1349 1350 // https://sourceforge.net/p/keepass/bugs/1698/ 1351 ++m_uBlockEntrySelectionEvent; 1352 1353 m_lvEntries.BeginUpdate(); 1354 lock(m_asyncListUpdate.ListEditSyncObject) 1355 { 1356 m_lvEntries.Items.Clear(); 1357 } 1358 m_lvEntries.Groups.Clear(); 1359 m_lvgLastEntryGroup = null; 1360 1361 // m_bEntryGrouping = (((pg != null) ? pg.IsVirtual : false) || bSubEntries); 1362 m_bEntryGrouping = bSubEntries; 1363 if(pg != null) 1364 { 1365 PwDatabase pd = m_docMgr.ActiveDatabase; 1366 if(bOnlyUpdateCurrentlyShown && !m_lvEntries.ShowGroups && 1367 EntryUtil.EntriesHaveSameParent(pwlSource) && pd.IsOpen) 1368 { 1369 // Just reorder, don't enable grouping 1370 EntryUtil.ReorderEntriesAsInDatabase(pwlSource, pd); 1371 peTop = null; // Don't scroll to previous top item 1372 } 1373 else m_bEntryGrouping |= pg.IsVirtual; 1374 } 1375 int iLg = Program.Config.MainWindow.ListGrouping; 1376 if((iLg & (int)AceListGrouping.Primary) == (int)AceListGrouping.On) 1377 m_bEntryGrouping = true; 1378 else if((iLg & (int)AceListGrouping.Primary) == (int)AceListGrouping.Off) 1379 m_bEntryGrouping = false; 1380 m_lvEntries.ShowGroups = m_bEntryGrouping; 1381 1382 int nTopIndex = -1; 1383 ListViewItem lviFocused = null; 1384 1385 m_dtCachedNow = DateTime.UtcNow; 1386 ListViewStateEx lvseCachedState = new ListViewStateEx(m_lvEntries); 1387 1388 if(pg != null) 1389 { 1390 foreach(PwEntry pe in pwlSource) 1391 { 1392 ListViewItem lvi = SetListEntry(pe, null); 1393 1394 if(vSelected != null) 1395 { 1396 if(Array.IndexOf(vSelected, pe) >= 0) 1397 lvi.Selected = true; 1398 } 1399 1400 if(pe == peTop) nTopIndex = m_lvEntries.Items.Count - 1; 1401 if(pe == peFocused) lviFocused = lvi; 1402 } 1403 } 1404 1405 Debug.Assert(lvseCachedState.CompareTo(m_lvEntries)); 1406 1407 UIUtil.SetAlternatingBgColors(m_lvEntries, m_clrAlternateItemBgColor, 1408 Program.Config.MainWindow.EntryListAlternatingBgColors); 1409 1410 Debug.Assert(m_bEntryGrouping == m_lvEntries.ShowGroups); 1411 if(UIUtil.GetGroupsEnabled(m_lvEntries)) 1412 { 1413 // Test nTopIndex to ensure we're not scrolling an unrelated list 1414 if((nTopIndex >= 0) && (iScrollY > 0)) 1415 NativeMethods.Scroll(m_lvEntries, 0, iScrollY); 1416 } 1417 else 1418 { 1419 Debug.Assert((nTopIndex != 0) || (UIUtil.GetTopVisibleItem( 1420 m_lvEntries) == 0)); // No scrolling required for item 0 1421 if(nTopIndex > 0) 1422 UIUtil.SetTopVisibleItem(m_lvEntries, nTopIndex, false); 1423 } 1424 1425 if(lviFocused != null) 1426 UIUtil.SetFocusedItem(m_lvEntries, lviFocused, false); 1427 1428 m_lvEntries.EndUpdate(); 1429 --m_uBlockEntrySelectionEvent; 1430 1431 // Switch the view *after* EndUpdate, otherwise drawing bug; 1432 // https://sourceforge.net/p/keepass/bugs/1651/ 1433 if(m_bSimpleTanView && (m_lvEntries.Items.Count != 0)) 1434 UIUtil.SetView(m_lvEntries, (m_bOnlyTans ? View.List : View.Details)); 1435 else UIUtil.SetView(m_lvEntries, View.Details); 1436 1437 // Resize columns *after* EndUpdate, otherwise sizing problem 1438 // caused by the scrollbar 1439 if(Program.Config.MainWindow.EntryListAutoResizeColumns && 1440 (m_lvEntries.View == View.Details)) 1441 UIUtil.ResizeColumns(m_lvEntries, true); 1442 } 1443 1444 /// <summary> 1445 /// Refresh the entries list. All currently displayed entries are updated. 1446 /// If you made changes to the list that change the number of visible entries 1447 /// (like adding or removing an entry), you must use the <c>UpdateEntryList</c> 1448 /// function instead. 1449 /// </summary> RefreshEntriesList()1450 public void RefreshEntriesList() 1451 { 1452 UpdateImageLists(false); // Important 1453 1454 m_lvEntries.BeginUpdate(); 1455 m_dtCachedNow = DateTime.UtcNow; 1456 1457 foreach(ListViewItem lvi in m_lvEntries.Items) 1458 { 1459 SetListEntry(((PwListItem)lvi.Tag).Entry, lvi); 1460 } 1461 1462 UIUtil.SetAlternatingBgColors(m_lvEntries, m_clrAlternateItemBgColor, 1463 Program.Config.MainWindow.EntryListAlternatingBgColors); 1464 m_lvEntries.EndUpdate(); 1465 } 1466 GetTopEntry()1467 private PwEntry GetTopEntry() 1468 { 1469 PwEntry peTop = null; 1470 try 1471 { 1472 int idxTop = UIUtil.GetTopVisibleItem(m_lvEntries); 1473 if(idxTop >= 0) 1474 peTop = ((PwListItem)m_lvEntries.Items[idxTop].Tag).Entry; 1475 } 1476 catch(Exception) { Debug.Assert(false); } 1477 1478 return peTop; 1479 } 1480 SortPasswordList(bool bEnableSorting, int nColumn, SortOrder? soForce, bool bUpdateEntryList)1481 private void SortPasswordList(bool bEnableSorting, int nColumn, 1482 SortOrder? soForce, bool bUpdateEntryList) 1483 { 1484 AceColumnType colType = GetAceColumn(nColumn).Type; 1485 1486 if(bEnableSorting) 1487 { 1488 bool bSortTimes = AceColumn.IsTimeColumn(colType); 1489 bool bSortNaturally = (colType != AceColumnType.Uuid); 1490 1491 int nOldColumn = m_pListSorter.Column; 1492 SortOrder sortOrder = m_pListSorter.Order; 1493 1494 if(soForce.HasValue) sortOrder = soForce.Value; 1495 else if(nColumn == nOldColumn) 1496 { 1497 if(sortOrder == SortOrder.None) 1498 sortOrder = SortOrder.Ascending; 1499 else if(sortOrder == SortOrder.Ascending) 1500 sortOrder = SortOrder.Descending; 1501 else if(sortOrder == SortOrder.Descending) 1502 sortOrder = SortOrder.None; 1503 else { Debug.Assert(false); } 1504 } 1505 else sortOrder = SortOrder.Ascending; 1506 1507 if(sortOrder != SortOrder.None) 1508 { 1509 m_pListSorter = new ListSorter(nColumn, sortOrder, 1510 bSortNaturally, bSortTimes); 1511 m_lvEntries.ListViewItemSorter = m_pListSorter; 1512 1513 // Workaround for XP bug 1514 if(bUpdateEntryList && !NativeLib.IsUnix() && WinUtil.IsWindowsXP) 1515 UpdateEntryList(null, true); // Only required on XP 1516 } 1517 else 1518 { 1519 m_pListSorter = new ListSorter(); 1520 m_lvEntries.ListViewItemSorter = null; 1521 1522 if(bUpdateEntryList) UpdateEntryList(null, true); 1523 } 1524 } 1525 else // Disable sorting 1526 { 1527 m_pListSorter = new ListSorter(); 1528 m_lvEntries.ListViewItemSorter = null; 1529 1530 if(bUpdateEntryList) UpdateEntryList(null, true); 1531 } 1532 1533 UpdateColumnSortingIcons(); 1534 UIUtil.SetAlternatingBgColors(m_lvEntries, m_clrAlternateItemBgColor, 1535 Program.Config.MainWindow.EntryListAlternatingBgColors); 1536 UpdateUI(false, null, false, null, false, null, false); // KPB 1134 1537 } 1538 UpdateColumnSortingIcons()1539 private void UpdateColumnSortingIcons() 1540 { 1541 if(UIUtil.SetSortIcon(m_lvEntries, m_pListSorter.Column, 1542 m_pListSorter.Order)) return; 1543 1544 // if(m_lvEntries.SmallImageList == null) return; 1545 1546 if(m_pListSorter.Column < 0) { Debug.Assert(m_lvEntries.ListViewItemSorter == null); } 1547 1548 string strAsc = " \u2191"; // Must have same length 1549 string strDsc = " \u2193"; // Must have same length 1550 if(WinUtil.IsWindows9x || WinUtil.IsWindows2000 || WinUtil.IsWindowsXP || 1551 NativeLib.IsUnix()) 1552 { 1553 strAsc = @" ^"; 1554 strDsc = @" v"; 1555 } 1556 else if(WinUtil.IsAtLeastWindowsVista) 1557 { 1558 strAsc = " \u25B3"; 1559 strDsc = " \u25BD"; 1560 } 1561 1562 foreach(ColumnHeader ch in m_lvEntries.Columns) 1563 { 1564 string strCur = ch.Text, strNew = null; 1565 1566 if(strCur.EndsWith(strAsc) || strCur.EndsWith(strDsc)) 1567 { 1568 strNew = strCur.Substring(0, strCur.Length - strAsc.Length); 1569 strCur = strNew; 1570 } 1571 1572 if((ch.Index == m_pListSorter.Column) && 1573 (m_pListSorter.Order != SortOrder.None)) 1574 { 1575 if(m_pListSorter.Order == SortOrder.Ascending) 1576 strNew = strCur + strAsc; 1577 else if(m_pListSorter.Order == SortOrder.Descending) 1578 strNew = strCur + strDsc; 1579 } 1580 1581 if(strNew != null) ch.Text = strNew; 1582 } 1583 } 1584 ShowEntryView(bool bShow)1585 private void ShowEntryView(bool bShow) 1586 { 1587 UIUtil.SetChecked(m_menuViewShowEntryView, bShow); 1588 1589 Program.Config.MainWindow.EntryView.Show = bShow; 1590 1591 m_richEntryView.Visible = bShow; 1592 m_splitHorizontal.Panel2Collapsed = !bShow; 1593 } 1594 ShowEntryDetails(PwEntry pe)1595 private void ShowEntryDetails(PwEntry pe) 1596 { 1597 if(pe == null) 1598 { 1599 m_richEntryView.Text = string.Empty; 1600 m_strLastEntryViewRtf = null; 1601 return; 1602 } 1603 1604 RichTextBuilder rb = new RichTextBuilder(); 1605 1606 AceFont af = Program.Config.UI.StandardFont; 1607 Font fontUI = (UISystemFonts.ListFont ?? m_lvEntries.Font); 1608 // string strFontFace = (af.OverrideUIDefault ? af.Family : fontUI.Name); 1609 // float fFontSize = (af.OverrideUIDefault ? af.ToFont().SizeInPoints : fontUI.SizeInPoints); 1610 if(af.OverrideUIDefault) rb.DefaultFont = af.ToFont(); 1611 else rb.DefaultFont = fontUI; 1612 1613 string strItemSeparator = ((m_splitHorizontal.Orientation == Orientation.Horizontal) ? 1614 ", " : Environment.NewLine); 1615 1616 List<KeyValuePair<string, bool>> lLinks = new List<KeyValuePair<string, bool>>(); 1617 GAction<string, string> fSetLink = delegate(string strPre, string strLink) 1618 { 1619 if(!string.IsNullOrEmpty(strPre)) 1620 lLinks.Add(new KeyValuePair<string, bool>(strPre, false)); 1621 if(!string.IsNullOrEmpty(strLink)) 1622 lLinks.Add(new KeyValuePair<string, bool>(strLink, true)); 1623 }; 1624 1625 // StringBuilder sb = new StringBuilder(); 1626 // StrUtil.InitRtf(sb, strFontFace, fFontSize); 1627 1628 rb.Append(KPRes.Group, FontStyle.Bold, null, null, ":", " "); 1629 PwGroup pg = pe.ParentGroup; 1630 if(pg != null) 1631 { 1632 rb.Append(pg.Name); 1633 fSetLink(KPRes.Group + ": ", pg.Name); 1634 } 1635 1636 AceMainWindow mw = Program.Config.MainWindow; 1637 EvAppendEntryField(rb, strItemSeparator, KPRes.Title, 1638 mw.IsColumnHidden(AceColumnType.Title) ? PwDefs.HiddenPassword : 1639 pe.Strings.ReadSafe(PwDefs.TitleField), pe, null); 1640 EvAppendEntryField(rb, strItemSeparator, KPRes.UserName, 1641 mw.IsColumnHidden(AceColumnType.UserName) ? PwDefs.HiddenPassword : 1642 pe.Strings.ReadSafe(PwDefs.UserNameField), pe, null); 1643 EvAppendEntryField(rb, strItemSeparator, KPRes.Password, 1644 mw.IsColumnHidden(AceColumnType.Password) ? PwDefs.HiddenPassword : 1645 pe.Strings.ReadSafe(PwDefs.PasswordField), pe, null); 1646 EvAppendEntryField(rb, strItemSeparator, KPRes.Url, 1647 mw.IsColumnHidden(AceColumnType.Url) ? PwDefs.HiddenPassword : 1648 pe.Strings.ReadSafe(PwDefs.UrlField), pe, fSetLink); 1649 1650 if(pe.Expires) 1651 EvAppendEntryField(rb, strItemSeparator, KPRes.ExpiryTime, 1652 TimeUtil.ToDisplayString(pe.ExpiryTime), null, null); 1653 1654 if(pe.Binaries.UCount != 0) 1655 { 1656 EvAppendEntryField(rb, strItemSeparator, KPRes.Attachments, 1657 pe.Binaries.KeysToString(), null, null); 1658 1659 fSetLink(KPRes.Attachments + ": ", null); 1660 foreach(KeyValuePair<string, ProtectedBinary> kvp in pe.Binaries) 1661 fSetLink(null, kvp.Key); 1662 } 1663 1664 EvAppendEntryField(rb, strItemSeparator, KPRes.Tags, 1665 TagUtil.TagsInheritedToString(pe.Tags, pe.ParentGroup), null, null); 1666 EvAppendEntryField(rb, strItemSeparator, KPRes.UrlOverride, 1667 pe.OverrideUrl, pe, null); 1668 1669 EvAppendEntryField(rb, strItemSeparator, KPRes.CreationTime, 1670 TimeUtil.ToDisplayString(pe.CreationTime), null, null); 1671 EvAppendEntryField(rb, strItemSeparator, KPRes.LastModificationTime, 1672 TimeUtil.ToDisplayString(pe.LastModificationTime), null, null); 1673 if((Program.Config.UI.UIFlags & (ulong)AceUIFlags.ShowLastAccessTime) != 0) 1674 EvAppendEntryField(rb, strItemSeparator, KPRes.LastAccessTime, 1675 TimeUtil.ToDisplayString(pe.LastAccessTime), null, null); 1676 1677 foreach(KeyValuePair<string, ProtectedString> kvp in pe.Strings) 1678 { 1679 if(PwDefs.IsStandardField(kvp.Key)) continue; 1680 1681 string strCustomValue = (mw.ShouldHideCustomString(kvp.Key, 1682 kvp.Value) ? PwDefs.HiddenPassword : kvp.Value.ReadString()); 1683 EvAppendEntryField(rb, strItemSeparator, kvp.Key, strCustomValue, pe, null); 1684 } 1685 1686 string strNotes = (mw.IsColumnHidden(AceColumnType.Notes) ? 1687 PwDefs.HiddenPassword : pe.Strings.ReadSafe(PwDefs.NotesField).Trim()); 1688 if(strNotes.Length != 0) 1689 { 1690 rb.AppendLine(); 1691 rb.AppendLine(); 1692 1693 KeyValuePair<string, string> kvpBold = RichTextBuilder.GetStyleIdCodes( 1694 FontStyle.Bold); 1695 KeyValuePair<string, string> kvpItalic = RichTextBuilder.GetStyleIdCodes( 1696 FontStyle.Italic); 1697 KeyValuePair<string, string> kvpUnderline = RichTextBuilder.GetStyleIdCodes( 1698 FontStyle.Underline); 1699 1700 strNotes = strNotes.Replace(@"<b>", kvpBold.Key); 1701 strNotes = strNotes.Replace(@"</b>", kvpBold.Value); 1702 strNotes = strNotes.Replace(@"<i>", kvpItalic.Key); 1703 strNotes = strNotes.Replace(@"</i>", kvpItalic.Value); 1704 strNotes = strNotes.Replace(@"<u>", kvpUnderline.Key); 1705 strNotes = strNotes.Replace(@"</u>", kvpUnderline.Value); 1706 1707 rb.Append(strNotes); 1708 } 1709 1710 // sb.Append("\\pard }"); 1711 // m_richEntryView.Rtf = sb.ToString(); 1712 1713 // If the RTF is the same, do not change the selection/view; 1714 // https://sourceforge.net/p/keepass/discussion/329220/thread/509843717a/ 1715 if(!rb.Build(m_richEntryView, false, ref m_strLastEntryViewRtf)) 1716 return; 1717 1718 UIUtil.RtfLinkifyUrls(m_richEntryView); // Before manual URL link. 1719 UIUtil.RtfLinkifyReferences(m_richEntryView, false); 1720 UIUtil.RtfLinkifyTexts(m_richEntryView, lLinks, true); // After auto. URL link. 1721 m_richEntryView.Select(0, 0); 1722 } 1723 EvAppendEntryField(RichTextBuilder rb, string strItemSeparator, string strName, string strRawValue, PwEntry peSprCompile, GAction<string, string> fSetLink)1724 private void EvAppendEntryField(RichTextBuilder rb, 1725 string strItemSeparator, string strName, string strRawValue, 1726 PwEntry peSprCompile, GAction<string, string> fSetLink) 1727 { 1728 if(strRawValue == null) { Debug.Assert(false); return; } 1729 1730 string strValue = strRawValue.Trim(); 1731 if(strValue.Length == 0) return; 1732 1733 rb.Append(strName, FontStyle.Bold, strItemSeparator, null, ":", " "); 1734 1735 string strLink = strValue; 1736 1737 if((peSprCompile == null) || !SprEngine.MightDeref(strValue)) 1738 rb.Append(strValue); 1739 else 1740 { 1741 string strCmp = SprEngine.Compile(strValue, 1742 GetEntryListSprContext(peSprCompile, 1743 m_docMgr.SafeFindContainerOf(peSprCompile))); 1744 if(strCmp == strValue) rb.Append(strValue); 1745 else 1746 { 1747 strCmp = strCmp.Trim(); 1748 1749 rb.Append(strCmp); 1750 rb.Append(" - "); 1751 rb.Append(strValue, FontStyle.Italic); 1752 1753 strLink = strCmp; 1754 } 1755 } 1756 1757 if((fSetLink != null) && (strLink != PwDefs.HiddenPassword)) 1758 fSetLink(strName + ": ", strLink); 1759 } 1760 PerformDefaultAction(object sender, EventArgs e, PwEntry pe, int colID)1761 private void PerformDefaultAction(object sender, EventArgs e, PwEntry pe, 1762 int colID) 1763 { 1764 if(pe == null) { Debug.Assert(false); return; } 1765 1766 if(this.DefaultEntryAction != null) 1767 { 1768 CancelEntryEventArgs args = new CancelEntryEventArgs(pe, colID); 1769 this.DefaultEntryAction(sender, args); 1770 if(args.Cancel) return; 1771 } 1772 1773 bool bCnt = false; 1774 1775 AceColumn col = GetAceColumn(colID); 1776 AceColumnType colType = col.Type; 1777 switch(colType) 1778 { 1779 case AceColumnType.Title: 1780 if(PwDefs.IsTanEntry(pe)) OnEntryCopyPassword(sender, e); 1781 else OnEntryEdit(sender, e); 1782 break; 1783 case AceColumnType.UserName: 1784 OnEntryCopyUserName(sender, e); 1785 break; 1786 case AceColumnType.Password: 1787 OnEntryCopyPassword(sender, e); 1788 break; 1789 case AceColumnType.Url: 1790 PerformDefaultUrlAction(null, null); 1791 break; 1792 case AceColumnType.Notes: 1793 bCnt = ClipboardUtil.CopyAndMinimize(pe.Strings.GetSafe( 1794 PwDefs.NotesField), true, this, pe, m_docMgr.ActiveDatabase); 1795 break; 1796 case AceColumnType.CreationTime: 1797 bCnt = ClipboardUtil.CopyAndMinimize(TimeUtil.ToDisplayString( 1798 pe.CreationTime), true, this, pe, null); 1799 break; 1800 case AceColumnType.LastModificationTime: 1801 bCnt = ClipboardUtil.CopyAndMinimize(TimeUtil.ToDisplayString( 1802 pe.LastModificationTime), true, this, pe, null); 1803 break; 1804 case AceColumnType.LastAccessTime: 1805 bCnt = ClipboardUtil.CopyAndMinimize(TimeUtil.ToDisplayString( 1806 pe.LastAccessTime), true, this, pe, null); 1807 break; 1808 case AceColumnType.ExpiryTime: 1809 if(pe.Expires) 1810 bCnt = ClipboardUtil.CopyAndMinimize(TimeUtil.ToDisplayString( 1811 pe.ExpiryTime), true, this, pe, null); 1812 else 1813 bCnt = ClipboardUtil.CopyAndMinimize(m_strNeverExpires, 1814 true, this, pe, null); 1815 break; 1816 case AceColumnType.Attachment: 1817 case AceColumnType.AttachmentCount: 1818 PerformDefaultAttachmentAction(); 1819 break; 1820 case AceColumnType.Uuid: 1821 bCnt = ClipboardUtil.CopyAndMinimize(pe.Uuid.ToHexString(), 1822 true, this, pe, null); 1823 break; 1824 case AceColumnType.CustomString: 1825 bCnt = ClipboardUtil.CopyAndMinimize(pe.Strings.ReadSafe( 1826 col.CustomName), true, this, pe, m_docMgr.ActiveDatabase); 1827 break; 1828 case AceColumnType.PluginExt: 1829 if(Program.ColumnProviderPool.SupportsCellAction(col.CustomName)) 1830 Program.ColumnProviderPool.PerformCellAction(col.CustomName, pe); 1831 else 1832 bCnt = ClipboardUtil.CopyAndMinimize( 1833 Program.ColumnProviderPool.GetCellData(col.CustomName, pe), 1834 true, this, pe, m_docMgr.ActiveDatabase); 1835 break; 1836 case AceColumnType.OverrideUrl: 1837 bCnt = ClipboardUtil.CopyAndMinimize(pe.OverrideUrl, 1838 true, this, pe, null); 1839 break; 1840 case AceColumnType.Tags: 1841 bCnt = ClipboardUtil.CopyAndMinimize(TagUtil.TagsInheritedToString( 1842 pe.Tags, pe.ParentGroup), true, this, pe, null); 1843 break; 1844 case AceColumnType.ExpiryTimeDateOnly: 1845 if(pe.Expires) 1846 bCnt = ClipboardUtil.CopyAndMinimize(TimeUtil.ToDisplayStringDateOnly( 1847 pe.ExpiryTime), true, this, pe, null); 1848 else 1849 bCnt = ClipboardUtil.CopyAndMinimize(m_strNeverExpires, 1850 true, this, pe, null); 1851 break; 1852 case AceColumnType.Size: 1853 bCnt = ClipboardUtil.CopyAndMinimize(StrUtil.FormatDataSizeKB( 1854 pe.GetSize()), true, this, pe, null); 1855 break; 1856 case AceColumnType.HistoryCount: 1857 EditSelectedEntry(PwEntryFormTab.History); 1858 break; 1859 case AceColumnType.LastPasswordModTime: 1860 bCnt = ClipboardUtil.CopyAndMinimize(TimeUtil.ToDisplayString( 1861 EntryUtil.GetLastPasswordModTime(pe)), true, this, pe, null); 1862 break; 1863 case AceColumnType.AutoTypeEnabled: 1864 object oBlocker; 1865 AutoType.GetEnabledText(pe, out oBlocker); 1866 if((oBlocker == null) || (oBlocker is PwEntry)) 1867 EditSelectedEntry(PwEntryFormTab.AutoType); 1868 else 1869 { 1870 PwGroup pg = (oBlocker as PwGroup); 1871 if(pg != null) 1872 { 1873 UpdateUI(false, null, true, pg, true, null, false); 1874 EditSelectedGroup(GroupFormTab.AutoType); 1875 } 1876 else { Debug.Assert(false); } 1877 } 1878 break; 1879 case AceColumnType.AutoTypeSequences: 1880 EditSelectedEntry(PwEntryFormTab.AutoType); 1881 break; 1882 default: 1883 Debug.Assert(false); 1884 break; 1885 } 1886 1887 if(bCnt) StartClipboardCountdown(); 1888 } 1889 1890 // obForceOpen true: open, false: copy, null: by option and UI PerformDefaultUrlAction(PwEntry[] vOptEntries, bool? obForceOpen)1891 internal void PerformDefaultUrlAction(PwEntry[] vOptEntries, bool? obForceOpen) 1892 { 1893 PwEntry[] v = (vOptEntries ?? GetSelectedEntries()); 1894 if((v == null) || (v.Length == 0)) { Debug.Assert(false); return; } 1895 1896 bool bCopy = Program.Config.MainWindow.CopyUrlsInsteadOfOpening; 1897 if(obForceOpen.HasValue) bCopy = !obForceOpen.Value; 1898 else if((Control.ModifierKeys & Keys.Shift) != Keys.None) 1899 bCopy = !bCopy; 1900 1901 if(bCopy) 1902 { 1903 if(ClipboardUtil.CopyAndMinimize(UrlsToString(v, true), true, this, null, null)) 1904 StartClipboardCountdown(); 1905 } 1906 else // Open 1907 { 1908 foreach(PwEntry pe in v) WinUtil.OpenEntryUrl(pe); 1909 } 1910 } 1911 PerformDefaultAttachmentAction()1912 private void PerformDefaultAttachmentAction() 1913 { 1914 PwEntry pe = GetSelectedEntry(false); 1915 if(pe == null) return; 1916 1917 foreach(KeyValuePair<string, ProtectedBinary> kvp in pe.Binaries) 1918 { 1919 ExecuteBinaryOpen(pe, kvp.Key); 1920 break; 1921 } 1922 } 1923 ExecuteBinaryOpen(PwEntry pe, string strBinName)1924 private void ExecuteBinaryOpen(PwEntry pe, string strBinName) 1925 { 1926 EntryBinaryDataContext ctx = new EntryBinaryDataContext(); 1927 ctx.Entry = pe; 1928 ctx.Name = strBinName; 1929 1930 DynamicMenuEventArgs args = new DynamicMenuEventArgs(strBinName, ctx); 1931 OnEntryBinaryOpen(null, args); 1932 } 1933 UrlsToString(PwEntry[] vEntries, bool bActive)1934 private string UrlsToString(PwEntry[] vEntries, bool bActive) 1935 { 1936 if((vEntries == null) || (vEntries.Length == 0)) return string.Empty; 1937 1938 StringBuilder sb = new StringBuilder(); 1939 foreach(PwEntry pe in vEntries) 1940 { 1941 if(sb.Length > 0) sb.Append(MessageService.NewLine); 1942 1943 PwDatabase pd = m_docMgr.SafeFindContainerOf(pe); 1944 1945 string strUrl = pe.Strings.ReadSafe(PwDefs.UrlField); 1946 strUrl = SprEngine.Compile(strUrl, new SprContext(pe, pd, 1947 (bActive ? SprCompileFlags.All : SprCompileFlags.NonActive))); 1948 1949 sb.Append(strUrl); 1950 } 1951 1952 UpdateUIState(false); // SprEngine.Compile might have modified the database 1953 return sb.ToString(); 1954 } 1955 PerformSearchQuickDelegate(string strSearch, bool bForceShowExpired, bool bRespectEntrySearchingDisabled)1956 private delegate void PerformSearchQuickDelegate(string strSearch, 1957 bool bForceShowExpired, bool bRespectEntrySearchingDisabled); 1958 PerformSearchQuick(string strSearch, bool bForceShowExpired, bool bRespectEntrySearchingDisabled)1959 private void PerformSearchQuick(string strSearch, bool bForceShowExpired, 1960 bool bRespectEntrySearchingDisabled) 1961 { 1962 if(strSearch == null) { Debug.Assert(false); strSearch = string.Empty; } 1963 1964 SearchParameters sp = new SearchParameters(); 1965 1966 if(strSearch.StartsWith("//") && strSearch.EndsWith("//") && 1967 (strSearch.Length > 4)) 1968 { 1969 sp.SearchString = strSearch.Substring(2, strSearch.Length - 4); 1970 sp.SearchMode = PwSearchMode.Regular; 1971 } 1972 else sp.SearchString = strSearch; 1973 1974 sp.SearchInPasswords = Program.Config.MainWindow.QuickFindSearchInPasswords; 1975 sp.SearchInTags = sp.SearchInUuids = sp.SearchInGroupPaths = 1976 sp.SearchInGroupNames = true; 1977 1978 sp.ExcludeExpired = (!bForceShowExpired && 1979 Program.Config.MainWindow.QuickFindExcludeExpired); 1980 sp.RespectEntrySearchingDisabled = bRespectEntrySearchingDisabled; 1981 1982 SearchUtil.SetTransformation(sp, (Program.Config.MainWindow.QuickFindDerefData ? 1983 SearchUtil.StrTrfDeref : string.Empty)); 1984 1985 PerformSearch(sp, false, Program.Config.MainWindow.FocusResultsAfterQuickFind); 1986 } 1987 ShowExpiredEntries(bool bOnlyIfExists, bool bShowExpired, int iExpInDays, bool bInMonths30, bool bFocusEntryList)1988 private void ShowExpiredEntries(bool bOnlyIfExists, bool bShowExpired, 1989 int iExpInDays, bool bInMonths30, bool bFocusEntryList) 1990 { 1991 if(!bShowExpired && (iExpInDays <= 0)) return; 1992 Debug.Assert(iExpInDays >= 0); // 0 = do not show expiring soon 1993 1994 PwDatabase pd = m_docMgr.ActiveDatabase; 1995 if((pd == null) || !pd.IsOpen || (pd.RootGroup == null)) return; 1996 1997 PwGroup pgResults = new PwGroup(true, true, string.Empty, PwIcon.Expired); 1998 pgResults.IsVirtual = true; 1999 2000 bool bExpInP = bShowExpired; // Past 2001 bool bExpInF = (iExpInDays == int.MaxValue); // Future 2002 bool bExpInD = ((iExpInDays > 0) && !bExpInF); 2003 2004 DateTime dtNow = DateTime.UtcNow; 2005 DateTime dtLimit = dtNow; 2006 if(bExpInD) 2007 { 2008 try 2009 { 2010 if(bInMonths30 && ((iExpInDays % 30) == 0)) 2011 dtLimit = dtNow.AddMonths(iExpInDays / 30); 2012 else 2013 { 2014 Debug.Assert(!bInMonths30); 2015 dtLimit = dtNow.AddDays(iExpInDays); 2016 } 2017 2018 dtLimit = TimeUtil.ToLocal(dtLimit, false); 2019 dtLimit = dtLimit.Date.Add(new TimeSpan(23, 59, 59)); 2020 dtLimit = TimeUtil.ToUtc(dtLimit, false); 2021 } 2022 catch(Exception) { Debug.Assert(false); } 2023 } 2024 2025 EntryHandler eh = delegate(PwEntry pe) 2026 { 2027 if(!pe.Expires) return true; 2028 if(!pe.GetSearchingEnabled()) return true; 2029 if(PwDefs.IsTanEntry(pe)) return true; // Exclude TANs 2030 2031 int iRelNow = pe.ExpiryTime.CompareTo(dtNow); 2032 2033 if((bExpInP && (iRelNow <= 0)) || 2034 (bExpInD && (pe.ExpiryTime <= dtLimit) && (iRelNow > 0)) || 2035 (bExpInF && (iRelNow > 0))) 2036 pgResults.AddEntry(pe, false, false); 2037 return true; 2038 }; 2039 2040 pd.RootGroup.TraverseTree(TraversalMethod.PreOrder, null, eh); 2041 2042 if((pgResults.Entries.UCount != 0) || !bOnlyIfExists) 2043 { 2044 SearchParameters sp = new SearchParameters(); 2045 sp.RespectEntrySearchingDisabled = true; 2046 2047 ShowSearchResults(pgResults, sp, pd.RootGroup, bFocusEntryList); 2048 } 2049 } 2050 PerformExport(PwGroup pgDataSource, bool bExportDeleted)2051 public void PerformExport(PwGroup pgDataSource, bool bExportDeleted) 2052 { 2053 PwDatabase pd = m_docMgr.ActiveDatabase; 2054 if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; } 2055 2056 if(!AppPolicy.Try(AppPolicyId.Export)) return; 2057 2058 PwGroup pg = (pgDataSource ?? pd.RootGroup); 2059 PwExportInfo pwInfo = new PwExportInfo(pg, pd, bExportDeleted); 2060 2061 MessageService.ExternalIncrementMessageCount(); 2062 ShowWarningsLogger swLogger = CreateShowWarningsLogger(); 2063 2064 ExportUtil.Export(pwInfo, swLogger); 2065 2066 MessageService.ExternalDecrementMessageCount(); 2067 UpdateUIState(false); 2068 } 2069 CompleteConnectionInfo(IOConnectionInfo ioc, bool bSave, bool bCanRememberCred, bool bTestConnection, bool bForceShow)2070 internal static IOConnectionInfo CompleteConnectionInfo(IOConnectionInfo ioc, 2071 bool bSave, bool bCanRememberCred, bool bTestConnection, bool bForceShow) 2072 { 2073 if(ioc == null) { Debug.Assert(false); return null; } 2074 if(!bForceShow && ((ioc.CredSaveMode == IOCredSaveMode.SaveCred) || 2075 ioc.IsLocalFile() || ioc.IsComplete)) 2076 return ioc.CloneDeep(); 2077 2078 IOConnectionForm dlg = new IOConnectionForm(); 2079 dlg.InitEx(bSave, ioc, bCanRememberCred, bTestConnection); 2080 if(UIUtil.ShowDialogNotValue(dlg, DialogResult.OK)) return null; 2081 2082 IOConnectionInfo iocResult = dlg.IOConnectionInfo; 2083 UIUtil.DestroyForm(dlg); 2084 return iocResult; 2085 } 2086 CompleteConnectionInfoUsingMru(IOConnectionInfo ioc)2087 internal IOConnectionInfo CompleteConnectionInfoUsingMru(IOConnectionInfo ioc) 2088 { 2089 if(ioc == null) { Debug.Assert(false); return null; } 2090 if(ioc.Password.Length > 0) return ioc; 2091 2092 for(uint u = 0; u < m_mruList.ItemCount; ++u) 2093 { 2094 IOConnectionInfo iocMru = (m_mruList.GetItem(u).Value as IOConnectionInfo); 2095 if(iocMru == null) { Debug.Assert(false); continue; } 2096 2097 if(iocMru.Path.Equals(ioc.Path, StrUtil.CaseIgnoreCmp)) 2098 { 2099 if((ioc.UserName.Length > 0) && !ioc.UserName.Equals( 2100 iocMru.UserName, StrUtil.CaseIgnoreCmp)) 2101 continue; 2102 2103 return iocMru.CloneDeep(); 2104 } 2105 } 2106 2107 return ioc; 2108 } 2109 2110 private sealed class OdKpfConstructParams 2111 { 2112 public IOConnectionInfo IOConnectionInfo = null; 2113 public bool CanExit = false; 2114 public bool SecureDesktopMode = false; // Must be false by default 2115 } 2116 OdKpfConstruct(object objParam)2117 private static Form OdKpfConstruct(object objParam) 2118 { 2119 OdKpfConstructParams p = (objParam as OdKpfConstructParams); 2120 if(p == null) { Debug.Assert(false); return null; } 2121 2122 KeyPromptForm kpf = new KeyPromptForm(); 2123 kpf.InitEx(p.IOConnectionInfo, p.CanExit, true); 2124 kpf.SecureDesktopMode = p.SecureDesktopMode; 2125 return kpf; 2126 } 2127 2128 private sealed class OdKpfResult 2129 { 2130 public CompositeKey Key = null; 2131 public bool ShowHelpAfterClose = false; 2132 public bool HasClosedWithExit = false; 2133 } 2134 OdKpfBuildResult(Form f)2135 private static object OdKpfBuildResult(Form f) 2136 { 2137 KeyPromptForm kpf = (f as KeyPromptForm); 2138 if(kpf == null) { Debug.Assert(false); return null; } 2139 2140 OdKpfResult kpfResult = new OdKpfResult(); 2141 kpfResult.Key = kpf.CompositeKey; 2142 kpfResult.ShowHelpAfterClose = kpf.ShowHelpAfterClose; 2143 kpfResult.HasClosedWithExit = kpf.HasClosedWithExit; 2144 2145 return kpfResult; 2146 } 2147 2148 /// <summary> 2149 /// Open a database. This function opens the specified database and 2150 /// updates the user interface. 2151 /// </summary> OpenDatabase(IOConnectionInfo ioConnection, CompositeKey cmpKey, bool bOpenLocal)2152 public void OpenDatabase(IOConnectionInfo ioConnection, CompositeKey cmpKey, 2153 bool bOpenLocal) 2154 { 2155 if(!IsCommandTypeInvokable(null, AppCommandType.Window)) { Debug.Assert(false); return; } 2156 2157 if((ioConnection != null) && (ioConnection.Path.Length > 0) && 2158 ioConnection.IsLocalFile() && !UrlUtil.IsAbsolutePath(ioConnection.Path)) 2159 { 2160 ioConnection = ioConnection.CloneDeep(); 2161 ioConnection.Path = UrlUtil.MakeAbsolutePath(WinUtil.GetExecutable(), 2162 ioConnection.Path); 2163 } 2164 2165 if(!m_bFormLoaded && Program.Config.Application.Start.MinimizedAndLocked && 2166 (ioConnection != null) && (ioConnection.Path.Length > 0)) 2167 { 2168 PwDocument ds = m_docMgr.CreateNewDocument(true); 2169 ds.LockedIoc = ioConnection.CloneDeep(); 2170 UpdateUI(true, ds, true, null, true, null, false); 2171 return; 2172 } 2173 2174 SaveWindowState(); // KPF 1093 2175 2176 IOConnectionInfo ioc; 2177 if(ioConnection == null) 2178 { 2179 if(bOpenLocal) 2180 { 2181 OpenFileDialogEx ofdDb = UIUtil.CreateOpenFileDialog(KPRes.OpenDatabaseFile, 2182 UIUtil.CreateFileTypeFilter(AppDefs.FileExtension.FileExt, 2183 KPRes.KdbxFiles, true), 1, null, false, 2184 AppDefs.FileDialogContext.Database); 2185 2186 GlobalWindowManager.AddDialog(ofdDb.FileDialog); 2187 DialogResult dr = ofdDb.ShowDialog(); 2188 GlobalWindowManager.RemoveDialog(ofdDb.FileDialog); 2189 if(dr != DialogResult.OK) return; 2190 2191 ioc = IOConnectionInfo.FromPath(ofdDb.FileName); 2192 } 2193 else 2194 { 2195 ioc = CompleteConnectionInfo(new IOConnectionInfo(), false, 2196 true, true, true); 2197 if(ioc == null) return; 2198 } 2199 } 2200 else // ioConnection != null 2201 { 2202 ioc = CompleteConnectionInfo(ioConnection, false, true, true, false); 2203 if(ioc == null) return; 2204 } 2205 2206 if(!ioc.CanProbablyAccess()) 2207 { 2208 MessageService.ShowWarning(ioc.GetDisplayName(), KPRes.FileNotFoundError); 2209 return; 2210 } 2211 2212 if(OpenDatabaseRestoreIfOpened(ioc)) return; 2213 2214 PwDatabase pwOpenedDb = null; 2215 bool bAbort; 2216 if(cmpKey == null) 2217 { 2218 for(int iTry = 0; iTry < Program.Config.Security.MasterKeyTries; ++iTry) 2219 { 2220 OdKpfConstructParams kpfParams = new OdKpfConstructParams(); 2221 kpfParams.IOConnectionInfo = ioc; 2222 kpfParams.CanExit = IsFileLocked(null); 2223 2224 DialogResult dr; 2225 OdKpfResult kpfResult; 2226 2227 if(Program.Config.Security.MasterKeyOnSecureDesktop && 2228 WinUtil.IsAtLeastWindows2000 && !NativeLib.IsUnix()) 2229 { 2230 kpfParams.SecureDesktopMode = true; 2231 2232 ProtectedDialog dlg = new ProtectedDialog(OdKpfConstruct, 2233 OdKpfBuildResult); 2234 object objResult; 2235 dr = dlg.ShowDialog(out objResult, kpfParams); 2236 if(dr == DialogResult.None) { Debug.Assert(false); dr = DialogResult.Cancel; } 2237 2238 kpfResult = (objResult as OdKpfResult); 2239 if(kpfResult == null) { Debug.Assert(false); continue; } 2240 2241 if(kpfResult.ShowHelpAfterClose) 2242 AppHelp.ShowHelp(AppDefs.HelpTopics.KeySources, null); 2243 } 2244 else // Show dialog on normal desktop 2245 { 2246 Form dlg = OdKpfConstruct(kpfParams); 2247 dr = dlg.ShowDialog(); 2248 2249 kpfResult = (OdKpfBuildResult(dlg) as OdKpfResult); 2250 UIUtil.DestroyForm(dlg); 2251 if(kpfResult == null) { Debug.Assert(false); continue; } 2252 } 2253 2254 if(dr == DialogResult.Cancel) break; 2255 else if(kpfResult.HasClosedWithExit) 2256 { 2257 Debug.Assert(dr == DialogResult.Abort); 2258 OnFileExit(null, null); 2259 return; 2260 } 2261 2262 pwOpenedDb = OpenDatabaseInternal(ioc, kpfResult.Key, 2263 out bAbort); 2264 if((pwOpenedDb != null) || bAbort) break; 2265 } 2266 } 2267 else // cmpKey != null 2268 { 2269 pwOpenedDb = OpenDatabaseInternal(ioc, cmpKey, out bAbort); 2270 } 2271 2272 if((pwOpenedDb == null) || !pwOpenedDb.IsOpen) 2273 { 2274 UpdateUIState(false); // Reset status bar text 2275 return; 2276 } 2277 2278 string strName = pwOpenedDb.IOConnectionInfo.GetDisplayName(); 2279 m_mruList.AddItem(strName, pwOpenedDb.IOConnectionInfo.CloneDeep()); 2280 2281 PwDocument dsExisting = m_docMgr.FindDocument(pwOpenedDb); 2282 if(dsExisting != null) m_docMgr.ActiveDocument = dsExisting; 2283 2284 bool bCorrectDbActive = (m_docMgr.ActiveDocument.Database == pwOpenedDb); 2285 Debug.Assert(bCorrectDbActive); 2286 2287 AutoEnableVisualHiding(true); 2288 2289 // SetLastUsedFile(pwOpenedDb.IOConnectionInfo); 2290 RememberKeySources(pwOpenedDb); 2291 2292 PwGroup pgRestoreSelect = null; 2293 if(bCorrectDbActive) 2294 { 2295 m_docMgr.ActiveDocument.LockedIoc = new IOConnectionInfo(); // Clear 2296 2297 pgRestoreSelect = pwOpenedDb.RootGroup.FindGroup( 2298 pwOpenedDb.LastSelectedGroup, true); 2299 } 2300 2301 UpdateUI(true, null, true, pgRestoreSelect, true, null, false); 2302 if(bCorrectDbActive) 2303 { 2304 SetTopVisibleGroup(pwOpenedDb.LastTopVisibleGroup); 2305 if(pgRestoreSelect != null) 2306 SetTopVisibleEntry(pgRestoreSelect.LastTopVisibleEntry); 2307 } 2308 UpdateColumnSortingIcons(); 2309 2310 long lMkDays = (DateTime.UtcNow - pwOpenedDb.MasterKeyChanged).Days; 2311 bool bMkChangeForce = ((pwOpenedDb.MasterKeyChangeForce >= 0) && 2312 (lMkDays >= pwOpenedDb.MasterKeyChangeForce)); 2313 bMkChangeForce |= pwOpenedDb.MasterKeyChangeForceOnce; 2314 2315 bool bMkChangeRec = ((pwOpenedDb.MasterKeyChangeRec >= 0) && 2316 (lMkDays >= pwOpenedDb.MasterKeyChangeRec)); 2317 string strMkExpiry = (Program.Config.Security.MasterKeyExpiryRec ?? 2318 string.Empty).Trim(); 2319 if(strMkExpiry.Length != 0) 2320 { 2321 try 2322 { 2323 if(strMkExpiry.StartsWith("P", StrUtil.CaseIgnoreCmp) || 2324 strMkExpiry.StartsWith("-P", StrUtil.CaseIgnoreCmp)) 2325 { 2326 TimeSpan tsExpiry = XmlConvert.ToTimeSpan(strMkExpiry); 2327 bMkChangeRec |= ((pwOpenedDb.MasterKeyChanged + 2328 tsExpiry) < DateTime.UtcNow); 2329 } 2330 else 2331 { 2332 DateTime dtExpiry = XmlConvert.ToDateTime(strMkExpiry, 2333 XmlDateTimeSerializationMode.Utc); 2334 bMkChangeRec |= (pwOpenedDb.MasterKeyChanged < dtExpiry); 2335 } 2336 } 2337 catch(Exception exMkExp) 2338 { 2339 MessageService.ShowWarning("Configuration/Security/MasterKeyExpiryRec:", 2340 exMkExp); 2341 } 2342 } 2343 2344 if(bMkChangeForce) 2345 { 2346 // Updating the UI might trigger an auto-save 2347 pwOpenedDb.MasterKeyChangeForceOnce = false; 2348 2349 while(true) 2350 { 2351 MessageService.ShowInfo(pwOpenedDb.IOConnectionInfo.GetDisplayName() + 2352 MessageService.NewParagraph + KPRes.MasterKeyChangeForce + 2353 MessageService.NewParagraph + KPRes.MasterKeyChangeInfo); 2354 if(ChangeMasterKey(pwOpenedDb)) break; 2355 if(!AppPolicy.Current.ChangeMasterKey) break; // Prevent endless loop 2356 } 2357 } 2358 else if(bMkChangeRec) 2359 { 2360 if(MessageService.AskYesNo(pwOpenedDb.IOConnectionInfo.GetDisplayName() + 2361 MessageService.NewParagraph + KPRes.MasterKeyChangeRec + 2362 MessageService.NewParagraph + KPRes.MasterKeyChangeQ)) 2363 ChangeMasterKey(pwOpenedDb); 2364 } 2365 2366 if(FixDuplicateUuids(pwOpenedDb, pwOpenedDb.IOConnectionInfo)) 2367 UpdateUIState(false); // Already marked as modified 2368 2369 if(this.FileOpened != null) 2370 { 2371 FileOpenedEventArgs ea = new FileOpenedEventArgs(pwOpenedDb); 2372 this.FileOpened(this, ea); 2373 } 2374 Program.TriggerSystem.RaiseEvent(EcasEventIDs.OpenedDatabaseFile, 2375 EcasProperty.Database, pwOpenedDb); 2376 2377 if(bCorrectDbActive && pwOpenedDb.IsOpen) 2378 { 2379 ShowExpiredEntries(true, 2380 Program.Config.Application.FileOpening.ShowExpiredEntries, 2381 (Program.Config.Application.FileOpening.ShowSoonToExpireEntries ? 2382 Program.Config.Application.ExpirySoonDays : 0), false, false); 2383 2384 // Avoid view being destroyed by the unlocking routine 2385 pwOpenedDb.LastSelectedGroup = PwUuid.Zero; 2386 } 2387 2388 if(Program.Config.MainWindow.MinimizeAfterOpeningDatabase) 2389 UIUtil.SetWindowState(this, FormWindowState.Minimized); 2390 2391 ResetDefaultFocus(null); 2392 } 2393 OpenDatabaseInternal(IOConnectionInfo ioc, CompositeKey cmpKey, out bool bAbort)2394 private PwDatabase OpenDatabaseInternal(IOConnectionInfo ioc, 2395 CompositeKey cmpKey, out bool bAbort) 2396 { 2397 bAbort = false; 2398 2399 PerformSelfTest(); 2400 2401 PwDocument ds = null; 2402 string strPathNrm = ioc.Path.Trim().ToLower(); 2403 for(int iScan = 0; iScan < m_docMgr.Documents.Count; ++iScan) 2404 { 2405 if(m_docMgr.Documents[iScan].LockedIoc.Path.Trim().ToLower() == strPathNrm) 2406 ds = m_docMgr.Documents[iScan]; 2407 else if(m_docMgr.Documents[iScan].Database.IOConnectionInfo.Path == strPathNrm) 2408 ds = m_docMgr.Documents[iScan]; 2409 } 2410 2411 PwDatabase pwDb; 2412 if(ds == null) pwDb = m_docMgr.CreateNewDocument(true).Database; 2413 else pwDb = ds.Database; 2414 2415 UIBlockInteraction(true); 2416 ShowWarningsLogger swLogger = CreateShowWarningsLogger(); 2417 swLogger.StartLogging(KPRes.OpeningDatabase2, true); 2418 m_sCancellable.Push(swLogger); 2419 2420 Exception ex = null; 2421 try 2422 { 2423 pwDb.Open(ioc, cmpKey, swLogger); 2424 2425 #if DEBUG 2426 byte[] pbDiskDirect = WinUtil.HashFile(ioc); 2427 Debug.Assert(MemUtil.ArraysEqual(pbDiskDirect, pwDb.HashOfFileOnDisk)); 2428 #endif 2429 } 2430 catch(Exception exLoad) 2431 { 2432 ex = exLoad; 2433 pwDb = null; 2434 } 2435 2436 m_sCancellable.Pop(); 2437 swLogger.EndLogging(); 2438 UIBlockInteraction(false); 2439 2440 if(pwDb == null) 2441 { 2442 if(ds == null) m_docMgr.CloseDatabase(m_docMgr.ActiveDatabase); 2443 } 2444 2445 if(ex != null) 2446 { 2447 string strMsg = MessageService.GetLoadWarningMessage( 2448 ioc.GetDisplayName(), ex, 2449 (Program.CommandLineArgs[AppDefs.CommandLineOptions.Debug] != null)); 2450 2451 bool bShowStd = true; 2452 if(!ioc.IsLocalFile()) 2453 { 2454 VistaTaskDialog vtd = new VistaTaskDialog(); 2455 vtd.CommandLinks = false; 2456 vtd.Content = strMsg; 2457 vtd.DefaultButtonID = (int)DialogResult.Cancel; 2458 // vtd.VerificationText = KPRes.CredSpecifyDifferent; 2459 vtd.WindowTitle = PwDefs.ShortProductName; 2460 2461 vtd.SetIcon(VtdIcon.Warning); 2462 vtd.AddButton((int)DialogResult.Cancel, KPRes.Ok, null); 2463 vtd.AddButton((int)DialogResult.Retry, 2464 KPRes.CredSpecifyDifferent, null); 2465 2466 if(vtd.ShowDialog(this)) 2467 { 2468 bShowStd = false; 2469 2470 // if(vtd.ResultVerificationChecked) 2471 if(vtd.Result == (int)DialogResult.Retry) 2472 { 2473 IOConnectionInfo iocNew = ioc.CloneDeep(); 2474 // iocNew.ClearCredentials(false); 2475 iocNew.CredSaveMode = IOCredSaveMode.NoSave; 2476 iocNew.IsComplete = false; 2477 // iocNew.Password = string.Empty; 2478 2479 OpenDatabase(iocNew, null, false); 2480 2481 bAbort = true; 2482 } 2483 } 2484 } 2485 2486 if(bShowStd) MessageService.ShowWarning(strMsg); 2487 // MessageService.ShowLoadWarning(ioc.GetDisplayName(), ex, 2488 // (Program.CommandLineArgs[AppDefs.CommandLineOptions.Debug] != null)); 2489 } 2490 2491 return pwDb; 2492 } 2493 OpenDatabaseRestoreIfOpened(IOConnectionInfo ioc)2494 private bool OpenDatabaseRestoreIfOpened(IOConnectionInfo ioc) 2495 { 2496 if(ioc == null) { Debug.Assert(false); return false; } 2497 2498 string strPathNrm = ioc.Path.Trim().ToLower(); 2499 2500 foreach(PwDocument ds in m_docMgr.Documents) 2501 { 2502 if(((ds.LockedIoc == null) || (ds.LockedIoc.Path.Length == 0)) && 2503 (ds.Database.IOConnectionInfo.Path.Trim().ToLower() == strPathNrm)) 2504 { 2505 MakeDocumentActive(ds); 2506 return true; 2507 } 2508 } 2509 2510 return false; 2511 } 2512 AutoEnableVisualHiding(bool bNewOpenDatabase)2513 private static void AutoEnableVisualHiding(bool bNewOpenDatabase) 2514 { 2515 // // KPF 1802197 2516 // // Turn on visual hiding if option is selected 2517 // if(m_docMgr.ActiveDatabase.MemoryProtection.AutoEnableVisualHiding) 2518 // { 2519 // if(m_docMgr.ActiveDatabase.MemoryProtection.ProtectTitle && !m_viewHideFields.ProtectTitle) 2520 // m_menuViewHideTitles.Checked = m_viewHideFields.ProtectTitle = true; 2521 // if(m_docMgr.ActiveDatabase.MemoryProtection.ProtectUserName && !m_viewHideFields.ProtectUserName) 2522 // m_menuViewHideUserNames.Checked = m_viewHideFields.ProtectUserName = true; 2523 // if(m_docMgr.ActiveDatabase.MemoryProtection.ProtectPassword && !m_viewHideFields.ProtectPassword) 2524 // m_menuViewHidePasswords.Checked = m_viewHideFields.ProtectPassword = true; 2525 // if(m_docMgr.ActiveDatabase.MemoryProtection.ProtectUrl && !m_viewHideFields.ProtectUrl) 2526 // m_menuViewHideURLs.Checked = m_viewHideFields.ProtectUrl = true; 2527 // if(m_docMgr.ActiveDatabase.MemoryProtection.ProtectNotes && !m_viewHideFields.ProtectNotes) 2528 // m_menuViewHideNotes.Checked = m_viewHideFields.ProtectNotes = true; 2529 // } 2530 2531 if(bNewOpenDatabase && !Program.Config.UI.Hiding.RememberHidingPasswordsMain) 2532 { 2533 List<AceColumn> l = Program.Config.MainWindow.FindColumns( 2534 AceColumnType.Password); 2535 foreach(AceColumn c in l) c.HideWithAsterisks = true; // Reset 2536 } 2537 } 2538 GuiFindGroup(PwUuid puSearch, TreeNode tnContainer)2539 private TreeNode GuiFindGroup(PwUuid puSearch, TreeNode tnContainer) 2540 { 2541 if(puSearch == null) { Debug.Assert(false); return null; } 2542 2543 if(tnContainer == null) 2544 { 2545 if(m_tvGroups.Nodes.Count == 0) return null; 2546 tnContainer = m_tvGroups.Nodes[0]; 2547 } 2548 2549 PwGroup pg = (tnContainer.Tag as PwGroup); 2550 if(pg != null) 2551 { 2552 if(pg.Uuid.Equals(puSearch)) return tnContainer; 2553 } 2554 else { Debug.Assert(false); } 2555 2556 foreach(TreeNode tn in tnContainer.Nodes) 2557 { 2558 if(tn != tnContainer) 2559 { 2560 TreeNode tnRet = GuiFindGroup(puSearch, tn); 2561 if(tnRet != null) return tnRet; 2562 } 2563 else { Debug.Assert(false); } 2564 } 2565 2566 return null; 2567 } 2568 GuiFindEntry(PwUuid puSearch)2569 private ListViewItem GuiFindEntry(PwUuid puSearch) 2570 { 2571 Debug.Assert(puSearch != null); 2572 if(puSearch == null) return null; 2573 2574 foreach(ListViewItem lvi in m_lvEntries.Items) 2575 { 2576 if(((PwListItem)lvi.Tag).Entry.Uuid.Equals(puSearch)) 2577 return lvi; 2578 } 2579 2580 return null; 2581 } 2582 PrintGroup(PwGroup pg)2583 private void PrintGroup(PwGroup pg) 2584 { 2585 if(pg == null) { Debug.Assert(false); return; } 2586 2587 PwDatabase pd = m_docMgr.ActiveDatabase; 2588 if((pd == null) || !pd.IsOpen) return; 2589 2590 if(!AppPolicy.Try(AppPolicyId.Print)) return; 2591 if(!AppPolicy.Current.PrintNoKey) 2592 { 2593 if(!KeyUtil.ReAskKey(pd, true)) return; 2594 } 2595 2596 // Printing selected entries should not always reveal parent 2597 // group information (group notes, ...) 2598 // pg = ExportUtil.WithParentGroups(pg, pd); 2599 2600 // Changing the root group of the database breaks Spr compilation 2601 // PwGroup pgOrgRoot = pd.RootGroup; 2602 // pd.RootGroup = pg; 2603 2604 PrintForm pf = new PrintForm(); 2605 pf.InitEx(pg, pd, m_ilCurrentIcons, true, m_pListSorter.Column); 2606 UIUtil.ShowDialogAndDestroy(pf); 2607 2608 // pd.RootGroup = pgOrgRoot; 2609 } 2610 OnMruExecute(string strDisplayName, object oTag, ToolStripMenuItem tsmiParent)2611 public void OnMruExecute(string strDisplayName, object oTag, 2612 ToolStripMenuItem tsmiParent) 2613 { 2614 if(tsmiParent == null) { Debug.Assert(false); return; } 2615 2616 IOConnectionInfo ioc = (oTag as IOConnectionInfo); 2617 if(ioc == null) { Debug.Assert(false); return; } 2618 2619 if(tsmiParent == m_menuFileRecent) 2620 OpenDatabase(ioc, null, false); 2621 else if(tsmiParent == m_menuFileSyncRecent) 2622 { 2623 PwDatabase pd = m_docMgr.ActiveDatabase; 2624 if(!pd.IsOpen) return; 2625 2626 ioc = CompleteConnectionInfo(ioc, false, true, true, false); 2627 if(ioc == null) return; 2628 2629 bool? b = ImportUtil.Synchronize(pd, this, ioc, false, this); 2630 UpdateUI(false, null, true, null, true, null, false); 2631 if(b.HasValue) SetStatusEx(b.Value ? KPRes.SyncSuccess : KPRes.SyncFailed); 2632 } 2633 else { Debug.Assert(false); } 2634 } 2635 OnMruClear()2636 public void OnMruClear() 2637 { 2638 m_mruList.Clear(); 2639 } 2640 2641 // For plugins UpdateTrayIcon()2642 public void UpdateTrayIcon() 2643 { 2644 UpdateTrayIcon(true); 2645 } 2646 UpdateTrayIcon(bool bRefreshIcon)2647 private void UpdateTrayIcon(bool bRefreshIcon) 2648 { 2649 if(m_ntfTray == null) { Debug.Assert(false); return; } // Required 2650 2651 bool bWindowVisible = this.Visible; 2652 bool bTrayVisible = m_ntfTray.Visible; 2653 2654 if(Program.Config.UI.TrayIcon.ShowOnlyIfTrayedEx) 2655 m_ntfTray.Visible = !bWindowVisible; 2656 else if(bWindowVisible && !bTrayVisible) 2657 m_ntfTray.Visible = true; 2658 2659 if(bRefreshIcon) m_ntfTray.RefreshShellIcon(); 2660 } 2661 OnSessionLock(object sender, SessionLockEventArgs e)2662 private void OnSessionLock(object sender, SessionLockEventArgs e) 2663 { 2664 // Shutdown is handled through OnFormClosing (the default 2665 // WndProc of the Form class tries to close the window when 2666 // receiving WM_QUERYENDSESSION or WM_ENDSESSION) 2667 2668 if((e.Reason == SessionLockReason.RemoteControlChange) && 2669 Program.Config.Security.WorkspaceLocking.LockOnRemoteControlChange) { } 2670 else if(((e.Reason == SessionLockReason.Lock) || 2671 (e.Reason == SessionLockReason.Ending) || 2672 (e.Reason == SessionLockReason.UserSwitch)) && 2673 Program.Config.Security.WorkspaceLocking.LockOnSessionSwitch) { } 2674 else if((e.Reason == SessionLockReason.Suspend) && 2675 Program.Config.Security.WorkspaceLocking.LockOnSuspend) { } 2676 else return; 2677 2678 if(IsAtLeastOneFileOpen()) LockAllDocuments(); 2679 } 2680 2681 /// <summary> 2682 /// Reset the internal user inactivity timers. 2683 /// </summary> NotifyUserActivity()2684 public void NotifyUserActivity() 2685 { 2686 // m_nLockTimerCur = m_nLockTimerMax; 2687 2688 if(m_nLockTimerMax == 0) m_lLockAtTicks = long.MaxValue; 2689 else 2690 { 2691 DateTime utcLockAt = DateTime.UtcNow; 2692 utcLockAt = utcLockAt.AddSeconds((double)m_nLockTimerMax); 2693 m_lLockAtTicks = utcLockAt.Ticks; 2694 } 2695 2696 Program.TriggerSystem.NotifyUserActivity(); 2697 2698 if(this.UserActivityPost != null) 2699 this.UserActivityPost(null, EventArgs.Empty); 2700 } 2701 UpdateGlobalLockTimeout(DateTime utcNow)2702 private void UpdateGlobalLockTimeout(DateTime utcNow) 2703 { 2704 uint uLockGlobal = Program.Config.Security.WorkspaceLocking.LockAfterGlobalTime; 2705 if(uLockGlobal == 0) { m_lLockAtGlobalTicks = long.MaxValue; return; } 2706 2707 uint? uLastInputTime = NativeMethods.GetLastInputTime(); 2708 if(!uLastInputTime.HasValue) return; 2709 2710 if(uLastInputTime.Value != m_uLastInputTime) 2711 { 2712 DateTime utcLockAt = utcNow.AddSeconds((double)uLockGlobal); 2713 m_lLockAtGlobalTicks = utcLockAt.Ticks; 2714 2715 m_uLastInputTime = uLastInputTime.Value; 2716 } 2717 } 2718 2719 /// <summary> 2720 /// Move selected entries. 2721 /// </summary> 2722 /// <param name="iMove">Must be -2/2 to move to top/bottom, -1/1 to 2723 /// move one up/down.</param> MoveSelectedEntries(int iMove)2724 private void MoveSelectedEntries(int iMove) 2725 { 2726 // Don't allow moving when sorting is enabled 2727 if((m_pListSorter.Column >= 0) && (m_pListSorter.Order != SortOrder.None)) 2728 return; 2729 2730 PwEntry[] vEntries = GetSelectedEntries(); 2731 Debug.Assert(vEntries != null); if(vEntries == null) return; 2732 Debug.Assert(vEntries.Length > 0); if(vEntries.Length == 0) return; 2733 2734 PwGroup pg = vEntries[0].ParentGroup; 2735 if(pg == null) { Debug.Assert(false); return; } 2736 foreach(PwEntry pe in vEntries) 2737 { 2738 if(pe.ParentGroup != pg) 2739 { 2740 MessageService.ShowWarning(KPRes.CannotMoveEntriesBcsGroup); 2741 return; 2742 } 2743 } 2744 2745 if((iMove == -1) || (iMove == 1)) 2746 pg.Entries.MoveOne(vEntries, (iMove < 0)); 2747 else if((iMove == -2) || (iMove == 2)) 2748 pg.Entries.MoveTopBottom(vEntries, (iMove < 0)); 2749 else { Debug.Assert(false); return; } 2750 2751 DateTime dtNow = DateTime.UtcNow; 2752 foreach(PwEntry peUpd in vEntries) peUpd.LocationChanged = dtNow; 2753 2754 // Blocking prevents correct scrolling when groups are enabled 2755 // and the scroll position is near the end 2756 // m_lvEntries.BeginUpdate(); 2757 2758 bool bScrollOne = ((Math.Abs(iMove) == 1) && 2759 !UIUtil.GetGroupsEnabled(m_lvEntries)); 2760 2761 int iTop = UIUtil.GetTopVisibleItem(m_lvEntries); 2762 ListView.SelectedIndexCollection lvsic = m_lvEntries.SelectedIndices; 2763 if(lvsic.Count > 0) 2764 { 2765 int pCrit = lvsic[(iMove < 0) ? 0 : (lvsic.Count - 1)]; 2766 2767 if(iMove < 0) 2768 { 2769 if(pCrit <= iTop) bScrollOne = false; 2770 else if((pCrit - iTop) <= 3) { } // Auto-scroll 2771 else bScrollOne = false; 2772 } 2773 else // iMove > 0 2774 { 2775 int nVisible = UIUtil.GetMaxVisibleItemCount(m_lvEntries); 2776 if(pCrit < (iTop + nVisible - 4)) bScrollOne = false; 2777 } 2778 } 2779 else { Debug.Assert(false); bScrollOne = false; } 2780 2781 if(bScrollOne) 2782 { 2783 // if(UIUtil.GetGroupsEnabled(m_lvEntries)) 2784 // { 2785 // if(m_lvEntries.Items.Count > 0) 2786 // { 2787 // int dy = m_lvEntries.Items[0].Bounds.Height; 2788 // if(dy > 1) 2789 // NativeMethods.Scroll(m_lvEntries, 0, 2790 // iMove * (dy + (dy / 4))); // With spacing added 2791 // } 2792 // } 2793 2794 // if(!UIUtil.GetGroupsEnabled(m_lvEntries)) 2795 // { 2796 iTop += iMove; 2797 iTop = Math.Max(Math.Min(iTop, m_lvEntries.Items.Count - 1), 0); 2798 UIUtil.SetTopVisibleItem(m_lvEntries, iTop, false); 2799 // } 2800 } 2801 2802 UpdateEntryList(null, false); 2803 EnsureVisibleSelected(iMove > 0); // In all cases 2804 2805 // m_lvEntries.EndUpdate(); 2806 UpdateUIState(true); 2807 } 2808 CreateStatusBarLogger()2809 public StatusBarLogger CreateStatusBarLogger() 2810 { 2811 StatusBarLogger sl = new StatusBarLogger(); 2812 sl.SetControls(m_statusPartInfo, m_statusPartProgress); 2813 return sl; 2814 } 2815 2816 /// <summary> 2817 /// Create a new warnings logger object that logs directly into 2818 /// the main status bar until the first warning is shown (in that 2819 /// case a dialog is opened displaying the warning). 2820 /// </summary> 2821 /// <returns>Reference to the new logger object.</returns> CreateShowWarningsLogger()2822 public ShowWarningsLogger CreateShowWarningsLogger() 2823 { 2824 StatusBarLogger sl = CreateStatusBarLogger(); 2825 return new ShowWarningsLogger(sl, this); 2826 } 2827 HandleHotKey(int wParam)2828 internal void HandleHotKey(int wParam) 2829 { 2830 if(HotKeyManager.HandleHotKeyIntoSelf(wParam)) return; 2831 2832 if(wParam == AppDefs.GlobalHotKeyId.AutoType) 2833 ExecuteGlobalAutoType(); 2834 else if(wParam == AppDefs.GlobalHotKeyId.AutoTypePassword) 2835 ExecuteGlobalAutoType(@"{PASSWORD}"); 2836 else if(wParam == AppDefs.GlobalHotKeyId.AutoTypeSelected) 2837 ExecuteEntryAutoType(); 2838 else if(wParam == AppDefs.GlobalHotKeyId.ShowWindow) 2839 { 2840 bool bWndVisible = ((this.WindowState != FormWindowState.Minimized) && 2841 !IsTrayed()); 2842 EnsureVisibleForegroundWindow(true, true); 2843 if(bWndVisible && IsFileLocked(null)) 2844 OnFileLock(null, EventArgs.Empty); // Unlock 2845 } 2846 else if(wParam == AppDefs.GlobalHotKeyId.EntryMenu) 2847 EntryMenu.Show(); 2848 else { Debug.Assert(false); } 2849 } 2850 WndProc(ref Message m)2851 protected override void WndProc(ref Message m) 2852 { 2853 if(m.Msg == NativeMethods.WM_HOTKEY) 2854 { 2855 NotifyUserActivity(); 2856 HandleHotKey((int)m.WParam); 2857 } 2858 else if((m.Msg == m_nAppMessage) && (m_nAppMessage != 0)) 2859 ProcessAppMessage(m.WParam, m.LParam); 2860 else if(m.Msg == NativeMethods.WM_SYSCOMMAND) 2861 { 2862 if((m.WParam == (IntPtr)NativeMethods.SC_MINIMIZE) || 2863 (m.WParam == (IntPtr)NativeMethods.SC_MAXIMIZE)) 2864 { 2865 SaveWindowPositionAndSize(); 2866 } 2867 } 2868 else if(m.Msg == NativeMethods.WM_SETTINGCHANGE) 2869 UIUtil.OnSystemSettingChange(); 2870 else if((m.Msg == NativeMethods.WM_POWERBROADCAST) && 2871 ((m.WParam == (IntPtr)NativeMethods.PBT_APMQUERYSUSPEND) || 2872 (m.WParam == (IntPtr)NativeMethods.PBT_APMSUSPEND))) 2873 { 2874 OnSessionLock(null, new SessionLockEventArgs(SessionLockReason.Suspend)); 2875 } 2876 else if((m.Msg == m_nTaskbarButtonMessage) && m_bTaskbarButtonMessage) 2877 { 2878 m_bTaskbarButtonMessage = false; 2879 UpdateUIState(false, null); // Set overlay icon 2880 m_bTaskbarButtonMessage = true; 2881 } 2882 else if(m.Msg == DwmUtil.WM_DWMSENDICONICTHUMBNAIL) 2883 { 2884 DwmUtil.SetIconicThumbnail(this, AppIcons.Default, ref m); 2885 return; 2886 } 2887 else if(m.Msg == DwmUtil.WM_DWMSENDICONICLIVEPREVIEWBITMAP) 2888 { 2889 DwmUtil.SetIconicPreview(this, AppIcons.Default, ref m); 2890 return; 2891 } 2892 2893 base.WndProc(ref m); 2894 } 2895 ProcessAppMessage(IntPtr wParam, IntPtr lParam)2896 public void ProcessAppMessage(IntPtr wParam, IntPtr lParam) 2897 { 2898 NotifyUserActivity(); 2899 2900 switch(wParam.ToInt64()) 2901 { 2902 case (long)Program.AppMessage.RestoreWindow: 2903 EnsureVisibleForegroundWindow(true, true); 2904 break; 2905 case (long)Program.AppMessage.Exit: 2906 OnFileExit(null, EventArgs.Empty); 2907 break; 2908 case (long)Program.AppMessage.IpcByFile: 2909 IpcUtilEx.ProcessGlobalMessage((int)lParam.ToInt64(), this, false); 2910 break; 2911 case (long)Program.AppMessage.AutoType: 2912 ExecuteGlobalAutoType(); 2913 break; 2914 case (long)Program.AppMessage.Lock: 2915 LockAllDocuments(); 2916 break; 2917 case (long)Program.AppMessage.Unlock: 2918 if(IsFileLocked(null)) 2919 { 2920 EnsureVisibleForegroundWindow(false, false); 2921 OnFileLock(null, EventArgs.Empty); // Unlock 2922 } 2923 break; 2924 case (long)Program.AppMessage.AutoTypeSelected: 2925 ExecuteEntryAutoType(); 2926 break; 2927 case (long)Program.AppMessage.Cancel: 2928 OnTrayCancel(null, EventArgs.Empty); 2929 break; 2930 case (long)Program.AppMessage.AutoTypePassword: 2931 ExecuteGlobalAutoType(@"{PASSWORD}"); 2932 break; 2933 case (long)Program.AppMessage.IpcByFile1: 2934 IpcUtilEx.ProcessGlobalMessage((int)lParam.ToInt64(), this, true); 2935 break; 2936 default: 2937 Debug.Assert(false); 2938 break; 2939 } 2940 } 2941 ExecuteGlobalAutoType()2942 public void ExecuteGlobalAutoType() 2943 { 2944 ExecuteGlobalAutoType(null); 2945 } 2946 ExecuteGlobalAutoType(string strSeq)2947 private void ExecuteGlobalAutoType(string strSeq) 2948 { 2949 if(m_bIsAutoTyping) return; 2950 m_bIsAutoTyping = true; 2951 2952 if(!IsAtLeastOneFileOpen()) 2953 { 2954 try 2955 { 2956 IntPtr hPrevWnd = NativeMethods.GetForegroundWindowHandle(); 2957 2958 EnsureVisibleForegroundWindow(false, false); 2959 2960 if(!IsCommandTypeInvokable(null, AppCommandType.Lock)) 2961 { 2962 m_bIsAutoTyping = false; 2963 return; 2964 } 2965 2966 // The window restoration function above maybe 2967 // restored the window already, therefore only 2968 // try to unlock if it's locked *now* 2969 if(IsFileLocked(null)) 2970 { 2971 // https://sourceforge.net/p/keepass/bugs/1163/ 2972 bool bFirst = true; 2973 EventHandler<GwmWindowEventArgs> eh = delegate(object sender, 2974 GwmWindowEventArgs e) 2975 { 2976 if(!bFirst) return; 2977 bFirst = false; 2978 GlobalWindowManager.ActivateTopWindow(); 2979 }; 2980 GlobalWindowManager.WindowAdded += eh; 2981 2982 OnFileLock(null, EventArgs.Empty); 2983 2984 GlobalWindowManager.WindowAdded -= eh; 2985 } 2986 2987 NativeMethods.EnsureForegroundWindow(hPrevWnd); 2988 } 2989 catch(Exception exAT) 2990 { 2991 MessageService.ShowWarning(exAT); 2992 } 2993 } 2994 if(!IsAtLeastOneFileOpen()) { m_bIsAutoTyping = false; return; } 2995 2996 try 2997 { 2998 AutoType.PerformGlobal(m_docMgr.GetOpenDatabases(), 2999 m_ilCurrentIcons, strSeq); 3000 } 3001 catch(Exception exGlobal) 3002 { 3003 MessageService.ShowWarning(exGlobal); 3004 } 3005 3006 m_bIsAutoTyping = false; 3007 } 3008 ExecuteEntryAutoType()3009 private void ExecuteEntryAutoType() 3010 { 3011 try 3012 { 3013 IntPtr hFG = NativeMethods.GetForegroundWindowHandle(); 3014 if(!AutoType.IsValidAutoTypeWindow(hFG, true)) return; 3015 } 3016 catch(Exception) { Debug.Assert(false); return; } 3017 3018 PwEntry peSel = GetSelectedEntry(true); 3019 if(peSel != null) 3020 AutoType.PerformIntoCurrentWindow(peSel, 3021 m_docMgr.SafeFindContainerOf(peSel)); 3022 else 3023 { 3024 EnsureVisibleForegroundWindow(true, true); 3025 MessageService.ShowWarning(KPRes.AutoTypeSelectedNoEntry, 3026 KPRes.AutoTypeGlobalHint); 3027 } 3028 } 3029 EnsureVisibleForegroundWindow(bool bUntray, bool bRestoreWindow)3030 public void EnsureVisibleForegroundWindow(bool bUntray, bool bRestoreWindow) 3031 { 3032 if(GlobalWindowManager.ActivateTopWindow()) return; 3033 3034 if(bUntray && IsTrayed()) MinimizeToTray(false); 3035 3036 if(bRestoreWindow && (this.WindowState == FormWindowState.Minimized)) 3037 UIUtil.SetWindowState(this, FormWindowState.Normal); 3038 3039 UIUtil.EnsureInsideScreen(this); 3040 3041 try 3042 { 3043 if(this.Visible) // && (this.WindowState != FormWindowState.Minimized) 3044 { 3045 this.BringToFront(); 3046 this.Activate(); 3047 } 3048 } 3049 catch(Exception) { Debug.Assert(false); } 3050 } 3051 SetListFont(AceFont font)3052 private void SetListFont(AceFont font) 3053 { 3054 if((font != null) && font.OverrideUIDefault) 3055 { 3056 m_tvGroups.Font = font.ToFont(); 3057 m_lvEntries.Font = font.ToFont(); 3058 m_richEntryView.Font = font.ToFont(); 3059 3060 Program.Config.UI.StandardFont = font; 3061 } 3062 else 3063 { 3064 if(UIUtil.VistaStyleListsSupported) 3065 { 3066 Font fontUI = UISystemFonts.ListFont; 3067 m_tvGroups.Font = fontUI; 3068 m_lvEntries.Font = fontUI; 3069 m_richEntryView.Font = fontUI; 3070 } 3071 3072 Program.Config.UI.StandardFont.OverrideUIDefault = false; 3073 } 3074 3075 m_fontExpired = FontUtil.CreateFont(m_lvEntries.Font, FontStyle.Strikeout); 3076 m_fontBoldUI = FontUtil.CreateFont(m_tabMain.Font, FontStyle.Bold); 3077 m_fontBoldTree = FontUtil.CreateFont(m_lvEntries.Font, FontStyle.Bold); 3078 m_fontItalicTree = FontUtil.CreateFont(m_lvEntries.Font, FontStyle.Italic); 3079 } 3080 SetSelectedEntryColor(Color clrBack)3081 private void SetSelectedEntryColor(Color clrBack) 3082 { 3083 PwDatabase pd = m_docMgr.ActiveDatabase; 3084 if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; } 3085 3086 PwEntry[] vSelected = GetSelectedEntries(); 3087 if((vSelected == null) || (vSelected.Length == 0)) return; 3088 3089 bool bMod = false; 3090 foreach(PwEntry pe in vSelected) 3091 { 3092 if(UIUtil.ColorsEqual(pe.BackgroundColor, clrBack)) continue; 3093 3094 pe.CreateBackup(pd); 3095 pe.BackgroundColor = clrBack; 3096 pe.Touch(true, false); 3097 3098 bMod = true; 3099 } 3100 3101 SelectEntries(new PwObjectList<PwEntry>(), true, false); // Ensure color visible 3102 RefreshEntriesList(); 3103 UpdateUIState(bMod); 3104 } 3105 OnCopyCustomString(object sender, DynamicMenuEventArgs e)3106 private void OnCopyCustomString(object sender, DynamicMenuEventArgs e) 3107 { 3108 string strKey = (e.Tag as string); 3109 if(string.IsNullOrEmpty(strKey)) { Debug.Assert(false); return; } 3110 3111 PwEntry pe = GetSelectedEntry(false); 3112 if(pe == null) { Debug.Assert(false); return; } 3113 3114 if(ClipboardUtil.CopyAndMinimize(pe.Strings.GetSafe(strKey), true, 3115 this, pe, m_docMgr.ActiveDatabase)) 3116 StartClipboardCountdown(); 3117 } 3118 SetMainWindowLayout(bool bSideBySide)3119 private void SetMainWindowLayout(bool bSideBySide) 3120 { 3121 if(!bSideBySide && (m_splitHorizontal.Orientation != Orientation.Horizontal)) 3122 { 3123 m_splitHorizontal.Orientation = Orientation.Horizontal; 3124 UpdateUIState(false); 3125 } 3126 else if(bSideBySide && (m_splitHorizontal.Orientation != Orientation.Vertical)) 3127 { 3128 m_splitHorizontal.Orientation = Orientation.Vertical; 3129 UpdateUIState(false); 3130 } 3131 3132 UIUtil.SetChecked(m_menuViewWindowsStacked, !bSideBySide); 3133 UIUtil.SetChecked(m_menuViewWindowsSideBySide, bSideBySide); 3134 } 3135 AssignMenuShortcuts()3136 private void AssignMenuShortcuts() 3137 { 3138 bool bMoveMono = MonoWorkarounds.IsRequired(1245); 3139 Keys kMoveMod = (bMoveMono ? (Keys.Control | Keys.Shift) : Keys.Alt); 3140 3141 UIUtil.AssignShortcut(m_menuFileNew, Keys.Control | Keys.N); 3142 UIUtil.AssignShortcut(m_menuFileOpenLocal, Keys.Control | Keys.O); 3143 UIUtil.AssignShortcut(m_menuFileOpenUrl, Keys.Control | Keys.Shift | Keys.O); 3144 UIUtil.AssignShortcut(m_menuFileClose, Keys.Control | Keys.W); 3145 UIUtil.AssignShortcut(m_menuFileSave, Keys.Control | Keys.S); 3146 UIUtil.AssignShortcut(m_menuFilePrintDatabase, Keys.Control | Keys.P); 3147 UIUtil.AssignShortcut(m_menuFileSyncFile, Keys.Control | Keys.R); 3148 UIUtil.AssignShortcut(m_menuFileSyncUrl, Keys.Control | Keys.Shift | Keys.R); 3149 UIUtil.AssignShortcut(m_menuFileLock, Keys.Control | Keys.L); 3150 UIUtil.AssignShortcut(m_menuFileExit, Keys.Control | Keys.Q); 3151 3152 UIUtil.AssignShortcut(m_menuGroupDelete, Keys.Delete, null, true); 3153 3154 UIUtil.AssignShortcut(m_menuGroupMoveToTop, (bMoveMono ? 3155 Keys.F5 : Keys.Home) | kMoveMod, null, true); 3156 UIUtil.AssignShortcut(m_menuGroupMoveOneUp, (bMoveMono ? 3157 Keys.F6 : Keys.Up) | kMoveMod, null, true); 3158 UIUtil.AssignShortcut(m_menuGroupMoveOneDown, (bMoveMono ? 3159 Keys.F7 : Keys.Down) | kMoveMod, null, true); 3160 UIUtil.AssignShortcut(m_menuGroupMoveToBottom, (bMoveMono ? 3161 Keys.F8 : Keys.End) | kMoveMod, null, true); 3162 // UIUtil.AssignShortcut(m_menuGroupSort, Keys.Control | Keys.Decimal); 3163 // UIUtil.AssignShortcut(m_menuGroupSortRec, Keys.Control | Keys.Shift | Keys.Decimal); // (Control+)Shift+Decimal = Delete 3164 UIUtil.AssignShortcut(m_menuGroupExpand, Keys.Control | Keys.Multiply); 3165 UIUtil.AssignShortcut(m_menuGroupCollapse, Keys.Control | Keys.Divide); 3166 3167 UIUtil.AssignShortcut(m_menuGroupPrint, Keys.Control | Keys.Shift | Keys.P); 3168 3169 UIUtil.AssignShortcut(m_menuEntryCopyUserName, Keys.Control | Keys.B); 3170 UIUtil.AssignShortcut(m_menuEntryCopyPassword, Keys.Control | Keys.C, 3171 null, true); 3172 UIUtil.AssignShortcut(m_menuEntryOpenUrl, Keys.Control | Keys.U); 3173 UIUtil.AssignShortcut(m_menuEntryCopyUrl, Keys.Control | Keys.Shift | Keys.U); 3174 UIUtil.AssignShortcut(m_menuEntryPerformAutoType, Keys.Control | Keys.V, 3175 null, true); 3176 3177 UIUtil.AssignShortcut(m_menuEntryAdd, Keys.Control | Keys.I); 3178 UIUtil.AssignShortcut(m_menuEntryEdit, Keys.Return, null, true); 3179 UIUtil.AssignShortcut(m_menuEntryDuplicate, Keys.Control | Keys.K); 3180 UIUtil.AssignShortcut(m_menuEntryDelete, Keys.Delete, null, true); 3181 3182 UIUtil.AssignShortcut(m_menuEntrySelectAll, Keys.Control | Keys.A, 3183 null, true); 3184 3185 UIUtil.AssignShortcut(m_menuEntryMoveToTop, (bMoveMono ? 3186 Keys.F5 : Keys.Home) | kMoveMod, null, true); 3187 UIUtil.AssignShortcut(m_menuEntryMoveOneUp, (bMoveMono ? 3188 Keys.F6 : Keys.Up) | kMoveMod, null, true); 3189 UIUtil.AssignShortcut(m_menuEntryMoveOneDown, (bMoveMono ? 3190 Keys.F7 : Keys.Down) | kMoveMod, null, true); 3191 UIUtil.AssignShortcut(m_menuEntryMoveToBottom, (bMoveMono ? 3192 Keys.F8 : Keys.End) | kMoveMod, null, true); 3193 3194 UIUtil.AssignShortcut(m_menuEntryClipCopy, Keys.Control | Keys.Shift | Keys.C, 3195 null, true); 3196 UIUtil.AssignShortcut(m_menuEntryClipPaste, Keys.Control | Keys.Shift | Keys.V, 3197 null, true); 3198 3199 UIUtil.AssignShortcut(m_menuFindInDatabase, Keys.Control | Keys.F); 3200 UIUtil.AssignShortcut(m_menuFindInGroup, Keys.Control | Keys.Shift | Keys.F, 3201 m_ctxGroupFind, false); 3202 3203 UIUtil.AssignShortcut(m_menuFindParentGroup, Keys.Control | Keys.G); 3204 3205 // UIUtil.AssignShortcut(m_menuViewHidePasswords, Keys.Control | Keys.H); 3206 // UIUtil.AssignShortcut(m_menuViewHideUserNames, Keys.Control | Keys.J); 3207 3208 UIUtil.AssignShortcut(m_menuHelpContents, Keys.F1); 3209 } 3210 ConstructContextMenus()3211 private void ConstructContextMenus() 3212 { 3213 m_ctxGroupList.SuspendLayout(); 3214 m_ctxPwList.SuspendLayout(); 3215 3216 ToolStripItemCollection tsicGC = m_ctxGroupList.Items; 3217 tsicGC.Insert(0, new ToolStripSeparator()); 3218 m_milMain.CreateCopy(tsicGC, null, false, m_menuGroupRearrange); 3219 tsicGC.Insert(0, new ToolStripSeparator()); 3220 m_milMain.CreateCopy(tsicGC, null, false, m_menuGroupEmptyRB); 3221 m_milMain.CreateCopy(tsicGC, null, false, m_menuGroupDelete); 3222 m_milMain.CreateCopy(tsicGC, null, false, m_menuGroupDuplicate); 3223 m_milMain.CreateCopy(tsicGC, null, false, m_menuGroupEdit); 3224 m_milMain.CreateCopy(tsicGC, null, false, m_menuGroupAdd); 3225 // tsicGC.Add(new ToolStripSeparator()); 3226 // m_milMain.CreateCopy(tsicGC, null, true, m_menuGroupPrint); 3227 // m_milMain.CreateCopy(tsicGC, null, true, m_menuGroupExport); 3228 3229 ToolStripItemCollection tsicEC = m_ctxPwList.Items; 3230 m_milMain.CreateCopy(tsicEC, null, false, m_menuEntryCopyPassword); 3231 m_milMain.CreateCopy(tsicEC, null, false, m_menuEntryCopyUserName); 3232 m_milMain.CreateLink(m_ctxEntryUrl, m_menuEntryUrl, false); 3233 3234 ToolStripItemCollection tsicECUrl = m_ctxEntryUrl.DropDownItems; 3235 m_milMain.CreateCopy(tsicECUrl, null, true, m_menuEntryOpenUrl); 3236 m_milMain.CreateCopy(tsicECUrl, null, true, m_menuEntryCopyUrl); 3237 3238 m_milMain.CreateLink(m_ctxEntryCopyString, m_menuEntryCopyString, false); 3239 m_milMain.CreateLink(m_ctxEntryAttachments, m_menuEntryAttachments, false); 3240 m_milMain.CreateCopy(tsicEC, m_ctxEntryAttachments, true, m_menuEntrySaveAttachedFiles); 3241 3242 m_milMain.CreateCopy(tsicEC, m_ctxEntryAutoTypeAdv, false, m_menuEntryPerformAutoType); 3243 m_milMain.CreateLink(m_ctxEntryAutoTypeAdv, m_menuEntryAutoTypeAdv, false); 3244 3245 m_milMain.CreateCopy(tsicEC, m_ctxEntryEditQuick, false, m_menuEntryAdd); 3246 m_milMain.CreateCopy(tsicEC, m_ctxEntryEditQuick, false, m_menuEntryEdit); 3247 m_milMain.CreateLink(m_ctxEntryEditQuick, m_menuEntryEditQuick, false); 3248 3249 ToolStripItemCollection tsicECQuick = m_ctxEntryEditQuick.DropDownItems; 3250 tsicECQuick.Insert(0, new ToolStripSeparator()); 3251 m_milMain.CreateCopy(tsicECQuick, null, false, m_menuEntryColor); 3252 m_milMain.CreateCopy(tsicECQuick, null, false, m_menuEntryIcon); 3253 3254 m_milMain.CreateLink(m_ctxEntryTagAdd, m_menuEntryTagAdd, false); 3255 m_milMain.CreateLink(m_ctxEntryTagRemove, m_menuEntryTagRemove, false); 3256 3257 ToolStripItemCollection tsicECTagAdd = m_ctxEntryTagAdd.DropDownItems; 3258 m_milMain.CreateCopy(tsicECTagAdd, null, false, m_menuEntryTagNew); 3259 3260 tsicECQuick.Add(new ToolStripSeparator()); 3261 m_milMain.CreateCopy(tsicECQuick, null, true, m_menuEntryExpiresNow); 3262 m_milMain.CreateCopy(tsicECQuick, null, true, m_menuEntryExpiresNever); 3263 3264 m_milMain.CreateCopy(tsicEC, m_ctxEntryEditQuick, true, m_menuEntryDelete); 3265 m_milMain.CreateCopy(tsicEC, m_ctxEntryEditQuick, true, m_menuEntryDuplicate); 3266 3267 ToolStripSeparator ts = new ToolStripSeparator(); 3268 tsicEC.Insert(tsicEC.IndexOf(m_ctxEntryRearrange), ts); 3269 m_milMain.CreateCopy(tsicEC, ts, false, m_menuEntrySelectAll); 3270 3271 m_milMain.CreateLink(m_ctxEntryRearrange, m_menuEntryRearrange, false); 3272 m_milMain.CreateLink(m_ctxEntryMoveToGroup, m_menuEntryMoveToGroup, false); 3273 3274 ToolStripItemCollection tsicECMove = m_ctxEntryRearrange.DropDownItems; 3275 tsicECMove.Insert(0, new ToolStripSeparator()); 3276 m_milMain.CreateCopy(tsicECMove, null, false, m_menuEntryMoveToBottom); 3277 m_milMain.CreateCopy(tsicECMove, null, false, m_menuEntryMoveOneDown); 3278 m_milMain.CreateCopy(tsicECMove, null, false, m_menuEntryMoveOneUp); 3279 m_milMain.CreateCopy(tsicECMove, null, false, m_menuEntryMoveToTop); 3280 m_milMain.CreateCopy(tsicECMove, null, true, m_menuEntryMoveToPreviousParent); 3281 3282 m_ctxPwList.ResumeLayout(true); 3283 m_ctxGroupList.ResumeLayout(true); 3284 } 3285 CopyMenuItemText(ToolStripMenuItem tsmiTarget, ToolStripMenuItem tsmiCopyFrom, string strTextOpt)3286 private static void CopyMenuItemText(ToolStripMenuItem tsmiTarget, 3287 ToolStripMenuItem tsmiCopyFrom, string strTextOpt) 3288 { 3289 tsmiTarget.Text = (strTextOpt ?? tsmiCopyFrom.Text); 3290 3291 string strSh = tsmiCopyFrom.ShortcutKeyDisplayString; 3292 if(!string.IsNullOrEmpty(strSh)) 3293 tsmiTarget.ShortcutKeyDisplayString = strSh; 3294 } 3295 3296 // Public for plugins SaveDatabaseAs(PwDatabase pdToSave, IOConnectionInfo iocTo, bool bOnline, object sender, bool bCopy)3297 public void SaveDatabaseAs(PwDatabase pdToSave, IOConnectionInfo iocTo, 3298 bool bOnline, object sender, bool bCopy) 3299 { 3300 PwDatabase pd = (pdToSave ?? m_docMgr.ActiveDatabase); 3301 3302 if(!pd.IsOpen) return; 3303 if(!AppPolicy.Try(AppPolicyId.SaveFile)) return; 3304 3305 Guid eventGuid = Guid.NewGuid(); 3306 if(this.FileSavingPre != null) 3307 { 3308 FileSavingEventArgs args = new FileSavingEventArgs(true, bCopy, pd, eventGuid); 3309 this.FileSavingPre(sender, args); 3310 if(args.Cancel) return; 3311 } 3312 if(this.FileSaving != null) 3313 { 3314 FileSavingEventArgs args = new FileSavingEventArgs(true, bCopy, pd, eventGuid); 3315 this.FileSaving(sender, args); 3316 if(args.Cancel) return; 3317 } 3318 3319 DialogResult dr; 3320 IOConnectionInfo ioc = iocTo; 3321 3322 if((ioc != null) && (ioc.Path.Length > 0)) 3323 { 3324 dr = DialogResult.OK; // Caller (plugin) specified target file 3325 } 3326 else if(bOnline) 3327 { 3328 IOConnectionForm iocf = new IOConnectionForm(); 3329 iocf.InitEx(true, pd.IOConnectionInfo, true, true); 3330 3331 dr = iocf.ShowDialog(); 3332 ioc = iocf.IOConnectionInfo; 3333 UIUtil.DestroyForm(iocf); 3334 } 3335 else 3336 { 3337 SaveFileDialogEx sfdDb = UIUtil.CreateSaveFileDialog(KPRes.SaveDatabase, 3338 UrlUtil.GetFileName(pd.IOConnectionInfo.Path), 3339 UIUtil.CreateFileTypeFilter(AppDefs.FileExtension.FileExt, 3340 KPRes.KdbxFiles, true), 1, AppDefs.FileExtension.FileExt, 3341 AppDefs.FileDialogContext.Database); 3342 3343 GlobalWindowManager.AddDialog(sfdDb.FileDialog); 3344 dr = sfdDb.ShowDialog(); 3345 GlobalWindowManager.RemoveDialog(sfdDb.FileDialog); 3346 3347 if(dr == DialogResult.OK) 3348 ioc = IOConnectionInfo.FromPath(sfdDb.FileName); 3349 } 3350 3351 if(dr == DialogResult.OK) 3352 { 3353 EcasPropertyDictionary dProps = new EcasPropertyDictionary(); 3354 dProps.Set(EcasProperty.IOConnectionInfo, ioc); 3355 dProps.Set(EcasProperty.Database, pd); 3356 Program.TriggerSystem.RaiseEvent(EcasEventIDs.SavingDatabaseFile, 3357 dProps); 3358 3359 UIBlockInteraction(true); 3360 3361 ShutdownBlocker sdb = new ShutdownBlocker(this.Handle, KPRes.SavingDatabase); 3362 ShowWarningsLogger swLogger = CreateShowWarningsLogger(); 3363 swLogger.StartLogging(KPRes.SavingDatabase, true); 3364 m_sCancellable.Push(swLogger); 3365 3366 bool bSuccess = true; 3367 try 3368 { 3369 PreSavingEx(pd, ioc); 3370 pd.SaveAs(ioc, !bCopy, swLogger); 3371 PostSavingEx(!bCopy, pd, ioc, swLogger); 3372 } 3373 catch(Exception exSaveAs) 3374 { 3375 MessageService.ShowSaveWarning(ioc, exSaveAs, true); 3376 bSuccess = false; 3377 } 3378 3379 m_sCancellable.Pop(); 3380 swLogger.EndLogging(); 3381 sdb.Dispose(); 3382 3383 // Immediately after the UIBlockInteraction call the form might 3384 // be closed and UpdateUIState might crash, if the order of the 3385 // two methods is swapped; so first update state, then unblock 3386 UpdateUIState(false); 3387 UIBlockInteraction(false); 3388 3389 if(this.FileSaved != null) 3390 { 3391 FileSavedEventArgs args = new FileSavedEventArgs(bSuccess, pd, eventGuid); 3392 this.FileSaved(sender, args); 3393 } 3394 if(bSuccess) 3395 Program.TriggerSystem.RaiseEvent(EcasEventIDs.SavedDatabaseFile, 3396 dProps); 3397 } 3398 } 3399 PreSavingEx(PwDatabase pd, IOConnectionInfo ioc)3400 private void PreSavingEx(PwDatabase pd, IOConnectionInfo ioc) 3401 { 3402 PerformSelfTest(); 3403 3404 FixDuplicateUuids(pd, ioc); 3405 3406 pd.UseFileTransactions = Program.Config.Application.UseTransactedFileWrites; 3407 pd.UseFileLocks = Program.Config.Application.UseFileLocks; 3408 3409 // AceColumn col = Program.Config.MainWindow.FindColumn(AceColumnType.Title); 3410 // if((col != null) && !col.HideWithAsterisks) 3411 // pd.MemoryProtection.ProtectTitle = false; 3412 // col = Program.Config.MainWindow.FindColumn(AceColumnType.UserName); 3413 // if((col != null) && !col.HideWithAsterisks) 3414 // pd.MemoryProtection.ProtectUserName = false; 3415 // col = Program.Config.MainWindow.FindColumn(AceColumnType.Url); 3416 // if((col != null) && !col.HideWithAsterisks) 3417 // pd.MemoryProtection.ProtectUrl = false; 3418 // col = Program.Config.MainWindow.FindColumn(AceColumnType.Notes); 3419 // if((col != null) && !col.HideWithAsterisks) 3420 // pd.MemoryProtection.ProtectNotes = false; 3421 3422 if(pd == m_docMgr.ActiveDatabase) SaveWindowState(); 3423 } 3424 PostSavingEx(bool bPrimary, PwDatabase pwDatabase, IOConnectionInfo ioc, IStatusLogger sl)3425 private void PostSavingEx(bool bPrimary, PwDatabase pwDatabase, 3426 IOConnectionInfo ioc, IStatusLogger sl) 3427 { 3428 if(ioc == null) { Debug.Assert(false); return; } 3429 3430 byte[] pbIO = null; 3431 if(Program.Config.Application.VerifyWrittenFileAfterSaving) 3432 { 3433 pbIO = WinUtil.HashFile(ioc); 3434 Debug.Assert((pbIO != null) && (pwDatabase.HashOfLastIO != null)); 3435 if(!MemUtil.ArraysEqual(pbIO, pwDatabase.HashOfLastIO)) 3436 MessageService.ShowWarning(ioc.GetDisplayName(), 3437 KPRes.FileVerifyHashFail, KPRes.FileVerifyHashFailRec); 3438 } 3439 3440 if(bPrimary) 3441 { 3442 #if DEBUG 3443 Debug.Assert(MemUtil.ArraysEqual(pbIO, pwDatabase.HashOfFileOnDisk)); 3444 3445 try 3446 { 3447 PwDatabase pwCheck = new PwDatabase(); 3448 pwCheck.Open(ioc.CloneDeep(), pwDatabase.MasterKey, null); 3449 3450 Debug.Assert(MemUtil.ArraysEqual(pwDatabase.HashOfLastIO, 3451 pwCheck.HashOfLastIO)); 3452 3453 uint uGroups1, uGroups2, uEntries1, uEntries2; 3454 pwDatabase.RootGroup.GetCounts(true, out uGroups1, out uEntries1); 3455 pwCheck.RootGroup.GetCounts(true, out uGroups2, out uEntries2); 3456 Debug.Assert((uGroups1 == uGroups2) && (uEntries1 == uEntries2)); 3457 } 3458 catch(Exception exVerify) { Debug.Assert(false, exVerify.Message); } 3459 #endif 3460 3461 m_mruList.AddItem(ioc.GetDisplayName(), ioc.CloneDeep()); 3462 3463 // SetLastUsedFile(ioc); 3464 3465 // if(Program.Config.Application.CreateBackupFileAfterSaving && bHashValid) 3466 // { 3467 // try { pwDatabase.CreateBackupFile(sl); } 3468 // catch(Exception exBackup) 3469 // { 3470 // MessageService.ShowWarning(KPRes.FileBackupFailed, exBackup); 3471 // } 3472 // } 3473 3474 // ulong uTotalBinSize = 0; 3475 // EntryHandler ehCnt = delegate(PwEntry pe) 3476 // { 3477 // foreach(KeyValuePair<string, ProtectedBinary> kvpCnt in pe.Binaries) 3478 // { 3479 // uTotalBinSize += kvpCnt.Value.Length; 3480 // } 3481 // 3482 // return true; 3483 // }; 3484 // pwDatabase.RootGroup.TraverseTree(TraversalMethod.PreOrder, null, ehCnt); 3485 } 3486 3487 RememberKeySources(pwDatabase); 3488 WinUtil.FlushStorageBuffers(ioc.Path, true); 3489 } 3490 UIFileSave(bool bForceSave)3491 public bool UIFileSave(bool bForceSave) 3492 { 3493 Control cFocus = UIUtil.GetActiveControl(this); 3494 3495 PwDatabase pd = m_docMgr.ActiveDatabase; 3496 pd.Modified = true; 3497 3498 if(bForceSave) ++m_uForceSave; 3499 OnFileSave(null, EventArgs.Empty); 3500 if(bForceSave) --m_uForceSave; 3501 3502 if(cFocus != null) ResetDefaultFocus(cFocus); 3503 return !pd.Modified; 3504 } 3505 ResetDefaultFocus(Control cExplicit)3506 public void ResetDefaultFocus(Control cExplicit) 3507 { 3508 Control c = cExplicit; 3509 3510 if(c == null) 3511 { 3512 // QuickFind must be the first choice (see e.g. 3513 // the option FocusQuickFindOnUntray) 3514 if(m_tbQuickFind.Visible && m_tbQuickFind.Enabled) 3515 c = m_tbQuickFind.Control; 3516 else if(m_lvEntries.Visible && m_lvEntries.Enabled) 3517 c = m_lvEntries; 3518 else if(m_tvGroups.Visible && m_tvGroups.Enabled) 3519 c = m_tvGroups; 3520 else if(m_richEntryView.Visible && m_richEntryView.Enabled) 3521 c = m_richEntryView; 3522 else c = m_lvEntries; 3523 } 3524 3525 if(this.FocusChanging != null) 3526 { 3527 FocusEventArgs ea = new FocusEventArgs(cExplicit, c); 3528 this.FocusChanging(null, ea); 3529 if(ea.Cancel) return; 3530 } 3531 3532 UIUtil.SetFocus(c, this); 3533 } 3534 PrepareLock()3535 private static bool PrepareLock() 3536 { 3537 if(GlobalWindowManager.WindowCount == 0) return true; 3538 3539 if(GlobalWindowManager.CanCloseAllWindows) 3540 { 3541 GlobalWindowManager.CloseAllWindows(); 3542 return true; 3543 } 3544 3545 return false; 3546 } 3547 UpdateImageLists(bool bForce)3548 private void UpdateImageLists(bool bForce) 3549 { 3550 if(!bForce) 3551 { 3552 if(!m_docMgr.ActiveDatabase.UINeedsIconUpdate) return; 3553 m_docMgr.ActiveDatabase.UINeedsIconUpdate = false; 3554 } 3555 3556 int cx = DpiUtil.ScaleIntX(16); 3557 int cy = DpiUtil.ScaleIntY(16); 3558 3559 // // foreach(Image img in m_ilClientIcons.Images) imgList.Images.Add(img); 3560 // List<Image> lStdImages = new List<Image>(); 3561 // foreach(Image imgStd in m_ilClientIcons.Images) 3562 // { 3563 // // Plugins may supply DPI-scaled images by changing m_ilClientIcons 3564 // bool bStd = (imgStd.Height == 16); 3565 // lStdImages.Add(bStd ? DpiUtil.ScaleImage(imgStd, false) : imgStd); 3566 // } 3567 3568 if(m_lStdClientImages == null) 3569 { 3570 ImageArchive arStd = new ImageArchive(); 3571 arStd.Load(DpiUtil.ScalingRequired ? 3572 Properties.Resources.Images_Client_HighRes : 3573 Properties.Resources.Images_Client_16); 3574 3575 m_lStdClientImages = arStd.GetImages(cx, cy, true); 3576 } 3577 3578 // ImageList imgListCustom = UIUtil.BuildImageList( 3579 // m_docMgr.ActiveDatabase.CustomIcons, cx, cy); 3580 // foreach(Image imgCustom in imgListCustom.Images) 3581 // imgList.Images.Add(imgCustom); // Breaks alpha partially 3582 List<Image> lCustom = UIUtil.BuildImageListEx( 3583 m_docMgr.ActiveDatabase.CustomIcons, cx, cy); 3584 3585 List<Image> lAll = new List<Image>(m_lStdClientImages); 3586 lAll.AddRange(lCustom); 3587 3588 ImageList imgList = new ImageList(); 3589 imgList.ImageSize = new Size(cx, cy); 3590 imgList.ColorDepth = ColorDepth.Depth32Bit; 3591 3592 imgList.Images.AddRange(lAll.ToArray()); 3593 Debug.Assert(imgList.Images.Count == ((int)PwIcon.Count + lCustom.Count)); 3594 3595 m_ilCurrentIcons = imgList; 3596 3597 if(UIUtil.VistaStyleListsSupported) 3598 { 3599 m_tvGroups.ImageList = imgList; 3600 m_lvEntries.SmallImageList = imgList; 3601 } 3602 else 3603 { 3604 ImageList imgSafe = UIUtil.ConvertImageList24(lAll, 3605 cx, cy, AppDefs.ColorControlNormal); 3606 m_tvGroups.ImageList = imgSafe; // TreeView doesn't fully support alpha on < Vista 3607 m_lvEntries.SmallImageList = ((WinUtil.IsAtLeastWindowsVista || 3608 WinUtil.IsWindowsXP) ? imgList : imgSafe); 3609 } 3610 } 3611 MoveSelectedGroup(int iMove)3612 private void MoveSelectedGroup(int iMove) 3613 { 3614 PwGroup pgMove = GetSelectedGroup(); 3615 if(pgMove == null) { Debug.Assert(false); return; } 3616 3617 PwGroup pgParent = pgMove.ParentGroup; 3618 if(pgParent == null) return; 3619 3620 PwGroup[] pgAffected = new PwGroup[] { pgMove }; 3621 3622 if(iMove == -2) 3623 pgParent.Groups.MoveTopBottom(pgAffected, true); 3624 else if(iMove == -1) 3625 pgParent.Groups.MoveOne(pgMove, true); 3626 else if(iMove == 1) 3627 pgParent.Groups.MoveOne(pgMove, false); 3628 else if(iMove == 2) 3629 pgParent.Groups.MoveTopBottom(pgAffected, false); 3630 else { Debug.Assert(false); return; } 3631 3632 pgMove.LocationChanged = DateTime.UtcNow; 3633 UpdateUI(false, null, true, null, true, null, true); 3634 } 3635 OnEntryBinaryOpen(object sender, DynamicMenuEventArgs e)3636 private void OnEntryBinaryOpen(object sender, DynamicMenuEventArgs e) 3637 { 3638 if(e == null) { Debug.Assert(false); return; } 3639 3640 EntryBinaryDataContext ctx = (e.Tag as EntryBinaryDataContext); 3641 if(ctx == null) { Debug.Assert(false); return; } 3642 3643 PwEntry pe = ctx.Entry; 3644 if(pe == null) { Debug.Assert(false); return; } 3645 Debug.Assert(object.ReferenceEquals(pe, GetSelectedEntry(false))); 3646 3647 if(string.IsNullOrEmpty(ctx.Name)) { Debug.Assert(false); return; } 3648 ProtectedBinary pb = pe.Binaries.Get(ctx.Name); 3649 if(pb == null) { Debug.Assert(false); return; } 3650 3651 ProtectedBinary pbMod = BinaryDataUtil.Open(ctx.Name, pb, ctx.Options); 3652 if(pbMod != null) 3653 { 3654 PwDatabase pd = m_docMgr.FindContainerOf(pe); 3655 Debug.Assert(object.ReferenceEquals(pd, this.ActiveDatabase)); 3656 pe.CreateBackup(pd); 3657 3658 pe.Binaries.Set(ctx.Name, pbMod); 3659 pe.Touch(true, false); 3660 3661 RefreshEntriesList(); 3662 UpdateUIState(true); 3663 } 3664 } 3665 SaveWindowState()3666 private void SaveWindowState() 3667 { 3668 PwDatabase pd = m_docMgr.ActiveDatabase; 3669 3670 PwGroup pgSelected = GetSelectedGroup(); 3671 if(pgSelected != null) 3672 pd.LastSelectedGroup = new PwUuid(pgSelected.Uuid.UuidBytes); 3673 3674 TreeNode tnTop = m_tvGroups.TopNode; 3675 if(tnTop != null) 3676 { 3677 PwGroup pgTop = (tnTop.Tag as PwGroup); 3678 if(pgTop != null) 3679 pd.LastTopVisibleGroup = new PwUuid(pgTop.Uuid.UuidBytes); 3680 else { Debug.Assert(false); } 3681 } 3682 3683 PwEntry peTop = GetTopEntry(); 3684 if((peTop != null) && (pgSelected != null)) 3685 pgSelected.LastTopVisibleEntry = new PwUuid(peTop.Uuid.UuidBytes); 3686 } 3687 RestoreWindowState(PwDatabase pd)3688 private void RestoreWindowState(PwDatabase pd) 3689 { 3690 PwGroup pgSelect = null; 3691 3692 if(!pd.LastSelectedGroup.Equals(PwUuid.Zero)) 3693 { 3694 pgSelect = pd.RootGroup.FindGroup(pd.LastSelectedGroup, true); 3695 UpdateGroupList(pgSelect); 3696 UpdateEntryList(pgSelect, false); 3697 } 3698 3699 SetTopVisibleGroup(pd.LastTopVisibleGroup); 3700 if(pgSelect != null) SetTopVisibleEntry(pgSelect.LastTopVisibleEntry); 3701 } 3702 SetTopVisibleGroup(PwUuid uuidGroup)3703 private void SetTopVisibleGroup(PwUuid uuidGroup) 3704 { 3705 TreeNode tnTop = GuiFindGroup(uuidGroup, null); 3706 if(tnTop != null) m_tvGroups.TopNode = tnTop; 3707 } 3708 SetTopVisibleEntry(PwUuid uuidEntry)3709 private void SetTopVisibleEntry(PwUuid uuidEntry) 3710 { 3711 ListViewItem lviTop = GuiFindEntry(uuidEntry); 3712 if(lviTop != null) 3713 UIUtil.SetTopVisibleItem(m_lvEntries, lviTop.Index, false); 3714 } 3715 CloseDocument(PwDocument dsToClose, bool bLocking, bool bExiting, bool bEcas, bool bUpdateUI)3716 internal void CloseDocument(PwDocument dsToClose, bool bLocking, 3717 bool bExiting, bool bEcas, bool bUpdateUI) 3718 { 3719 PwDocument ds = (dsToClose ?? m_docMgr.ActiveDocument); 3720 PwDatabase pd = ds.Database; 3721 bool bIsActive = (ds == m_docMgr.ActiveDocument); 3722 3723 FileEventFlags f = FileEventFlags.None; 3724 if(bLocking) f |= FileEventFlags.Locking; 3725 if(bExiting) f |= FileEventFlags.Exiting; 3726 if(bEcas) f |= FileEventFlags.Ecas; 3727 3728 Program.TriggerSystem.RaiseEvent(EcasEventIDs.ClosingDatabaseFilePre, 3729 EcasProperty.Database, pd); 3730 if(this.FileClosingPre != null) 3731 { 3732 FileClosingEventArgs fcea = new FileClosingEventArgs(pd, f); 3733 this.FileClosingPre(null, fcea); 3734 if(fcea.Cancel) return; 3735 } 3736 3737 if(pd.Modified) // Implies pd.IsOpen 3738 { 3739 bool bInvokeSave = false; 3740 3741 // https://sourceforge.net/p/keepass/discussion/329220/thread/c3c823c6/ 3742 bool bCanAutoSave = AppPolicy.Current.SaveFile; 3743 3744 if(Program.Config.Application.FileClosing.AutoSave && bCanAutoSave) 3745 bInvokeSave = true; 3746 else 3747 { 3748 FileSaveOrigin fso = FileSaveOrigin.Closing; 3749 if(bLocking) fso = FileSaveOrigin.Locking; 3750 if(bExiting) fso = FileSaveOrigin.Exiting; 3751 3752 DialogResult dr = FileDialogsEx.ShowFileSaveQuestion( 3753 pd.IOConnectionInfo.GetDisplayName(), fso); 3754 3755 if(dr == DialogResult.Cancel) return; 3756 else if(dr == DialogResult.Yes) bInvokeSave = true; 3757 else if(dr == DialogResult.No) { } // Changes are lost 3758 } 3759 3760 if(bInvokeSave) 3761 { 3762 SaveDatabase(pd, null); 3763 if(pd.Modified) return; 3764 } 3765 } 3766 3767 Program.TriggerSystem.RaiseEvent(EcasEventIDs.ClosingDatabaseFilePost, 3768 EcasProperty.Database, pd); 3769 if(this.FileClosingPost != null) 3770 { 3771 FileClosingEventArgs fcea = new FileClosingEventArgs(pd, f); 3772 this.FileClosingPost(null, fcea); 3773 if(fcea.Cancel) return; 3774 } 3775 3776 IOConnectionInfo ioClosing = pd.IOConnectionInfo.CloneDeep(); 3777 3778 pd.Close(); 3779 if(!bLocking) m_docMgr.CloseDatabase(pd); 3780 3781 if(bIsActive) 3782 { 3783 if((Program.Config.UI.UIFlags & (ulong)AceUIFlags.NoQuickSearchClear) == 0) 3784 { 3785 ++m_uBlockQuickFind; 3786 m_tbQuickFind.Items.Clear(); 3787 m_tbQuickFind.Text = string.Empty; 3788 --m_uBlockQuickFind; 3789 } 3790 3791 if(!bLocking) 3792 { 3793 m_docMgr.ActiveDatabase.UINeedsIconUpdate = true; 3794 ResetDefaultFocus(null); 3795 } 3796 } 3797 if(bUpdateUI) 3798 UpdateUI(true, null, true, null, true, null, false); 3799 3800 // NativeMethods.ClearIconicBitmaps(this.Handle); 3801 Program.TempFilesPool.Clear(TempClearFlags.ContentTaggedFiles); 3802 3803 if(this.FileClosed != null) 3804 { 3805 FileClosedEventArgs fcea = new FileClosedEventArgs(ioClosing, f); 3806 this.FileClosed(null, fcea); 3807 } 3808 } 3809 3810 // Public for plugins LockAllDocuments()3811 public void LockAllDocuments() 3812 { 3813 NotifyUserActivity(); 3814 3815 if(Program.Config.Security.WorkspaceLocking.AlwaysExitInsteadOfLocking) 3816 { 3817 OnFileExit(null, EventArgs.Empty); 3818 return; 3819 } 3820 3821 if(UIIsInteractionBlocked()) { Debug.Assert(false); return; } 3822 if(!PrepareLock()) return; // Tries to close windows 3823 3824 SaveWindowState(); 3825 3826 List<PwDocument> lDocs = m_docMgr.GetDocuments(int.MaxValue); 3827 foreach(PwDocument ds in lDocs) 3828 { 3829 PwDatabase pd = ds.Database; 3830 if(!pd.IsOpen) continue; // Nothing to lock 3831 3832 IOConnectionInfo ioIoc = pd.IOConnectionInfo; 3833 Debug.Assert(ioIoc != null); 3834 3835 CloseDocument(ds, true, false, false, false); 3836 if(pd.IsOpen) continue; 3837 3838 ds.LockedIoc = ioIoc; 3839 } 3840 3841 UpdateUI(true, null, true, null, true, null, false); 3842 3843 if(Program.Config.MainWindow.MinimizeAfterLocking && 3844 !IsAtLeastOneFileOpen()) 3845 UIUtil.SetWindowState(this, FormWindowState.Minimized); 3846 } 3847 SaveAllDocuments()3848 private void SaveAllDocuments() 3849 { 3850 List<PwDocument> lDocs = m_docMgr.GetDocuments(int.MaxValue); 3851 foreach(PwDocument ds in lDocs) 3852 { 3853 PwDatabase pd = ds.Database; 3854 if(!IsFileLocked(ds) && pd.Modified) 3855 SaveDatabase(pd, null); 3856 } 3857 3858 UpdateUI(false, null, true, null, true, null, false); 3859 } 3860 3861 // Does not update the UI (for performance when exiting) CloseAllDocuments(bool bExiting)3862 private bool CloseAllDocuments(bool bExiting) 3863 { 3864 if(UIIsInteractionBlocked()) { Debug.Assert(false); return false; } 3865 3866 while(true) 3867 { 3868 List<PwDocument> lDocs = m_docMgr.GetDocuments(int.MaxValue); 3869 if(lDocs.Count <= 0) { Debug.Assert(false); break; } 3870 3871 PwDocument ds = lDocs[0]; 3872 if((lDocs.Count == 1) && !ds.Database.IsOpen) break; 3873 3874 CloseDocument(ds, false, bExiting, false, false); 3875 if(ds.Database.IsOpen) return false; 3876 } 3877 3878 return true; 3879 } 3880 RecreateUITabs()3881 private void RecreateUITabs() 3882 { 3883 ++m_uTabChangeBlocked; 3884 m_tabMain.SuspendLayout(); 3885 3886 m_tabMain.TabPages.Clear(); 3887 3888 m_tabMain.ImageList = null; 3889 if(m_ilTabImages != null) 3890 { 3891 m_ilTabImages.Dispose(); 3892 m_ilTabImages = null; 3893 } 3894 foreach(Image img in m_lTabImages) img.Dispose(); 3895 m_lTabImages.Clear(); 3896 3897 bool bImgs = !MonoWorkarounds.IsRequired(891029); 3898 3899 List<TabPage> lPages = new List<TabPage>(); 3900 for(int i = 0; i < m_docMgr.Documents.Count; ++i) 3901 { 3902 PwDocument ds = m_docMgr.Documents[i]; 3903 if(ds == null) { Debug.Assert(false); continue; } 3904 3905 TabPage tb = new TabPage(); 3906 tb.Tag = ds; 3907 3908 string strName, strTip; 3909 GetTabText(ds, out strName, out strTip); 3910 tb.Text = strName; 3911 tb.ToolTipText = strTip; 3912 3913 if(bImgs && (ds.Database != null) && ds.Database.IsOpen && 3914 !UIUtil.ColorsEqual(ds.Database.Color, Color.Empty)) 3915 { 3916 Image img = UIUtil.CreateTabColorImage(AppIcons.RoundColor( 3917 ds.Database.Color), m_tabMain); 3918 if(img != null) 3919 { 3920 m_lTabImages.Add(img); 3921 tb.ImageIndex = m_lTabImages.Count - 1; 3922 } 3923 else { Debug.Assert(false); } 3924 } 3925 3926 lPages.Add(tb); 3927 } 3928 3929 if(m_lTabImages.Count > 0) 3930 { 3931 int qSize = m_lTabImages[0].Height; 3932 m_ilTabImages = UIUtil.BuildImageListUnscaled(m_lTabImages, qSize, qSize); 3933 m_tabMain.ImageList = m_ilTabImages; 3934 } 3935 3936 m_tabMain.TabPages.AddRange(lPages.ToArray()); 3937 3938 // m_tabMain.SelectedTab.Font = m_fontBoldUI; 3939 3940 m_tabMain.ResumeLayout(); 3941 --m_uTabChangeBlocked; 3942 } 3943 SelectUITab()3944 private void SelectUITab() 3945 { 3946 ++m_uTabChangeBlocked; 3947 3948 PwDocument dsSelect = m_docMgr.ActiveDocument; 3949 foreach(TabPage tb in m_tabMain.TabPages) 3950 { 3951 if((PwDocument)tb.Tag == dsSelect) 3952 { 3953 m_tabMain.SelectedTab = tb; 3954 break; 3955 } 3956 } 3957 3958 --m_uTabChangeBlocked; 3959 } 3960 MakeDocumentActive(PwDocument ds)3961 public void MakeDocumentActive(PwDocument ds) 3962 { 3963 if(ds == null) { Debug.Assert(false); return; } 3964 3965 ds.Database.UINeedsIconUpdate = true; 3966 3967 UpdateUI(false, ds, true, null, true, null, false); 3968 3969 RestoreWindowState(ds.Database); 3970 UpdateUIState(false); 3971 } 3972 GetTabText(PwDocument ds, out string strName, out string strTip)3973 private void GetTabText(PwDocument ds, out string strName, out string strTip) 3974 { 3975 if(ds == null) { Debug.Assert(false); strName = string.Empty; strTip = string.Empty; return; } 3976 3977 bool bLocked = IsFileLocked(ds); 3978 PwDatabase pd = (bLocked ? null : ds.Database); 3979 if((pd == null) || !pd.IsOpen) bLocked = true; 3980 string strPath = (bLocked ? ds.LockedIoc.Path : pd.IOConnectionInfo.Path); 3981 3982 strName = strPath; 3983 // if(!Program.Config.MainWindow.ShowFullPathOnTab) 3984 strName = UrlUtil.GetFileName(strName); 3985 3986 strTip = strPath; 3987 3988 if(bLocked) strName += " [" + KPRes.Locked + "]"; 3989 else 3990 { 3991 // if(Program.Config.MainWindow.ShowDatabaseNameOnTab && (pd.Name.Length != 0)) 3992 // strName += " (" + pd.Name + ")"; 3993 3994 if(pd.Modified) strName += "*"; 3995 3996 if(pd.Name.Length != 0) 3997 strTip += "\r\n\r\n" + pd.Name; 3998 if(pd.Description.Length != 0) 3999 strTip += "\r\n\r\n" + pd.Description; 4000 } 4001 4002 strTip = StrUtil.EncodeToolTipText(strTip); 4003 } 4004 UpdateUITabs()4005 private void UpdateUITabs() 4006 { 4007 foreach(TabPage tp in m_tabMain.TabPages) 4008 { 4009 PwDocument ds = (tp.Tag as PwDocument); 4010 if(ds == null) { Debug.Assert(false); continue; } 4011 4012 string strName, strTip; 4013 GetTabText(ds, out strName, out strTip); 4014 tp.Text = strName; 4015 tp.ToolTipText = strTip; 4016 } 4017 } 4018 UpdateUI(bool bRecreateTabBar, PwDocument dsSelect, bool bUpdateGroupList, PwGroup pgSelect, bool bUpdateEntryList, PwGroup pgEntrySource, bool bSetModified)4019 public void UpdateUI(bool bRecreateTabBar, PwDocument dsSelect, 4020 bool bUpdateGroupList, PwGroup pgSelect, bool bUpdateEntryList, 4021 PwGroup pgEntrySource, bool bSetModified) 4022 { 4023 UpdateUI(bRecreateTabBar, dsSelect, bUpdateGroupList, pgSelect, 4024 bUpdateEntryList, pgEntrySource, bSetModified, null); 4025 } 4026 UpdateUI(bool bRecreateTabBar, PwDocument dsSelect, bool bUpdateGroupList, PwGroup pgSelect, bool bUpdateEntryList, PwGroup pgEntrySource, bool bSetModified, Control cOptFocus)4027 public void UpdateUI(bool bRecreateTabBar, PwDocument dsSelect, 4028 bool bUpdateGroupList, PwGroup pgSelect, bool bUpdateEntryList, 4029 PwGroup pgEntrySource, bool bSetModified, Control cOptFocus) 4030 { 4031 if(bRecreateTabBar) RecreateUITabs(); 4032 4033 if(dsSelect != null) m_docMgr.ActiveDocument = dsSelect; 4034 SelectUITab(); 4035 4036 UpdateImageLists(false); 4037 4038 if(bUpdateGroupList) UpdateGroupList(pgSelect); 4039 4040 if(bUpdateEntryList) UpdateEntryList(pgEntrySource, false); 4041 else { Debug.Assert(pgEntrySource == null); } 4042 4043 UpdateUIState(bSetModified, cOptFocus); 4044 } 4045 ShowSearchResults(PwGroup pgResults, SearchParameters sp, PwGroup pgRoot, bool bFocusEntryList)4046 private void ShowSearchResults(PwGroup pgResults, SearchParameters sp, 4047 PwGroup pgRoot, bool bFocusEntryList) 4048 { 4049 if(pgResults == null) { Debug.Assert(false); return; } 4050 4051 UpdateEntryList(pgResults, false); 4052 SelectFirstEntryIfNoneSelected(); 4053 4054 if(bFocusEntryList) ResetDefaultFocus(m_lvEntries); 4055 4056 UpdateUIState(false); 4057 4058 PwGroup pgSkipped = null; 4059 if((sp != null) && sp.RespectEntrySearchingDisabled) 4060 pgSkipped = pgRoot; 4061 ShowSearchResultsStatusMessage(pgSkipped); 4062 } 4063 ShowSearchResultsStatusMessage(PwGroup pgSearchSkippedRoot)4064 private void ShowSearchResultsStatusMessage(PwGroup pgSearchSkippedRoot) 4065 { 4066 const string strParam = @"{PARAM}"; 4067 4068 StringBuilder sb = new StringBuilder(); 4069 4070 int n = m_lvEntries.Items.Count; 4071 if(n == 1) sb.Append(KPRes.SearchEntriesFound1); 4072 else 4073 { 4074 string strFound = KPRes.SearchEntriesFound; 4075 string str = StrUtil.ReplaceCaseInsensitive(strFound, 4076 strParam, n.ToString()); 4077 if(str == strFound) { Debug.Assert(false); return; } 4078 sb.Append(str); 4079 } 4080 4081 if(pgSearchSkippedRoot != null) 4082 { 4083 List<PwGroup> lSkipped = pgSearchSkippedRoot.GetTopSearchSkippedGroups(); 4084 4085 if(lSkipped.Count > 0) 4086 { 4087 sb.Append(", "); 4088 4089 if(lSkipped.Count == 1) sb.Append(KPRes.GroupsSkipped1); 4090 else 4091 { 4092 string strSkipped = KPRes.GroupsSkipped; 4093 string str = StrUtil.ReplaceCaseInsensitive(strSkipped, 4094 strParam, lSkipped.Count.ToString()); 4095 if(str == strSkipped) { Debug.Assert(false); return; } 4096 sb.Append(str); 4097 } 4098 4099 sb.Append(" ("); 4100 4101 int m = Math.Min(lSkipped.Count, 3); 4102 for(int i = 0; i < m; ++i) 4103 { 4104 if(i > 0) sb.Append(", "); 4105 sb.Append('\''); 4106 sb.Append(StrUtil.EncodeMenuText(lSkipped[i].Name.Trim())); 4107 sb.Append('\''); 4108 } 4109 if(m < lSkipped.Count) sb.Append(", ..."); 4110 4111 sb.Append(')'); 4112 } 4113 } 4114 4115 sb.Append('.'); 4116 SetStatusEx(sb.ToString()); 4117 } 4118 MinimizeToTray(bool bMinimize)4119 private void MinimizeToTray(bool bMinimize) 4120 { 4121 if(bMinimize) // Order of Visible and ShowInTaskbar is important 4122 { 4123 // if(MonoWorkarounds.IsRequired(649266)) this.ShowInTaskbar = false; 4124 this.Visible = false; 4125 } 4126 else 4127 { 4128 this.Visible = true; 4129 // if(MonoWorkarounds.IsRequired(649266)) this.ShowInTaskbar = true; 4130 } 4131 4132 if(bMinimize) 4133 { 4134 if(Program.Config.Security.WorkspaceLocking.LockOnWindowMinimizeToTray && 4135 !IsFileLocked(null)) 4136 OnFileLock(null, EventArgs.Empty); 4137 } 4138 else // Restore 4139 { 4140 // EnsureVisibleForegroundWindow(false, false); // Don't! 4141 4142 if(this.WindowState == FormWindowState.Minimized) 4143 UIUtil.SetWindowState(this, (Program.Config.MainWindow.Maximized ? 4144 FormWindowState.Maximized : FormWindowState.Normal)); 4145 else if(IsFileLocked(null) && !UIIsAutoUnlockBlocked()) 4146 OnFileLock(null, EventArgs.Empty); // Unlock 4147 4148 if(Program.Config.MainWindow.FocusQuickFindOnUntray) 4149 ResetDefaultFocus(null); 4150 } 4151 4152 UpdateTrayIcon(false); 4153 } 4154 GetStartMinimized()4155 private bool GetStartMinimized() 4156 { 4157 return (Program.Config.Application.Start.MinimizedAndLocked || 4158 (Program.CommandLineArgs[AppDefs.CommandLineOptions.Minimize] != null)); 4159 } 4160 MinimizeToTrayAtStartIfEnabled(bool bFormLoading)4161 private void MinimizeToTrayAtStartIfEnabled(bool bFormLoading) 4162 { 4163 if(GetStartMinimized()) 4164 { 4165 if(bFormLoading) 4166 UIUtil.SetWindowState(this, FormWindowState.Minimized); 4167 else 4168 { 4169 // The following isn't required anymore, because the 4170 // TaskbarButtonCreated message is handled 4171 4172 // Set the lock overlay icon again (the first time 4173 // Windows ignores the call, maybe because the window 4174 // wasn't fully constructed at that time yet) 4175 // if(IsFileLocked(null)) 4176 // TaskbarList.SetOverlayIcon(this, 4177 // Properties.Resources.LockOverlay, KPRes.Locked); 4178 } 4179 4180 if(Program.Config.MainWindow.MinimizeToTray) MinimizeToTray(true); 4181 else if(!bFormLoading) 4182 UIUtil.SetWindowState(this, FormWindowState.Minimized); 4183 } 4184 4185 // Remove taskbar item 4186 if(Program.Config.MainWindow.MinimizeAfterOpeningDatabase && 4187 Program.Config.MainWindow.MinimizeToTray && !bFormLoading && 4188 IsAtLeastOneFileOpen()) 4189 { 4190 MinimizeToTray(true); 4191 } 4192 } 4193 SelectFirstEntryIfNoneSelected()4194 private void SelectFirstEntryIfNoneSelected() 4195 { 4196 if((m_lvEntries.Items.Count > 0) && 4197 (m_lvEntries.SelectedIndices.Count == 0)) 4198 UIUtil.SetFocusedItem(m_lvEntries, m_lvEntries.Items[0], true); 4199 } 4200 SelectEntries(PwObjectList<PwEntry> lEntries, bool bDeselectOthers, bool bFocusFirst)4201 public void SelectEntries(PwObjectList<PwEntry> lEntries, 4202 bool bDeselectOthers, bool bFocusFirst) 4203 { 4204 if(lEntries == null) { Debug.Assert(false); return; } 4205 4206 ++m_uBlockEntrySelectionEvent; 4207 try 4208 { 4209 bool bDoFocus = bFocusFirst; 4210 foreach(ListViewItem lvi in m_lvEntries.Items) 4211 { 4212 if(lvi == null) { Debug.Assert(false); continue; } 4213 4214 PwListItem pli = (lvi.Tag as PwListItem); 4215 if(pli == null) { Debug.Assert(false); continue; } 4216 4217 PwEntry pe = pli.Entry; 4218 if(pe == null) { Debug.Assert(false); continue; } 4219 4220 if(lEntries.IndexOf(pe) >= 0) 4221 { 4222 lvi.Selected = true; 4223 4224 if(bDoFocus) 4225 { 4226 UIUtil.SetFocusedItem(m_lvEntries, lvi, false); 4227 bDoFocus = false; 4228 } 4229 } 4230 else if(bDeselectOthers) lvi.Selected = false; 4231 } 4232 } 4233 catch(Exception) { Debug.Assert(false); } 4234 finally { --m_uBlockEntrySelectionEvent; } 4235 } 4236 GetCurrentEntries()4237 internal PwGroup GetCurrentEntries() 4238 { 4239 PwGroup pg = new PwGroup(true, true); 4240 pg.IsVirtual = true; 4241 4242 if(!m_lvEntries.ShowGroups) 4243 { 4244 foreach(ListViewItem lvi in m_lvEntries.Items) 4245 pg.AddEntry(((PwListItem)lvi.Tag).Entry, false); 4246 } 4247 else // Groups 4248 { 4249 foreach(ListViewGroup lvg in m_lvEntries.Groups) 4250 { 4251 foreach(ListViewItem lvi in lvg.Items) 4252 pg.AddEntry(((PwListItem)lvi.Tag).Entry, false); 4253 } 4254 } 4255 4256 return pg; 4257 } 4258 4259 // Public for plugins EnsureVisibleEntry(PwUuid uuid)4260 public void EnsureVisibleEntry(PwUuid uuid) 4261 { 4262 ListViewItem lvi = GuiFindEntry(uuid); 4263 if(lvi == null) { Debug.Assert(false); return; } 4264 4265 m_lvEntries.EnsureVisible(lvi.Index); 4266 } 4267 4268 internal void EnsureVisibleSelected(bool? obLast) 4269 { 4270 // PwEntry pe = GetSelectedEntry(true, bLast); 4271 // if(pe == null) return; 4272 // EnsureVisibleEntry(pe.Uuid); 4273 4274 ListView.SelectedIndexCollection lvsic = m_lvEntries.SelectedIndices; 4275 if(lvsic == null) { Debug.Assert(false); return; } 4276 int n = lvsic.Count; 4277 if(n == 0) return; 4278 4279 // Getting lvsic[n - 1] sends n messages 4280 if(obLast.HasValue) 4281 m_lvEntries.EnsureVisible(obLast.Value ? lvsic[n - 1] : lvsic[0]); 4282 else 4283 { 4284 int iFirst = lvsic[0]; 4285 if(n != 1) m_lvEntries.EnsureVisible(lvsic[n - 1]); 4286 m_lvEntries.EnsureVisible(iFirst); 4287 } 4288 } 4289 RemoveEntriesFromList(List<PwEntry> lEntries, bool bLockUIUpdate)4290 private void RemoveEntriesFromList(List<PwEntry> lEntries, bool bLockUIUpdate) 4291 { 4292 Debug.Assert(lEntries != null); if(lEntries == null) return; 4293 if(lEntries.Count == 0) return; 4294 4295 RemoveEntriesFromList(lEntries.ToArray(), bLockUIUpdate); 4296 } 4297 RemoveEntriesFromList(PwEntry[] vEntries, bool bLockUIUpdate)4298 private void RemoveEntriesFromList(PwEntry[] vEntries, bool bLockUIUpdate) 4299 { 4300 Debug.Assert(vEntries != null); if(vEntries == null) return; 4301 if(vEntries.Length == 0) return; 4302 4303 if(MonoWorkarounds.IsRequired(1690) && (m_lvEntries.Items.Count != 0)) 4304 UIUtil.SetFocusedItem(m_lvEntries, m_lvEntries.Items[0], false); 4305 4306 ++m_uBlockEntrySelectionEvent; 4307 if(bLockUIUpdate) m_lvEntries.BeginUpdate(); 4308 4309 lock(m_asyncListUpdate.ListEditSyncObject) 4310 { 4311 for(int i = m_lvEntries.Items.Count - 1; i >= 0; --i) 4312 { 4313 PwEntry pe = ((PwListItem)m_lvEntries.Items[i].Tag).Entry; 4314 Debug.Assert(pe != null); 4315 4316 if(Array.IndexOf<PwEntry>(vEntries, pe) >= 0) 4317 m_lvEntries.Items.RemoveAt(i); 4318 } 4319 } 4320 4321 // Refresh alternating item background colors 4322 UIUtil.SetAlternatingBgColors(m_lvEntries, m_clrAlternateItemBgColor, 4323 Program.Config.MainWindow.EntryListAlternatingBgColors); 4324 4325 if(bLockUIUpdate) m_lvEntries.EndUpdate(); 4326 --m_uBlockEntrySelectionEvent; 4327 } 4328 PreSaveValidate(PwDatabase pd)4329 private bool PreSaveValidate(PwDatabase pd) 4330 { 4331 if(m_uForceSave > 0) return true; 4332 4333 byte[] pbOnDisk = WinUtil.HashFile(pd.IOConnectionInfo); 4334 4335 if((pbOnDisk != null) && (pd.HashOfFileOnDisk != null) && 4336 !MemUtil.ArraysEqual(pbOnDisk, pd.HashOfFileOnDisk)) 4337 { 4338 DialogResult dr = DialogResult.Yes; 4339 if(!Program.Config.Application.SaveForceSync) 4340 dr = AskIfSynchronizeInstead(pd.IOConnectionInfo); 4341 4342 if(dr == DialogResult.Yes) // Synchronize 4343 { 4344 bool? b = ImportUtil.Synchronize(pd, this, pd.IOConnectionInfo, 4345 true, this); 4346 UpdateUI(false, null, true, null, true, null, false); 4347 if(b.HasValue) SetStatusEx(b.Value ? KPRes.SyncSuccess : KPRes.SyncFailed); 4348 return false; 4349 } 4350 else if(dr == DialogResult.Cancel) return false; 4351 else { Debug.Assert(dr == DialogResult.No); } 4352 } 4353 4354 return true; 4355 } 4356 AskIfSynchronizeInstead(IOConnectionInfo ioc)4357 private static DialogResult AskIfSynchronizeInstead(IOConnectionInfo ioc) 4358 { 4359 VistaTaskDialog dlg = new VistaTaskDialog(); 4360 4361 string strText = string.Empty; 4362 if(ioc.GetDisplayName().Length > 0) 4363 strText += ioc.GetDisplayName() + MessageService.NewParagraph; 4364 strText += KPRes.FileChanged; 4365 4366 dlg.CommandLinks = true; 4367 dlg.WindowTitle = PwDefs.ShortProductName; 4368 dlg.Content = strText; 4369 dlg.SetIcon(VtdCustomIcon.Question); 4370 4371 dlg.MainInstruction = KPRes.OverwriteExistingFileQuestion; 4372 dlg.AddButton((int)DialogResult.Yes, KPRes.Synchronize, KPRes.FileChangedSync); 4373 dlg.AddButton((int)DialogResult.No, KPRes.Overwrite, KPRes.FileChangedOverwrite); 4374 dlg.AddButton((int)DialogResult.Cancel, KPRes.Cancel, KPRes.FileSaveQOpCancel); 4375 4376 DialogResult dr; 4377 if(dlg.ShowDialog()) dr = (DialogResult)dlg.Result; 4378 else 4379 { 4380 strText += MessageService.NewParagraph; 4381 strText += @"[" + KPRes.Yes + @"]: " + KPRes.Synchronize + @". " + 4382 KPRes.FileChangedSync + MessageService.NewParagraph; 4383 strText += @"[" + KPRes.No + @"]: " + KPRes.Overwrite + @". " + 4384 KPRes.FileChangedOverwrite + MessageService.NewParagraph; 4385 strText += @"[" + KPRes.Cancel + @"]: " + KPRes.FileSaveQOpCancel; 4386 4387 dr = MessageService.Ask(strText, PwDefs.ShortProductName, 4388 MessageBoxButtons.YesNoCancel); 4389 } 4390 4391 return dr; 4392 } 4393 ActivateNextDocumentEx(int iDir)4394 private void ActivateNextDocumentEx(int iDir) 4395 { 4396 int n = m_tabMain.TabPages.Count; 4397 if(n > 1) 4398 m_tabMain.SelectedIndex = ((m_tabMain.SelectedIndex + 4399 iDir + n) % n); 4400 } 4401 ProcessCmdKey(ref Message msg, Keys keyData)4402 protected override bool ProcessCmdKey(ref Message msg, Keys keyData) 4403 { 4404 bool bDown; 4405 if(!NativeMethods.GetKeyMessageState(ref msg, out bDown)) 4406 return base.ProcessCmdKey(ref msg, keyData); 4407 4408 if(keyData == Keys.Escape) // No modifiers 4409 { 4410 // Control c = UIUtil.GetActiveControl(this); // Returns wrong control 4411 // bool bMainMenu = (c == m_menuMain); 4412 bool bMainMenu = ((msg.HWnd == m_menuMain.Handle) && 4413 (UIUtil.GetSelectedItem(m_menuMain.Items) != null)); 4414 4415 if(!bMainMenu) 4416 { 4417 if(bDown && IsCommandTypeInvokable(null, AppCommandType.Window)) 4418 { 4419 AceEscAction a = Program.Config.MainWindow.EscAction; 4420 4421 if(a == AceEscAction.Lock) 4422 LockAllDocuments(); 4423 else if(a == AceEscAction.Minimize) 4424 this.WindowState = FormWindowState.Minimized; 4425 else if(a == AceEscAction.MinimizeToTray) 4426 { 4427 if(!IsTrayed()) MinimizeToTray(true); 4428 } 4429 else if(a == AceEscAction.Exit) 4430 OnFileExit(null, EventArgs.Empty); 4431 else { Debug.Assert(a == AceEscAction.None); } 4432 } 4433 4434 return true; 4435 } 4436 } 4437 4438 KeyEventArgs e = new KeyEventArgs(keyData); 4439 if(HandleMainWindowKeyMessage(e, bDown)) return true; 4440 4441 return base.ProcessCmdKey(ref msg, keyData); 4442 } 4443 HandleMainWindowKeyMessage(KeyEventArgs e, bool bDown)4444 private bool HandleMainWindowKeyMessage(KeyEventArgs e, bool bDown) 4445 { 4446 if(e == null) { Debug.Assert(false); return false; } 4447 4448 Keys kc = e.KeyCode; 4449 bool bCtrl = e.Control, bAlt = e.Alt, bShift = e.Shift; 4450 bool bHandled = true; 4451 4452 // Enforce that Alt is up (e.g. on Polish systems AltGr+E, 4453 // i.e. Ctrl+Alt+E, is used to enter a character) 4454 if(bCtrl && !bAlt) 4455 { 4456 if(kc == Keys.Tab) 4457 { 4458 if(bDown) ActivateNextDocumentEx(bShift ? -1 : 1); 4459 } 4460 // When changing Ctrl+E, also change the tooltip of the quick search box 4461 else if(kc == Keys.E) // Standard key for quick searches 4462 ResetDefaultFocus(m_tbQuickFind.Control); // Down and up (RichTextBox) 4463 else if(kc == Keys.H) 4464 { 4465 if(bDown) ToggleFieldAsterisks(AceColumnType.Password); 4466 } 4467 else if(kc == Keys.J) 4468 { 4469 if(bDown) ToggleFieldAsterisks(AceColumnType.UserName); 4470 } 4471 else bHandled = false; 4472 } 4473 else if(!bCtrl && !bAlt && (kc == Keys.F3)) 4474 { 4475 if(bDown) OnFindInDatabase(null, EventArgs.Empty); 4476 } 4477 else bHandled = false; 4478 4479 if(bHandled) UIUtil.SetHandled(e, true); 4480 return bHandled; 4481 } 4482 HandleMoveKeyMessage(KeyEventArgs e, bool bDown, bool bEntry)4483 private bool HandleMoveKeyMessage(KeyEventArgs e, bool bDown, bool bEntry) 4484 { 4485 // Mono does not raise key events while Alt is down 4486 bool bMoveMod = e.Alt; 4487 if(MonoWorkarounds.IsRequired(1245)) bMoveMod = (e.Control && e.Shift); 4488 if(!bMoveMod) return false; 4489 4490 // Mono raises key events *after* changing the selection, 4491 // thus we cannot use any navigation keys 4492 Keys[] vMove = new Keys[] { Keys.Home, Keys.Up, Keys.Down, Keys.End }; 4493 if(MonoWorkarounds.IsRequired(1245)) 4494 vMove = new Keys[] { Keys.F5, Keys.F6, Keys.F7, Keys.F8 }; 4495 int m = Array.IndexOf<Keys>(vMove, e.KeyCode); 4496 if(m < 0) return false; 4497 4498 if(bDown) 4499 { 4500 EventHandler[] vHandlers; 4501 if(bEntry) 4502 vHandlers = new EventHandler[] { OnEntryMoveToTop, 4503 OnEntryMoveOneUp, OnEntryMoveOneDown, OnEntryMoveToBottom }; 4504 else 4505 vHandlers = new EventHandler[] { OnGroupMoveToTop, 4506 OnGroupMoveOneUp, OnGroupMoveOneDown, OnGroupMoveToBottom }; 4507 4508 vHandlers[m].Invoke(null, e); 4509 } 4510 4511 UIUtil.SetHandled(e, true); 4512 return true; 4513 } 4514 UIIsInteractionBlocked()4515 public bool UIIsInteractionBlocked() 4516 { 4517 return (m_uUIBlocked > 0); 4518 } 4519 UIBlockInteraction(bool bBlock)4520 public void UIBlockInteraction(bool bBlock) 4521 { 4522 NotifyUserActivity(); 4523 4524 if(bBlock) ++m_uUIBlocked; 4525 else if(m_uUIBlocked > 0) --m_uUIBlocked; 4526 else { Debug.Assert(false); } 4527 4528 bool bNotBlocked = !UIIsInteractionBlocked(); 4529 this.Enabled = bNotBlocked; 4530 4531 if(bNotBlocked) 4532 { 4533 try { if(!IsPrimaryControlActive()) ResetDefaultFocus(null); } 4534 catch(Exception) { Debug.Assert(false); } 4535 } 4536 else // Blocked 4537 { 4538 // Redraw the UI, but only when the UI is blocked (otherwise 4539 // the Application.DoEvents call could indirectly run 4540 // triggers/automations now, which could result in problems) 4541 UIUtil.DoEventsByTime(true); 4542 } 4543 } 4544 UIIsAutoUnlockBlocked()4545 public bool UIIsAutoUnlockBlocked() 4546 { 4547 return (m_uUnlockAutoBlocked > 0); 4548 } 4549 UIBlockAutoUnlock(bool bBlock)4550 public void UIBlockAutoUnlock(bool bBlock) 4551 { 4552 if(bBlock) ++m_uUnlockAutoBlocked; 4553 else if(m_uUnlockAutoBlocked > 0) --m_uUnlockAutoBlocked; 4554 else { Debug.Assert(false); } 4555 } 4556 UIIsWindowStateAutoBlocked()4557 public bool UIIsWindowStateAutoBlocked() 4558 { 4559 return (m_uWindowStateAutoBlocked > 0); 4560 } 4561 UIBlockWindowStateAuto(bool bBlock)4562 public void UIBlockWindowStateAuto(bool bBlock) 4563 { 4564 if(bBlock) ++m_uWindowStateAutoBlocked; 4565 else if(m_uWindowStateAutoBlocked > 0) --m_uWindowStateAutoBlocked; 4566 else { Debug.Assert(false); } 4567 } 4568 EnsureRecycleBin(ref PwGroup pgRecycleBin, PwDatabase pdContext, ref bool bGroupListUpdateRequired)4569 private static void EnsureRecycleBin(ref PwGroup pgRecycleBin, 4570 PwDatabase pdContext, ref bool bGroupListUpdateRequired) 4571 { 4572 if(pdContext == null) { Debug.Assert(false); return; } 4573 4574 if(pgRecycleBin == pdContext.RootGroup) 4575 { 4576 Debug.Assert(false); 4577 pgRecycleBin = null; 4578 } 4579 4580 if(pgRecycleBin == null) 4581 { 4582 pgRecycleBin = new PwGroup(true, true, KPRes.RecycleBin, 4583 PwIcon.TrashBin); 4584 pgRecycleBin.EnableAutoType = false; 4585 pgRecycleBin.EnableSearching = false; 4586 pgRecycleBin.IsExpanded = !Program.Config.Defaults.RecycleBinCollapse; 4587 pdContext.RootGroup.AddGroup(pgRecycleBin, true); 4588 4589 pdContext.RecycleBinUuid = pgRecycleBin.Uuid; 4590 4591 bGroupListUpdateRequired = true; 4592 } 4593 else { Debug.Assert(pgRecycleBin.Uuid.Equals(pdContext.RecycleBinUuid)); } 4594 } 4595 DeleteSelectedEntries()4596 private void DeleteSelectedEntries() 4597 { 4598 PwEntry[] vSelected = GetSelectedEntries(); 4599 if((vSelected == null) || (vSelected.Length == 0)) return; 4600 4601 PwDatabase pd = m_docMgr.ActiveDatabase; 4602 PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true); 4603 bool bShiftPressed = ((Control.ModifierKeys & Keys.Shift) != Keys.None); 4604 4605 bool bAtLeastOnePermanent = false; 4606 if(!pd.RecycleBinEnabled) bAtLeastOnePermanent = true; 4607 else if(bShiftPressed) bAtLeastOnePermanent = true; 4608 else if(pgRecycleBin == null) { } // Not permanent 4609 else 4610 { 4611 foreach(PwEntry peEnum in vSelected) 4612 { 4613 if((peEnum.ParentGroup == pgRecycleBin) || 4614 peEnum.ParentGroup.IsContainedIn(pgRecycleBin)) 4615 { 4616 bAtLeastOnePermanent = true; 4617 break; 4618 } 4619 } 4620 } 4621 4622 bool bSingle = (vSelected.Length == 1); 4623 string strSummary = EntryUtil.CreateSummaryList(null, vSelected); 4624 4625 if(bAtLeastOnePermanent) 4626 { 4627 VistaTaskDialog dlg = new VistaTaskDialog(); 4628 dlg.CommandLinks = false; 4629 dlg.Content = strSummary; 4630 dlg.MainInstruction = (bSingle ? KPRes.DeleteEntriesQuestionSingle : 4631 KPRes.DeleteEntriesQuestion); 4632 dlg.SetIcon(VtdCustomIcon.Question); 4633 dlg.WindowTitle = PwDefs.ShortProductName; 4634 dlg.AddButton((int)DialogResult.OK, KPRes.DeleteCmd, null); 4635 dlg.AddButton((int)DialogResult.Cancel, KPRes.Cancel, null); 4636 4637 if(dlg.ShowDialog()) 4638 { 4639 if(dlg.Result == (int)DialogResult.Cancel) return; 4640 } 4641 else 4642 { 4643 string strText = (bSingle ? KPRes.DeleteEntriesQuestionSingle : 4644 KPRes.DeleteEntriesQuestion); 4645 if(strSummary.Length > 0) 4646 strText += MessageService.NewParagraph + strSummary; 4647 4648 if(!MessageService.AskYesNo(strText, (bSingle ? 4649 KPRes.DeleteEntriesTitleSingle : KPRes.DeleteEntriesTitle))) 4650 return; 4651 } 4652 } 4653 else if(Program.Config.UI.ShowRecycleConfirmDialog) 4654 { 4655 VistaTaskDialog dlg = new VistaTaskDialog(); 4656 dlg.CommandLinks = false; 4657 dlg.Content = strSummary; 4658 dlg.MainInstruction = (bSingle ? KPRes.RecycleEntryConfirmSingle : 4659 KPRes.RecycleEntryConfirm); 4660 dlg.SetIcon(VtdCustomIcon.Question); 4661 dlg.VerificationText = KPRes.DialogNoShowAgain; 4662 dlg.WindowTitle = PwDefs.ShortProductName; 4663 dlg.AddButton((int)DialogResult.OK, KPRes.YesCmd, null); 4664 dlg.AddButton((int)DialogResult.Cancel, KPRes.NoCmd, null); 4665 4666 if(dlg.ShowDialog()) 4667 { 4668 if(dlg.Result == (int)DialogResult.Cancel) return; 4669 4670 if(dlg.ResultVerificationChecked) 4671 Program.Config.UI.ShowRecycleConfirmDialog = false; 4672 } 4673 else 4674 { 4675 string strText = (bSingle ? KPRes.RecycleEntryConfirmSingle : 4676 KPRes.RecycleEntryConfirm); 4677 if(strSummary.Length > 0) 4678 strText += MessageService.NewParagraph + strSummary; 4679 4680 if(!MessageService.AskYesNo(strText, (bSingle ? 4681 KPRes.DeleteEntriesTitleSingle : KPRes.DeleteEntriesTitle))) 4682 return; 4683 } 4684 } 4685 4686 bool bUpdateGroupList = false; 4687 DateTime dtNow = DateTime.UtcNow; 4688 foreach(PwEntry pe in vSelected) 4689 { 4690 PwGroup pgParent = pe.ParentGroup; 4691 if(pgParent == null) continue; // Can't remove 4692 4693 if(!pgParent.Entries.Remove(pe)) { Debug.Assert(false); continue; } 4694 4695 bool bPermanent = false; 4696 if(!pd.RecycleBinEnabled) bPermanent = true; 4697 else if(bShiftPressed) bPermanent = true; 4698 else if(pgRecycleBin == null) { } // Recycle 4699 else if(pgParent == pgRecycleBin) bPermanent = true; 4700 else if(pgParent.IsContainedIn(pgRecycleBin)) bPermanent = true; 4701 4702 if(bPermanent) 4703 { 4704 PwDeletedObject pdo = new PwDeletedObject(pe.Uuid, dtNow); 4705 pd.DeletedObjects.Add(pdo); 4706 } 4707 else // Recycle 4708 { 4709 EnsureRecycleBin(ref pgRecycleBin, pd, ref bUpdateGroupList); 4710 4711 pgRecycleBin.AddEntry(pe, true, true); 4712 pe.PreviousParentGroup = pgParent.Uuid; 4713 pe.Touch(false); 4714 } 4715 } 4716 4717 RemoveEntriesFromList(vSelected, true); 4718 UpdateUI(false, null, bUpdateGroupList, null, false, null, true); 4719 } 4720 DeleteSelectedGroup()4721 private void DeleteSelectedGroup() 4722 { 4723 PwGroup pg = GetSelectedGroup(); 4724 if(pg == null) { Debug.Assert(false); return; } 4725 4726 PwGroup pgParent = pg.ParentGroup; 4727 if(pgParent == null) return; // Can't remove virtual or root group 4728 4729 PwDatabase pd = m_docMgr.ActiveDatabase; 4730 PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true); 4731 bool bShiftPressed = ((Control.ModifierKeys & Keys.Shift) != Keys.None); 4732 4733 bool bPermanent = false; 4734 if(!pd.RecycleBinEnabled) bPermanent = true; 4735 else if(bShiftPressed) bPermanent = true; 4736 else if(pgRecycleBin == null) { } 4737 else if(pg == pgRecycleBin) bPermanent = true; 4738 else if(pg.IsContainedIn(pgRecycleBin)) bPermanent = true; 4739 else if(pgRecycleBin.IsContainedIn(pg)) bPermanent = true; 4740 4741 string strContent = EntryUtil.CreateSummaryList(pg, false); 4742 if(strContent.Length > 0) 4743 strContent = KPRes.DeleteGroupInfo + MessageService.NewParagraph + 4744 strContent; 4745 4746 if(bPermanent) 4747 { 4748 VistaTaskDialog dlg = new VistaTaskDialog(); 4749 dlg.CommandLinks = false; 4750 dlg.Content = strContent; 4751 dlg.MainInstruction = KPRes.DeleteGroupQuestion; 4752 dlg.SetIcon(VtdCustomIcon.Question); 4753 dlg.WindowTitle = PwDefs.ShortProductName; 4754 dlg.AddButton((int)DialogResult.OK, KPRes.DeleteCmd, null); 4755 dlg.AddButton((int)DialogResult.Cancel, KPRes.Cancel, null); 4756 4757 if(dlg.ShowDialog()) 4758 { 4759 if(dlg.Result == (int)DialogResult.Cancel) return; 4760 } 4761 else 4762 { 4763 string strText = KPRes.DeleteGroupQuestion; 4764 if(strContent.Length > 0) 4765 strText += MessageService.NewParagraph + strContent; 4766 4767 if(!MessageService.AskYesNo(strText, KPRes.DeleteGroupTitle)) 4768 return; 4769 } 4770 } 4771 else if(Program.Config.UI.ShowRecycleConfirmDialog) 4772 { 4773 VistaTaskDialog dlg = new VistaTaskDialog(); 4774 dlg.CommandLinks = false; 4775 dlg.Content = strContent; 4776 dlg.MainInstruction = KPRes.RecycleGroupConfirm; 4777 dlg.SetIcon(VtdCustomIcon.Question); 4778 dlg.VerificationText = KPRes.DialogNoShowAgain; 4779 dlg.WindowTitle = PwDefs.ShortProductName; 4780 dlg.AddButton((int)DialogResult.OK, KPRes.YesCmd, null); 4781 dlg.AddButton((int)DialogResult.Cancel, KPRes.NoCmd, null); 4782 4783 if(dlg.ShowDialog()) 4784 { 4785 if(dlg.Result == (int)DialogResult.Cancel) return; 4786 4787 if(dlg.ResultVerificationChecked) 4788 Program.Config.UI.ShowRecycleConfirmDialog = false; 4789 } 4790 else 4791 { 4792 string strText = KPRes.RecycleGroupConfirm; 4793 if(strContent.Length > 0) 4794 strText += MessageService.NewParagraph + strContent; 4795 4796 if(!MessageService.AskYesNo(strText, KPRes.DeleteGroupTitle)) 4797 return; 4798 } 4799 } 4800 4801 if(!pgParent.Groups.Remove(pg)) { Debug.Assert(false); return; } 4802 4803 if(bPermanent) 4804 { 4805 pg.DeleteAllObjects(pd); 4806 4807 PwDeletedObject pdo = new PwDeletedObject(pg.Uuid, DateTime.UtcNow); 4808 pd.DeletedObjects.Add(pdo); 4809 } 4810 else // Recycle 4811 { 4812 bool bDummy = false; 4813 EnsureRecycleBin(ref pgRecycleBin, pd, ref bDummy); 4814 4815 try 4816 { 4817 pgRecycleBin.AddGroup(pg, true, true); 4818 pg.PreviousParentGroup = pgParent.Uuid; 4819 } 4820 catch(Exception ex) 4821 { 4822 if(pgRecycleBin.Groups.IndexOf(pg) < 0) 4823 pgParent.AddGroup(pg, true, true); // Undo removal 4824 MessageService.ShowWarning(ex); 4825 } 4826 4827 pg.Touch(false); 4828 } 4829 4830 UpdateUI(false, null, true, pgParent, true, null, true); 4831 } 4832 EmptyRecycleBin()4833 private void EmptyRecycleBin() 4834 { 4835 PwDatabase pd = m_docMgr.ActiveDatabase; 4836 if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; } 4837 4838 PwGroup pg = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true); 4839 if(pg == null) { Debug.Assert(false); return; } 4840 4841 string strSummary = EntryUtil.CreateSummaryList(pg, false); 4842 4843 VistaTaskDialog dlg = new VistaTaskDialog(); 4844 dlg.CommandLinks = false; 4845 dlg.Content = strSummary; 4846 dlg.MainInstruction = KPRes.EmptyRecycleBinQuestion; 4847 dlg.SetIcon(VtdCustomIcon.Question); 4848 dlg.WindowTitle = PwDefs.ShortProductName; 4849 dlg.AddButton((int)DialogResult.OK, KPRes.DeleteCmd, null); 4850 dlg.AddButton((int)DialogResult.Cancel, KPRes.Cancel, null); 4851 4852 if(dlg.ShowDialog()) 4853 { 4854 if(dlg.Result == (int)DialogResult.Cancel) return; 4855 } 4856 else 4857 { 4858 string strText = KPRes.EmptyRecycleBinQuestion; 4859 if(strSummary.Length > 0) 4860 strText += MessageService.NewParagraph + strSummary; 4861 4862 if(!MessageService.AskYesNo(strText)) 4863 return; 4864 } 4865 4866 pg.DeleteAllObjects(pd); 4867 4868 UpdateUI(false, null, true, pg, true, null, true); 4869 } 4870 4871 // private static string GetGroupSuffixText(PwGroup pg) 4872 // { 4873 // if(pg == null) { Debug.Assert(false); return string.Empty; } 4874 // if(pg.Entries.UCount == 0) return string.Empty; 4875 // if(!GroupOnlyContainsTans(pg, true)) return string.Empty; 4876 // DateTime dtNow = DateTime.UtcNow; 4877 // uint uValid = 0; 4878 // foreach(PwEntry pe in pg.Entries) 4879 // { 4880 // if(pe.Expires && (pe.ExpiryTime <= dtNow)) { } 4881 // else ++uValid; 4882 // } 4883 // return (" (" + uValid.ToString() + "/" + pg.Entries.UCount.ToString() + ")"); 4884 // } 4885 AddCustomToolBarButton(string strID, string strName, string strDesc)4886 public void AddCustomToolBarButton(string strID, string strName, string strDesc) 4887 { 4888 if(string.IsNullOrEmpty(strID)) { Debug.Assert(false); return; } // No throw 4889 if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return; } // No throw 4890 4891 if(m_vCustomToolBarButtons.Count == 0) 4892 { 4893 m_tsSepCustomToolBar = new ToolStripSeparator(); 4894 m_toolMain.Items.Add(m_tsSepCustomToolBar); 4895 } 4896 4897 ToolStripButton btn = new ToolStripButton(strName); 4898 btn.Tag = strID; 4899 btn.Click += OnCustomToolBarButtonClicked; 4900 if(!string.IsNullOrEmpty(strDesc)) btn.ToolTipText = strDesc; 4901 4902 m_toolMain.Items.Add(btn); 4903 m_vCustomToolBarButtons.Add(btn); 4904 } 4905 RemoveCustomToolBarButton(string strID)4906 public void RemoveCustomToolBarButton(string strID) 4907 { 4908 if(string.IsNullOrEmpty(strID)) { Debug.Assert(false); return; } // No throw 4909 4910 foreach(ToolStripButton tb in m_vCustomToolBarButtons) 4911 { 4912 string str = (tb.Tag as string); 4913 if(string.IsNullOrEmpty(str)) { Debug.Assert(false); continue; } 4914 4915 if(str.Equals(strID, StrUtil.CaseIgnoreCmp)) 4916 { 4917 tb.Click -= OnCustomToolBarButtonClicked; 4918 m_toolMain.Items.Remove(tb); 4919 m_vCustomToolBarButtons.Remove(tb); 4920 break; 4921 } 4922 } 4923 4924 if((m_vCustomToolBarButtons.Count == 0) && (m_tsSepCustomToolBar != null)) 4925 { 4926 m_toolMain.Items.Remove(m_tsSepCustomToolBar); 4927 m_tsSepCustomToolBar = null; 4928 } 4929 } 4930 OnCustomToolBarButtonClicked(object sender, EventArgs e)4931 private void OnCustomToolBarButtonClicked(object sender, EventArgs e) 4932 { 4933 ToolStripButton btn = (sender as ToolStripButton); 4934 if(btn == null) { Debug.Assert(false); return; } 4935 4936 string strID = (btn.Tag as string); 4937 if(string.IsNullOrEmpty(strID)) { Debug.Assert(false); return; } 4938 4939 Program.TriggerSystem.RaiseEvent(EcasEventIDs.CustomTbButtonClicked, 4940 EcasProperty.CommandID, strID); 4941 } 4942 IocFromCommandLine()4943 internal IOConnectionInfo IocFromCommandLine() 4944 { 4945 CommandLineArgs args = Program.CommandLineArgs; 4946 IOConnectionInfo ioc = IOConnectionInfo.FromPath(args.FileName ?? 4947 string.Empty); 4948 4949 if(ioc.IsLocalFile()) // Expand relative path to absolute 4950 ioc.Path = UrlUtil.MakeAbsolutePath(UrlUtil.EnsureTerminatingSeparator( 4951 WinUtil.GetWorkingDirectory(), false) + "Sentinel", ioc.Path); 4952 4953 // Set the user name, which acts as a filter for the MRU items 4954 string strUserName = args[AppDefs.CommandLineOptions.IoCredUserName]; 4955 if(strUserName != null) ioc.UserName = strUserName; 4956 4957 if(args[AppDefs.CommandLineOptions.IoCredFromRecent] != null) 4958 ioc = CompleteConnectionInfoUsingMru(ioc); 4959 4960 // Override the password 4961 string strPassword = args[AppDefs.CommandLineOptions.IoCredPassword]; 4962 if(strPassword != null) ioc.Password = strPassword; 4963 4964 string strComplete = args[AppDefs.CommandLineOptions.IoCredIsComplete]; 4965 if(strComplete != null) ioc.IsComplete = true; 4966 4967 return ioc; 4968 } 4969 RememberKeySources(PwDatabase pwDb)4970 private static void RememberKeySources(PwDatabase pwDb) 4971 { 4972 if(pwDb == null) { Debug.Assert(false); return; } 4973 4974 Program.Config.Defaults.SetKeySources(pwDb.IOConnectionInfo, 4975 pwDb.MasterKey); 4976 } 4977 SerializeMruList(bool bStore)4978 private void SerializeMruList(bool bStore) 4979 { 4980 AceMru aceMru = Program.Config.Application.MostRecentlyUsed; 4981 4982 if(bStore) 4983 { 4984 if(!m_mruList.IsValid) { Debug.Assert(false); return; } 4985 4986 aceMru.MaxItemCount = m_mruList.MaxItemCount; 4987 4988 aceMru.Items.Clear(); 4989 // Count <= max is not guaranteed, therefore take minimum of both 4990 uint uMax = Math.Min(m_mruList.MaxItemCount, m_mruList.ItemCount); 4991 for(uint uMru = 0; uMru < uMax; ++uMru) 4992 { 4993 KeyValuePair<string, object> kvpMru = m_mruList.GetItem(uMru); 4994 IOConnectionInfo ioMru = (kvpMru.Value as IOConnectionInfo); 4995 if(ioMru != null) aceMru.Items.Add(ioMru); 4996 else { Debug.Assert(false); } 4997 } 4998 } 4999 else // Load 5000 { 5001 m_mruList.MaxItemCount = aceMru.MaxItemCount; 5002 5003 int nMax = Math.Min((int)m_mruList.MaxItemCount, aceMru.Items.Count); 5004 for(int iMru = 0; iMru < nMax; ++iMru) 5005 { 5006 IOConnectionInfo ioMru = aceMru.Items[nMax - iMru - 1]; 5007 m_mruList.AddItem(ioMru.GetDisplayName(), ioMru.CloneDeep()); 5008 } 5009 } 5010 } 5011 5012 [Obsolete] RedirectActivationPush(Form formTarget)5013 public void RedirectActivationPush(Form formTarget) 5014 { 5015 // m_vRedirectActivation.Push(formTarget); 5016 } 5017 5018 [Obsolete] RedirectActivationPop()5019 public void RedirectActivationPop() 5020 { 5021 // if(m_vRedirectActivation.Count == 0) { Debug.Assert(false); return; } 5022 // m_vRedirectActivation.Pop(); 5023 } 5024 ChangeMasterKey(PwDatabase pdOf)5025 private bool ChangeMasterKey(PwDatabase pdOf) 5026 { 5027 PwDatabase pd = (pdOf ?? m_docMgr.ActiveDatabase); 5028 if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return false; } 5029 5030 if(!AppPolicy.Try(AppPolicyId.ChangeMasterKey)) return false; 5031 if(!AppPolicy.Current.ChangeMasterKeyNoKey) 5032 { 5033 if(!KeyUtil.ReAskKey(pd, true)) return false; 5034 } 5035 5036 KeyCreationForm kcf = new KeyCreationForm(); 5037 kcf.InitEx(pd.IOConnectionInfo, false); 5038 5039 if(UIUtil.ShowDialogNotValue(kcf, DialogResult.OK)) return false; 5040 5041 pd.MasterKey = kcf.CompositeKey; 5042 pd.MasterKeyChanged = DateTime.UtcNow; 5043 // pd.MasterKeyChangeForceOnce = false; 5044 pd.Modified = true; 5045 5046 UIUtil.DestroyForm(kcf); 5047 UpdateUIState(false); // Show modified state in the UI 5048 5049 string str = KPRes.MasterKeyChanged + MessageService.NewParagraph + 5050 KPRes.MasterKeyChangedSave + MessageService.NewParagraph + 5051 KPRes.FileSaveQ; 5052 5053 VistaTaskDialog dlg = new VistaTaskDialog(); 5054 dlg.CommandLinks = false; 5055 dlg.Content = str; 5056 dlg.DefaultButtonID = (int)DialogResult.OK; 5057 dlg.MainInstruction = KPRes.MasterKeyChangedShort; 5058 dlg.WindowTitle = PwDefs.ShortProductName; 5059 5060 dlg.SetIcon(VtdCustomIcon.Question); 5061 dlg.AddButton((int)DialogResult.OK, KPRes.Save, null); 5062 dlg.AddButton((int)DialogResult.Cancel, KPRes.NotNow, null); 5063 5064 bool b; 5065 if(dlg.ShowDialog(this)) b = (dlg.Result == (int)DialogResult.OK); 5066 else b = MessageService.AskYesNo(str); 5067 5068 if(b) 5069 { 5070 SaveDatabase(pd, null); 5071 5072 // Show emergency sheet creation prompt only when the 5073 // database was saved successfully, otherwise the sheet 5074 // could contain incorrect information 5075 if(!pd.Modified) EmergencySheet.AskCreate(pd); 5076 } 5077 5078 return true; 5079 } 5080 UpdateColumnsEx(bool bGuiToInternal)5081 private void UpdateColumnsEx(bool bGuiToInternal) 5082 { 5083 if(m_bBlockColumnUpdates) return; 5084 m_bBlockColumnUpdates = true; 5085 5086 if(!bGuiToInternal) // Internal to GUI 5087 { 5088 m_asyncListUpdate.CancelPendingUpdatesAsync(); 5089 5090 m_lvEntries.BeginUpdate(); 5091 lock(m_asyncListUpdate.ListEditSyncObject) 5092 { 5093 m_lvEntries.Items.Clear(); 5094 } 5095 m_lvEntries.Columns.Clear(); 5096 5097 if(Program.Config.MainWindow.EntryListColumns.Count == 0) 5098 { 5099 int nWidth = (m_lvEntries.ClientRectangle.Width - 5100 UIUtil.GetVScrollBarWidth()) / 5; 5101 EntryListAddColumn(AceColumnType.Title, nWidth, false); 5102 EntryListAddColumn(AceColumnType.UserName, nWidth, false); 5103 EntryListAddColumn(AceColumnType.Password, nWidth, true); 5104 EntryListAddColumn(AceColumnType.Url, nWidth, false); 5105 EntryListAddColumn(AceColumnType.Notes, nWidth, false); 5106 } 5107 5108 int nDefaultWidth = m_lvEntries.ClientRectangle.Width / 5109 Program.Config.MainWindow.EntryListColumns.Count; 5110 foreach(AceColumn col in Program.Config.MainWindow.EntryListColumns) 5111 { 5112 ColumnHeader ch = m_lvEntries.Columns.Add(col.GetDisplayName(), 5113 col.SafeGetWidth(nDefaultWidth)); 5114 5115 HorizontalAlignment ha; 5116 if(col.Type == AceColumnType.PluginExt) 5117 ha = Program.ColumnProviderPool.GetTextAlign(col.CustomName); 5118 else ha = AceColumn.GetTextAlign(col.Type); 5119 if(ha != HorizontalAlignment.Left) ch.TextAlign = ha; 5120 } 5121 5122 int[] vDisplayIndices = StrUtil.DeserializeIntArray( 5123 Program.Config.MainWindow.EntryListColumnDisplayOrder); 5124 UIUtil.SetDisplayIndices(m_lvEntries, vDisplayIndices); 5125 5126 m_lvEntries.EndUpdate(); 5127 UpdateColumnSortingIcons(); 5128 } 5129 else // GUI to internal 5130 { 5131 List<AceColumn> l = Program.Config.MainWindow.EntryListColumns; 5132 if(l.Count == m_lvEntries.Columns.Count) 5133 { 5134 int[] vDisplayIndices = new int[l.Count]; 5135 for(int i = 0; i < l.Count; ++i) 5136 { 5137 l[i].Width = m_lvEntries.Columns[i].Width; 5138 vDisplayIndices[i] = m_lvEntries.Columns[i].DisplayIndex; 5139 } 5140 5141 Program.Config.MainWindow.EntryListColumnDisplayOrder = 5142 StrUtil.SerializeIntArray(vDisplayIndices); 5143 } 5144 else { Debug.Assert(false); } 5145 } 5146 5147 m_bBlockColumnUpdates = false; 5148 } 5149 EntryListAddColumn(AceColumnType t, int nWidth, bool bHide)5150 private static void EntryListAddColumn(AceColumnType t, int nWidth, bool bHide) 5151 { 5152 AceColumn c = new AceColumn(t, string.Empty, bHide, nWidth); 5153 Program.Config.MainWindow.EntryListColumns.Add(c); 5154 } 5155 GetEntryFieldEx(PwEntry pe, int iColumnID, bool bFormatForDisplay, out bool bRequestAsync)5156 private string GetEntryFieldEx(PwEntry pe, int iColumnID, 5157 bool bFormatForDisplay, out bool bRequestAsync) 5158 { 5159 List<AceColumn> l = Program.Config.MainWindow.EntryListColumns; 5160 if((iColumnID < 0) || (iColumnID >= l.Count)) 5161 { 5162 Debug.Assert(false); 5163 bRequestAsync = false; 5164 return string.Empty; 5165 } 5166 5167 AceColumn col = l[iColumnID]; 5168 if(bFormatForDisplay && col.HideWithAsterisks) 5169 { 5170 bRequestAsync = false; 5171 return PwDefs.HiddenPassword; 5172 } 5173 5174 string str; 5175 switch(col.Type) 5176 { 5177 case AceColumnType.Title: str = pe.Strings.ReadSafe(PwDefs.TitleField); break; 5178 case AceColumnType.UserName: str = pe.Strings.ReadSafe(PwDefs.UserNameField); break; 5179 case AceColumnType.Password: str = pe.Strings.ReadSafe(PwDefs.PasswordField); break; 5180 case AceColumnType.Url: str = pe.Strings.ReadSafe(PwDefs.UrlField); break; 5181 case AceColumnType.Notes: 5182 str = pe.Strings.ReadSafe(PwDefs.NotesField); 5183 if(bFormatForDisplay) str = StrUtil.MultiToSingleLine(str); 5184 break; 5185 case AceColumnType.CreationTime: 5186 str = TimeUtil.ToDisplayString(pe.CreationTime); 5187 break; 5188 case AceColumnType.LastModificationTime: 5189 str = TimeUtil.ToDisplayString(pe.LastModificationTime); 5190 break; 5191 case AceColumnType.LastAccessTime: 5192 str = TimeUtil.ToDisplayString(pe.LastAccessTime); 5193 break; 5194 case AceColumnType.ExpiryTime: 5195 if(pe.Expires) str = TimeUtil.ToDisplayString(pe.ExpiryTime); 5196 else str = m_strNeverExpires; 5197 break; 5198 case AceColumnType.Uuid: str = pe.Uuid.ToHexString(); break; 5199 case AceColumnType.Attachment: str = pe.Binaries.KeysToString(); break; 5200 case AceColumnType.CustomString: 5201 str = pe.Strings.ReadSafe(col.CustomName); 5202 if(bFormatForDisplay) str = StrUtil.MultiToSingleLine(str); 5203 break; 5204 case AceColumnType.PluginExt: 5205 str = Program.ColumnProviderPool.GetCellData(col.CustomName, pe); 5206 if(bFormatForDisplay) str = StrUtil.MultiToSingleLine(str); 5207 break; 5208 case AceColumnType.OverrideUrl: str = pe.OverrideUrl; break; 5209 case AceColumnType.Tags: 5210 str = TagUtil.TagsInheritedToString(pe.Tags, pe.ParentGroup); 5211 break; 5212 case AceColumnType.ExpiryTimeDateOnly: 5213 if(pe.Expires) str = TimeUtil.ToDisplayStringDateOnly(pe.ExpiryTime); 5214 else str = m_strNeverExpires; 5215 break; 5216 case AceColumnType.Size: 5217 str = StrUtil.FormatDataSizeKB(pe.GetSize()); 5218 break; 5219 case AceColumnType.HistoryCount: 5220 str = pe.History.UCount.ToString(); 5221 break; 5222 case AceColumnType.AttachmentCount: 5223 uint uc = pe.Binaries.UCount; 5224 str = ((uc > 0) ? uc.ToString() : string.Empty); 5225 break; 5226 case AceColumnType.LastPasswordModTime: 5227 str = TimeUtil.ToDisplayString(EntryUtil.GetLastPasswordModTime(pe)); 5228 break; 5229 case AceColumnType.AutoTypeEnabled: 5230 object oBlocker; 5231 str = AutoType.GetEnabledText(pe, out oBlocker); 5232 break; 5233 case AceColumnType.AutoTypeSequences: 5234 str = AutoType.GetSequencesText(pe); 5235 break; 5236 default: Debug.Assert(false); str = string.Empty; break; 5237 } 5238 5239 if(Program.Config.MainWindow.EntryListShowDerefData && bFormatForDisplay) 5240 { 5241 switch(col.Type) 5242 { 5243 case AceColumnType.Title: 5244 case AceColumnType.UserName: 5245 case AceColumnType.Password: 5246 case AceColumnType.Url: 5247 case AceColumnType.Notes: 5248 case AceColumnType.CustomString: 5249 bRequestAsync = SprEngine.MightDeref(str); 5250 break; 5251 default: bRequestAsync = false; break; 5252 } 5253 5254 if(!Program.Config.MainWindow.EntryListShowDerefDataAsync && 5255 bRequestAsync) 5256 { 5257 PwListItem pli = new PwListItem(pe); 5258 str = AsyncPwListUpdate.SprCompileFn(str, pli); 5259 bRequestAsync = false; 5260 } 5261 } 5262 else bRequestAsync = false; 5263 5264 return str; 5265 } 5266 GetAceColumn(int nColID)5267 private static AceColumn GetAceColumn(int nColID) 5268 { 5269 List<AceColumn> v = Program.Config.MainWindow.EntryListColumns; 5270 if((nColID < 0) || (nColID >= v.Count)) { Debug.Assert(false); return new AceColumn(); } 5271 5272 return v[nColID]; 5273 } 5274 ToggleFieldAsterisks(AceColumnType colType)5275 private void ToggleFieldAsterisks(AceColumnType colType) 5276 { 5277 List<AceColumn> l = Program.Config.MainWindow.EntryListColumns; 5278 foreach(AceColumn c in l) 5279 { 5280 if(c.Type == colType) 5281 { 5282 if((colType == AceColumnType.Password) && c.HideWithAsterisks && 5283 !AppPolicy.Try(AppPolicyId.UnhidePasswords)) 5284 return; 5285 5286 c.HideWithAsterisks = !c.HideWithAsterisks; 5287 } 5288 } 5289 5290 RefreshEntriesList(); 5291 UpdateUIState(false); // Update entry view 5292 } 5293 EditSelectedEntry(PwEntryFormTab eftInit)5294 private void EditSelectedEntry(PwEntryFormTab eftInit) 5295 { 5296 PwDatabase pd = m_docMgr.ActiveDatabase; 5297 if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; } 5298 5299 MultipleValuesEntryContext mvec = null; 5300 bool bMod; 5301 try 5302 { 5303 PwEntry pe; 5304 PwEntry[] v = GetSelectedEntries(); 5305 if((v == null) || (v.Length <= 1)) 5306 { 5307 pe = GetSelectedEntry(false); 5308 if(pe == null) return; // Do not assert 5309 } 5310 else 5311 { 5312 mvec = new MultipleValuesEntryContext(v, pd, out pe); 5313 5314 // if(pd.UINeedsIconUpdate) 5315 // { 5316 // RefreshEntriesList(); 5317 // UpdateUI(false, null, true, null, false, null, false); 5318 // } 5319 UpdateImageLists(false); // Image indices remain the same 5320 } 5321 5322 PwEntryForm dlg = new PwEntryForm(); 5323 dlg.InitEx(pe, PwEditMode.EditExistingEntry, pd, m_ilCurrentIcons, 5324 false, false); 5325 dlg.InitialTab = eftInit; 5326 dlg.MultipleValuesEntryContext = mvec; 5327 5328 bool bOK = (dlg.ShowDialog() == DialogResult.OK); 5329 bMod = (bOK && dlg.HasModifiedEntry); 5330 UIUtil.DestroyForm(dlg); 5331 5332 // Check bOK instead of bMod (dialog mod. check ignores multi states) 5333 if(bOK && (mvec != null)) 5334 { 5335 if(mvec.ApplyChanges()) bMod = true; 5336 } 5337 } 5338 finally { if(mvec != null) mvec.Dispose(); } 5339 5340 bool bUpdImg = pd.UINeedsIconUpdate; // Refreshing entries resets it 5341 RefreshEntriesList(); // Last access time 5342 UpdateUI(false, null, bUpdImg, null, false, null, bMod); 5343 5344 if(Program.Config.Application.AutoSaveAfterEntryEdit && bMod) 5345 SaveDatabase(pd, null); 5346 } 5347 EditSelectedGroup(GroupFormTab gftInit)5348 private void EditSelectedGroup(GroupFormTab gftInit) 5349 { 5350 PwDatabase pd = m_docMgr.ActiveDatabase; 5351 if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; } 5352 5353 PwGroup pg = GetSelectedGroup(); 5354 if(pg == null) { Debug.Assert(false); return; } 5355 5356 GroupForm gf = new GroupForm(); 5357 gf.InitEx(pg, false, m_ilCurrentIcons, pd); 5358 gf.InitialTab = gftInit; 5359 5360 if(UIUtil.ShowDialogAndDestroy(gf) == DialogResult.OK) 5361 UpdateUI(false, null, true, null, true, null, true); 5362 else UpdateUI(false, null, pd.UINeedsIconUpdate, null, 5363 pd.UINeedsIconUpdate, null, false); 5364 } 5365 ListContainsOnlyTans(PwObjectList<PwEntry> vEntries)5366 private static bool ListContainsOnlyTans(PwObjectList<PwEntry> vEntries) 5367 { 5368 if(vEntries == null) { Debug.Assert(false); return true; } 5369 5370 foreach(PwEntry pe in vEntries) 5371 { 5372 if(!PwDefs.IsTanEntry(pe)) return false; 5373 } 5374 5375 return true; 5376 } 5377 PerformSearch(SearchParameters sp, bool bInGroup, bool bFocusEntryList)5378 private void PerformSearch(SearchParameters sp, bool bInGroup, 5379 bool bFocusEntryList) 5380 { 5381 if(sp == null) { Debug.Assert(false); return; } 5382 5383 PwDatabase pd = m_docMgr.ActiveDatabase; 5384 if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; } 5385 5386 PwGroup pgRoot = (bInGroup ? GetSelectedGroup() : null); 5387 if(pgRoot == null) pgRoot = pd.RootGroup; 5388 if(pgRoot == null) { Debug.Assert(false); return; } 5389 5390 Application.DoEvents(); // Handle UI messages before blocking 5391 UIBlockInteraction(true); 5392 StatusBarLogger sl = CreateStatusBarLogger(); 5393 sl.StartLogging(KPRes.SearchingOp + "...", false); 5394 5395 PwGroup pgResults = null; 5396 Exception exFind = null; 5397 try { pgResults = SearchUtil.Find(sp, pgRoot, sl); } 5398 catch(Exception ex) { exFind = ex; } 5399 5400 sl.EndLogging(); 5401 UIBlockInteraction(false); 5402 5403 if(exFind != null) 5404 MessageService.ShowWarning(sp.SearchString, exFind); 5405 else ShowSearchResults(pgResults, sp, pgRoot, bFocusEntryList); 5406 } 5407 PerformSearchDialog(string strProfile, bool bInGroup)5408 private void PerformSearchDialog(string strProfile, bool bInGroup) 5409 { 5410 PwDatabase pd = m_docMgr.ActiveDatabase; 5411 if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; } 5412 5413 PwGroup pgRoot = (bInGroup ? GetSelectedGroup() : null); 5414 if(pgRoot == null) pgRoot = pd.RootGroup; 5415 if(pgRoot == null) { Debug.Assert(false); return; } 5416 5417 SearchForm sf = new SearchForm(); 5418 sf.InitEx(pd, pgRoot); 5419 sf.InitProfile = strProfile; 5420 5421 if(sf.ShowDialog() == DialogResult.OK) 5422 ShowSearchResults(sf.SearchResultsGroup, sf.SearchResultParameters, 5423 pgRoot, true); 5424 UIUtil.DestroyForm(sf); 5425 } 5426 5427 private static string[] g_vProfileCmdTexts = null; UpdateFindProfilesMenu(ToolStripMenuItem tsmiProfiles, bool bEnsurePopupOnly)5428 private void UpdateFindProfilesMenu(ToolStripMenuItem tsmiProfiles, 5429 bool bEnsurePopupOnly) 5430 { 5431 if(tsmiProfiles == null) { Debug.Assert(false); return; } 5432 5433 ToolStripItemCollection tsic = tsmiProfiles.DropDownItems; 5434 tsic.Clear(); 5435 5436 if(bEnsurePopupOnly) 5437 { 5438 tsic.Add(new ToolStripMenuItem(KPRes.None)); 5439 return; 5440 } 5441 5442 if(g_vProfileCmdTexts == null) 5443 { 5444 List<char> lCmdAvailKeys = new List<char>(PwCharSet.MenuAccels); 5445 GFunc<string, string, bool, string> f = delegate(string str, 5446 string strSuffix, bool bDots) 5447 { 5448 str = StrUtil.TrimDots(StrUtil.RemoveAccelerator(str), true); 5449 if(!string.IsNullOrEmpty(strSuffix)) 5450 str += " (" + StrUtil.EncodeMenuText(strSuffix) + ")"; 5451 if(bDots) str += "..."; 5452 return StrUtil.AddAccelerator(str, lCmdAvailKeys); 5453 }; 5454 5455 g_vProfileCmdTexts = new string[4]; 5456 g_vProfileCmdTexts[0] = f(KPRes.Find, null, false); 5457 g_vProfileCmdTexts[1] = f(KPRes.Find, KPRes.SelectedGroup, false); 5458 g_vProfileCmdTexts[2] = f(KPRes.OpenCmd, null, true); 5459 g_vProfileCmdTexts[3] = f(KPRes.OpenCmd, KPRes.SelectedGroup, true); 5460 } 5461 5462 List<ToolStripItem> lTsi = new List<ToolStripItem>(); 5463 List<char> lPrfAvailKeys = new List<char>(PwCharSet.MenuAccels); 5464 5465 AceSearch aceSearch = Program.Config.Search; 5466 aceSearch.SortProfiles(); 5467 5468 foreach(SearchParameters sp in aceSearch.UserProfiles) 5469 { 5470 ToolStripMenuItem tsmi = new ToolStripMenuItem( 5471 StrUtil.AddAccelerator(StrUtil.EncodeMenuText(sp.Name), 5472 lPrfAvailKeys)); 5473 lTsi.Add(tsmi); 5474 5475 ToolStripMenuItem tsmiFD = new ToolStripMenuItem( 5476 g_vProfileCmdTexts[0], Properties.Resources.B16x16_XMag, 5477 this.OnFindProfileFind); 5478 tsmiFD.Tag = sp.Name; 5479 tsmi.DropDownItems.Add(tsmiFD); 5480 5481 ToolStripMenuItem tsmiFG = new ToolStripMenuItem( 5482 g_vProfileCmdTexts[1], Properties.Resources.B16x16_XMag, 5483 this.OnFindProfileFindInGroup); 5484 tsmiFG.Tag = sp.Name; 5485 tsmi.DropDownItems.Add(tsmiFG); 5486 5487 tsmi.DropDownItems.Add(new ToolStripSeparator()); 5488 5489 ToolStripMenuItem tsmiOD = new ToolStripMenuItem( 5490 g_vProfileCmdTexts[2], Properties.Resources.B16x16_Misc, 5491 this.OnFindProfileOpen); 5492 tsmiOD.Tag = sp.Name; 5493 tsmi.DropDownItems.Add(tsmiOD); 5494 5495 ToolStripMenuItem tsmiOG = new ToolStripMenuItem( 5496 g_vProfileCmdTexts[3], Properties.Resources.B16x16_Misc, 5497 this.OnFindProfileOpenInGroup); 5498 tsmiOG.Tag = sp.Name; 5499 tsmi.DropDownItems.Add(tsmiOG); 5500 } 5501 5502 if(lTsi.Count == 0) 5503 { 5504 ToolStripMenuItem tsmi = new ToolStripMenuItem( 5505 StrUtil.EncodeMenuText("(" + KPRes.None + ")")); 5506 tsmi.Enabled = false; 5507 lTsi.Add(tsmi); 5508 } 5509 5510 tsic.AddRange(lTsi.ToArray()); 5511 } 5512 PerformSearchMenu(object sender, bool bInGroup, bool bOpen)5513 private void PerformSearchMenu(object sender, bool bInGroup, bool bOpen) 5514 { 5515 ToolStripMenuItem tsmi = (sender as ToolStripMenuItem); 5516 if(tsmi == null) { Debug.Assert(false); return; } 5517 5518 string strProfile = (tsmi.Tag as string); 5519 if(string.IsNullOrEmpty(strProfile)) { Debug.Assert(false); return; } 5520 5521 if(bOpen) PerformSearchDialog(strProfile, bInGroup); 5522 else 5523 { 5524 SearchParameters sp = Program.Config.Search.FindProfile(strProfile); 5525 PerformSearch(sp, bInGroup, true); 5526 } 5527 } 5528 OnFindProfileFind(object sender, EventArgs e)5529 private void OnFindProfileFind(object sender, EventArgs e) 5530 { 5531 PerformSearchMenu(sender, false, false); 5532 } 5533 OnFindProfileFindInGroup(object sender, EventArgs e)5534 private void OnFindProfileFindInGroup(object sender, EventArgs e) 5535 { 5536 PerformSearchMenu(sender, true, false); 5537 } 5538 OnFindProfileOpen(object sender, EventArgs e)5539 private void OnFindProfileOpen(object sender, EventArgs e) 5540 { 5541 PerformSearchMenu(sender, false, true); 5542 } 5543 OnFindProfileOpenInGroup(object sender, EventArgs e)5544 private void OnFindProfileOpenInGroup(object sender, EventArgs e) 5545 { 5546 PerformSearchMenu(sender, true, true); 5547 } 5548 OnShowEntriesByTag(object sender, DynamicMenuEventArgs e)5549 private void OnShowEntriesByTag(object sender, DynamicMenuEventArgs e) 5550 { 5551 if(e == null) { Debug.Assert(false); return; } 5552 ShowEntriesByTag((e.Tag as string), true); 5553 } 5554 ShowEntriesByTag(string strTag, bool bFocusEntryList)5555 internal void ShowEntriesByTag(string strTag, bool bFocusEntryList) 5556 { 5557 if(strTag == null) { Debug.Assert(false); return; } 5558 if(strTag.Length == 0) return; // No assert (call from trigger) 5559 5560 PwDatabase pd = m_docMgr.ActiveDatabase; 5561 if((pd == null) || !pd.IsOpen) return; // No assert (call from trigger) 5562 5563 PwObjectList<PwEntry> l = new PwObjectList<PwEntry>(); 5564 pd.RootGroup.FindEntriesByTag(strTag, l, true); 5565 5566 PwGroup pgResults = new PwGroup(true, true); 5567 pgResults.IsVirtual = true; 5568 foreach(PwEntry pe in l) pgResults.AddEntry(pe, false, false); 5569 5570 ShowSearchResults(pgResults, null, pd.RootGroup, bFocusEntryList); 5571 } 5572 5573 private enum TagsMenuMode 5574 { 5575 All = 0, 5576 Add = 1, 5577 Remove = 2, 5578 5579 EnsurePopupOnly = 127 // Ensure drawing of menu popup arrow 5580 } 5581 UpdateTagsMenu(DynamicMenu dm, bool bWithSeparator, bool bPrefixTag, TagsMenuMode tmm)5582 private void UpdateTagsMenu(DynamicMenu dm, bool bWithSeparator, bool bPrefixTag, 5583 TagsMenuMode tmm) 5584 { 5585 if(dm == null) { Debug.Assert(false); return; } 5586 dm.Clear(); 5587 5588 if(bWithSeparator) dm.AddSeparator(); 5589 5590 string strNoTags = "(" + KPRes.TagsNotFound + ")"; 5591 PwDatabase pd = m_docMgr.ActiveDatabase; 5592 if(!pd.IsOpen || (tmm == TagsMenuMode.EnsurePopupOnly)) 5593 { 5594 ToolStripMenuItem tsmi = dm.AddItem(strNoTags, null, string.Empty); 5595 tsmi.Enabled = false; 5596 return; 5597 } 5598 5599 bool bReqEntrySel = ((tmm == TagsMenuMode.Add) || (tmm == TagsMenuMode.Remove)); 5600 PwGroup pgSel = GetSelectedEntriesAsGroup(); 5601 uint uSelCount = pgSel.Entries.UCount; 5602 bool bForceDisabled = (bReqEntrySel && (uSelCount == 0)); 5603 5604 IDictionary<string, uint> dAllTags = pd.RootGroup.BuildEntryTagsDict(false); 5605 List<string> lAllTags = new List<string>(dAllTags.Keys); 5606 lAllTags.Sort(StrUtil.CompareNaturally); 5607 5608 Dictionary<string, bool> dEnabledTags = null; 5609 if((tmm == TagsMenuMode.Add) && (uSelCount > 0)) 5610 { 5611 dEnabledTags = new Dictionary<string, bool>(); 5612 List<string> lIntersect = pgSel.Entries.GetAt(0).Tags; 5613 for(uint u = 1; u < uSelCount; ++u) 5614 lIntersect = new List<string>(MemUtil.Intersect(lIntersect, 5615 pgSel.Entries.GetAt(u).Tags, null)); 5616 foreach(string strTag in MemUtil.Except(lAllTags, lIntersect, null)) 5617 dEnabledTags[strTag] = true; 5618 } 5619 else if(tmm == TagsMenuMode.Remove) 5620 { 5621 dEnabledTags = new Dictionary<string, bool>(); 5622 List<string> lSelectedTags = pgSel.BuildEntryTagsList(false, false); 5623 foreach(string strTag in lSelectedTags) 5624 dEnabledTags[strTag] = true; 5625 } 5626 5627 string strPrefix = StrUtil.EncodeMenuText(KPRes.Tag + ": "); 5628 Image imgIcon = Properties.Resources.B16x16_KNotes; 5629 5630 List<char> lAvailKeys = new List<char>(PwCharSet.MenuAccels); 5631 foreach(string strTag in lAllTags) 5632 { 5633 string strText = StrUtil.EncodeMenuText(strTag); 5634 strText = StrUtil.AddAccelerator(strText, lAvailKeys); 5635 if(bPrefixTag) strText = strPrefix + strText; 5636 5637 ToolStripMenuItem tsmi = dm.AddItem(strText, imgIcon, strTag); 5638 if(tmm == TagsMenuMode.All) 5639 { 5640 uint uCount; 5641 dAllTags.TryGetValue(strTag, out uCount); 5642 tsmi.ShortcutKeyDisplayString = "(" + uCount.ToString() + ")"; 5643 } 5644 5645 if(bForceDisabled) tsmi.Enabled = false; 5646 else if(dEnabledTags != null) 5647 { 5648 if(!dEnabledTags.ContainsKey(strTag)) tsmi.Enabled = false; 5649 } 5650 } 5651 5652 if(lAllTags.Count == 0) 5653 { 5654 ToolStripMenuItem tsmi = dm.AddItem(strNoTags, null, string.Empty); 5655 tsmi.Enabled = false; 5656 } 5657 } 5658 OnAddEntryTag(object sender, DynamicMenuEventArgs e)5659 private void OnAddEntryTag(object sender, DynamicMenuEventArgs e) 5660 { 5661 string strTag = (e.Tag as string); 5662 if(strTag == null) { Debug.Assert(false); return; } 5663 5664 if(strTag.Length == 0) 5665 { 5666 SingleLineEditForm dlg = new SingleLineEditForm(); 5667 dlg.InitEx(KPRes.TagNew, KPRes.TagAddNew, KPRes.Name + ":", 5668 Properties.Resources.B48x48_KMag, string.Empty, null); 5669 5670 if(UIUtil.ShowDialogNotValue(dlg, DialogResult.OK)) return; 5671 strTag = dlg.ResultString; 5672 UIUtil.DestroyForm(dlg); 5673 } 5674 5675 AddOrRemoveTagsToFromSelectedEntries(strTag, true); 5676 } 5677 OnRemoveEntryTag(object sender, DynamicMenuEventArgs e)5678 private void OnRemoveEntryTag(object sender, DynamicMenuEventArgs e) 5679 { 5680 AddOrRemoveTagsToFromSelectedEntries((e.Tag as string), false); 5681 } 5682 AddOrRemoveTagsToFromSelectedEntries(string strTags, bool bAdd)5683 private void AddOrRemoveTagsToFromSelectedEntries(string strTags, bool bAdd) 5684 { 5685 if(strTags == null) { Debug.Assert(false); return; } 5686 if(strTags.Length == 0) return; 5687 5688 PwDatabase pd = m_docMgr.ActiveDatabase; 5689 PwEntry[] vEntries = GetSelectedEntries(); 5690 if((vEntries == null) || (vEntries.Length == 0)) return; 5691 5692 List<string> lToProcess = StrUtil.StringToTags(strTags); 5693 bool bModified = false; 5694 5695 foreach(PwEntry pe in vEntries) 5696 { 5697 List<string> l = pe.Tags; 5698 int cBefore = l.Count; 5699 5700 if(bAdd) 5701 l = new List<string>(MemUtil.Union<string>(l, lToProcess, null)); 5702 else 5703 l = new List<string>(MemUtil.Except<string>(l, lToProcess, null)); 5704 5705 if(l.Count != cBefore) 5706 { 5707 pe.CreateBackup(pd); 5708 5709 pe.Tags = l; 5710 pe.Touch(true, false); 5711 5712 bModified = true; 5713 } 5714 } 5715 5716 if(bModified) RefreshEntriesList(); 5717 UpdateUIState(bModified); 5718 } 5719 5720 private static bool? m_bCachedSelfTestResult = null; PerformSelfTest()5721 private static bool PerformSelfTest() 5722 { 5723 if(m_bCachedSelfTestResult.HasValue) 5724 return m_bCachedSelfTestResult.Value; 5725 5726 bool bResult = true; 5727 try { SelfTest.Perform(); } 5728 catch(Exception exSelfTest) 5729 { 5730 MessageService.ShowWarning(KPRes.SelfTestFailed, exSelfTest); 5731 bResult = false; 5732 } 5733 5734 m_bCachedSelfTestResult = bResult; 5735 return bResult; 5736 } 5737 SortSubGroups(bool bRecursive)5738 private void SortSubGroups(bool bRecursive) 5739 { 5740 PwGroup pg = GetSelectedGroup(); 5741 if(pg == null) return; 5742 5743 if(!bRecursive && (pg.Groups.UCount <= 1)) return; // Nothing to do 5744 if(pg.Groups.UCount == 0) return; // Nothing to do 5745 5746 pg.SortSubGroups(bRecursive); 5747 UpdateUI(false, null, true, null, false, null, true); 5748 } 5749 5750 /* private bool CreateColorizedIcon(Icon icoBase, int qSize, 5751 ref KeyValuePair<Color, Icon> kvpStore, out Icon icoAssignable, 5752 out Icon icoDisposable) 5753 { 5754 PwDatabase pd = m_docMgr.ActiveDatabase; 5755 5756 Color clrNew = Color.Empty; 5757 if((pd != null) && pd.IsOpen && !UIUtil.ColorsEqual(pd.Color, Color.Empty)) 5758 clrNew = pd.Color; 5759 5760 if(UIUtil.ColorsEqual(clrNew, kvpStore.Key)) 5761 { 5762 icoDisposable = null; 5763 icoAssignable = (kvpStore.Value ?? icoBase); 5764 return false; 5765 } 5766 5767 icoDisposable = kvpStore.Value; 5768 if(UIUtil.ColorsEqual(clrNew, Color.Empty)) 5769 { 5770 kvpStore = new KeyValuePair<Color, Icon>(Color.Empty, null); 5771 icoAssignable = icoBase; 5772 } 5773 else 5774 { 5775 kvpStore = new KeyValuePair<Color, Icon>(clrNew, 5776 UIUtil.CreateColorizedIcon(icoBase, clrNew, qSize)); 5777 icoAssignable = kvpStore.Value; 5778 } 5779 5780 return true; 5781 } */ 5782 CreateColorizedIcon(AppIconType t, bool bSmall)5783 private Icon CreateColorizedIcon(AppIconType t, bool bSmall) 5784 { 5785 PwDatabase pd = m_docMgr.ActiveDatabase; 5786 5787 Color clr = Color.Empty; 5788 if((pd != null) && pd.IsOpen) clr = pd.Color; 5789 5790 Size sz = (bSmall ? UIUtil.GetSmallIconSize() : UIUtil.GetIconSize()); 5791 5792 return AppIcons.Get(t, sz, clr); 5793 } 5794 SetObjectsDeletedStatus(uint uDeleted, bool bDbMntnc)5795 private void SetObjectsDeletedStatus(uint uDeleted, bool bDbMntnc) 5796 { 5797 string str = (StrUtil.ReplaceCaseInsensitive(KPRes.ObjectsDeleted, 5798 @"{PARAM}", uDeleted.ToString()) + "."); 5799 SetStatusEx(str); 5800 if(!bDbMntnc || !Program.Config.UI.ShowDbMntncResultsDialog) return; 5801 5802 VistaTaskDialog dlg = new VistaTaskDialog(); 5803 dlg.CommandLinks = false; 5804 dlg.Content = str; 5805 dlg.SetIcon(VtdIcon.Information); 5806 dlg.VerificationText = KPRes.DialogNoShowAgain; 5807 dlg.WindowTitle = PwDefs.ShortProductName; 5808 if(dlg.ShowDialog()) 5809 { 5810 if(dlg.ResultVerificationChecked) 5811 Program.Config.UI.ShowDbMntncResultsDialog = false; 5812 } 5813 else MessageService.ShowInfo(str); 5814 } 5815 ApplyUICustomizations()5816 private void ApplyUICustomizations() 5817 { 5818 ulong u = Program.Config.UI.UIFlags; 5819 if((u & (ulong)AceUIFlags.DisableOptions) != 0) 5820 m_menuToolsOptions.Enabled = false; 5821 if((u & (ulong)AceUIFlags.DisablePlugins) != 0) 5822 m_menuToolsPlugins.Enabled = false; 5823 if((u & (ulong)AceUIFlags.DisableTriggers) != 0) 5824 m_menuToolsTriggers.Enabled = false; 5825 if((u & (ulong)AceUIFlags.DisableUpdateCheck) != 0) 5826 m_menuHelpCheckForUpdates.Enabled = false; 5827 } 5828 OnFormLoadParallelAsync(object stateInfo)5829 private static void OnFormLoadParallelAsync(object stateInfo) 5830 { 5831 try 5832 { 5833 PopularPasswords.Add(Properties.Resources.MostPopularPasswords, true); 5834 5835 string strShInstUtil = UrlUtil.GetFileDirectory( 5836 WinUtil.GetExecutable(), true, false) + AppDefs.FileNames.ShInstUtil; 5837 5838 // Unblock the application such that the user isn't 5839 // prompted next time anymore 5840 WinUtil.RemoveZoneIdentifier(WinUtil.GetExecutable()); 5841 WinUtil.RemoveZoneIdentifier(AppHelp.LocalHelpFile); 5842 WinUtil.RemoveZoneIdentifier(strShInstUtil); 5843 5844 // https://stackoverflow.com/questions/26256917/how-can-i-prevent-my-application-from-causing-a-0xc0000142-error-in-csc-exe 5845 XmlSerializer xs = new XmlSerializer(typeof(IpcParamEx)); 5846 IpcParamEx ipc = new IpcParamEx(); 5847 using(MemoryStream ms = new MemoryStream()) 5848 { 5849 xs.Serialize(ms, ipc); 5850 } 5851 5852 FileTransactionEx.ClearOld(); 5853 } 5854 catch(Exception) { Debug.Assert(false); } 5855 } 5856 IsCommandTypeInvokable(MainAppState sContext, AppCommandType t)5857 internal bool IsCommandTypeInvokable(MainAppState sContext, AppCommandType t) 5858 { 5859 MainAppState s = (sContext ?? GetMainAppState()); 5860 5861 if(t == AppCommandType.Window) 5862 return (s.NoWindowShown && !UIIsInteractionBlocked()); 5863 if(t == AppCommandType.Lock) 5864 return (s.NoWindowShown && !UIIsInteractionBlocked() && s.EnableLockCmd); 5865 5866 return false; 5867 } 5868 UpdateTrayState()5869 private void UpdateTrayState() 5870 { 5871 UpdateUIState(false); // Update tray lock text 5872 5873 ulong u = Program.Config.UI.UIFlags; 5874 bool bOptions = ((u & (ulong)AceUIFlags.DisableOptions) == 0); 5875 5876 MainAppState s = GetMainAppState(); 5877 bool bInvkWnd = IsCommandTypeInvokable(s, AppCommandType.Window); 5878 5879 m_ctxTrayTray.Enabled = bInvkWnd; 5880 m_ctxTrayGenPw.Enabled = bInvkWnd; 5881 m_ctxTrayOptions.Enabled = (bInvkWnd && bOptions); 5882 m_ctxTrayCancel.Enabled = (m_sCancellable.Count != 0); 5883 m_ctxTrayLock.Enabled = IsCommandTypeInvokable(s, AppCommandType.Lock); 5884 m_ctxTrayFileExit.Enabled = bInvkWnd; 5885 } 5886 GetEntryListSprContext(PwEntry pe, PwDatabase pd)5887 internal static SprContext GetEntryListSprContext(PwEntry pe, 5888 PwDatabase pd) 5889 { 5890 SprContext ctx = new SprContext(pe, pd, SprCompileFlags.Deref); 5891 ctx.ForcePlainTextPasswords = false; 5892 return ctx; 5893 } 5894 EnsureAlwaysOnTopOpt()5895 private void EnsureAlwaysOnTopOpt() 5896 { 5897 bool bWish = Program.Config.MainWindow.AlwaysOnTop; 5898 if(NativeLib.IsUnix()) { this.TopMost = bWish; return; } 5899 5900 // Workaround for issue reported in KPB 3475997 5901 this.TopMost = false; 5902 if(bWish) this.TopMost = true; 5903 } 5904 IsPrimaryControlActive()5905 private bool IsPrimaryControlActive() 5906 { 5907 try 5908 { 5909 // return (m_lvEntries.Focused || m_tvGroups.Focused || 5910 // m_richEntryView.Focused || m_tbQuickFind.Focused); 5911 5912 Control c = UIUtil.GetActiveControl(this); 5913 if(c == null) return false; 5914 5915 return ((c == m_lvEntries) || (c == m_tvGroups) || 5916 (c == m_richEntryView) || (c == m_tbQuickFind.Control)); 5917 } 5918 catch(Exception) { Debug.Assert(false); } 5919 5920 return false; 5921 } 5922 SetVisibleCore(bool value)5923 protected override void SetVisibleCore(bool value) 5924 { 5925 if(MonoWorkarounds.IsRequired(3574233558U)) 5926 { 5927 if(!value && m_bFormShown && (this.WindowState == 5928 FormWindowState.Minimized)) 5929 { 5930 // Mono destroys window when trying to hide minimized 5931 // window; so, restore it before hiding 5932 this.WindowState = FormWindowState.Normal; 5933 Application.DoEvents(); 5934 Thread.Sleep(250); 5935 } 5936 5937 if(m_bFormShown) m_menuMain.Visible = value; 5938 } 5939 5940 base.SetVisibleCore(value); 5941 } 5942 MoveOrCopySelectedEntries(PwGroup pgTo, DragDropEffects e)5943 private void MoveOrCopySelectedEntries(PwGroup pgTo, DragDropEffects e) 5944 { 5945 PwEntry[] vSelected = GetSelectedEntries(); 5946 if((vSelected == null) || (vSelected.Length == 0)) return; 5947 5948 PwGroup pgSafeView = (m_pgActiveAtDragStart ?? new PwGroup()); 5949 bool bFullUpdateView = false; 5950 List<PwEntry> vNowInvisible = new List<PwEntry>(); 5951 5952 if(e == DragDropEffects.Move) 5953 { 5954 foreach(PwEntry pe in vSelected) 5955 { 5956 PwGroup pgParent = pe.ParentGroup; 5957 if(pgParent == null) { Debug.Assert(false); continue; } 5958 if(pgParent == pgTo) continue; 5959 5960 if(!pgParent.Entries.Remove(pe)) { Debug.Assert(false); continue; } 5961 5962 pgTo.AddEntry(pe, true, true); 5963 5964 // pe.CreateBackup(m_docMgr.ActiveDatabase); 5965 pe.PreviousParentGroup = pgParent.Uuid; 5966 // pe.Touch(true, false); 5967 5968 if(pe.IsContainedIn(pgSafeView)) bFullUpdateView = true; 5969 else vNowInvisible.Add(pe); 5970 } 5971 } 5972 else if(e == DragDropEffects.Copy) 5973 { 5974 foreach(PwEntry pe in vSelected) 5975 { 5976 PwEntry peCopy = pe.Duplicate(); 5977 5978 pgTo.AddEntry(peCopy, true, true); 5979 5980 if(peCopy.IsContainedIn(pgSafeView)) bFullUpdateView = true; 5981 } 5982 } 5983 else { Debug.Assert(false); } 5984 5985 if(!bFullUpdateView) 5986 { 5987 RemoveEntriesFromList(vNowInvisible, true); 5988 UpdateUI(false, null, true, m_pgActiveAtDragStart, false, null, true); 5989 } 5990 else UpdateUI(false, null, true, m_pgActiveAtDragStart, true, null, true); 5991 5992 m_pgActiveAtDragStart = null; 5993 } 5994 UpdateEntryMoveMenu(DynamicMenu dm, bool bDummyOnly)5995 private void UpdateEntryMoveMenu(DynamicMenu dm, bool bDummyOnly) 5996 { 5997 if(dm == null) { Debug.Assert(false); return; } 5998 5999 dm.Clear(); 6000 6001 PwDatabase pd = m_docMgr.ActiveDatabase; 6002 PwGroup pgRoot = pd.RootGroup; 6003 if((pd == null) || !pd.IsOpen || (pgRoot == null) || bDummyOnly) 6004 { 6005 ToolStripMenuItem tsmi = dm.AddItem(m_strNoneP, null); 6006 tsmi.Enabled = false; 6007 return; 6008 } 6009 6010 List<char> lAvailKeys = new List<char>(PwCharSet.MenuAccels); 6011 GroupHandler gh = delegate(PwGroup pg) 6012 { 6013 string strName = StrUtil.EncodeMenuText(pg.Name); 6014 strName = StrUtil.AddAccelerator(strName, lAvailKeys); 6015 strName = strName.PadLeft(((int)pg.GetDepth() * 4) + strName.Length); 6016 6017 int nIconID = ((!pg.CustomIconUuid.Equals(PwUuid.Zero)) ? 6018 ((int)PwIcon.Count + pd.GetCustomIconIndex( 6019 pg.CustomIconUuid)) : (int)pg.IconId); 6020 6021 ToolStripMenuItem tsmi = dm.AddItem(strName, 6022 m_ilCurrentIcons.Images[nIconID], pg); 6023 6024 if(pd.RecycleBinEnabled && pg.Uuid.Equals(pd.RecycleBinUuid) && 6025 (m_fontItalicTree != null)) 6026 tsmi.Font = m_fontItalicTree; 6027 6028 return true; 6029 }; 6030 6031 gh(pgRoot); 6032 pgRoot.TraverseTree(TraversalMethod.PreOrder, gh, null); 6033 } 6034 OnEntryMoveToGroup(object sender, DynamicMenuEventArgs e)6035 private void OnEntryMoveToGroup(object sender, DynamicMenuEventArgs e) 6036 { 6037 PwGroup pgTo = (e.Tag as PwGroup); 6038 if(pgTo == null) { Debug.Assert(false); return; } 6039 6040 m_pgActiveAtDragStart = GetSelectedGroup(); 6041 MoveOrCopySelectedEntries(pgTo, DragDropEffects.Move); 6042 } 6043 FixDuplicateUuids(PwDatabase pd, IOConnectionInfo ioc)6044 private bool FixDuplicateUuids(PwDatabase pd, IOConnectionInfo ioc) 6045 { 6046 if(pd == null) { Debug.Assert(false); return false; } 6047 6048 if(!pd.HasDuplicateUuids()) return false; 6049 6050 string str = string.Empty; 6051 if(ioc != null) 6052 { 6053 string strFile = ioc.GetDisplayName(); 6054 if(!string.IsNullOrEmpty(strFile)) 6055 str += strFile + MessageService.NewParagraph; 6056 } 6057 6058 str += KPRes.UuidDupInDb + MessageService.NewParagraph + 6059 KPRes.CorruptionByExt + MessageService.NewParagraph + 6060 KPRes.UuidFix; 6061 6062 if(VistaTaskDialog.ShowMessageBoxEx(str, null, 6063 PwDefs.ShortProductName, VtdIcon.Warning, null, 6064 KPRes.RepairCmd, (int)DialogResult.Cancel, null, 0) < 0) 6065 MessageService.ShowWarning(str); 6066 6067 pd.FixDuplicateUuids(); 6068 pd.Modified = true; 6069 return true; 6070 } 6071 BlockMainTimer(bool bBlock)6072 private void BlockMainTimer(bool bBlock) 6073 { 6074 if(bBlock) 6075 { 6076 m_timerMain.Enabled = false; 6077 ++m_uMainTimerBlocked; 6078 } 6079 else 6080 { 6081 if(m_uMainTimerBlocked == 0) { Debug.Assert(false); return; } 6082 6083 m_timerMain.Enabled = true; 6084 --m_uMainTimerBlocked; 6085 } 6086 } 6087 ShowSelectedEntryParentGroup()6088 private void ShowSelectedEntryParentGroup() 6089 { 6090 PwEntry pe = GetSelectedEntry(false); 6091 if(pe == null) return; 6092 6093 PwGroup pg = pe.ParentGroup; 6094 if(pg == null) { Debug.Assert(false); return; } 6095 6096 UpdateUI(false, null, true, pg, true, null, false); 6097 6098 TreeNode tnSel = m_tvGroups.SelectedNode; 6099 if(tnSel != null) tnSel.EnsureVisible(); 6100 6101 EnsureVisibleSelected(false); 6102 } 6103 PerformAutoType(string strSeq)6104 private void PerformAutoType(string strSeq) 6105 { 6106 PwEntry pe = GetSelectedEntry(false); 6107 if(pe != null) 6108 { 6109 try 6110 { 6111 AutoType.PerformIntoPreviousWindow(this, pe, 6112 m_docMgr.SafeFindContainerOf(pe), strSeq); 6113 } 6114 catch(Exception ex) { MessageService.ShowWarning(ex); } 6115 } 6116 } 6117 OnEntryPerformAutoTypeAdv(object sender, DynamicMenuEventArgs e)6118 private void OnEntryPerformAutoTypeAdv(object sender, DynamicMenuEventArgs e) 6119 { 6120 if(e == null) { Debug.Assert(false); return; } 6121 6122 string strSeq = e.ItemName; 6123 if(string.IsNullOrEmpty(strSeq)) { Debug.Assert(false); return; } 6124 6125 PerformAutoType(strSeq); 6126 } 6127 UpdateAlternatingBgColor()6128 private void UpdateAlternatingBgColor() 6129 { 6130 m_clrAlternateItemBgColor = UIUtil.GetAlternateColorEx(m_lvEntries.BackColor); 6131 } 6132 CreateAndShowEntryList(EntryReportDelegate f, string strStatus, Image imgIcon, string strTitle, string strSubTitle, string strNote, bool bForceDialog, bool bDisableSorting)6133 private int CreateAndShowEntryList(EntryReportDelegate f, string strStatus, 6134 Image imgIcon, string strTitle, string strSubTitle, string strNote, 6135 bool bForceDialog, bool bDisableSorting) 6136 { 6137 if(f == null) { Debug.Assert(false); return -1; } 6138 6139 PwDatabase pd = m_docMgr.ActiveDatabase; 6140 if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return -1; } 6141 6142 Form fOptDialog; 6143 IStatusLogger sl = StatusUtil.CreateStatusDialog(this, out fOptDialog, 6144 null, (strStatus ?? "..."), true, false); 6145 UIBlockInteraction(true); 6146 6147 Action<ListView> fInit = null; 6148 List<object> l = null; 6149 6150 string strExcp = null; 6151 try { l = f(pd, sl, out fInit); } 6152 catch(Exception ex) { strExcp = ex.Message; } 6153 6154 UIBlockInteraction(false); 6155 sl.EndLogging(); 6156 6157 if(!string.IsNullOrEmpty(strExcp)) 6158 MessageService.ShowWarning(strExcp); 6159 6160 if(l == null) return -1; 6161 if((l.Count == 0) && !bForceDialog) return 0; 6162 6163 ListViewForm dlg = new ListViewForm(); 6164 dlg.InitEx(strTitle, strSubTitle, strNote, imgIcon, l, 6165 m_ilCurrentIcons, fInit); 6166 UIUtil.ShowDialogAndDestroy(dlg, this); 6167 6168 PwGroup pg = (dlg.ResultGroup as PwGroup); 6169 PwEntry pe = (dlg.ResultItem as PwEntry); 6170 if((pg == null) && (pe == null)) 6171 pg = (dlg.ResultItem as PwGroup); 6172 6173 if((pg != null) || (pe != null)) 6174 { 6175 if(pg != null) 6176 UpdateUI(false, null, false, null, true, pg, false, m_lvEntries); 6177 else 6178 UpdateUI(false, null, true, pe.ParentGroup, true, null, false, m_lvEntries); 6179 6180 if(bDisableSorting) SortPasswordList(false, 0, null, true); 6181 6182 if(pe != null) 6183 { 6184 PwObjectList<PwEntry> lSel = new PwObjectList<PwEntry>(); 6185 lSel.Add(pe); 6186 SelectEntries(lSel, true, true); 6187 6188 EnsureVisibleSelected(false); 6189 } 6190 else SelectFirstEntryIfNoneSelected(); 6191 6192 UpdateUIState(false); // For selected entry 6193 } 6194 6195 return l.Count; 6196 } 6197 CopySelectedObjects(Type t, bool bEncrypt)6198 private void CopySelectedObjects(Type t, bool bEncrypt) 6199 { 6200 PwDatabase pd = m_docMgr.ActiveDatabase; 6201 if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; } 6202 6203 if(!AppPolicy.Try(AppPolicyId.CopyWholeEntries)) return; 6204 6205 try 6206 { 6207 if(t == typeof(PwGroup)) 6208 { 6209 PwGroup pg = GetSelectedGroup(); 6210 if(pg == null) { Debug.Assert(false); return; } 6211 6212 EntryUtil.CopyGroupToClipboard(pd, pg, this.Handle, bEncrypt); 6213 } 6214 else if(t == typeof(PwEntry)) 6215 { 6216 PwEntry[] v = GetSelectedEntries(); 6217 if((v == null) || (v.Length == 0)) { Debug.Assert(false); return; } 6218 6219 EntryUtil.CopyEntriesToClipboard(pd, v, this.Handle, bEncrypt); 6220 } 6221 else { Debug.Assert(false); return; } 6222 6223 StartClipboardCountdown(); 6224 } 6225 catch(Exception ex) { MessageService.ShowWarning(ex); } 6226 } 6227 SetSelectedEntryExpiry(bool bExpNow)6228 private void SetSelectedEntryExpiry(bool bExpNow) 6229 { 6230 PwDatabase pd = m_docMgr.ActiveDatabase; 6231 if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; } 6232 6233 PwEntry[] v = GetSelectedEntries(); 6234 if((v == null) || (v.Length == 0)) return; 6235 6236 DateTime dt = DateTime.UtcNow.AddSeconds(-1); 6237 bool bMod = false; 6238 6239 foreach(PwEntry pe in v) 6240 { 6241 if(pe == null) { Debug.Assert(false); continue; } 6242 if(!bExpNow && !pe.Expires) continue; 6243 6244 pe.CreateBackup(pd); 6245 6246 pe.Expires = bExpNow; 6247 if(bExpNow) pe.ExpiryTime = dt; 6248 6249 pe.Touch(true, false); 6250 bMod = true; 6251 } 6252 6253 RefreshEntriesList(); 6254 UpdateUIState(bMod); 6255 } 6256 AddEntryEx(object sender, GFunc<PwEntry> fNewEntry, Action<PwEntry> fAddPre, Action<PwEntry> fAddPost)6257 internal void AddEntryEx(object sender, GFunc<PwEntry> fNewEntry, 6258 Action<PwEntry> fAddPre, Action<PwEntry> fAddPost) 6259 { 6260 PwDatabase pd = m_docMgr.ActiveDatabase; 6261 if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; } 6262 6263 PwGroup pg = GetSelectedGroup(); 6264 if((pg == null) || pg.IsVirtual) 6265 { 6266 MessageService.ShowWarning(KPRes.GroupCannotStoreEntries, 6267 KPRes.SelectDifferentGroup); 6268 return; 6269 } 6270 6271 PwEntry pe; 6272 if(fNewEntry != null) 6273 { 6274 pe = fNewEntry(); 6275 if(pe == null) { Debug.Assert(false); return; } 6276 } 6277 else 6278 { 6279 pe = new PwEntry(true, true); 6280 6281 pe.IconId = PwDefs.GroupIconToEntryIcon(pg.IconId); 6282 pe.CustomIconUuid = pg.CustomIconUuid; 6283 } 6284 6285 // Temporarily assume that the entry is in pg; required for retrieving 6286 // the default auto-type sequence and other things 6287 pe.ParentGroup = pg; 6288 6289 PwGroup pgT = EntryTemplates.GetTemplatesGroup(pd); 6290 Debug.Assert(pe.ParentGroup != null); 6291 if((pgT == null) || !pe.IsContainedIn(pgT)) // No auto. for new template 6292 { 6293 if(pe.Strings.GetSafe(PwDefs.UserNameField).IsEmpty) 6294 pe.Strings.Set(PwDefs.UserNameField, new ProtectedString( 6295 pd.MemoryProtection.ProtectUserName, pd.DefaultUserName)); 6296 6297 if(pe.Strings.GetSafe(PwDefs.PasswordField).IsEmpty) 6298 PwGeneratorUtil.GenerateAuto(pe, pd); 6299 6300 if(!pe.Expires) 6301 { 6302 int nExpireDays = Program.Config.Defaults.NewEntryExpiresInDays; 6303 if(nExpireDays >= 0) 6304 { 6305 pe.Expires = true; 6306 pe.ExpiryTime = DateTime.UtcNow.AddDays(nExpireDays); 6307 } 6308 } 6309 } 6310 6311 if(fAddPre != null) fAddPre(pe); 6312 6313 bool bSelectFullTitle = !pe.Strings.GetSafe(PwDefs.TitleField).IsEmpty; 6314 6315 PwEntryForm pForm = new PwEntryForm(); 6316 pForm.InitEx(pe, PwEditMode.AddNewEntry, pd, m_ilCurrentIcons, 6317 false, bSelectFullTitle); 6318 if(UIUtil.ShowDialogAndDestroy(pForm) == DialogResult.OK) 6319 { 6320 pg.AddEntry(pe, true); 6321 UpdateUI(false, null, pd.UINeedsIconUpdate, null, true, 6322 null, true, m_lvEntries); 6323 6324 PwObjectList<PwEntry> lSelect = new PwObjectList<PwEntry>(); 6325 lSelect.Add(pe); 6326 SelectEntries(lSelect, true, true); 6327 EnsureVisibleSelected(false); 6328 UpdateUIState(false); 6329 6330 if(Program.Config.Application.AutoSaveAfterEntryEdit) 6331 SaveDatabase(pd, sender); 6332 6333 if(fAddPost != null) fAddPost(pe); 6334 } 6335 else UpdateUI(false, null, pd.UINeedsIconUpdate, null, 6336 pd.UINeedsIconUpdate, null, false); 6337 } 6338 6339 #if DEBUG ConstructDebugMenu()6340 private void ConstructDebugMenu() 6341 { 6342 ToolStripMenuItem tsmiDebug = new ToolStripMenuItem("Debug"); 6343 m_menuTools.DropDownItems.Insert(m_menuTools.DropDownItems.IndexOf( 6344 m_menuToolsAdv) + 1, tsmiDebug); 6345 6346 ToolStripMenuItem tsmi = new ToolStripMenuItem("Set Database Custom Data Items (KDBX 4.1)"); 6347 tsmiDebug.DropDownItems.Add(tsmi); 6348 tsmi.Click += delegate(object sender, EventArgs e) 6349 { 6350 PwDatabase pd = m_docMgr.ActiveDatabase; 6351 if((pd == null) || !pd.IsOpen) return; 6352 6353 Random r = Program.GlobalRandom; 6354 byte[] pb = new byte[12]; 6355 Action<string> f = delegate(string str) 6356 { 6357 r.NextBytes(pb); 6358 pd.CustomData.Set("Test_" + str, Convert.ToBase64String(pb), 6359 DateTime.UtcNow.AddSeconds(-(r.Next() % (60 * 60 * 24 * 7)))); 6360 }; 6361 6362 for(char ch = 'A'; ch < 'E'; ++ch) f(ch.ToString()); 6363 6364 UpdateUIState(true); 6365 }; 6366 } 6367 #endif 6368 GetPreviousParentGroupSafeI(IStructureItem it, PwGroup pgRoot)6369 private static PwGroup GetPreviousParentGroupSafeI(IStructureItem it, 6370 PwGroup pgRoot) 6371 { 6372 if(it == null) { Debug.Assert(false); return null; } 6373 6374 PwGroup pgParent = it.ParentGroup; 6375 if(pgParent == null) return null; // Cannot move root group 6376 6377 PwUuid puPrev = it.PreviousParentGroup; 6378 if(puPrev.Equals(PwUuid.Zero)) return null; 6379 6380 PwGroup pgPrev = pgRoot.FindGroup(puPrev, true); 6381 if(pgPrev == null) return null; 6382 if(pgPrev == pgParent) return null; 6383 6384 return pgPrev; 6385 } 6386 GetPreviousParentGroupSafe(PwGroup pg, PwGroup pgRoot)6387 private static PwGroup GetPreviousParentGroupSafe(PwGroup pg, PwGroup pgRoot) 6388 { 6389 PwGroup pgPrev = GetPreviousParentGroupSafeI(pg, pgRoot); 6390 if(pgPrev == null) return null; 6391 if(pgPrev == pg) { Debug.Assert(false); return null; } // Cannot move into itself 6392 if(pgPrev.IsContainedIn(pg)) return null; 6393 if(!pgPrev.CanAddGroup(pg)) return null; 6394 6395 return pgPrev; 6396 } 6397 GetPreviousParentGroupSafe(PwEntry pe, PwGroup pgRoot)6398 private static PwGroup GetPreviousParentGroupSafe(PwEntry pe, PwGroup pgRoot) 6399 { 6400 return GetPreviousParentGroupSafeI(pe, pgRoot); 6401 } 6402 GetPreviousParentGroupCmdInfo(PwGroup pg, PwEntry[] v, out string strCommand, out bool bEnabled, out bool bRecycleAtLeast1)6403 private void GetPreviousParentGroupCmdInfo(PwGroup pg, PwEntry[] v, 6404 out string strCommand, out bool bEnabled, out bool bRecycleAtLeast1) 6405 { 6406 strCommand = KPRes.MoveToPreviousParentGroup; 6407 bEnabled = false; 6408 bRecycleAtLeast1 = false; 6409 6410 PwDatabase pd = m_docMgr.ActiveDatabase; 6411 if((pd == null) || !pd.IsOpen) return; 6412 6413 PwGroup pgRoot = pd.RootGroup; 6414 6415 bool bRestore = true, bRecycle = false; 6416 PwGroup pgRecBin = null; 6417 if(pd.RecycleBinEnabled) 6418 pgRecBin = pgRoot.FindGroup(pd.RecycleBinUuid, true); 6419 6420 GFunc<IStructureItem, PwGroup, bool> fCheck = delegate( 6421 IStructureItem it, PwGroup pgPrev) 6422 { 6423 if(it == null) { Debug.Assert(false); return false; } 6424 if(pgPrev == null) return false; 6425 Debug.Assert(pgPrev.Uuid.Equals(it.PreviousParentGroup)); 6426 6427 PwGroup pgParent = it.ParentGroup; 6428 if(pgParent == null) { Debug.Assert(false); return false; } 6429 6430 bool bInRecNow = ((pgRecBin != null) ? ((pgParent == pgRecBin) || 6431 pgParent.IsContainedIn(pgRecBin)) : false); 6432 bool bInRecThen = ((pgRecBin != null) ? ((pgPrev == pgRecBin) || 6433 pgPrev.IsContainedIn(pgRecBin)) : false); 6434 6435 bRestore &= (bInRecNow && !bInRecThen); 6436 bRecycle |= bInRecThen; // Move into/within 6437 6438 return true; 6439 }; 6440 6441 if(pg != null) 6442 { 6443 PwGroup pgPrev = GetPreviousParentGroupSafe(pg, pgRoot); 6444 if(!fCheck(pg, pgPrev)) return; 6445 } 6446 else if((v != null) && (v.Length != 0)) // Empty => disable 6447 { 6448 foreach(PwEntry pe in v) 6449 { 6450 PwGroup pgPrev = GetPreviousParentGroupSafe(pe, pgRoot); 6451 if(!fCheck(pe, pgPrev)) return; 6452 } 6453 } 6454 else return; 6455 6456 if(bRestore) strCommand += " (" + KPRes.Restore + ")"; 6457 bEnabled = true; 6458 bRecycleAtLeast1 = bRecycle; 6459 } 6460 UpdateMoveToPreviousParentGroupUI(PwGroup pg, PwEntry[] v, ToolStripMenuItem tsmi)6461 private void UpdateMoveToPreviousParentGroupUI(PwGroup pg, PwEntry[] v, 6462 ToolStripMenuItem tsmi) 6463 { 6464 if(tsmi == null) { Debug.Assert(false); return; } 6465 6466 string strCommand; 6467 bool bEnabled, bRecycle; 6468 GetPreviousParentGroupCmdInfo(pg, v, out strCommand, out bEnabled, 6469 out bRecycle); 6470 6471 tsmi.Text = strCommand; 6472 tsmi.Enabled = bEnabled; 6473 } 6474 ConfirmMoveToPreviousParentGroup(PwGroup pg, PwEntry[] v)6475 private bool ConfirmMoveToPreviousParentGroup(PwGroup pg, PwEntry[] v) 6476 { 6477 string strCommand; 6478 bool bEnabled, bRecycle; 6479 GetPreviousParentGroupCmdInfo(pg, v, out strCommand, out bEnabled, 6480 out bRecycle); 6481 6482 if(!bEnabled) { Debug.Assert(false); return false; } 6483 if(bRecycle) 6484 { 6485 string strMsg = KPRes.RecycleMoveInfo + MessageService.NewParagraph + 6486 KPRes.AskContinue; 6487 if(!MessageService.AskYesNo(strMsg, PwDefs.ShortProductName, false)) 6488 return false; 6489 } 6490 6491 return true; 6492 } 6493 MoveToPreviousParentGroup(bool bEntry)6494 private void MoveToPreviousParentGroup(bool bEntry) 6495 { 6496 PwDatabase pd = m_docMgr.ActiveDatabase; 6497 if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; } 6498 6499 PwGroup pgRoot = pd.RootGroup; 6500 6501 if(bEntry) 6502 { 6503 PwEntry[] v = GetSelectedEntries(); 6504 if((v == null) || (v.Length == 0)) { Debug.Assert(false); return; } 6505 6506 if(!ConfirmMoveToPreviousParentGroup(null, v)) return; 6507 6508 foreach(PwEntry pe in v) 6509 { 6510 PwGroup pgPrev = GetPreviousParentGroupSafe(pe, pgRoot); 6511 if(pgPrev == null) { Debug.Assert(false); continue; } 6512 6513 PwGroup pgParent = pe.ParentGroup; 6514 if(!pgParent.Entries.Remove(pe)) { Debug.Assert(false); continue; } 6515 6516 pgPrev.AddEntry(pe, true, true); 6517 pe.PreviousParentGroup = pgParent.Uuid; 6518 } 6519 } 6520 else // Group 6521 { 6522 PwGroup pg = GetSelectedGroup(); 6523 6524 if(!ConfirmMoveToPreviousParentGroup(pg, null)) return; 6525 6526 PwGroup pgPrev = GetPreviousParentGroupSafe(pg, pgRoot); 6527 if(pgPrev == null) { Debug.Assert(false); return; } 6528 6529 PwGroup pgParent = pg.ParentGroup; 6530 if(!pgParent.Groups.Remove(pg)) { Debug.Assert(false); return; } 6531 6532 pgPrev.AddGroup(pg, true, true); 6533 pg.PreviousParentGroup = pgParent.Uuid; 6534 } 6535 6536 UpdateUI(false, null, !bEntry, null, true, null, true); 6537 } 6538 } 6539 } 6540