All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.smallmind.swing.catalog.Catalog Maven / Gradle / Ivy

There is a newer version: 6.2.0
Show newest version
/*
 * Copyright (c) 2007 through 2024 David Berkman
 *
 * This file is part of the SmallMind Code Project.
 *
 * The SmallMind Code Project is free software, you can redistribute
 * it and/or modify it under either, at your discretion...
 *
 * 1) The terms of GNU Affero General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 *
 * ...or...
 *
 * 2) The terms of the Apache License, Version 2.0.
 *
 * The SmallMind Code Project is distributed in the hope that it will
 * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License or Apache License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * and the Apache License along with the SmallMind Code Project. If not, see
 *  or .
 *
 * Additional permission under the GNU Affero GPL version 3 section 7
 * ------------------------------------------------------------------
 * If you modify this Program, or any covered work, by linking or
 * combining it with other code, such other code is not for that reason
 * alone subject to any of the requirements of the GNU Affero GPL
 * version 3.
 */
package org.smallmind.swing.catalog;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.HierarchyBoundsListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.LinkedList;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JViewport;
import javax.swing.Scrollable;
import javax.swing.SwingUtilities;
import org.smallmind.swing.MultiListSelectionEvent;
import org.smallmind.swing.MultiListSelectionListener;
import org.smallmind.swing.layout.ListLayout;

public class Catalog, D> extends JLayeredPane implements Scrollable, CatalogDataListener, HierarchyBoundsListener, ComponentListener, MouseListener, MouseMotionListener {

  private final JPanel glassPanel;
  private final JPanel catalogPanel;
  private final CatalogModel model;
  private final CatalogMultiListDataProvider multiListDataProvider;
  private MultiListSelectionModel selectionModel;
  private CatalogCellRenderer renderer;
  private CatalogScrollModel scrollModel;
  private Component lastTargetedComponent = null;

  public Catalog (T key, CatalogModel model) {

    super();

    this.model = model;

    setFocusable(true);

    glassPanel = new JPanel();
    glassPanel.setOpaque(false);
    glassPanel.setSize(getSize());

    catalogPanel = new JPanel(new ListLayout(0));
    catalogPanel.setOpaque(true);
    catalogPanel.setSize(getSize());

    add(catalogPanel, 10);
    add(glassPanel, 11);

    multiListDataProvider = new CatalogMultiListDataProvider(key, this);
    setSelectionModel(new DefaultMultiListSelectionModel());

    model.addCatalogDataListener(this);

    setCellRenderer(new DefaultCatalogCellRenderer());
    setScrollModel(new DefaultCatalogScrollModel(this));

    addHierarchyBoundsListener(this);
    addComponentListener(this);

    glassPanel.addMouseListener(this);
    glassPanel.addMouseMotionListener(this);
  }

  public CatalogModel getModel () {

    return model;
  }

  public synchronized CatalogCellRenderer getCellRenderer () {

    return renderer;
  }

  public synchronized void setCellRenderer (CatalogCellRenderer renderer) {

    this.renderer = renderer;

    catalogPanel.removeAll();
    for (int count = 0; count < model.getSize(); count++) {
      catalogPanel.add(getRenderedComponent(count), count);
    }

    catalogPanel.invalidate();
  }

  public synchronized CatalogScrollModel getScrollModel () {

    return scrollModel;
  }

  public synchronized void setScrollModel (CatalogScrollModel scrollModel) {

    this.scrollModel = scrollModel;
  }

  public synchronized MultiListSelectionModel getSelectionModel () {

    return selectionModel;
  }

  public synchronized void setSelectionModel (MultiListSelectionModel selectionModel) {

    if (this.selectionModel != null) {
      this.selectionModel.removeMultiListDataProvider(multiListDataProvider);
      this.selectionModel.removeMultiListSelectionListener(multiListDataProvider);
    }

    if (selectionModel != null) {
      this.selectionModel = selectionModel;
      selectionModel.addMultiListDataProvider(multiListDataProvider);
      selectionModel.addMultiListSelectionListener(multiListDataProvider);

      reRenderAllComponents();
    }
  }

  public synchronized MultiListSelectionModel.SelctionMode getSelectionMode () {

    return selectionModel.getSelectionMode();
  }

  public synchronized void setSelectionMode (MultiListSelectionModel.SelctionMode selectionMode) {

    selectionModel.setSelectionMode(selectionMode);
  }

