Crossfire JXClient, Trunk  R20561
GUIList.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-2011 Andreas Kirschbaum.
20  */
21 
22 package com.realtime.crossfire.jxclient.gui.list;
23 
32 import java.awt.Adjustable;
33 import java.awt.Component;
34 import java.awt.Dimension;
35 import java.awt.Rectangle;
36 import java.awt.Transparency;
37 import java.awt.event.MouseEvent;
38 import javax.swing.DefaultListModel;
39 import javax.swing.JList;
40 import javax.swing.JScrollPane;
41 import javax.swing.JViewport;
42 import javax.swing.ListSelectionModel;
43 import javax.swing.ScrollPaneConstants;
44 import javax.swing.border.EmptyBorder;
45 import javax.swing.event.ListSelectionListener;
46 import org.jetbrains.annotations.NotNull;
47 import org.jetbrains.annotations.Nullable;
48 
54 public abstract class GUIList<T extends GUIElement> extends ActivatableGUIElement implements GUIScrollable {
55 
59  private static final long serialVersionUID = 1;
60 
64  private final int cellHeight;
65 
69  @NotNull
71 
76  @Nullable
78 
82  @NotNull
83  private final DefaultListModel<T> model = new DefaultListModel<>();
84 
88  @NotNull
89  private final JList<T> list = new JList<>(model);
90 
94  @NotNull
95  private final GUIListViewport viewport = new GUIListViewport();
96 
100  @NotNull
101  private final JScrollPane scrollPane;
102 
107  private int tooltipIndex = -1;
108 
112  @Nullable
113  private Rectangle tooltipRectangle;
114 
118  @NotNull
119  private final ListSelectionListener listSelectionListener = e -> selectionChanged();
120 
132  protected GUIList(@NotNull final TooltipManager tooltipManager, @NotNull final GUIElementListener elementListener, @NotNull final String name, final int cellWidth, final int cellHeight, @NotNull final GUIListCellRenderer<T> listCellRenderer, @Nullable final CommandList doubleClickCommandList) {
133  super(tooltipManager, elementListener, name, Transparency.TRANSLUCENT);
134  this.cellHeight = cellHeight;
135  this.listCellRenderer = listCellRenderer;
136  this.doubleClickCommandList = doubleClickCommandList;
137 
138  list.setCellRenderer(listCellRenderer);
139  list.setFixedCellWidth(cellWidth);
140  list.setFixedCellHeight(cellHeight);
141  list.setOpaque(false);
142  list.setFocusable(false);
143  list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
144  list.addListSelectionListener(listSelectionListener);
145 
146  viewport.setView(list);
147  viewport.setScrollMode(JViewport.BLIT_SCROLL_MODE);
148  viewport.setOpaque(false);
149  viewport.setFocusable(false);
150 
151  scrollPane = new JScrollPane(null, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
152  scrollPane.setViewport(viewport);
153  scrollPane.setOpaque(false);
154  scrollPane.setFocusable(false);
155  scrollPane.setBorder(new EmptyBorder(0, 0, 0, 0));
156 
157  add(scrollPane);
158 
159  listCellRenderer.setSize(getWidth(), cellHeight);
160  }
161 
165  @Override
166  public void dispose() {
167  super.dispose();
168  list.removeListSelectionListener(listSelectionListener);
169  synchronized (getTreeLock()) {
170  resizeElements(0);
171  }
172  }
173 
179  @NotNull
180  public T getElement(final int index) {
181  return model.get(index);
182  }
183 
188  protected void addElement(@NotNull final T element) {
189  assert Thread.holdsLock(getTreeLock());
190  model.addElement(element);
191  list.setSize(getWidth(), Integer.MAX_VALUE);
192  viewport.update();
193  if (model.getSize() == 1) {
194  setSelectedIndex(0);
195  }
196  }
197 
205  protected int resizeElements(final int newSize) {
206  assert Thread.holdsLock(getTreeLock());
207  final int index = list.getSelectedIndex();
208  final int oldSize = model.getSize();
209  if (newSize < oldSize) {
210  for (int i = newSize; i < oldSize; i++) {
211  final GUIElement element = model.get(i);
212  if (element instanceof GUIItemItem) {
213  element.dispose();
214  }
215  }
216  model.removeRange(newSize, oldSize-1);
217  list.setSize(getWidth(), Integer.MAX_VALUE);
218  if (index >= newSize && newSize > 0) {
219  setSelectedIndex(newSize-1);
220  }
221  setChanged();
222  }
223  return oldSize;
224  }
225 
232  public boolean canMoveSelection(final int diffLines, final int diffElements) {
233  synchronized (getTreeLock()) {
234  final int distance;
235  switch (list.getLayoutOrientation()) {
236  case JList.HORIZONTAL_WRAP:
237  distance = (list.getWidth()/cellHeight)*diffLines+diffElements;
238  break;
239 
240  default:
241  distance = diffLines+diffElements;
242  break;
243  }
244  final int index = list.getSelectedIndex();
245  if (distance > 0) {
246  return index == -1 || index+distance < list.getModel().getSize();
247  }
248  //noinspection SimplifiableIfStatement
249  if (distance < 0) {
250  return index == -1 || index >= -distance;
251  }
252  return false;
253  }
254  }
255 
261  public void moveSelection(final int diffLines, final int diffElements) {
262  synchronized (getTreeLock()) {
263  final int distance;
264  switch (list.getLayoutOrientation()) {
265  case JList.HORIZONTAL_WRAP:
266  distance = (list.getWidth()/cellHeight)*diffLines+diffElements;
267  break;
268 
269  default:
270  distance = diffLines+diffElements;
271  break;
272  }
273  final int index = list.getSelectedIndex();
274  final int newIndex;
275  if (distance > 0) {
276  newIndex = index == -1 ? 0 : Math.min(index+distance, list.getModel().getSize()-1);
277  } else if (distance < 0) {
278  //noinspection IfMayBeConditional
279  if (index == -1) {
280  newIndex = list.getModel().getSize()-1;
281  } else {
282  newIndex = Math.max(index+distance, 0);
283  }
284  } else {
285  newIndex = index == -1 ? 0 : index;
286  }
287  setSelectedIndex(newIndex);
288  }
289  }
290 
296  @Override
297  public boolean canScroll(final int distance) {
298  synchronized (getTreeLock()) {
299  final Adjustable scrollBar = scrollPane.getVerticalScrollBar();
300  if (distance > 0) {
301  return scrollBar.getValue() < scrollBar.getMaximum()-scrollBar.getVisibleAmount();
302  }
303  //noinspection SimplifiableIfStatement
304  if (distance < 0) {
305  return scrollBar.getValue() > scrollBar.getMinimum();
306  }
307  return false;
308  }
309  }
310 
315  @Override
316  public void scroll(final int distance) {
317  synchronized (getTreeLock()) {
318  final Adjustable scrollBar = scrollPane.getVerticalScrollBar();
319  final int value = scrollBar.getValue()+distance*cellHeight;
320  scrollBar.setValue(value);
321  final int index = list.getSelectedIndex();
322  if (index != -1) {
323  final int firstIndex = list.getFirstVisibleIndex();
324  if (index < firstIndex) {
325  switch (list.getLayoutOrientation()) {
326  case JList.HORIZONTAL_WRAP:
327  final int columns = list.getWidth()/cellHeight;
328  setSelectedIndex(firstIndex+index%columns);
329  break;
330 
331  default:
332  setSelectedIndex(firstIndex);
333  break;
334  }
335  } else {
336  final int lastIndex = list.getLastVisibleIndex();
337  if (index > lastIndex) {
338  switch (list.getLayoutOrientation()) {
339  case JList.HORIZONTAL_WRAP:
340  final int columns = list.getWidth()/cellHeight;
341  final int newTmpColumn = lastIndex-lastIndex%columns+index%columns;
342  final int newColumn;
343  if (newTmpColumn <= lastIndex) {
344  newColumn = newTmpColumn;
345  } else {
346  newColumn = newTmpColumn >= columns ? newTmpColumn-columns : lastIndex;
347  }
348  setSelectedIndex(newColumn);
349  break;
350 
351  default:
352  setSelectedIndex(lastIndex);
353  break;
354  }
355  }
356  }
357  }
358  }
359  setChanged();
360  }
361 
365  @Override
366  public void resetScroll() {
367  setSelectedIndex(0);
368  }
369 
373  @Override
374  public void mouseClicked(@NotNull final MouseEvent e) {
375  doSelect(e);
376  if (doubleClickCommandList != null && e.getClickCount() > 1) {
377  doubleClickCommandList.execute();
378  }
379  super.mouseClicked(e);
380  }
381 
385  @Override
386  public void mouseEntered(@NotNull final MouseEvent e, final boolean debugGui) {
387  super.mouseEntered(e, debugGui);
388  doTooltip(e);
389  }
390 
394  @Override
395  public void mouseExited(@NotNull final MouseEvent e) {
396  super.mouseExited(e);
397  doTooltip(e);
398  }
399 
403  @Override
404  public void mousePressed(@NotNull final MouseEvent e) {
405  super.mouseClicked(e);
406  doSelect(e);
407  }
408 
412  @Override
413  public void mouseMoved(@NotNull final MouseEvent e) {
414  super.mouseMoved(e);
415  doTooltip(e);
416  }
417 
421  @Override
422  public void mouseDragged(@NotNull final MouseEvent e) {
423  super.mouseClicked(e);
424  doSelect(e);
425  }
426 
431  private void doSelect(@NotNull final MouseEvent e) {
432  synchronized (getTreeLock()) {
433  setSelectedIndex(list.getFirstVisibleIndex()+list.locationToIndex(e.getPoint()));
434  }
435  }
436 
441  private void doTooltip(@NotNull final MouseEvent e) {
442  synchronized (getTreeLock()) {
443  final int index = list.locationToIndex(e.getPoint());
444  if (index == -1) {
445  tooltipIndex = -1;
446  tooltipRectangle = null;
447  updateTooltip();
448  return;
449  }
450 
451  final Rectangle rectangle = list.getCellBounds(index, index);
452  if (rectangle == null || !rectangle.contains(e.getPoint())) {
453  tooltipIndex = -1;
454  tooltipRectangle = null;
455  updateTooltip();
456  return;
457  }
458 
459  tooltipIndex = list.getFirstVisibleIndex()+index;
460  tooltipRectangle = rectangle;
461  updateTooltip();
462  }
463  }
464 
469  protected void setSelectedIndex(final int newIndex) {
470  synchronized (getTreeLock()) {
471  final int newIndex2 = Math.min(Math.max(newIndex, 0), list.getModel().getSize()-1);
472  final int index = list.getSelectedIndex();
473  if (newIndex2 == index) {
474  return;
475  }
476 
477  list.setSelectedIndex(newIndex2);
478  if (newIndex2 >= 0) {
479  list.ensureIndexIsVisible(newIndex2);
480  }
481  }
482  setChanged();
483  }
484 
488  protected void selectionChanged() {
489  synchronized (getTreeLock()) {
490  selectionChanged(list.getSelectedIndex());
491  }
492  }
493 
498  protected abstract void selectionChanged(final int selectedIndex);
499 
503  private void updateTooltip() {
504  if (tooltipIndex == -1) {
505  setTooltipText(null);
506  } else {
507  final Rectangle rectangle = tooltipRectangle;
508  if (rectangle == null) {
509  setTooltipText(null);
510  } else {
511  final Component gui = GuiUtils.getGui(this);
512  if (gui == null) {
513  tooltipIndex = -1;
514  tooltipRectangle = null;
515  setTooltipText(null);
516  } else {
517  updateTooltip(tooltipIndex, gui.getX()+getX()+rectangle.x, gui.getY()+getY()+rectangle.y, rectangle.width, rectangle.height);
518  }
519  }
520  }
521  }
522 
526  @Override
527  public void setChanged() {
528  super.setChanged();
529  updateTooltip();
530  }
531 
535  @Override
536  public void execute() {
537  // ignore
538  }
539 
548  protected abstract void updateTooltip(final int index, final int x, final int y, final int w, final int h);
549 
556  protected void setLayoutOrientation(final int layoutOrientation, final int visibleRowCount) {
557  synchronized (getTreeLock()) {
558  list.setLayoutOrientation(layoutOrientation);
559  list.setVisibleRowCount(visibleRowCount);
560  }
561  }
562 
567  @NotNull
568  protected Object getSelectedObject() {
569  synchronized (getTreeLock()) {
570  return list.getSelectedValue();
571  }
572  }
573 
577  @Override
578  public Dimension getPreferredSize() {
579  final Dimension result = list.getPreferredSize();
580  return result == null ? super.getPreferredSize() : result;
581  }
582 
586  @Override
587  public Dimension getMinimumSize() {
588  final Dimension result = list.getMinimumSize();
589  return result == null ? super.getMinimumSize() : result;
590  }
591 
595  @Override
596  public void setBounds(final int x, final int y, final int width, final int height) {
597  super.setBounds(x, y, width, height);
598  scrollPane.setSize(width, height);
599  listCellRenderer.setSize(width, cellHeight);
600  }
601 
602 }
static Gui getGui(@NotNull final Component element)
Returns the Gui an element is part of.
Definition: GuiUtils.java:91
void setChanged()
Records that the contents have changed and must be repainted.
Definition: GUIList.java:527
void setSelectedIndex(final int newIndex)
Update the selected list entry.
Definition: GUIList.java:469
final TooltipManager tooltipManager
The TooltipManager to update.
final JList< T > list
The list used to display the cells.
Definition: GUIList.java:89
final int cellHeight
The height of a list cell in pixels.
Definition: GUIList.java:64
void dispose()
Releases all allocated resources.
final GUIElementListener elementListener
The GUIElementListener to notify.
void setLayoutOrientation(final int layoutOrientation, final int visibleRowCount)
Sets the layout orientation.
Definition: GUIList.java:556
boolean canMoveSelection(final int diffLines, final int diffElements)
Returns whether the selection can be moved.
Definition: GUIList.java:232
void updateTooltip()
Updates the current tooltip text.
Definition: GUIList.java:503
final JScrollPane scrollPane
The scroll pane instance used to display the list.
Definition: GUIList.java:101
A GUIElement instance representing an in-game item.
T getElement(final int index)
Returns the GUIElement for a given index.
Definition: GUIList.java:180
void dispose()
Releases all allocated resources.
Definition: GUIList.java:166
GUIList(@NotNull final TooltipManager tooltipManager, @NotNull final GUIElementListener elementListener, @NotNull final String name, final int cellWidth, final int cellHeight, @NotNull final GUIListCellRenderer< T > listCellRenderer, @Nullable final CommandList doubleClickCommandList)
Creates a new instance.
Definition: GUIList.java:132
static final long serialVersionUID
The serial version UID.
Definition: GUIList.java:59
A JViewport that allows updating the viewport state.
Interface defining an abstract GUI element.
Definition: GUIElement.java:32
int resizeElements(final int newSize)
Changes the number of list elements.
Definition: GUIList.java:205
void resetScroll()
Resets the scroll index to the default value.
Definition: GUIList.java:366
A GUIElement that can be set to active or inactive.
Rectangle tooltipRectangle
The location of the tooltip.
Definition: GUIList.java:113
void addElement(@NotNull final T element)
Adds an GUIElement to the list.
Definition: GUIList.java:188
final DefaultListModel< T > model
The list model of list.
Definition: GUIList.java:83
Utility class for Gui related functions.
Definition: GuiUtils.java:35
void setTooltipText(@Nullable final String tooltipText)
Sets the tooltip text to show when the mouse is inside this element.the text to show ornull to disab...
boolean canScroll(final int distance)
Returns whether the list can be scrolled.
Definition: GUIList.java:297
final CommandList doubleClickCommandList
The CommandList to execute on double-clicks or.
Definition: GUIList.java:77
final GUIListCellRenderer<? extends T > listCellRenderer
The GUIListCellRenderer for the list.
Definition: GUIList.java:70
void mouseClicked(@NotNull final MouseEvent e)
Will be called when the user has clicked (pressed+released) this element.This event will be delivered...
Definition: GUIList.java:374
void selectionChanged()
Called whenever the selected list entry has changed.
Definition: GUIList.java:488
void doSelect(@NotNull final MouseEvent e)
Selects the list entry corresponding to a MouseEvent instance.
Definition: GUIList.java:431
void setSize(final int width, int height)
Updates the component&#39;s size.
void moveSelection(final int diffLines, final int diffElements)
Moves the selection.
Definition: GUIList.java:261
void mouseMoved(@NotNull final MouseEvent e)
Will be called when the mouse moves within this component.before. the mouse event relative to this el...
Definition: GUIList.java:413
final ListSelectionListener listSelectionListener
The ListSelectionListener attached to list.
Definition: GUIList.java:119
void setBounds(final int x, final int y, final int width, final int height)
Definition: GUIList.java:596
Object getSelectedObject()
Returns the selected list object.
Definition: GUIList.java:568
void mouseEntered(@NotNull final MouseEvent e, final boolean debugGui)
Will be called when the mouse has entered the bounding box of this element.the mouse event relative t...
Definition: GUIList.java:386
void mouseExited(@NotNull final MouseEvent e)
Will be called when the mouse has left the bounding box of this element.This function will not be cal...
Definition: GUIList.java:395
int tooltipIndex
The index of the currently shown tooltip.
Definition: GUIList.java:107
A GUIElement that displays a list of entries.
Definition: GUIList.java:54
final GUIListViewport viewport
The viewport used by scrollPane.
Definition: GUIList.java:95
void execute()
Execute the command list by calling GUICommand#execute() for each command in order.
void mouseDragged(@NotNull final MouseEvent e)
Will be called when the mouse moves within this component while the button is pressed.This event will be delivered after mouseMoved(MouseEvent). Note: if the mouse leaves this element&#39;s bounding box while the mouse button is still pressed, furthermouseDragged (but nomouseMoved ) events will be generated. the mouse event relative to this element
Definition: GUIList.java:422
void doTooltip(@NotNull final MouseEvent e)
Updates the tooltip text corresponding to a MouseEvent instance.
Definition: GUIList.java:441
void scroll(final int distance)
Moves the list.
Definition: GUIList.java:316
void mousePressed(@NotNull final MouseEvent e)
Will be called when the user has pressed the mouse inside this element.the mouse event relative to th...
Definition: GUIList.java:404
Interface for GUIElements that support scrolling.