1 /* 2 KeePass Password Safe - The Open-Source Password Manager 3 Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de> 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 20 using System; 21 using System.Collections.Generic; 22 using System.ComponentModel; 23 using System.Diagnostics; 24 using System.Drawing; 25 using System.Drawing.Imaging; 26 using System.IO; 27 using System.Text; 28 using System.Text.RegularExpressions; 29 using System.Windows.Forms; 30 31 using KeePass.App; 32 using KeePass.Resources; 33 using KeePass.UI; 34 using KeePass.Util.MultipleValues; 35 36 using KeePassLib; 37 using KeePassLib.Utility; 38 39 namespace KeePass.Forms 40 { 41 public partial class IconPickerForm : Form 42 { 43 private ImageList m_ilIcons = null; 44 private uint m_uNumberOfStandardIcons = 0; 45 private PwDatabase m_pd = null; 46 private uint m_uDefaultIcon = 0; 47 private PwUuid m_puDefaultCustomIcon = PwUuid.Zero; 48 49 private uint m_uChosenIcon = 0; 50 private PwUuid m_puChosenCustomIcon = PwUuid.Zero; 51 52 public uint ChosenIconId 53 { 54 get { return m_uChosenIcon; } 55 } 56 57 public PwUuid ChosenCustomIconUuid 58 { 59 get { return m_puChosenCustomIcon; } 60 } 61 IconPickerForm()62 public IconPickerForm() 63 { 64 InitializeComponent(); 65 GlobalWindowManager.InitializeForm(this); 66 } 67 InitEx(ImageList ilIcons, uint uNumberOfStandardIcons, PwDatabase pd, uint uDefaultIcon, PwUuid puDefaultCustomIcon)68 public void InitEx(ImageList ilIcons, uint uNumberOfStandardIcons, 69 PwDatabase pd, uint uDefaultIcon, PwUuid puDefaultCustomIcon) 70 { 71 m_ilIcons = ilIcons; 72 m_uNumberOfStandardIcons = uNumberOfStandardIcons; 73 m_pd = pd; 74 m_uDefaultIcon = uDefaultIcon; 75 m_puDefaultCustomIcon = puDefaultCustomIcon; 76 } 77 OnFormLoad(object sender, EventArgs e)78 private void OnFormLoad(object sender, EventArgs e) 79 { 80 if(m_ilIcons == null) { Debug.Assert(false); throw new InvalidOperationException(); } 81 if(m_pd == null) { Debug.Assert(false); throw new InvalidOperationException(); } 82 83 GlobalWindowManager.AddWindow(this); 84 85 this.Icon = AppIcons.Default; 86 87 FontUtil.AssignDefaultBold(m_radioStandard); 88 FontUtil.AssignDefaultBold(m_radioCustom); 89 90 // With the Explorer theme, item selection rectangles are larger 91 // and thus (gray/inactive) selections are easier to see 92 UIUtil.SetExplorerTheme(m_lvIcons, false); 93 UIUtil.SetExplorerTheme(m_lvCustomIcons, false); 94 95 if(m_ilIcons.Images.Count < (long)m_uNumberOfStandardIcons) 96 { 97 Debug.Assert(false); 98 m_uNumberOfStandardIcons = (uint)m_ilIcons.Images.Count; 99 } 100 101 m_lvIcons.BeginUpdate(); 102 m_lvIcons.SmallImageList = m_ilIcons; 103 for(uint i = 0; i < m_uNumberOfStandardIcons; ++i) 104 m_lvIcons.Items.Add(i.ToString(), (int)i); 105 m_lvIcons.EndUpdate(); 106 107 if(m_uDefaultIcon < m_uNumberOfStandardIcons) 108 { 109 m_lvIcons.EnsureVisible((int)m_uDefaultIcon); 110 UIUtil.SetFocusedItem(m_lvIcons, m_lvIcons.Items[ 111 (int)m_uDefaultIcon], true); 112 } 113 else { Debug.Assert(false); } 114 115 RecreateCustomIconList(m_puDefaultCustomIcon); 116 117 if(!m_puDefaultCustomIcon.Equals(PwUuid.Zero)) 118 { 119 m_radioCustom.Checked = true; 120 UIUtil.SetFocus(m_lvCustomIcons, this); 121 } 122 else 123 { 124 m_radioStandard.Checked = true; 125 UIUtil.SetFocus(m_lvIcons, this); 126 } 127 128 EnableControlsEx(); 129 } 130 EnableControlsEx()131 private void EnableControlsEx() 132 { 133 int cCustom = m_lvCustomIcons.Items.Count; 134 int cSelStd = m_lvIcons.SelectedIndices.Count; 135 int cSelCustom = m_lvCustomIcons.SelectedIndices.Count; 136 137 if(m_radioStandard.Checked && (cSelStd == 1)) 138 m_btnOK.Enabled = true; 139 else if(m_radioCustom.Checked && (cSelCustom == 1)) 140 m_btnOK.Enabled = true; 141 else m_btnOK.Enabled = false; 142 143 m_btnCustomDelete.Enabled = (cSelCustom >= 1); 144 m_btnCustomExport.Enabled = (cSelCustom >= 1); 145 146 UIUtil.SetEnabledFast((cCustom != 0), m_lblFind, m_tbFind); 147 148 // if(m_bBlockCancel) 149 // { 150 // m_btnCancel.Enabled = false; 151 // if(this.ControlBox) this.ControlBox = false; 152 // } 153 } 154 SelectCustomIcon(PwUuid pu)155 private void SelectCustomIcon(PwUuid pu) 156 { 157 if(pu.Equals(PwUuid.Zero)) return; 158 159 foreach(ListViewItem lvi in m_lvCustomIcons.Items) 160 { 161 PwCustomIcon ci = (lvi.Tag as PwCustomIcon); 162 if(ci == null) { Debug.Assert(false); continue; } 163 164 if(ci.Uuid.Equals(pu)) 165 { 166 m_lvCustomIcons.BeginUpdate(); // Avoid animation from left 167 lvi.EnsureVisible(); 168 UIUtil.SetFocusedItem(m_lvCustomIcons, lvi, true); 169 m_lvCustomIcons.EndUpdate(); 170 return; 171 } 172 } 173 174 Debug.Assert(false); 175 } 176 RecreateCustomIconList(PwUuid puSelect)177 private void RecreateCustomIconList(PwUuid puSelect) 178 { 179 StringBuilder sb = new StringBuilder(); 180 181 List<ListViewItem> lUnnamed = new List<ListViewItem>(); 182 List<ListViewItem> lNamed = new List<ListViewItem>(); 183 184 for(int i = 0; i < m_pd.CustomIcons.Count; ++i) 185 { 186 PwCustomIcon ci = m_pd.CustomIcons[i]; 187 bool bMulti = (ci.Name == MultipleValuesEx.CueString); 188 189 ListViewItem lvi = new ListViewItem(); 190 if(ci.Name.Length == 0) 191 { 192 lvi.Text = lUnnamed.Count.ToString(); 193 lUnnamed.Add(lvi); 194 } 195 else 196 { 197 lvi.Text = ci.Name; 198 if(bMulti) lUnnamed.Insert(0, lvi); 199 else lNamed.Add(lvi); 200 } 201 202 lvi.ImageIndex = i; 203 lvi.Tag = ci; 204 205 Image img = ci.GetImage(); 206 if(bMulti) 207 lvi.ToolTipText = ci.Name; 208 else if(img != null) 209 { 210 if(sb.Length != 0) sb.Remove(0, sb.Length); 211 212 if(ci.Name.Length != 0) sb.AppendLine(ci.Name); 213 sb.Append(img.Width); 214 sb.Append(" \u00D7 "); 215 sb.Append(img.Height); 216 sb.AppendLine(" px"); 217 sb.Append(StrUtil.FormatDataSizeKB((ulong)ci.ImageDataPng.Length)); 218 #if DEBUG 219 if(ci.LastModificationTime.HasValue) 220 { 221 sb.AppendLine(); 222 sb.Append("((( "); // Debug indicator 223 sb.Append(TimeUtil.ToDisplayString(ci.LastModificationTime.Value)); 224 sb.Append(" )))"); // Debug indicator 225 } 226 #endif 227 228 lvi.ToolTipText = sb.ToString(); 229 } 230 } 231 232 Comparison<ListViewItem> f = delegate(ListViewItem x, ListViewItem y) 233 { 234 string strX = (((x != null) ? x.Text : null) ?? string.Empty); 235 string strY = (((y != null) ? y.Text : null) ?? string.Empty); 236 return StrUtil.CompareNaturally(strX, strY); 237 }; 238 lNamed.Sort(f); 239 240 List<ListViewItem> lAll = new List<ListViewItem>(lUnnamed.Count + lNamed.Count); 241 lAll.AddRange(lUnnamed); 242 lAll.AddRange(lNamed); 243 244 ImageList ilCustom = UIUtil.BuildImageList(m_pd.CustomIcons, 245 DpiUtil.ScaleIntX(16), DpiUtil.ScaleIntY(16)); 246 247 m_lvCustomIcons.BeginUpdate(); 248 m_lvCustomIcons.Items.Clear(); 249 250 m_lvCustomIcons.SmallImageList = ilCustom; 251 m_lvCustomIcons.Items.AddRange(lAll.ToArray()); 252 253 m_lvCustomIcons.EndUpdate(); 254 255 SelectCustomIcon(puSelect); // Doesn't always work before EndUpdate() 256 } 257 OnBtnOK(object sender, EventArgs e)258 private void OnBtnOK(object sender, EventArgs e) 259 { 260 if(!SaveChosenIcon()) 261 { 262 this.DialogResult = DialogResult.None; 263 MessageService.ShowWarning(KPRes.PickIcon); 264 } 265 } 266 SaveChosenIcon()267 private bool SaveChosenIcon() 268 { 269 ListView.SelectedIndexCollection lvsicS = m_lvIcons.SelectedIndices; 270 int cS = lvsicS.Count; 271 m_uChosenIcon = ((cS > 0) ? (uint)lvsicS[0] : m_uDefaultIcon); 272 273 if(m_radioStandard.Checked && (cS != 1)) return false; 274 275 if(m_radioCustom.Checked) 276 { 277 ListView.SelectedListViewItemCollection lvsicC = m_lvCustomIcons.SelectedItems; 278 if(lvsicC.Count != 1) return false; 279 280 PwCustomIcon ci = (lvsicC[0].Tag as PwCustomIcon); 281 if(ci == null) { Debug.Assert(false); return false; } 282 283 m_puChosenCustomIcon = ci.Uuid; 284 } 285 else m_puChosenCustomIcon = PwUuid.Zero; 286 287 return true; 288 } 289 OnIconsItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)290 private void OnIconsItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) 291 { 292 m_radioCustom.Checked = false; 293 m_radioStandard.Checked = true; 294 EnableControlsEx(); 295 } 296 OnCustomIconsItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)297 private void OnCustomIconsItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) 298 { 299 m_radioStandard.Checked = false; 300 m_radioCustom.Checked = true; 301 EnableControlsEx(); 302 } 303 OnFormClosed(object sender, FormClosedEventArgs e)304 private void OnFormClosed(object sender, FormClosedEventArgs e) 305 { 306 // Detach event handlers 307 m_lvIcons.SmallImageList = null; 308 m_lvCustomIcons.SmallImageList = null; 309 310 GlobalWindowManager.RemoveWindow(this); 311 } 312 OnStandardRadioCheckedChanged(object sender, EventArgs e)313 private void OnStandardRadioCheckedChanged(object sender, EventArgs e) 314 { 315 EnableControlsEx(); 316 } 317 OnBtnCustomAdd(object sender, EventArgs e)318 private void OnBtnCustomAdd(object sender, EventArgs e) 319 { 320 string strAllSupportedFilter = KPRes.AllSupportedFiles + 321 @" (*.bmp; *.emf; *.gif; *.ico; *.jpg; *.jpe; *.jpeg; *.jfif; *.jfi; *.jif; *.png; *.tif; *.tiff; *.wmf)" + 322 @"|*.bmp;*.emf;*.gif;*.ico;*.jpg;*.jpe;*.jpeg;*.jfif;*.jfi;*.jif;*.png;*.tif;*.tiff;*.wmf"; 323 StringBuilder sbFilter = new StringBuilder(); 324 sbFilter.Append(strAllSupportedFilter); 325 AddFileType(sbFilter, "*.bmp", "Windows Bitmap (*.bmp)"); 326 AddFileType(sbFilter, "*.emf", "Windows Enhanced Metafile (*.emf)"); 327 AddFileType(sbFilter, "*.gif", "Graphics Interchange Format (*.gif)"); 328 AddFileType(sbFilter, "*.ico", "Windows Icon (*.ico)"); 329 AddFileType(sbFilter, "*.jpg;*.jpe;*.jpeg;*.jfif;*.jfi;*.jif", "JPEG (*.jpg; *.jpe; *.jpeg; *.jfif; *.jfi; *.jif)"); 330 AddFileType(sbFilter, "*.png", "Portable Network Graphics (*.png)"); 331 AddFileType(sbFilter, "*.tif;*.tiff", "Tagged Image File Format (*.tif; *.tiff)"); 332 AddFileType(sbFilter, "*.wmf", "Windows Metafile (*.wmf)"); 333 sbFilter.Append(@"|" + KPRes.AllFiles + @" (*.*)|*.*"); 334 335 OpenFileDialogEx ofd = UIUtil.CreateOpenFileDialog(KPRes.ImportFileTitle, 336 sbFilter.ToString(), 1, null, true, AppDefs.FileDialogContext.Import); 337 if(ofd.ShowDialog() != DialogResult.OK) return; 338 339 // Predicate<PwCustomIcon> fRequiresKdbx4p1 = delegate(PwCustomIcon ci) 340 // { 341 // return ((ci.Name.Length != 0) || ci.LastModificationTime.HasValue); 342 // }; 343 // bool bUseFileName = m_pd.CustomIcons.Exists(fRequiresKdbx4p1); 344 345 PwUuid puSelect = PwUuid.Zero; 346 foreach(string strFile in ofd.FileNames) 347 { 348 MemoryStream msPng = new MemoryStream(); 349 string strError = null; 350 351 try 352 { 353 byte[] pb = File.ReadAllBytes(strFile); 354 355 using(Image img = GfxUtil.LoadImage(pb)) 356 { 357 if(img == null) throw new FormatException(); 358 359 const int wMax = PwCustomIcon.MaxWidth; 360 const int hMax = PwCustomIcon.MaxHeight; 361 362 if((img.Width <= wMax) && (img.Height <= hMax)) 363 img.Save(msPng, ImageFormat.Png); 364 else 365 { 366 using(Image imgSc = GfxUtil.ScaleImage(img, wMax, hMax)) 367 { 368 imgSc.Save(msPng, ImageFormat.Png); 369 } 370 } 371 } 372 373 PwUuid pu = new PwUuid(true); 374 PwCustomIcon ci = new PwCustomIcon(pu, msPng.ToArray()); 375 376 // if(bUseFileName) 377 // { 378 // string strName = UrlUtil.StripExtension( 379 // UrlUtil.GetFileName(strFile)); 380 // if(!string.IsNullOrEmpty(strName)) ci.Name = strName; 381 // } 382 383 m_pd.CustomIcons.Add(ci); 384 m_pd.UINeedsIconUpdate = true; 385 m_pd.Modified = true; 386 387 if(puSelect.Equals(PwUuid.Zero)) puSelect = pu; 388 } 389 catch(ArgumentException) 390 { 391 strError = KPRes.ImageFormatFeatureUnsupported; 392 } 393 catch(System.Runtime.InteropServices.ExternalException) 394 { 395 strError = KPRes.ImageFormatFeatureUnsupported; 396 } 397 catch(Exception ex) 398 { 399 strError = ex.Message; 400 } 401 finally { msPng.Close(); } 402 403 if(!string.IsNullOrEmpty(strError)) 404 MessageService.ShowWarning(strFile, strError); 405 } 406 407 RecreateCustomIconList(puSelect); 408 EnableControlsEx(); 409 UIUtil.SetFocus(m_lvCustomIcons, this); 410 } 411 AddFileType(StringBuilder sbBuffer, string strEnding, string strName)412 private static void AddFileType(StringBuilder sbBuffer, string strEnding, 413 string strName) 414 { 415 if(sbBuffer.Length > 0) sbBuffer.Append('|'); 416 sbBuffer.Append(strName); 417 sbBuffer.Append('|'); 418 sbBuffer.Append(strEnding); 419 } 420 OnBtnCustomRemove(object sender, EventArgs e)421 private void OnBtnCustomRemove(object sender, EventArgs e) 422 { 423 ListView.SelectedListViewItemCollection lvsic = m_lvCustomIcons.SelectedItems; 424 if(lvsic.Count == 0) { Debug.Assert(false); return; } 425 426 List<PwUuid> lDel = new List<PwUuid>(); 427 foreach(ListViewItem lvi in lvsic) 428 { 429 PwCustomIcon ci = (lvi.Tag as PwCustomIcon); 430 if(ci != null) lDel.Add(ci.Uuid); 431 else { Debug.Assert(false); } 432 } 433 434 m_pd.DeleteCustomIcons(lDel); 435 m_pd.UINeedsIconUpdate = true; 436 m_pd.Modified = true; 437 438 RecreateCustomIconList(PwUuid.Zero); 439 EnableControlsEx(); 440 } 441 OnIconsItemActivate(object sender, EventArgs e)442 private void OnIconsItemActivate(object sender, EventArgs e) 443 { 444 OnIconsItemSelectionChanged(sender, null); 445 if(!SaveChosenIcon()) return; 446 this.DialogResult = DialogResult.OK; 447 } 448 OnCustomIconsItemActivate(object sender, EventArgs e)449 private void OnCustomIconsItemActivate(object sender, EventArgs e) 450 { 451 OnCustomIconsItemSelectionChanged(sender, null); 452 if(!SaveChosenIcon()) return; 453 this.DialogResult = DialogResult.OK; 454 } 455 OnBtnCustomSave(object sender, EventArgs e)456 private void OnBtnCustomSave(object sender, EventArgs e) 457 { 458 ListView.SelectedListViewItemCollection lvsic = m_lvCustomIcons.SelectedItems; 459 if(lvsic.Count == 0) return; 460 461 if(lvsic.Count == 1) 462 { 463 PwCustomIcon ci = (lvsic[0].Tag as PwCustomIcon); 464 if(ci == null) { Debug.Assert(false); return; } 465 466 string strName = KPRes.Export; 467 if(ci.Name.Length != 0) strName = ci.Name; 468 strName = UrlUtil.FilterFileName(strName); 469 470 StringBuilder sbFilter = new StringBuilder(); 471 AddFileType(sbFilter, "*.png", "Portable Network Graphics (*.png)"); 472 // AddFileType(sbFilter, "*.ico", "Windows Icon (*.ico)"); 473 sbFilter.Append(@"|" + KPRes.AllFiles + @" (*.*)|*.*"); 474 475 SaveFileDialogEx sfd = UIUtil.CreateSaveFileDialog(KPRes.ExportFileTitle, 476 strName + ".png", sbFilter.ToString(), 1, null, 477 AppDefs.FileDialogContext.Export); 478 if(sfd.ShowDialog() == DialogResult.OK) 479 SaveImageFile(ci, sfd.FileName); 480 } 481 else // lvsic.Count >= 2 482 { 483 FolderBrowserDialog fbd = UIUtil.CreateFolderBrowserDialog(KPRes.ExportToPrompt); 484 if(fbd.ShowDialog() != DialogResult.OK) return; 485 486 string strDir = UrlUtil.EnsureTerminatingSeparator( 487 fbd.SelectedPath, false); 488 Dictionary<string, int> dStart = new Dictionary<string, int>(); 489 490 foreach(ListViewItem lvi in lvsic) 491 { 492 try 493 { 494 PwCustomIcon ci = (lvi.Tag as PwCustomIcon); 495 if(ci == null) { Debug.Assert(false); continue; } 496 497 string strName = KPRes.Export; 498 if(ci.Name.Length != 0) strName = ci.Name; 499 strName = UrlUtil.FilterFileName(strName); 500 501 int iStart; 502 dStart.TryGetValue(strName, out iStart); 503 504 for(int i = iStart; i < int.MaxValue; ++i) 505 { 506 string strFile = strDir + strName + ((i == 0) ? 507 string.Empty : (" (" + i.ToString() + ")")) + 508 ".png"; 509 510 if(!File.Exists(strFile)) 511 { 512 SaveImageFile(ci, strFile); 513 dStart[strName] = i + 1; 514 break; 515 } 516 } 517 } 518 catch(Exception ex) { MessageService.ShowWarning(ex); } 519 } 520 } 521 } 522 SaveImageFile(PwCustomIcon ci, string strFile)523 private static void SaveImageFile(PwCustomIcon ci, string strFile) 524 { 525 if((ci == null) || string.IsNullOrEmpty(strFile)) { Debug.Assert(false); return; } 526 527 try 528 { 529 Image img = ci.GetImage(); 530 if(img == null) { Debug.Assert(false); return; } 531 532 // string strExt = UrlUtil.GetExtension(strFile); 533 ImageFormat fmt = ImageFormat.Png; 534 // if(strExt.Equals("ico", StrUtil.CaseIgnoreCmp)) fmt = ImageFormat.Icon; 535 536 img.Save(strFile, fmt); 537 } 538 catch(Exception ex) { MessageService.ShowWarning(strFile, ex); } 539 } 540 OnCustomIconsBeforeLabelEdit(object sender, LabelEditEventArgs e)541 private void OnCustomIconsBeforeLabelEdit(object sender, LabelEditEventArgs e) 542 { 543 PwCustomIcon ci = (m_lvCustomIcons.Items[e.Item].Tag as PwCustomIcon); 544 if(ci == null) { Debug.Assert(false); e.CancelEdit = true; return; } 545 546 if(ci.Name == MultipleValuesEx.CueString) 547 e.CancelEdit = true; 548 } 549 OnCustomIconsAfterLabelEdit(object sender, LabelEditEventArgs e)550 private void OnCustomIconsAfterLabelEdit(object sender, LabelEditEventArgs e) 551 { 552 string strNew = e.Label; 553 int iItem = e.Item; 554 e.CancelEdit = true; 555 556 if(strNew == null) return; // Edit aborted (Esc) 557 if(strNew == MultipleValuesEx.CueString) return; 558 if(Regex.IsMatch(strNew, "^\\d+$")) strNew = string.Empty; 559 560 PwCustomIcon ci = (m_lvCustomIcons.Items[iItem].Tag as PwCustomIcon); 561 if(ci == null) { Debug.Assert(false); return; } 562 563 if(ci.Name != strNew) 564 { 565 ci.Name = strNew; 566 ci.LastModificationTime = DateTime.UtcNow; 567 568 m_pd.UINeedsIconUpdate = true; 569 m_pd.Modified = true; 570 } 571 572 RecreateCustomIconList(ci.Uuid); 573 EnableControlsEx(); 574 } 575 ProcessCmdKey(ref Message msg, Keys keyData)576 protected override bool ProcessCmdKey(ref Message msg, Keys keyData) 577 { 578 if(((keyData & Keys.KeyCode) == Keys.Return) && m_tbFind.Focused) 579 { 580 msg.Result = IntPtr.Zero; 581 582 string strFind = m_tbFind.Text; 583 if(string.IsNullOrEmpty(strFind)) return true; 584 585 bool bFwd = ((keyData & Keys.Shift) == Keys.None); 586 int n = m_lvCustomIcons.Items.Count; 587 int iStart = (bFwd ? 0 : (n - 1)); 588 589 ListView.SelectedIndexCollection lvsic = m_lvCustomIcons.SelectedIndices; 590 if(lvsic.Count != 0) 591 { 592 iStart = lvsic[0] + (bFwd ? 1 : -1); 593 UIUtil.DeselectAllItems(m_lvCustomIcons); 594 } 595 596 for(int i = 0; i < n; ++i) 597 { 598 int j = (bFwd ? (iStart + i) : (iStart - i + n)) % n; 599 ListViewItem lvi = m_lvCustomIcons.Items[j]; 600 601 string strText = lvi.Text; 602 if(strText.IndexOf(strFind, StrUtil.CaseIgnoreCmp) >= 0) 603 { 604 lvi.EnsureVisible(); 605 UIUtil.SetFocusedItem(m_lvCustomIcons, lvi, true); 606 break; 607 } 608 } 609 610 return true; 611 } 612 613 return base.ProcessCmdKey(ref msg, keyData); 614 } 615 } 616 } 617