Crossfire JXClient, Trunk
MouseTracker.java
Go to the documentation of this file.
1 /*
2  * This file is part of JXClient, the Fullscreen Java Crossfire Client.
3  *
4  * JXClient is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * JXClient is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with JXClient; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17  *
18  * Copyright (C) 2005-2008 Yann Chachkoff
19  * Copyright (C) 2006-2017,2019-2023 Andreas Kirschbaum
20  * Copyright (C) 2010-2012,2014-2018,2020-2023 Nicolas Weeger
21  */
22 
23 package com.realtime.crossfire.jxclient.gui.misc;
24 
29 import java.awt.Color;
30 import java.awt.Component;
31 import java.awt.Container;
32 import java.awt.Dimension;
33 import java.awt.Graphics;
34 import java.awt.Point;
35 import java.awt.event.MouseEvent;
36 import java.awt.event.MouseWheelEvent;
37 import java.io.IOException;
38 import java.io.Writer;
39 import java.time.LocalDateTime;
40 import java.time.format.DateTimeFormatter;
41 import java.util.Locale;
42 import java.util.regex.Pattern;
43 import javax.swing.JRootPane;
44 import org.jetbrains.annotations.NotNull;
45 import org.jetbrains.annotations.Nullable;
46 
55 public class MouseTracker {
56 
61  private static final int CLICK_DISTANCE = 20;
62 
66  @NotNull
67  private static final Pattern PATTERN_PACKAGE_NAME = Pattern.compile(".*\\.");
68 
72  private final boolean debugGui;
73 
77  @Nullable
78  private final Writer debugMouse;
79 
83  @NotNull
84  private final GuiFactory guiFactory;
85 
89  @NotNull
90  private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss,SSS ", Locale.ENGLISH);
91 
95  @Nullable
97 
101  @Nullable
103 
107  private boolean isDragging;
108 
112  private boolean isClicked;
113 
117  @NotNull
118  private final Point clickPosition = new Point();
119 
126  public MouseTracker(final boolean debugGui, @Nullable final Writer debugMouse, @NotNull final GuiFactory guiFactory) {
127  this.debugGui = debugGui;
128  this.debugMouse = debugMouse;
129  this.guiFactory = guiFactory;
130  }
131 
137  public void mouseClicked(@Nullable final AbstractGUIElement element, @NotNull final MouseEvent e) {
138  debugMouseWrite("mouseClicked: "+e+" ["+element+"]");
139  }
140 
146  @SuppressWarnings("UnusedParameters")
147  public void mouseDragged(@Nullable final GUIElement element, @NotNull final MouseEvent e) {
148  debugMouseWrite("mouseDragged: "+e+" ["+element+"]");
149  if (isClicked) {
150  final double distance = clickPosition.distanceSq(e.getLocationOnScreen());
151  if (distance > CLICK_DISTANCE) {
152  debugMouseWrite("mouseDragged: distance "+distance+" is too far for a click event; click point="+clickPosition+", current point="+e.getLocationOnScreen());
153  setClicked(false);
154  }
155  }
156  if (mouseElement != null) {
157  debugMouseWrite(mouseElement+".mouseMoved");
159  }
160  if (isDragging && mouseElement != null) {
161  debugMouseWrite(mouseElement+".mouseDragged");
163  }
164  }
165 
171  public void mouseMoved(@Nullable final AbstractGUIElement element, @NotNull final MouseEvent e) {
172  debugMouseWrite("mouseMoved: "+e+" ["+element+"]");
173  enterElement(element, e);
174  if (mouseElement != null) {
175  debugMouseWrite(mouseElement+".mouseMoved");
177  }
178  }
179 
185  public void mousePressed(@Nullable final AbstractGUIElement element, @NotNull final MouseEvent e) {
186  debugMouseWrite("mousePressed: "+e+" ["+element+"]");
187  enterElement(element, e);
188  if (mouseElement != null) {
189  debugMouseWrite(mouseElement+".mousePressed");
191  }
192  setDragging(true);
193  clickPosition.setLocation(e.getLocationOnScreen());
194  setClicked(true);
195  }
196 
202  public void mouseReleased(@Nullable final AbstractGUIElement element, @NotNull final MouseEvent e) {
203  debugMouseWrite("mouseReleased: "+e+" ["+element+"]");
204  final boolean tmpIsClicked = isClicked;
205  setDragging(false);
206  enterElement(element, e);
207  if (tmpIsClicked && element != null) {
208  // cannot use mouseElement here: it might be invalid if the
209  // previous call to mouseReleased() did close the owner dialog
210  debugMouseWrite(element+".mouseClicked");
211  element.mouseClicked(e);
212  }
213  if (mouseElement != null) {
214  debugMouseWrite(mouseElement+".mouseReleased");
216  }
217  }
218 
224  public void mouseEntered(@Nullable final AbstractGUIElement element, @NotNull final MouseEvent e) {
225  debugMouseWrite("mouseEntered: "+e+" ["+element+"]");
226  setClicked(false);
227  if (!isDragging) {
228  enterElement(element, e);
229  }
230  }
231 
236  public void mouseExited(@NotNull final MouseEvent e) {
237  debugMouseWrite("mouseExited: "+e);
238  setClicked(false);
239  if (!isDragging) {
240  enterElement(null, e);
241  }
242  }
243 
249  private void enterElement(@Nullable final AbstractGUIElement element, @NotNull final MouseEvent e) {
250  if (mouseElement == element) {
251  return;
252  }
253 
254  final GUIElement tmp = mouseElement;
255  if (tmp != null) {
256  tmp.mouseExited(e);
257  setActiveComponent(null);
258  }
259 
260  mouseElement = element;
261  debugMouseWrite("mouseElement="+mouseElement);
262 
263  if (element != null) {
264  element.mouseEntered(e);
265  if (debugGui) {
266  setActiveComponent(element);
267  }
268  }
269  }
270 
276  public void mouseWheelMoved(@Nullable final GUIElement element, @NotNull final MouseWheelEvent e) {
277  if (element == null) {
278  return;
279  }
280 
281  final int wheelRotation = e.getWheelRotation();
282  debugMouseWrite(element+".mouseWheelMoved "+wheelRotation);
283  element.mouseWheelMoved(wheelRotation);
284  }
285 
290  public void paintActiveComponent(@NotNull final Graphics g) {
291  final AbstractGUIElement component = activeComponent;
292  if (component != null) {
293  final int x1 = guiFactory.getElementX(component);
294  final int y1 = guiFactory.getElementY(component);
295  final String text1 = formatName(component)+" "+component.getWidth()+"x"+component.getHeight()+"+"+x1+"+"+y1;
296 
297  final Container parent = component.getParent();
298  final int x2 = parent instanceof AbstractGUIElement ? guiFactory.getElementX((AbstractGUIElement)parent) : parent == null ? 0 : parent.getX();
299  final int y2 = parent instanceof AbstractGUIElement ? guiFactory.getElementY((AbstractGUIElement)parent) : parent == null ? 0 : parent.getY();
300  final String text2 = parent == null ? "" : formatName(parent)+" "+parent.getWidth()+"x"+parent.getHeight()+"+"+x2+"+"+y2;
301 
302  final Dimension dimension1 = GuiUtils.getTextDimension(text1, g.getFontMetrics());
303  final Dimension dimension2 = GuiUtils.getTextDimension(text2, g.getFontMetrics());
304 
305  g.setColor(Color.BLACK);
306  g.fillRect(0, 2, Math.max(dimension1.width, dimension2.width)+4, dimension1.height+dimension2.height+16);
307 
308  g.setColor(Color.RED);
309  g.drawString(text1, 2, dimension1.height+8-6);
310  g.drawRect(x1, y1, component.getWidth()-1, component.getHeight()-1);
311 
312  if (parent != null) {
313  g.setColor(Color.ORANGE);
314  g.drawString(text2, 2, dimension1.height+dimension2.height+16-6);
315  g.drawRect(x2, y2, parent.getWidth()-1, parent.getHeight()-1);
316  }
317  }
318  }
319 
325  @NotNull
326  private static String formatName(@NotNull final Component component) {
327  return PATTERN_PACKAGE_NAME.matcher(component.getClass().getName()).replaceAll("")+"/"+component.getName();
328  }
329 
334  private void debugMouseWrite(@NotNull final CharSequence message) {
335  if (debugMouse == null) {
336  return;
337  }
338 
339  try {
340  debugMouse.append(FORMATTER.format(LocalDateTime.now()));
341  debugMouse.append(message);
342  debugMouse.append("\n");
343  debugMouse.flush();
344  } catch (final IOException ex) {
345  System.err.println("Cannot write mouse debug: "+ex.getMessage());
346  System.exit(1);
347  throw new AssertionError(ex);
348  }
349  }
350 
356  private void setActiveComponent(@Nullable final AbstractGUIElement activeComponent) {
357  if (this.activeComponent == activeComponent) {
358  return;
359  }
360 
361  if (this.activeComponent != null) {
362  this.activeComponent.setChanged();
363  if (debugGui) {
364  final AbstractGUIElement tmp = this.activeComponent;
365  if (tmp != null) {
366  final JRootPane rootPane = tmp.getRootPane();
367  if (rootPane != null) {
368  rootPane.repaint();
369  }
370  }
371  }
372  }
373  this.activeComponent = activeComponent;
374  if (this.activeComponent != null) {
375  this.activeComponent.setChanged();
376  if (debugGui) {
377  final AbstractGUIElement tmp = this.activeComponent;
378  final JRootPane rootPane = tmp.getRootPane();
379  if (rootPane != null) {
380  rootPane.repaint();
381  }
382  }
383  }
384  debugMouseWrite("activeComponent="+activeComponent);
385  }
386 
392  private void setDragging(final boolean isDragging) {
393  if (this.isDragging == isDragging) {
394  return;
395  }
396 
397  this.isDragging = isDragging;
398  debugMouseWrite("isDragging="+isDragging);
399  }
400 
405  private void setClicked(final boolean isClicked) {
406  if (this.isClicked == isClicked) {
407  return;
408  }
409 
410  this.isClicked = isClicked;
411  debugMouseWrite("isClicked="+isClicked);
412  }
413 
414 }
com.realtime.crossfire.jxclient
com.realtime.crossfire.jxclient.skin.skin
Definition: DefaultJXCSkin.java:23
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.mousePressed
void mousePressed(@Nullable final AbstractGUIElement element, @NotNull final MouseEvent e)
Definition: MouseTracker.java:185
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.enterElement
void enterElement(@Nullable final AbstractGUIElement element, @NotNull final MouseEvent e)
Definition: MouseTracker.java:249
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.debugMouse
final Writer debugMouse
Definition: MouseTracker.java:78
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.formatName
static String formatName(@NotNull final Component component)
Definition: MouseTracker.java:326
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.MouseTracker
MouseTracker(final boolean debugGui, @Nullable final Writer debugMouse, @NotNull final GuiFactory guiFactory)
Definition: MouseTracker.java:126
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.setClicked
void setClicked(final boolean isClicked)
Definition: MouseTracker.java:405
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.setDragging
void setDragging(final boolean isDragging)
Definition: MouseTracker.java:392
com.realtime.crossfire.jxclient.skin
com.realtime.crossfire.jxclient.gui.misc.MouseTracker
Definition: MouseTracker.java:55
com.realtime.crossfire.jxclient.gui.gui.GUIElement.mouseDragged
void mouseDragged(@NotNull MouseEvent e)
com.realtime.crossfire.jxclient.skin.skin.GuiFactory
Definition: GuiFactory.java:41
com.realtime.crossfire.jxclient.gui.gui.GuiUtils.getTextDimension
static Dimension getTextDimension(@NotNull final String text, @NotNull final FontMetrics fontMetrics)
Definition: GuiUtils.java:50
com.realtime.crossfire.jxclient.gui.gui.GuiUtils
Definition: GuiUtils.java:34
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.debugMouseWrite
void debugMouseWrite(@NotNull final CharSequence message)
Definition: MouseTracker.java:334
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.guiFactory
final GuiFactory guiFactory
Definition: MouseTracker.java:84
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.mouseElement
GUIElement mouseElement
Definition: MouseTracker.java:96
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.mouseMoved
void mouseMoved(@Nullable final AbstractGUIElement element, @NotNull final MouseEvent e)
Definition: MouseTracker.java:171
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.mouseEntered
void mouseEntered(@Nullable final AbstractGUIElement element, @NotNull final MouseEvent e)
Definition: MouseTracker.java:224
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.debugGui
final boolean debugGui
Definition: MouseTracker.java:72
com.realtime.crossfire.jxclient.gui.gui.GUIElement.mouseMoved
void mouseMoved(@NotNull MouseEvent e)
com.realtime.crossfire.jxclient.gui
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.mouseExited
void mouseExited(@NotNull final MouseEvent e)
Definition: MouseTracker.java:236
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.isClicked
boolean isClicked
Definition: MouseTracker.java:112
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.mouseDragged
void mouseDragged(@Nullable final GUIElement element, @NotNull final MouseEvent e)
Definition: MouseTracker.java:147
com.realtime.crossfire.jxclient.skin.skin.GuiFactory.getElementY
int getElementY(@NotNull final AbstractGUIElement element)
Definition: GuiFactory.java:95
com.realtime.crossfire.jxclient.gui.gui.GUIElement
Definition: GUIElement.java:33
com.realtime.crossfire.jxclient.skin.skin.GuiFactory.getElementX
int getElementX(@NotNull final AbstractGUIElement element)
Definition: GuiFactory.java:81
com.realtime.crossfire.jxclient.gui.gui
Definition: AbstractGUIElement.java:23
com.realtime.crossfire.jxclient.gui.gui.GUIElement.mouseReleased
void mouseReleased(@NotNull MouseEvent e)
com.realtime.crossfire
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.mouseWheelMoved
void mouseWheelMoved(@Nullable final GUIElement element, @NotNull final MouseWheelEvent e)
Definition: MouseTracker.java:276
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.FORMATTER
static final DateTimeFormatter FORMATTER
Definition: MouseTracker.java:90
com.realtime
com.realtime.crossfire.jxclient.gui.gui.GUIElement.mouseExited
void mouseExited(@NotNull MouseEvent e)
com
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.CLICK_DISTANCE
static final int CLICK_DISTANCE
Definition: MouseTracker.java:61
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.mouseClicked
void mouseClicked(@Nullable final AbstractGUIElement element, @NotNull final MouseEvent e)
Definition: MouseTracker.java:137
com.realtime.crossfire.jxclient.gui.gui.AbstractGUIElement.setChanged
void setChanged()
Definition: AbstractGUIElement.java:223
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.PATTERN_PACKAGE_NAME
static final Pattern PATTERN_PACKAGE_NAME
Definition: MouseTracker.java:67
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.mouseReleased
void mouseReleased(@Nullable final AbstractGUIElement element, @NotNull final MouseEvent e)
Definition: MouseTracker.java:202
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.clickPosition
final Point clickPosition
Definition: MouseTracker.java:118
com.realtime.crossfire.jxclient.gui.gui.GUIElement.mousePressed
void mousePressed(@NotNull MouseEvent e)
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.paintActiveComponent
void paintActiveComponent(@NotNull final Graphics g)
Definition: MouseTracker.java:290
com.realtime.crossfire.jxclient.gui.gui.AbstractGUIElement
Definition: AbstractGUIElement.java:37
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.setActiveComponent
void setActiveComponent(@Nullable final AbstractGUIElement activeComponent)
Definition: MouseTracker.java:356
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.activeComponent
AbstractGUIElement activeComponent
Definition: MouseTracker.java:102
com.realtime.crossfire.jxclient.gui.misc.MouseTracker.isDragging
boolean isDragging
Definition: MouseTracker.java:107