  public synchronized void addMultiListSelectionListener (MultiListSelectionListener listener) {

    selectionModel.addMultiListSelectionListener(listener);
  }

  public synchronized void removeMultiListSelectionListener (MultiListSelectionListener listener) {

    selectionModel.removeMultiListSelectionListener(listener);
  }

  public synchronized void clearSelection () {

    selectionModel.clearSelection();
  }

  public synchronized void addSelectionInterval (int startIndex, int endIndex) {

    selectionModel.addSelectionInterval(new MultiListSelection(multiListDataProvider.getKey(), startIndex), new MultiListSelection(multiListDataProvider.getKey(), startIndex));
  }

  public synchronized MultiListSelection getAnchorSelectionIndex () {

    return selectionModel.getAnchorSelection();
  }

  public synchronized MultiListSelection getLeadSelectionIndex () {

    return selectionModel.getLeadSelection();
  }

  public synchronized MultiListSelection getMinSelectionIndex () {

    return selectionModel.getMinSelection();
  }

  public synchronized MultiListSelection getMaxSelectionIndex () {

    return selectionModel.getMaxSelection();
  }

  public synchronized int getSelectedIndex () {

    return selectionModel.getSelectedIndex(multiListDataProvider.getKey());
  }

  public synchronized void setSelectedIndex (int index) {

    selectionModel.setSelectionInterval(new MultiListSelection(multiListDataProvider.getKey(), index), new MultiListSelection(multiListDataProvider.getKey(), index));
  }

  public synchronized int[] getSelectedIndices () {

    ArrayList indexList;
    int[] indices;

    indexList = new ArrayList();
    for (int count = 0; count < catalogPanel.getComponentCount(); count++) {
      if (selectionModel.isSelected(new MultiListSelection(multiListDataProvider.getKey(), count))) {
        indexList.add(count);
      }
    }

    indices = new int[indexList.size()];
    for (int count = 0; count < indexList.size(); count++) {
      indices[count] = indexList.get(count);
    }

    return indices;
  }

  public synchronized void setSelectedIndices (int[] indices) {

    selectionModel.clearSelection();
    for (int index : indices) {
      selectionModel.addSelectionInterval(new MultiListSelection(multiListDataProvider.getKey(), index), new MultiListSelection(multiListDataProvider.getKey(), index));
    }
  }

  public synchronized D getSelectedValue () {

    int index;

    if ((index = getSelectedIndex()) >= 0) {
      return model.getElementAt(index);
    }

    return null;
  }

  public synchronized Object[] getSelectedValues () {

    LinkedList valueList;

    valueList = new LinkedList();
    for (int count = 0; count < catalogPanel.getComponentCount(); count++) {
      if (selectionModel.isSelected(new MultiListSelection(multiListDataProvider.getKey(), count))) {
        valueList.add(model.getElementAt(count));
      }
    }

    return valueList.toArray();
  }

  public synchronized boolean isSelectedIndex (int index) {

    return selectionModel.isSelected(new MultiListSelection(multiListDataProvider.getKey(), index));
  }

  public synchronized boolean isSelectedValue (D element) {

    return selectionModel.isSelected(new MultiListSelection(multiListDataProvider.getKey(), model.indexOf(element)));
  }

  public synchronized boolean isSelectionEmpty () {

    return selectionModel.isSelectionEmpty();
  }

  public synchronized void removeSelectionInterval (int startIndex, int endIndex) {

    selectionModel.removeSelectionInterval(new MultiListSelection(multiListDataProvider.getKey(), startIndex), new MultiListSelection(multiListDataProvider.getKey(), endIndex));
  }

  public synchronized void setSelectionInterval (int startIndex, int endIndex) {

    selectionModel.setSelectionInterval(new MultiListSelection(multiListDataProvider.getKey(), startIndex), new MultiListSelection(multiListDataProvider.getKey(), endIndex));
  }

  private void reRenderAllComponents () {

    for (int count = 0; count < catalogPanel.getComponentCount(); count++) {
      reRenderComponent(count);
    }
  }

  private void reRenderComponent (int index) {

    getRenderedComponent(index).repaint();
  }

  public Component getRenderedComponent (int index) {

    return renderer.getCatalogCellRendererComponent(this, model.getElementAt(index), index, isSelectedIndex(index), false);
  }

