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.Globalization; 25 using System.Text; 26 using System.Windows.Forms; 27 28 using KeePass.Resources; 29 30 using KeePassLib; 31 using KeePassLib.Serialization; 32 using KeePassLib.Utility; 33 34 namespace KeePass.UI 35 { 36 /// <summary> 37 /// MRU handler interface. An MRU handler must support executing an MRU 38 /// item and clearing the MRU list. 39 /// </summary> 40 public interface IMruExecuteHandler 41 { 42 /// <summary> 43 /// Function that is called when an MRU item is executed (i.e. when 44 /// the user has clicked the menu item). 45 /// </summary> OnMruExecute(string strDisplayName, object oTag, ToolStripMenuItem tsmiParent)46 void OnMruExecute(string strDisplayName, object oTag, 47 ToolStripMenuItem tsmiParent); 48 49 /// <summary> 50 /// Function to clear the MRU (for example all menu items must be 51 /// removed from the menu). 52 /// </summary> OnMruClear()53 void OnMruClear(); 54 } 55 56 public sealed class MruList 57 { 58 private List<KeyValuePair<string, object>> m_vItems = 59 new List<KeyValuePair<string, object>>(); 60 61 private IMruExecuteHandler m_handler = null; 62 private List<ToolStripMenuItem> m_lContainers = 63 new List<ToolStripMenuItem>(); 64 65 private ToolStripMenuItem m_tsmiClear = null; 66 private List<ToolStripMenuItem> m_lMruMenuItems = 67 new List<ToolStripMenuItem>(); 68 69 private enum MruMenuItemType 70 { 71 None = 0, 72 Item, 73 Clear 74 } 75 76 // private Font m_fItalic = null; 77 78 private uint m_uMaxItemCount = 0; 79 public uint MaxItemCount 80 { 81 get { return m_uMaxItemCount; } 82 set { m_uMaxItemCount = value; } 83 } 84 85 private bool m_bMarkOpened = false; 86 public bool MarkOpened 87 { 88 get { return m_bMarkOpened; } 89 set { m_bMarkOpened = value; } 90 } 91 92 public uint ItemCount 93 { 94 get { return (uint)m_vItems.Count; } 95 } 96 97 public bool IsValid 98 { 99 get { return (m_handler != null); } 100 } 101 MruList()102 public MruList() 103 { 104 } 105 Initialize(IMruExecuteHandler handler, params ToolStripMenuItem[] vContainers)106 public void Initialize(IMruExecuteHandler handler, 107 params ToolStripMenuItem[] vContainers) 108 { 109 Release(); 110 111 Debug.Assert(handler != null); // No throw 112 m_handler = handler; 113 114 if(vContainers != null) 115 { 116 foreach(ToolStripMenuItem tsmi in vContainers) 117 { 118 if(tsmi != null) 119 { 120 m_lContainers.Add(tsmi); 121 122 tsmi.DropDownOpening += this.OnDropDownOpening; 123 } 124 } 125 } 126 } 127 Release()128 public void Release() 129 { 130 ReleaseMenuItems(); 131 132 foreach(ToolStripMenuItem tsmi in m_lContainers) 133 { 134 tsmi.DropDownOpening -= this.OnDropDownOpening; 135 } 136 m_lContainers.Clear(); 137 138 m_handler = null; 139 } 140 ReleaseMenuItems()141 private void ReleaseMenuItems() 142 { 143 if(m_tsmiClear != null) 144 { 145 m_tsmiClear.Click -= this.ClearHandler; 146 m_tsmiClear = null; 147 } 148 149 foreach(ToolStripMenuItem tsmi in m_lMruMenuItems) 150 { 151 tsmi.Click -= this.ClickHandler; 152 } 153 m_lMruMenuItems.Clear(); 154 } 155 Clear()156 public void Clear() 157 { 158 m_vItems.Clear(); 159 } 160 161 [Obsolete] AddItem(string strDisplayName, object oTag, bool bUpdateMenu)162 public void AddItem(string strDisplayName, object oTag, bool bUpdateMenu) 163 { 164 AddItem(strDisplayName, oTag); 165 } 166 AddItem(string strDisplayName, object oTag)167 public void AddItem(string strDisplayName, object oTag) 168 { 169 Debug.Assert(strDisplayName != null); 170 if(strDisplayName == null) throw new ArgumentNullException("strDisplayName"); 171 // oTag may be null 172 173 bool bExists = false; 174 foreach(KeyValuePair<string, object> kvp in m_vItems) 175 { 176 Debug.Assert(kvp.Key != null); 177 if(kvp.Key.Equals(strDisplayName, StrUtil.CaseIgnoreCmp)) 178 { 179 bExists = true; 180 break; 181 } 182 } 183 184 if(bExists) MoveItemToTop(strDisplayName, oTag); 185 else 186 { 187 m_vItems.Insert(0, new KeyValuePair<string, object>( 188 strDisplayName, oTag)); 189 190 if(m_vItems.Count > m_uMaxItemCount) 191 m_vItems.RemoveAt(m_vItems.Count - 1); 192 } 193 194 // if(bUpdateMenu) UpdateMenu(); 195 } 196 MoveItemToTop(string strName, object oNewTag)197 private void MoveItemToTop(string strName, object oNewTag) 198 { 199 for(int i = 0; i < m_vItems.Count; ++i) 200 { 201 if(m_vItems[i].Key.Equals(strName, StrUtil.CaseIgnoreCmp)) 202 { 203 KeyValuePair<string, object> t = 204 new KeyValuePair<string, object>(strName, oNewTag); 205 206 m_vItems.RemoveAt(i); 207 m_vItems.Insert(0, t); 208 return; 209 } 210 } 211 212 Debug.Assert(false); 213 } 214 215 [Obsolete] UpdateMenu()216 public void UpdateMenu() 217 { 218 } 219 UpdateMenu(object oContainer)220 private void UpdateMenu(object oContainer) 221 { 222 ToolStripMenuItem tsmiContainer = (oContainer as ToolStripMenuItem); 223 if(tsmiContainer == null) { Debug.Assert(false); return; } 224 if(!m_lContainers.Contains(tsmiContainer)) { Debug.Assert(false); return; } 225 226 tsmiContainer.DropDown.SuspendLayout(); 227 228 // Verify that the popup arrow has been drawn (i.e. items existed) 229 Debug.Assert(tsmiContainer.DropDownItems.Count > 0); 230 231 ReleaseMenuItems(); 232 tsmiContainer.DropDownItems.Clear(); 233 234 uint uAccessKey = 1, uNull = 0; 235 if(m_vItems.Count > 0) 236 { 237 foreach(KeyValuePair<string, object> kvp in m_vItems) 238 { 239 AddMenuItem(tsmiContainer, MruMenuItemType.Item, 240 StrUtil.EncodeMenuText(kvp.Key), null, kvp.Value, 241 true, ref uAccessKey); 242 } 243 244 tsmiContainer.DropDownItems.Add(new ToolStripSeparator()); 245 246 AddMenuItem(tsmiContainer, MruMenuItemType.Clear, KPRes.ClearMru, 247 Properties.Resources.B16x16_EditDelete, null, true, ref uNull); 248 } 249 else 250 { 251 AddMenuItem(tsmiContainer, MruMenuItemType.None, "(" + 252 KPRes.Empty + ")", null, null, false, ref uNull); 253 } 254 255 tsmiContainer.DropDown.ResumeLayout(true); 256 } 257 AddMenuItem(ToolStripMenuItem tsmiParent, MruMenuItemType t, string strText, Image img, object oTag, bool bEnabled, ref uint uAccessKey)258 private void AddMenuItem(ToolStripMenuItem tsmiParent, MruMenuItemType t, 259 string strText, Image img, object oTag, bool bEnabled, 260 ref uint uAccessKey) 261 { 262 ToolStripMenuItem tsmi = CreateMenuItem(t, strText, img, oTag, 263 bEnabled, uAccessKey); 264 tsmiParent.DropDownItems.Add(tsmi); 265 266 if(t == MruMenuItemType.Item) 267 m_lMruMenuItems.Add(tsmi); 268 else if(t == MruMenuItemType.Clear) 269 { 270 Debug.Assert(m_tsmiClear == null); 271 m_tsmiClear = tsmi; 272 } 273 274 if(uAccessKey != 0) ++uAccessKey; 275 } 276 CreateMenuItem(MruMenuItemType t, string strText, Image img, object oTag, bool bEnabled, uint uAccessKey)277 private ToolStripMenuItem CreateMenuItem(MruMenuItemType t, string strText, 278 Image img, object oTag, bool bEnabled, uint uAccessKey) 279 { 280 string strItem = strText; 281 if(uAccessKey >= 1) 282 { 283 NumberFormatInfo nfi = NumberFormatInfo.InvariantInfo; 284 if(uAccessKey < 10) 285 strItem = @"&" + uAccessKey.ToString(nfi) + " " + strItem; 286 else if(uAccessKey == 10) 287 strItem = @"1&0 " + strItem; 288 else strItem = uAccessKey.ToString(nfi) + " " + strItem; 289 } 290 291 ToolStripMenuItem tsmi = new ToolStripMenuItem(strItem); 292 if(img != null) tsmi.Image = img; 293 if(oTag != null) tsmi.Tag = oTag; 294 295 IOConnectionInfo ioc = (oTag as IOConnectionInfo); 296 if(m_bMarkOpened && (ioc != null) && (Program.MainForm != null)) 297 { 298 foreach(PwDatabase pd in Program.MainForm.DocumentManager.GetOpenDatabases()) 299 { 300 if(pd.IOConnectionInfo.GetDisplayName().Equals( 301 ioc.GetDisplayName(), StrUtil.CaseIgnoreCmp)) 302 { 303 // if(m_fItalic == null) 304 // { 305 // Font f = tsi.Font; 306 // if(f != null) 307 // m_fItalic = FontUtil.CreateFont(f, FontStyle.Italic); 308 // else { Debug.Assert(false); } 309 // } 310 311 // if(m_fItalic != null) tsmi.Font = m_fItalic; 312 // 153, 51, 153 313 tsmi.ForeColor = Color.FromArgb(64, 64, 255); 314 tsmi.Text += " (" + KPRes.Opened + ")"; 315 break; 316 } 317 } 318 } 319 320 if(t == MruMenuItemType.Item) 321 tsmi.Click += this.ClickHandler; 322 else if(t == MruMenuItemType.Clear) 323 tsmi.Click += this.ClearHandler; 324 // t == MruMenuItemType.None needs no handler 325 326 if(!bEnabled) tsmi.Enabled = false; 327 328 return tsmi; 329 } 330 GetItem(uint uIndex)331 public KeyValuePair<string, object> GetItem(uint uIndex) 332 { 333 Debug.Assert(uIndex < (uint)m_vItems.Count); 334 if(uIndex >= (uint)m_vItems.Count) throw new ArgumentException(); 335 336 return m_vItems[(int)uIndex]; 337 } 338 RemoveItem(string strDisplayName)339 public bool RemoveItem(string strDisplayName) 340 { 341 Debug.Assert(strDisplayName != null); 342 if(strDisplayName == null) throw new ArgumentNullException("strDisplayName"); 343 344 for(int i = 0; i < m_vItems.Count; ++i) 345 { 346 KeyValuePair<string, object> kvp = m_vItems[i]; 347 if(kvp.Key.Equals(strDisplayName, StrUtil.CaseIgnoreCmp)) 348 { 349 m_vItems.RemoveAt(i); 350 return true; 351 } 352 } 353 354 return false; 355 } 356 ClickHandler(object sender, EventArgs args)357 private void ClickHandler(object sender, EventArgs args) 358 { 359 ToolStripMenuItem tsmi = (sender as ToolStripMenuItem); 360 if(tsmi == null) { Debug.Assert(false); return; } 361 Debug.Assert(m_lMruMenuItems.Contains(tsmi)); 362 363 ToolStripMenuItem tsmiParent = (tsmi.OwnerItem as ToolStripMenuItem); 364 if(tsmiParent == null) { Debug.Assert(false); return; } 365 if(!m_lContainers.Contains(tsmiParent)) { Debug.Assert(false); return; } 366 367 if(m_handler == null) { Debug.Assert(false); return; } 368 369 string strName = tsmi.Text; 370 object oTag = tsmi.Tag; 371 372 m_handler.OnMruExecute(strName, oTag, tsmiParent); 373 374 // MoveItemToTop(strName); 375 } 376 ClearHandler(object sender, EventArgs e)377 private void ClearHandler(object sender, EventArgs e) 378 { 379 if(m_handler != null) m_handler.OnMruClear(); 380 else { Debug.Assert(false); } 381 } 382 OnDropDownOpening(object sender, EventArgs e)383 private void OnDropDownOpening(object sender, EventArgs e) 384 { 385 UpdateMenu(sender); 386 } 387 } 388 } 389