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