  public synchronized void itemAdded (CatalogDataEvent catalogDataEvent) {

    Component component;
    int index;

    index = catalogDataEvent.getEndIndex();
    component = getRenderedComponent(index);
    catalogPanel.add(component, index);

    adjustToContents();

    for (int count = catalogDataEvent.getEndIndex() + 1; count < catalogPanel.getComponentCount(); count++) {
      reRenderComponent(count);
    }

    selectionModel.insertIndexInterval(new MultiListSelection(multiListDataProvider.getKey(), index), 1, true);
  }

  public synchronized void intervalRemoved (CatalogDataEvent catalogDataEvent) {

    for (int count = catalogDataEvent.getStartIndex(); count <= catalogDataEvent.getEndIndex(); count++) {
      catalogPanel.remove(catalogDataEvent.getStartIndex());
    }

    adjustToContents();

    for (int count = 0; count < catalogPanel.getComponentCount(); count++) {
      reRenderComponent(count);
    }

    selectionModel.removeIndexInterval(new MultiListSelection(multiListDataProvider.getKey(), catalogDataEvent.getStartIndex()), new MultiListSelection(multiListDataProvider.getKey(), catalogDataEvent.getEndIndex()));
  }

  public synchronized void intervalChanged (CatalogDataEvent catalogDataEvent) {

    for (int count = catalogDataEvent.getStartIndex(); count <= catalogDataEvent.getEndIndex(); count++) {
      catalogPanel.remove(catalogDataEvent.getStartIndex());
    }

    for (int count = catalogDataEvent.getEndIndex(); count >= catalogDataEvent.getStartIndex(); count--) {
      catalogPanel.add(getRenderedComponent(count), catalogDataEvent.getStartIndex());
    }

    adjustToContents();

    for (int count = catalogDataEvent.getStartIndex(); count <= catalogDataEvent.getEndIndex(); count++) {
      reRenderComponent(count);
    }
  }

  private void adjustToContents () {

    Rectangle bounds;
    double preferredWidth;
    double preferredHeight = 0;

    if (getParent() != null) {
      if (getParent() instanceof JViewport) {
        preferredWidth = ((JViewport)getParent()).getViewSize().getWidth();
      } else {
        preferredWidth = getParent().getWidth();
      }

      for (int count = 0; count < catalogPanel.getComponentCount(); count++) {
        preferredHeight += catalogPanel.getComponent(count).getPreferredSize().getHeight();
      }

      bounds = new Rectangle(new Point(0, 0), new Dimension((int)preferredWidth, (int)preferredHeight));
      setPreferredSize(bounds.getSize());
      catalogPanel.setBounds(bounds);
      glassPanel.setBounds(bounds);
    }
  }

  public synchronized void revalidate () {

    adjustToContents();
    super.revalidate();
  }

  public synchronized void repaint () {

    reRenderAllComponents();
  }

  public synchronized int getIndexAtPoint (Point point) {

    int height = 0;

    for (int count = 0; count < catalogPanel.getComponentCount(); count++) {
      height += catalogPanel.getComponent(count).getPreferredSize().getHeight();
      if (point.getY() <= height) {
        return count;
      }
    }

    return -1;
  }

  public synchronized Rectangle getSquashedRectangleAtIndex (int index) {

    int yPos = 0;

    for (int count = 0; count < index; count++) {
      yPos += catalogPanel.getComponent(count).getPreferredSize().getHeight();
    }

    return new Rectangle(0, yPos, 0, (int)catalogPanel.getComponent(index).getPreferredSize().getHeight());
  }

  public synchronized Dimension getPreferredScrollableViewportSize () {

    return getPreferredSize();
  }

  public boolean getScrollableTracksViewportWidth () {

    return true;
  }

  public boolean getScrollableTracksViewportHeight () {

    return false;
  }

  public synchronized int getScrollableUnitIncrement (Rectangle visibleRect, int orientation, int direction) {

    return scrollModel.getScrollableUnitIncrement(visibleRect, orientation, direction);
  }

  public synchronized int getScrollableBlockIncrement (Rectangle visibleRect, int orientation, int direction) {

    return scrollModel.getScrollableBlockIncrement(visibleRect, orientation, direction);
  }

  public synchronized void valueChanged (MultiListSelectionEvent multiListSelectionEvent) {

    for (int count = multiListSelectionEvent.getFirstIndex(); count <= multiListSelectionEvent.getLastIndex(); count++) {
      if (count >= catalogPanel.getComponentCount()) {
        break;
      } else {
        reRenderComponent(count);
      }
    }
  }

