PageRenderTime 42ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/ZeroKLobby_NET4.0/MicroLobby/ChatControl.cs

https://github.com/db81/Zero-K-Infrastructure
C# | 608 lines | 495 code | 96 blank | 17 comment | 143 complexity | 56d560f825e92909ef7ece91263aa851 MD5 | raw file
Possible License(s): GPL-3.0, MIT
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.Diagnostics;
  5. using System.Diagnostics.CodeAnalysis;
  6. using System.Drawing;
  7. using System.Linq;
  8. using System.Windows.Forms;
  9. using LobbyClient;
  10. using ZeroKLobby.Lines;
  11. using ZkData;
  12. using Timer = System.Timers.Timer;
  13. namespace ZeroKLobby.MicroLobby
  14. {
  15. public partial class ChatControl: UserControl
  16. {
  17. ZKLMouseClick playerBox_zklclick = new ZKLMouseClick();
  18. protected bool filtering; //playerList filter
  19. bool mouseIsDown;
  20. readonly PlayerListItem notResultsItem = new PlayerListItem { Title = "No match", SortCategory = 3 };
  21. protected List<PlayerListItem> playerListItems = new List<PlayerListItem>();
  22. readonly PlayerListItem searchResultsItem = new PlayerListItem { Title = "Search results", SortCategory = 1 };
  23. public bool CanLeave { get { return ChannelName != "Battle"; } }
  24. public static EventHandler<ChannelLineArgs> ChannelLineAdded = (sender, args) => { };
  25. Timer minuteTimer;
  26. public string ChannelName { get; set; }
  27. public GameInfo GameInfo { get; set; }
  28. public bool IsTopicVisible {
  29. get { return topicPanel.Visible; }
  30. set {
  31. //Note: topic window doesn't have listener to any resize event. This is minor issues.
  32. float height = topicBox.LineSize;
  33. height *= topicBox.TotalDisplayLines + 1;
  34. //height *= 1.1f;
  35. height += topicBox.Margin.Top + topicBox.Margin.Bottom;
  36. topicPanel.Height = (int)height;
  37. topicPanel.Visible = value;
  38. if (value) Program.Conf.Topics.Remove(ChannelName);
  39. else {
  40. Channel channel;
  41. if (Program.TasClient.JoinedChannels.TryGetValue(ChannelName, out channel)) Program.Conf.Topics[channel.Name] = channel.TopicSetDate;
  42. }
  43. }
  44. }
  45. public IEnumerable<PlayerListItem> PlayerListItems { get { return playerListItems; } }
  46. public event EventHandler<EventArgs<string>> ChatLine { add { sendBox.LineEntered += value; } remove { sendBox.LineEntered -= value; } }
  47. public ChatControl() {}
  48. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
  49. public ChatControl(string name) {
  50. InitializeComponent();
  51. var isDesignMode = Process.GetCurrentProcess().ProcessName == "devenv"; // workaround for this.DesignMode not working in constructor
  52. if (isDesignMode) return;
  53. var extras = new BitmapButton();
  54. extras.Text = "Extras";
  55. extras.Click += (s, e) => {
  56. var contextMenu = ContextMenus.GetChannelContextMenu(this);
  57. contextMenu = LineDehighlighter(contextMenu, null);
  58. contextMenu.Show(extras, new Point(0, 0));
  59. };
  60. ChatBox.Controls.Add(extras);
  61. playerBox.DrawMode = DrawMode.OwnerDrawVariable;
  62. playerBox.MeasureItem += (s, e) => { }; // needed for ListBox.OnMeasureItem
  63. playerBox.BackColor = Program.Conf.BgColor;
  64. playerBox.ForeColor = Program.Conf.TextColor;
  65. playerBox_zklclick.AttachTo(playerBox);
  66. playerBox_zklclick.MouseClick += PlayerBox_MouseClick;
  67. playerSearchBox.BackColor = Program.Conf.BgColor;
  68. playerSearchBox.ForeColor = Program.Conf.TextColor;
  69. ChatBox.Font = Program.Conf.ChatFont; //make sure this is done before HistoryManager adds text, or text becomes black.
  70. Name = name;
  71. ChannelName = name;
  72. if (!DesignMode) HistoryManager.InsertLastLines(ChannelName, ChatBox);
  73. playerBox.Sorted = true;
  74. var lookingGlass = new PictureBox { Width = 20, Height = 20, Image = ZklResources.search, SizeMode = PictureBoxSizeMode.CenterImage };
  75. searchBarContainer.Controls.Add(lookingGlass);
  76. Program.ToolTip.SetText(lookingGlass, "Enter name or country shortcut to find");
  77. Program.ToolTip.SetText(playerSearchBox, "Enter name or country shortcut to find");
  78. VisibleChanged += ChatControl_VisibleChanged;
  79. ChatBox.MouseUp += chatBox_MouseUp;
  80. ChatBox.MouseDown += chatBox_MouseDown;
  81. ChatBox.MouseMove += chatBox_MouseMove;
  82. ChatBox.FocusInputRequested += (s, e) => GoToSendBox();
  83. ChatBox.ChatBackgroundColor = TextColor.background; //same as Program.Conf.BgColor but TextWindow.cs need this.
  84. ChatBox.IRCForeColor = 14; //mirc grey. Unknown use
  85. Program.TasClient.ChannelUserAdded += client_ChannelUserAdded;
  86. Program.TasClient.ChannelUserRemoved += client_ChannelUserRemoved;
  87. Program.TasClient.UserStatusChanged += TasClient_UserStatusChanged;
  88. // Program.TasClient.ChannelUsersAdded += TasClient_ChannelUsersAdded;
  89. Program.TasClient.Said += client_Said;
  90. Program.TasClient.UserRemoved += TasClient_UserRemoved;
  91. Program.TasClient.ChannelTopicChanged += TasClient_ChannelTopicChanged;
  92. Program.SteamHandler.Voice.UserStartsTalking += VoiceOnUserChanged;
  93. Program.SteamHandler.Voice.UserStopsTalking += VoiceOnUserChanged;
  94. Program.SteamHandler.Voice.UserVoiceEnabled += VoiceOnUserChanged;
  95. Channel channel;
  96. Program.TasClient.JoinedChannels.TryGetValue(ChannelName, out channel);
  97. minuteTimer = new Timer(60000) { AutoReset = true };
  98. minuteTimer.Elapsed += (s, e) => {
  99. if (DateTime.Now.Minute == 0) this.Invoke(new Action(() => AddLine(new ChimeLine())));
  100. };
  101. minuteTimer.Start();
  102. //Topic Box that displays over the channel
  103. topicBox.IRCForeColor = 14; //mirc grey. Unknown use
  104. topicBox.ChatBackgroundColor = TextColor.topicBackground;
  105. topicBox.HorizontalScroll.Enabled = true;
  106. topicBox.BorderStyle = BorderStyle.FixedSingle;
  107. topicBox.VerticalScroll.Visible = false;
  108. topicBox.VerticalScroll.Enabled = false;
  109. topicBox.AutoSize = true;
  110. topicBox.AutoSizeMode = AutoSizeMode.GrowAndShrink;
  111. topicBox.HideScroll = true;
  112. topicBox.ShowUnreadLine = false;
  113. topicBox.ShowHistory = false;
  114. //hide mappanel for normal chat operation. Overriden in BattleChatControl.cs
  115. playerListMapSplitContainer.Panel2Collapsed = true;
  116. sendBox.CompleteWord += (word) => //autocomplete of username
  117. {
  118. var w = word.ToLower();
  119. IEnumerable <string> firstResult = playerBox.GetUserNames()
  120. .Where(x => x.ToLower().StartsWith(w))
  121. .Union(playerBox.GetUserNames().Where(x => x.ToLower().Contains(w)));
  122. if (true)
  123. {
  124. ChatControl zkChatArea = Program.MainWindow.navigationControl.ChatTab.GetChannelControl("zk");
  125. if (zkChatArea != null)
  126. {
  127. IEnumerable<string> extraResult = zkChatArea.playerBox.GetUserNames()
  128. .Where(x => x.ToLower().StartsWith(w))
  129. .Union(zkChatArea.playerBox.GetUserNames().Where(x => x.ToLower().Contains(w)));
  130. firstResult = firstResult.Concat(extraResult); //Reference: http://stackoverflow.com/questions/590991/merging-two-ienumerablets
  131. }
  132. }
  133. return firstResult;
  134. };
  135. if (channel != null) foreach (var userName in Program.TasClient.JoinedChannels[ChannelName].Users.Keys) AddUser(userName);
  136. }
  137. void VoiceOnUserChanged(ulong steamID)
  138. {
  139. Program.MainWindow.InvokeFunc(() => {
  140. var user = Program.TasClient.ExistingUsers.Values.FirstOrDefault(x => x.SteamID == steamID);
  141. if (user != null) RefreshUser(user.Name);
  142. });
  143. }
  144. public virtual void AddLine(IChatLine line) {
  145. if (ChannelName != "zkadmin" &&
  146. ((line is SaidLine && Program.Conf.IgnoredUsers.Contains(((SaidLine)line).AuthorName)) ||
  147. (line is SaidExLine && Program.Conf.IgnoredUsers.Contains(((SaidExLine)line).AuthorName)))) return;
  148. ChatBox.AddLine(line);
  149. ChannelLineAdded(this, new ChannelLineArgs() { Channel = ChannelName, Line = line });
  150. HistoryManager.LogLine(ChannelName, line);
  151. }
  152. public void GoToSendBox() {
  153. sendBox.Focus();
  154. }
  155. public void RefreshUser(string userName) {
  156. if (PlayerListItems.Any(i => i.UserName == userName)) {
  157. SortByTeam();
  158. if (filtering) FilterPlayers(); else playerBox.Invalidate();
  159. }
  160. }
  161. public virtual void Reset() {
  162. playerBox.Items.Clear();
  163. playerListItems.Clear();
  164. ChatBox.Text = String.Empty;
  165. }
  166. protected void AddUser(string userName) {
  167. Channel channel;
  168. if (Program.TasClient.JoinedChannels.TryGetValue(ChannelName, out channel)) {
  169. if (!channel.Users.ContainsKey(userName)) {
  170. Trace.WriteLine("Trying to add a user to a channel he hasn't joined (" + ChannelName + "/" + userName + ").");
  171. return;
  172. }
  173. }
  174. playerListItems.RemoveAll(u => u.UserName == userName);
  175. var item = new PlayerListItem { UserName = userName };
  176. playerListItems.Add(item);
  177. if (filtering) FilterPlayers();
  178. else {
  179. playerBox.Items.Remove(playerBox.Items.SingleOrDefault(u => u.UserName == userName));
  180. playerBox.Items.Add(item);
  181. SortByTeam();
  182. }
  183. }
  184. void FilterPlayers() {
  185. if (!filtering) return;
  186. playerBox.BeginUpdate();
  187. var words = playerSearchBox.Text.ToUpper().Split(' ');
  188. foreach (var playerListItem in PlayerListItems) {
  189. var user = playerListItem.User;
  190. var match = true;
  191. foreach (var iteratedWord in words) {
  192. var word = iteratedWord;
  193. var negation = false;
  194. if (word.StartsWith("-")) {
  195. negation = true;
  196. word = word.Substring(1);
  197. }
  198. if (String.IsNullOrEmpty(word)) continue;
  199. bool isSpecialWordMatch;
  200. if (FilterSpecialWordCheck(user, word, out isSpecialWordMatch)) {
  201. if ((!negation && !isSpecialWordMatch) || (negation && isSpecialWordMatch)) {
  202. match = false;
  203. break;
  204. }
  205. }
  206. else {
  207. var userNameFound = playerListItem.UserName.ToUpper().Contains(word);
  208. var countryFound = user.Country.ToUpper() == word;
  209. var countryNameFound = CountryNames.GetName(user.Country).ToUpper() == word;
  210. var clanFound = user.Clan != null && user.Clan.ToUpper() == word;
  211. var factionFound = user.Faction != null && user.Faction.ToUpper() == word;
  212. if (!negation) {
  213. if (!(userNameFound || countryFound || countryNameFound || clanFound || factionFound)) {
  214. match = false;
  215. break;
  216. }
  217. }
  218. else {
  219. if ((userNameFound || countryFound || countryNameFound || clanFound || factionFound)) {
  220. match = false;
  221. break;
  222. }
  223. }
  224. }
  225. }
  226. if (match) {
  227. playerListItem.SortCategory = 2;
  228. playerListItem.IsGrayedOut = false;
  229. }
  230. else {
  231. playerListItem.SortCategory = 4;
  232. playerListItem.IsGrayedOut = true;
  233. }
  234. }
  235. playerBox.Sorted = false;
  236. playerBox.Sorted = true;
  237. playerBox.EndUpdate();
  238. }
  239. static bool FilterSpecialWordCheck(User user, string word, out bool isMatch) {
  240. switch (word) {
  241. case "BOT":
  242. isMatch = user.IsBot;
  243. return true;
  244. case "AFK":
  245. isMatch = user.IsAway;
  246. return true;
  247. case "ADMIN":
  248. isMatch = user.IsAdmin;
  249. return true;
  250. case "INGAME":
  251. isMatch = user.IsInGame;
  252. return true;
  253. case "INBATTLE":
  254. isMatch = user.IsInBattleRoom;
  255. return true;
  256. case "FRIEND":
  257. isMatch = Program.FriendManager.Friends.Any(x => x == user.Name);
  258. return true;
  259. }
  260. isMatch = false;
  261. return false;
  262. }
  263. protected void RemoveUser(string userName) {
  264. var item = playerListItems.SingleOrDefault(u => u.UserName == userName);
  265. playerListItems.Remove(item);
  266. playerBox.Items.Remove(item);
  267. if (filtering) FilterPlayers();
  268. }
  269. /// <summary>
  270. /// Grey out lines that do not contain the targeted text. Can be used by user to sort out who says what
  271. /// </summary>
  272. ContextMenu LineDehighlighter(ContextMenu cm, string word)
  273. {
  274. if (!string.IsNullOrWhiteSpace(word) || !string.IsNullOrWhiteSpace(ChatBox.LineHighlight))
  275. {
  276. cm.MenuItems.Add("-");
  277. }
  278. if (!string.IsNullOrWhiteSpace(ChatBox.LineHighlight))
  279. {
  280. var lineFilter = new System.Windows.Forms.MenuItem("Defocus Chatlines except: \"" + ChatBox.LineHighlight + "\"") { Checked = true};
  281. lineFilter.Click += (s, e) => { ChatBox.LineHighlight = null; };
  282. cm.MenuItems.Add(lineFilter);
  283. }
  284. if (ChatBox.LineHighlight!=word && !string.IsNullOrWhiteSpace(word))
  285. {
  286. var lineFilter = new System.Windows.Forms.MenuItem("Defocus Chatlines except: \"" + word + "\"") { Checked = false};
  287. lineFilter.Click += (s, e) => { ChatBox.LineHighlight = word; };
  288. cm.MenuItems.Add(lineFilter);
  289. }
  290. return cm;
  291. }
  292. void ShowChatContextMenu(Point location, string word = null) {
  293. var contextMenu = ContextMenus.GetChannelContextMenu(this);
  294. contextMenu = LineDehighlighter(contextMenu, word);
  295. try {
  296. Program.ToolTip.Visible = false;
  297. contextMenu.Show(ChatBox, location);
  298. } catch (Exception ex) {
  299. Trace.TraceError("Error displaying tooltip:{0}", ex);
  300. } finally {
  301. Program.ToolTip.Visible = true;
  302. }
  303. }
  304. void ShowPlayerContextMenu(User user, Control control, Point location) {
  305. var contextMenu = ContextMenus.GetPlayerContextMenu(user, this is BattleChatControl);
  306. contextMenu = LineDehighlighter(contextMenu, user.Name);
  307. try {
  308. Program.ToolTip.Visible = false;
  309. contextMenu.Show(control, location);
  310. } catch (Exception ex) {
  311. Trace.TraceError("Error displaying tooltip:{0}", ex);
  312. } finally {
  313. Program.ToolTip.Visible = true;
  314. }
  315. }
  316. protected virtual void SortByTeam() {}
  317. void ChatControl_VisibleChanged(object sender, EventArgs e) {
  318. if (Visible) GoToSendBox();
  319. else ChatBox.ResetUnread();
  320. }
  321. void TasClient_ChannelTopicChanged(object sender, TasEventArgs e) {
  322. if (ChannelName == e.ServerParams[0]) {
  323. var channel = Program.TasClient.JoinedChannels[ChannelName];
  324. DateTime? lastChange;
  325. Program.Conf.Topics.TryGetValue(channel.Name, out lastChange);
  326. var topicLine = new TopicLine(channel.Topic, channel.TopicSetBy, channel.TopicSetDate);
  327. topicBox.Reset();
  328. topicBox.AddLine(topicLine);
  329. if (channel.Topic != null && lastChange != channel.TopicSetDate) IsTopicVisible = true;
  330. else IsTopicVisible = false;
  331. }
  332. }
  333. void TasClient_UserRemoved(object sender, EventArgs<UserDisconnected> e) {
  334. var userName = e.Data.Name;
  335. if (PlayerListItems.Any(u => u.UserName == userName)) AddLine(new LeaveLine(userName, string.Format("User has disconnected ({0}).", e.Data.Reason)));
  336. }
  337. void TasClient_UserStatusChanged(object sender, EventArgs<OldNewPair<User>> oldNewPair) {
  338. RefreshUser(oldNewPair.Data.New.Name);
  339. }
  340. void chatBox_MouseDown(object sender, MouseEventArgs e) {
  341. mouseIsDown = true;
  342. }
  343. void chatBox_MouseMove(object sender, MouseEventArgs e) {
  344. if (mouseIsDown) return;
  345. }
  346. void chatBox_MouseUp(object sender, MouseEventArgs me) {
  347. mouseIsDown = false;
  348. var word = ChatBox.HoveredWord.TrimEnd();
  349. if (word != null) {
  350. var user = Program.TasClient.ExistingUsers.Values.SingleOrDefault(x => x.Name.ToString().ToUpper() == word.ToUpper());
  351. if (user != null) {
  352. if (me.Button == MouseButtons.Right || !Program.Conf.LeftClickSelectsPlayer) {
  353. ShowPlayerContextMenu(user, ChatBox, me.Location);
  354. return;
  355. }
  356. else playerBox.SelectUser(word);
  357. }
  358. }
  359. if (me.Button == MouseButtons.Right)
  360. ShowChatContextMenu(me.Location,word);
  361. }
  362. protected virtual void client_ChannelUserAdded(object sender, EventArgs<ChannelUserInfo> e) {
  363. var channelName = e.Data.Channel.Name;
  364. if (ChannelName != channelName) return;
  365. // todo fix/simplify code .. does not need separate adduser and mass add
  366. if (e.Data.Users.Count == 1) {
  367. AddLine(new JoinLine(e.Data.Users.First().Name));
  368. AddUser(e.Data.Users.First().Name);
  369. } else {
  370. foreach (var user in e.Data.Users) {
  371. if (!playerListItems.Any(x => x.UserName == user.Name)) {
  372. playerListItems.Add(new PlayerListItem() { UserName = user.Name });
  373. }
  374. }
  375. if (filtering) FilterPlayers();
  376. else {
  377. playerBox.AddItemRange(playerListItems.Where(x => !playerBox.Items.Any(y => y.UserName == x.UserName)).ToList());
  378. }
  379. }
  380. }
  381. protected virtual void client_ChannelUserRemoved(object sender, EventArgs<ChannelUserRemovedInfo> e) {
  382. var channelName = e.Data.Channel.Name;
  383. if (ChannelName != channelName) return;
  384. var userName = e.Data.User.Name;
  385. var reason = e.Data.Reason;
  386. RemoveUser(userName);
  387. AddLine(new LeaveLine(userName, reason));
  388. }
  389. void client_Said(object sender, TasSayEventArgs e) {
  390. if (e.Channel != ChannelName) return;
  391. if (!string.IsNullOrEmpty(e.UserName)) {
  392. if (e.Place == SayPlace.Channel) {
  393. if (e.Text.Contains(Program.Conf.LobbyPlayerName) && e.UserName != GlobalConst.NightwatchName) Program.MainWindow.NotifyUser("chat/channel/" + e.Channel, string.Format("{0}: {1}", e.UserName, e.Text), false, true);
  394. if (!e.IsEmote) AddLine(new SaidLine(e.UserName, e.Text));
  395. else AddLine(new SaidExLine(e.UserName, e.Text));
  396. }
  397. }
  398. else if (e.Place == SayPlace.Channel) AddLine(new ChannelMessageLine(e.Text));
  399. }
  400. void hideButton_Click(object sender, EventArgs e) {
  401. IsTopicVisible = false;
  402. }
  403. //using MouseUp because it allow the PlayerBox's "HoverItem" to show correct value when rapid clicking
  404. protected virtual void PlayerBox_MouseClick(object sender, MouseEventArgs mea) //from BattleChatControl
  405. {
  406. if (playerBox_zklclick.clickCount >= 2)
  407. { //Double click
  408. var playerListItem = playerBox.SelectedItem as PlayerListItem;
  409. if (playerListItem != null && playerListItem.User != null)
  410. NavigationControl.Instance.Path = "chat/user/" + playerListItem.User.Name;
  411. } else
  412. {
  413. var item = playerBox.HoverItem;
  414. if (item != null && item.UserName != null) {
  415. playerBox.SelectedItem = item;
  416. if (item.User != null && !Program.Conf.LeftClickSelectsPlayer) ShowPlayerContextMenu(item.User, playerBox, mea.Location);
  417. }
  418. //playerBox.ClearSelected();
  419. }
  420. }
  421. void playerSearchBox_TextChanged(object sender, EventArgs e) {
  422. if (!String.IsNullOrEmpty(playerSearchBox.Text)) {
  423. filtering = true;
  424. if (!playerBox.Items.Contains(searchResultsItem)) playerBox.Items.Add(searchResultsItem);
  425. if (!playerBox.Items.Contains(notResultsItem)) playerBox.Items.Add(notResultsItem);
  426. FilterPlayers();
  427. }
  428. else {
  429. filtering = false;
  430. playerBox.BeginUpdate();
  431. playerBox.Items.Remove(searchResultsItem);
  432. playerBox.Items.Remove(notResultsItem);
  433. foreach (var item in playerListItems) {
  434. item.SortCategory = 0;
  435. item.IsGrayedOut = false;
  436. }
  437. SortByTeam();
  438. playerBox.Sorted = false;
  439. playerBox.Sorted = true;
  440. playerBox.EndUpdate();
  441. }
  442. }
  443. void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e) {
  444. OnResize(e); //OnResize(e) will be intercepted by BattleChatControl.cs & resize minimap.
  445. }
  446. void playerListMapSplitContainer_SplitterMoved(object sender, SplitterEventArgs e)
  447. {
  448. OnResize(e); //OnResize(e) will be intercepted by BattleChatControl.cs & resize minimap.
  449. }
  450. public class ChannelLineArgs: EventArgs
  451. {
  452. public string Channel;
  453. public IChatLine Line;
  454. }
  455. /// <summary>
  456. /// A reimplementation of regular MouseClick event using MouseDown & MouseUp pair which can be used
  457. /// for the specific aim of delaying a click event until MouseUp, or/and to read Right-click event for
  458. /// NET's Control which didn't allow them (such as ListBox), or/and to simply count successive clicks.
  459. /// </summary>
  460. public class ZKLMouseClick
  461. {
  462. public event EventHandler<MouseEventArgs> MouseClick = delegate { };
  463. public int clickCount = 0;
  464. bool isDown;
  465. long lastClick = DateTime.Now.Ticks;
  466. Point lastLocation = new Point(0,0);
  467. MouseButtons lastButton = MouseButtons.None;
  468. readonly int systemDoubleClickTime = SystemInformation.DoubleClickTime * 10000; //10,000 ticks is a milisecond . http://msdn.microsoft.com/en-us/library/system.datetime.ticks.aspx
  469. public void AttachTo(Control toListenTo)
  470. {
  471. toListenTo.MouseUp += MouseUp;
  472. toListenTo.MouseDown += MouseDown;
  473. }
  474. bool IsDoubleClick(Point newLocation)
  475. {
  476. if( DateTime.Now.Ticks - lastClick <= systemDoubleClickTime &&
  477. lastLocation.X - newLocation.X < 10 &&
  478. lastLocation.Y - newLocation.Y < 10)
  479. {
  480. return true;
  481. }
  482. return false;
  483. }
  484. void UpdatePositionAndTime(Point newLocation)
  485. {
  486. lastClick = DateTime.Now.Ticks;
  487. lastLocation = newLocation;
  488. }
  489. void MouseDown(object sender, MouseEventArgs mea)
  490. {
  491. if (!isDown) {
  492. isDown= true;
  493. lastButton = mea.Button;
  494. }
  495. }
  496. void MouseUp(object sender, MouseEventArgs mea)
  497. {
  498. //check for mouseUp/Down pair
  499. if (!isDown) return;
  500. isDown = false;
  501. if (lastButton != mea.Button) return;
  502. lastButton = MouseButtons.None;
  503. if (IsDoubleClick(mea.Location))
  504. clickCount = clickCount + 1;
  505. else
  506. clickCount = 1;
  507. UpdatePositionAndTime(mea.Location);
  508. MouseClick(sender, mea);
  509. }
  510. }
  511. }
  512. }