00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 package com.realtime.crossfire.jxclient.gui.list;
00023
00024 import com.realtime.crossfire.jxclient.gui.commandlist.CommandList;
00025 import com.realtime.crossfire.jxclient.gui.gui.ActivatableGUIElement;
00026 import com.realtime.crossfire.jxclient.gui.gui.GUIElement;
00027 import com.realtime.crossfire.jxclient.gui.gui.GUIElementListener;
00028 import com.realtime.crossfire.jxclient.gui.gui.GuiUtils;
00029 import com.realtime.crossfire.jxclient.gui.gui.TooltipManager;
00030 import com.realtime.crossfire.jxclient.gui.item.GUIItemItem;
00031 import com.realtime.crossfire.jxclient.gui.scrollable.GUIScrollable;
00032 import java.awt.Adjustable;
00033 import java.awt.Component;
00034 import java.awt.Dimension;
00035 import java.awt.Rectangle;
00036 import java.awt.Transparency;
00037 import java.awt.event.MouseEvent;
00038 import javax.swing.DefaultListModel;
00039 import javax.swing.JList;
00040 import javax.swing.JScrollPane;
00041 import javax.swing.JViewport;
00042 import javax.swing.ListSelectionModel;
00043 import javax.swing.ScrollPaneConstants;
00044 import javax.swing.border.EmptyBorder;
00045 import javax.swing.event.ListSelectionEvent;
00046 import javax.swing.event.ListSelectionListener;
00047 import org.jetbrains.annotations.NotNull;
00048 import org.jetbrains.annotations.Nullable;
00049
00054 public abstract class GUIList extends ActivatableGUIElement implements GUIScrollable {
00055
00059 private static final long serialVersionUID = 1;
00060
00064 private final int cellHeight;
00065
00069 @NotNull
00070 private final GUIListCellRenderer listCellRenderer;
00071
00076 @Nullable
00077 private final CommandList doubleClickCommandList;
00078
00082 @NotNull
00083 private final DefaultListModel model = new DefaultListModel();
00084
00088 @NotNull
00089 private final JList list = new JList(model);
00090
00094 @NotNull
00095 private final GUIListViewport viewport = new GUIListViewport();
00096
00100 @NotNull
00101 private final JScrollPane scrollPane;
00102
00107 private int tooltipIndex = -1;
00108
00113 @Nullable
00114 private Rectangle tooltipRectangle = null;
00115
00119 @NotNull
00120 private final ListSelectionListener listSelectionListener = new ListSelectionListener() {
00121
00122 @Override
00123 public void valueChanged(@NotNull final ListSelectionEvent e) {
00124 selectionChanged();
00125 }
00126
00127 };
00128
00140 protected GUIList(@NotNull final TooltipManager tooltipManager, @NotNull final GUIElementListener elementListener, @NotNull final String name, final int cellWidth, final int cellHeight, @NotNull final GUIListCellRenderer listCellRenderer, @Nullable final CommandList doubleClickCommandList) {
00141 super(tooltipManager, elementListener, name, Transparency.TRANSLUCENT);
00142 this.cellHeight = cellHeight;
00143 this.listCellRenderer = listCellRenderer;
00144 this.doubleClickCommandList = doubleClickCommandList;
00145
00146 list.setCellRenderer(listCellRenderer);
00147 list.setFixedCellWidth(cellWidth);
00148 list.setFixedCellHeight(cellHeight);
00149 list.setOpaque(false);
00150 list.setFocusable(false);
00151 list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
00152 list.addListSelectionListener(listSelectionListener);
00153
00154 viewport.setView(list);
00155 viewport.setScrollMode(JViewport.BLIT_SCROLL_MODE);
00156 viewport.setOpaque(false);
00157 viewport.setFocusable(false);
00158
00159 scrollPane = new JScrollPane(null, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
00160 scrollPane.setViewport(viewport);
00161 scrollPane.setOpaque(false);
00162 scrollPane.setFocusable(false);
00163 scrollPane.setBorder(new EmptyBorder(0, 0, 0, 0));
00164
00165 add(scrollPane);
00166
00167 listCellRenderer.setSize(getWidth(), cellHeight);
00168 }
00169
00173 @Override
00174 public void dispose() {
00175 super.dispose();
00176 list.removeListSelectionListener(listSelectionListener);
00177 synchronized (getTreeLock()) {
00178 resizeElements(0);
00179 }
00180 }
00181
00187 @NotNull
00188 public GUIElement getElement(final int index) {
00189 return (GUIElement)model.get(index);
00190 }
00191
00196 protected void addElement(@NotNull final GUIElement element) {
00197 assert Thread.holdsLock(getTreeLock());
00198 model.addElement(element);
00199 list.setSize(getWidth(), Integer.MAX_VALUE);
00200 viewport.update();
00201 if (model.getSize() == 1) {
00202 setSelectedIndex(0);
00203 }
00204 }
00205
00213 protected int resizeElements(final int newSize) {
00214 assert Thread.holdsLock(getTreeLock());
00215 final int index = list.getSelectedIndex();
00216 final int oldSize = model.getSize();
00217 if (newSize < oldSize) {
00218 for (int i = newSize; i < oldSize; i++) {
00219 final GUIElement element = (GUIElement)model.get(i);
00220 if (element instanceof GUIItemItem) {
00221 element.dispose();
00222 }
00223 }
00224 model.removeRange(newSize, oldSize-1);
00225 list.setSize(getWidth(), Integer.MAX_VALUE);
00226 if (index >= newSize && newSize > 0) {
00227 setSelectedIndex(newSize-1);
00228 }
00229 setChanged();
00230 }
00231 return oldSize;
00232 }
00233
00240 public boolean canMoveSelection(final int diffLines, final int diffElements) {
00241 synchronized (getTreeLock()) {
00242 final int distance;
00243 switch (list.getLayoutOrientation()) {
00244 case JList.HORIZONTAL_WRAP:
00245 distance = (list.getWidth()/cellHeight)*diffLines+diffElements;
00246 break;
00247
00248 default:
00249 distance = diffLines+diffElements;
00250 break;
00251 }
00252 final int index = list.getSelectedIndex();
00253 if (distance > 0) {
00254 return index == -1 || index+distance < list.getModel().getSize();
00255 } else if (distance < 0) {
00256 return index == -1 || index >= -distance;
00257 } else {
00258 return false;
00259 }
00260 }
00261 }
00262
00268 public void moveSelection(final int diffLines, final int diffElements) {
00269 synchronized (getTreeLock()) {
00270 final int distance;
00271 switch (list.getLayoutOrientation()) {
00272 case JList.HORIZONTAL_WRAP:
00273 distance = (list.getWidth()/cellHeight)*diffLines+diffElements;
00274 break;
00275
00276 default:
00277 distance = diffLines+diffElements;
00278 break;
00279 }
00280 final int index = list.getSelectedIndex();
00281 final int newIndex;
00282 if (distance > 0) {
00283 if (index == -1) {
00284 newIndex = 0;
00285 } else {
00286 newIndex = Math.min(index+distance, list.getModel().getSize()-1);
00287 }
00288 } else if (distance < 0) {
00289 if (index == -1) {
00290 newIndex = list.getModel().getSize()-1;
00291 } else {
00292 newIndex = Math.max(index+distance, 0);
00293 }
00294 } else {
00295 if (index == -1) {
00296 newIndex = 0;
00297 } else {
00298 newIndex = index;
00299 }
00300 }
00301 setSelectedIndex(newIndex);
00302 }
00303 }
00304
00310 @Override
00311 public boolean canScroll(final int distance) {
00312 synchronized (getTreeLock()) {
00313 final Adjustable scrollBar = scrollPane.getVerticalScrollBar();
00314 if (distance > 0) {
00315 return scrollBar.getValue() < scrollBar.getMaximum()-scrollBar.getVisibleAmount();
00316 } else if (distance < 0) {
00317 return scrollBar.getValue() > scrollBar.getMinimum();
00318 } else {
00319 return false;
00320 }
00321 }
00322 }
00323
00328 @Override
00329 public void scroll(final int distance) {
00330 synchronized (getTreeLock()) {
00331 final Adjustable scrollBar = scrollPane.getVerticalScrollBar();
00332 final int value = scrollBar.getValue()+distance*cellHeight;
00333 scrollBar.setValue(value);
00334 final int index = list.getSelectedIndex();
00335 if (index != -1) {
00336 final int firstIndex = list.getFirstVisibleIndex();
00337 if (index < firstIndex) {
00338 switch (list.getLayoutOrientation()) {
00339 case JList.HORIZONTAL_WRAP:
00340 final int columns = list.getWidth()/cellHeight;
00341 setSelectedIndex(firstIndex+index%columns);
00342 break;
00343
00344 default:
00345 setSelectedIndex(firstIndex);
00346 break;
00347 }
00348 } else {
00349 final int lastIndex = list.getLastVisibleIndex();
00350 if (index > lastIndex) {
00351 switch (list.getLayoutOrientation()) {
00352 case JList.HORIZONTAL_WRAP:
00353 final int columns = list.getWidth()/cellHeight;
00354 final int newTmpColumn = lastIndex-lastIndex%columns+index%columns;
00355 final int newColumn;
00356 if (newTmpColumn <= lastIndex) {
00357 newColumn = newTmpColumn;
00358 } else if (newTmpColumn >= columns) {
00359 newColumn = newTmpColumn-columns;
00360 } else {
00361 newColumn = lastIndex;
00362 }
00363 setSelectedIndex(newColumn);
00364 break;
00365
00366 default:
00367 setSelectedIndex(lastIndex);
00368 break;
00369 }
00370 }
00371 }
00372 }
00373 }
00374 setChanged();
00375 }
00376
00380 @Override
00381 public void resetScroll() {
00382 setSelectedIndex(0);
00383 }
00384
00388 @Override
00389 public void mouseClicked(@NotNull final MouseEvent e) {
00390 doSelect(e);
00391 if (doubleClickCommandList != null && e.getClickCount() > 1) {
00392 doubleClickCommandList.execute();
00393 }
00394 super.mouseClicked(e);
00395 }
00396
00400 @Override
00401 public void mouseEntered(@NotNull final MouseEvent e, final boolean debugGui) {
00402 super.mouseEntered(e, debugGui);
00403 doTooltip(e);
00404 }
00405
00409 @Override
00410 public void mouseExited(@NotNull final MouseEvent e) {
00411 super.mouseExited(e);
00412 doTooltip(e);
00413 }
00414
00418 @Override
00419 public void mousePressed(@NotNull final MouseEvent e) {
00420 super.mouseClicked(e);
00421 doSelect(e);
00422 }
00423
00427 @Override
00428 public void mouseMoved(@NotNull final MouseEvent e) {
00429 super.mouseMoved(e);
00430 doTooltip(e);
00431 }
00432
00436 @Override
00437 public void mouseDragged(@NotNull final MouseEvent e) {
00438 super.mouseClicked(e);
00439 doSelect(e);
00440 }
00441
00446 private void doSelect(@NotNull final MouseEvent e) {
00447 synchronized (getTreeLock()) {
00448 setSelectedIndex(list.getFirstVisibleIndex()+list.locationToIndex(e.getPoint()));
00449 }
00450 }
00451
00456 private void doTooltip(@NotNull final MouseEvent e) {
00457 synchronized (getTreeLock()) {
00458 final int index = list.locationToIndex(e.getPoint());
00459 if (index == -1) {
00460 tooltipIndex = -1;
00461 tooltipRectangle = null;
00462 updateTooltip();
00463 return;
00464 }
00465
00466 final Rectangle rectangle = list.getCellBounds(index, index);
00467 if (rectangle == null || !rectangle.contains(e.getPoint())) {
00468 tooltipIndex = -1;
00469 tooltipRectangle = null;
00470 updateTooltip();
00471 return;
00472 }
00473
00474 tooltipIndex = list.getFirstVisibleIndex()+index;
00475 tooltipRectangle = rectangle;
00476 updateTooltip();
00477 }
00478 }
00479
00484 protected void setSelectedIndex(final int newIndex) {
00485 synchronized (getTreeLock()) {
00486 final int newIndex2 = Math.min(Math.max(newIndex, 0), list.getModel().getSize()-1);
00487 final int index = list.getSelectedIndex();
00488 if (newIndex2 == index) {
00489 return;
00490 }
00491
00492 list.setSelectedIndex(newIndex2);
00493 if (newIndex2 >= 0) {
00494 list.ensureIndexIsVisible(newIndex2);
00495 }
00496 }
00497 setChanged();
00498 }
00499
00503 protected void selectionChanged() {
00504 synchronized (getTreeLock()) {
00505 selectionChanged(list.getSelectedIndex());
00506 }
00507 }
00508
00513 protected abstract void selectionChanged(final int selectedIndex);
00514
00518 private void updateTooltip() {
00519 if (tooltipIndex == -1) {
00520 setTooltipText(null);
00521 } else {
00522 final Rectangle rectangle = tooltipRectangle;
00523 if (rectangle == null) {
00524 setTooltipText(null);
00525 } else {
00526 final Component gui = GuiUtils.getGui(this);
00527 if (gui == null) {
00528 tooltipIndex = -1;
00529 tooltipRectangle = null;
00530 setTooltipText(null);
00531 } else {
00532 updateTooltip(tooltipIndex, gui.getX()+getX()+rectangle.x, gui.getY()+getY()+rectangle.y, rectangle.width, rectangle.height);
00533 }
00534 }
00535 }
00536 }
00537
00541 @Override
00542 public void setChanged() {
00543 super.setChanged();
00544 updateTooltip();
00545 }
00546
00550 @Override
00551 public void execute() {
00552
00553 }
00554
00563 protected abstract void updateTooltip(final int index, final int x, final int y, final int w, final int h);
00564
00571 protected void setLayoutOrientation(final int layoutOrientation, final int visibleRowCount) {
00572 synchronized (getTreeLock()) {
00573 list.setLayoutOrientation(layoutOrientation);
00574 list.setVisibleRowCount(visibleRowCount);
00575 }
00576 }
00577
00582 @NotNull
00583 protected Object getSelectedObject() {
00584 synchronized (getTreeLock()) {
00585 return list.getSelectedValue();
00586 }
00587 }
00588
00592 @Override
00593 public Dimension getPreferredSize() {
00594 final Dimension result = list.getPreferredSize();
00595 return result == null ? super.getPreferredSize() : result;
00596 }
00597
00601 @Override
00602 public Dimension getMinimumSize() {
00603 final Dimension result = list.getMinimumSize();
00604 return result == null ? super.getMinimumSize() : result;
00605 }
00606
00610 @Override
00611 public void setBounds(final int x, final int y, final int width, final int height) {
00612 super.setBounds(x, y, width, height);
00613 scrollPane.setSize(width, height);
00614 listCellRenderer.setSize(width, cellHeight);
00615 }
00616
00617 }