  public synchronized void ancestorResized (HierarchyEvent hierarchyEvent) {

    adjustToContents();
  }

  public void ancestorMoved (HierarchyEvent hierarchyEvent) {

  }

  public synchronized void componentHidden (ComponentEvent componentEvnt) {

    adjustToContents();
  }

  public void componentMoved (ComponentEvent componentEvnt) {

  }

  public synchronized void componentShown (ComponentEvent componentEvnt) {

    adjustToContents();
  }

  public synchronized void componentResized (ComponentEvent componentEvnt) {

    adjustToContents();
  }

  private void dispatchGlassMouseEvent (MouseEvent mouseEvent, boolean moved) {

    Component targetComponent;
    MouseEvent targetEvent;
    Point mousePoint;

    if ((targetComponent = SwingUtilities.getDeepestComponentAt(catalogPanel, mouseEvent.getX(), mouseEvent.getY())) != null) {
      if (moved && (targetComponent != lastTargetedComponent)) {
        if (lastTargetedComponent != null) {
          mousePoint = SwingUtilities.convertPoint(glassPanel, mouseEvent.getPoint(), lastTargetedComponent);
          lastTargetedComponent.dispatchEvent(new MouseEvent(lastTargetedComponent, MouseEvent.MOUSE_EXITED, mouseEvent.getWhen(), 0, (int)mousePoint.getX(), (int)mousePoint.getY(), mouseEvent.getClickCount(), mouseEvent.isPopupTrigger(), mouseEvent.getButton()));
        }

        mousePoint = SwingUtilities.convertPoint(glassPanel, mouseEvent.getPoint(), targetComponent);
        targetComponent.dispatchEvent(new MouseEvent(targetComponent, MouseEvent.MOUSE_ENTERED, mouseEvent.getWhen(), 0, (int)mousePoint.getX(), (int)mousePoint.getY(), mouseEvent.getClickCount(), mouseEvent.isPopupTrigger(), mouseEvent.getButton()));
      }

      targetEvent = SwingUtilities.convertMouseEvent(glassPanel, mouseEvent, targetComponent);
      targetComponent.dispatchEvent(targetEvent);
      lastTargetedComponent = targetComponent;
    }
  }

  private int getComponentIndex (Point point) {

    Component cellComponent;

    if ((cellComponent = catalogPanel.getComponentAt(point)) != null) {
      for (int count = 0; count < catalogPanel.getComponentCount(); count++) {
        if (catalogPanel.getComponent(count) == cellComponent) {
          return count;
        }
      }
    }

    return -1;
  }

  public synchronized void mouseClicked (MouseEvent mouseEvent) {

    dispatchGlassMouseEvent(mouseEvent, false);
  }

  public synchronized void mousePressed (MouseEvent mouseEvent) {

    MultiListSelection anchorSelection;
    int index;

    if ((index = getComponentIndex(mouseEvent.getPoint())) >= 0) {
      if (mouseEvent.isShiftDown()) {
        if ((anchorSelection = selectionModel.getAnchorSelection()) == null) {
          setSelectedIndex(index);
        } else {
          selectionModel.setSelectionInterval(anchorSelection, new MultiListSelection(multiListDataProvider.getKey(), index));
        }
      } else if (mouseEvent.isControlDown()) {
        if (isSelectedIndex(index)) {
          selectionModel.removeSelectionInterval(new MultiListSelection(multiListDataProvider.getKey(), index), new MultiListSelection(multiListDataProvider.getKey(), index));
        } else {
          selectionModel.addSelectionInterval(new MultiListSelection(multiListDataProvider.getKey(), index), new MultiListSelection(multiListDataProvider.getKey(), index));
        }
      } else {
        setSelectedIndex(index);
      }
      requestFocusInWindow();
    }

    dispatchGlassMouseEvent(mouseEvent, false);
  }

  public synchronized void mouseReleased (MouseEvent mouseEvent) {

    dispatchGlassMouseEvent(mouseEvent, false);
  }

  public synchronized void mouseEntered (MouseEvent mouseEvent) {

    lastTargetedComponent = null;
  }

  public synchronized void mouseExited (MouseEvent mouseEvent) {

  }

  public synchronized void mouseDragged (MouseEvent mouseEvent) {

    dispatchGlassMouseEvent(mouseEvent, true);
  }

  public synchronized void mouseMoved (MouseEvent mouseEvent) {

    dispatchGlassMouseEvent(mouseEvent, true);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy