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.Windows.Forms;
24 
25 using KeePass.Resources;
26 using KeePass.Util;
27 
28 namespace KeePass.UI
29 {
30 	public sealed class RichTextBoxContextMenu
31 	{
32 		private RichTextBox m_rtb = null;
33 		private Form m_form = null;
34 		private CustomContextMenuStripEx m_ctx = null;
35 		private ToolStripItem[] m_vMenuItems =
36 			new ToolStripItem[(int)RtbCtxCommands.Count];
37 		private string m_strCurLink = string.Empty;
38 
39 		private enum RtbCtxCommands
40 		{
41 			Undo = 0,
42 			Cut,
43 			Copy,
44 			Paste,
45 			Delete,
46 			CopyLink,
47 			CopyAll,
48 			SelectAll,
49 			Count
50 		}
51 
RichTextBoxContextMenu()52 		public RichTextBoxContextMenu()
53 		{
54 		}
55 
~RichTextBoxContextMenu()56 		~RichTextBoxContextMenu()
57 		{
58 			try { Detach(); }
59 			catch(Exception) { Debug.Assert(false); }
60 		}
61 
62 		[Obsolete]
Attach(RichTextBox rtb)63 		public void Attach(RichTextBox rtb)
64 		{
65 			Attach(rtb, null);
66 		}
67 
Attach(RichTextBox rtb, Form fParent)68 		public void Attach(RichTextBox rtb, Form fParent)
69 		{
70 			Detach();
71 
72 			m_rtb = rtb;
73 			m_form = fParent;
74 
75 			m_ctx = CreateContextMenu();
76 			m_ctx.Opening += this.OnMenuOpening;
77 
78 			m_rtb.ContextMenuStrip = m_ctx;
79 		}
80 
Detach()81 		public void Detach()
82 		{
83 			if(m_rtb != null)
84 			{
85 				m_rtb.ContextMenuStrip = null;
86 
87 				m_ctx.Opening -= this.OnMenuOpening;
88 				m_ctx = null;
89 
90 				for(int i = 0; i < (int)RtbCtxCommands.Count; ++i)
91 					m_vMenuItems[i] = null;
92 
93 				m_rtb = null;
94 				m_form = null;
95 			}
96 		}
97 
CreateContextMenu()98 		private CustomContextMenuStripEx CreateContextMenu()
99 		{
100 			CustomContextMenuStripEx ctx = new CustomContextMenuStripEx();
101 			int iPos = -1;
102 
103 			ToolStripItem tsiUndo = ctx.Items.Add(KPRes.Undo,
104 				Properties.Resources.B16x16_Undo, this.OnUndoCommand);
105 			tsiUndo.RightToLeftAutoMirrorImage = true;
106 			m_vMenuItems[++iPos] = tsiUndo;
107 			ctx.Items.Add(new ToolStripSeparator());
108 
109 			m_vMenuItems[++iPos] = ctx.Items.Add(KPRes.Cut,
110 				Properties.Resources.B16x16_Cut, this.OnCutCommand);
111 			m_vMenuItems[++iPos] = ctx.Items.Add(KPRes.Copy,
112 				Properties.Resources.B16x16_EditCopy, this.OnCopyCommand);
113 			m_vMenuItems[++iPos] = ctx.Items.Add(KPRes.Paste,
114 				Properties.Resources.B16x16_EditPaste, this.OnPasteCommand);
115 			m_vMenuItems[++iPos] = ctx.Items.Add(KPRes.Delete,
116 				Properties.Resources.B16x16_EditDelete, this.OnDeleteCommand);
117 			ctx.Items.Add(new ToolStripSeparator());
118 
119 			ToolStripItem tsiLink = ctx.Items.Add(KPRes.CopyLink,
120 				Properties.Resources.B16x16_EditCopyLink, this.OnCopyLinkCommand);
121 			tsiLink.RightToLeftAutoMirrorImage = true;
122 			m_vMenuItems[++iPos] = tsiLink;
123 			m_vMenuItems[++iPos] = ctx.Items.Add(KPRes.CopyAll,
124 				Properties.Resources.B16x16_EditShred, this.OnCopyAllCommand);
125 			m_vMenuItems[++iPos] = ctx.Items.Add(KPRes.SelectAll,
126 				Properties.Resources.B16x16_Edit, this.OnSelectAllCommand);
127 
128 			Debug.Assert(iPos == ((int)RtbCtxCommands.Count - 1));
129 			return ctx;
130 		}
131 
OnMenuOpening(object sender, EventArgs e)132 		private void OnMenuOpening(object sender, EventArgs e)
133 		{
134 			bool bHasText = (m_rtb.TextLength > 0);
135 			bool bHasSel = (m_rtb.SelectionLength > 0);
136 
137 			if(m_rtb.ReadOnly)
138 			{
139 				m_vMenuItems[(int)RtbCtxCommands.Undo].Enabled = false;
140 				m_vMenuItems[(int)RtbCtxCommands.Cut].Enabled = false;
141 				m_vMenuItems[(int)RtbCtxCommands.Paste].Enabled = false;
142 				m_vMenuItems[(int)RtbCtxCommands.Delete].Enabled = false;
143 			}
144 			else // Editable
145 			{
146 				m_vMenuItems[(int)RtbCtxCommands.Undo].Enabled = m_rtb.CanUndo;
147 				m_vMenuItems[(int)RtbCtxCommands.Cut].Enabled = bHasSel;
148 				m_vMenuItems[(int)RtbCtxCommands.Paste].Enabled = true; // Optimistic
149 				m_vMenuItems[(int)RtbCtxCommands.Delete].Enabled = bHasSel;
150 			}
151 
152 			m_vMenuItems[(int)RtbCtxCommands.Copy].Enabled = bHasSel;
153 			m_vMenuItems[(int)RtbCtxCommands.CopyAll].Enabled = bHasText;
154 			m_vMenuItems[(int)RtbCtxCommands.SelectAll].Enabled = bHasText;
155 
156 			m_strCurLink = GetLinkBelowMouse();
157 			m_vMenuItems[(int)RtbCtxCommands.CopyLink].Enabled = (m_strCurLink.Length > 0);
158 		}
159 
GetLinkBelowMouse()160 		private string GetLinkBelowMouse()
161 		{
162 			// Save selection
163 			int iSelStart = m_rtb.SelectionStart, iSelLength = m_rtb.SelectionLength;
164 
165 			string strLink = string.Empty;
166 			try
167 			{
168 				int p = m_rtb.GetCharIndexFromPosition(m_rtb.PointToClient(
169 					Cursor.Position));
170 				m_rtb.Select(p, 1);
171 				if(UIUtil.RtfIsFirstCharLink(m_rtb))
172 				{
173 					int l = p;
174 					while((l - 1) >= 0)
175 					{
176 						m_rtb.Select(l - 1, 1);
177 						if(!UIUtil.RtfIsFirstCharLink(m_rtb)) break;
178 						--l;
179 					}
180 
181 					int r = p, n = m_rtb.TextLength;
182 					while((r + 1) < n)
183 					{
184 						m_rtb.Select(r + 1, 1);
185 						if(!UIUtil.RtfIsFirstCharLink(m_rtb)) break;
186 						++r;
187 					}
188 
189 					strLink = m_rtb.Text.Substring(l, r - l + 1);
190 				}
191 			}
192 			catch(Exception) { Debug.Assert(false); }
193 
194 			m_rtb.Select(iSelStart, iSelLength); // Restore selection
195 			return strLink;
196 		}
197 
OnUndoCommand(object sender, EventArgs e)198 		private void OnUndoCommand(object sender, EventArgs e)
199 		{
200 			m_rtb.Undo();
201 		}
202 
OnCutCommand(object sender, EventArgs e)203 		private void OnCutCommand(object sender, EventArgs e)
204 		{
205 			m_rtb.Cut();
206 		}
207 
OnCopyCommand(object sender, EventArgs e)208 		private void OnCopyCommand(object sender, EventArgs e)
209 		{
210 			m_rtb.Copy();
211 		}
212 
OnPasteCommand(object sender, EventArgs e)213 		private void OnPasteCommand(object sender, EventArgs e)
214 		{
215 			CustomRichTextBoxEx crtb = (m_rtb as CustomRichTextBoxEx);
216 			if(crtb != null) crtb.PasteAcceptable();
217 			else m_rtb.Paste();
218 		}
219 
OnDeleteCommand(object sender, EventArgs e)220 		private void OnDeleteCommand(object sender, EventArgs e)
221 		{
222 			// The following resets formattings
223 			/* int nStart = m_rtb.SelectionStart, nLength = m_rtb.SelectionLength;
224 			if((nStart < 0) || (nLength <= 0)) return;
225 			string strText = m_rtb.Text;
226 			strText = strText.Remove(nStart, nLength);
227 			m_rtb.Text = strText;
228 			m_rtb.Select(nStart, 0); */
229 
230 			m_rtb.SelectedText = string.Empty;
231 		}
232 
OnCopyLinkCommand(object sender, EventArgs e)233 		private void OnCopyLinkCommand(object sender, EventArgs e)
234 		{
235 			ClipboardUtil.Copy(m_strCurLink, false, false, null, null,
236 				(m_form != null) ? m_form.Handle : IntPtr.Zero);
237 		}
238 
OnCopyAllCommand(object sender, EventArgs e)239 		private void OnCopyAllCommand(object sender, EventArgs e)
240 		{
241 			int nStart = m_rtb.SelectionStart, nLength = m_rtb.SelectionLength;
242 			m_rtb.SelectAll();
243 			m_rtb.Copy();
244 			m_rtb.Select(nStart, nLength);
245 		}
246 
OnSelectAllCommand(object sender, EventArgs e)247 		private void OnSelectAllCommand(object sender, EventArgs e)
248 		{
249 			m_rtb.SelectAll();
250 		}
251 	}
252 }
253