/* * Copyright 2011 Uwe Krueger. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.mandelsoft.swing; import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Insets; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.ListCellRenderer; import javax.swing.border.Border; import com.mandelsoft.swing.DnDJList.ContextMenuHandler; import java.awt.event.ActionListener; import javax.swing.Timer; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /** * * @author Uwe Krueger */ public abstract class GaleryPanel<E,M extends ThumbnailListModel<E>> extends GBCPanel { static Color backColor = Color.lightGray; private int maxframe=0; private Dimension maximage=new Dimension(180,180); private int gap=40; private Dimension item; private Renderer renderer; protected DnDJList list; protected JScrollPane scrollPane; private GBCPanel panel; private M model; private ThumbnailChangeListener thumblistener; public GaleryPanel(M model) { this(model,1,null); } public GaleryPanel(M model, int rows, Dimension d) { if (d!=null) maximage=d; System.out.println("Galery panel for items of size "+Dimensions.toString(d)); thumblistener=new ThumbnailChangeListener(); panel=new GBCPanel(){ }; panel.setBackground(backColor); this.model=model; renderer=new Renderer(); list=new DnDJList(model); list.setLayoutOrientation(JList.HORIZONTAL_WRAP); list.setVisibleRowCount(rows); list.setCellRenderer(renderer); list.setBackground(backColor); //list.setBorder(new BevelBorder(BevelBorder.LOWERED)); panel.add(list,GBC(0,0).setAnchor(GBC.CENTER)); // panel.setBorder(new BevelBorder(BevelBorder.RAISED)); panel.setTransferHandler(list.getTransferHandler()); scrollPane=new JScrollPane(panel); setBorder(new BevelBorder(BevelBorder.LOWERED)); add(scrollPane,GBC(0,0,GBC.BOTH).setAnchor(GBC.CENTER)); item=renderer.getPreferredSize(); ResizeListener l=new ResizeListener(); addComponentListener(l); } @Override protected void panelBound() { System.out.println("galery item size "+Dimensions.toString(item)); super.panelBound(); if (model!=null) { model.addThumbnailListener(thumblistener); if (model instanceof ProxyModel) { ((ProxyModel)model).bind(); } } } @Override protected void panelUnbound() { System.out.println("cleanup galery"); super.panelUnbound(); if (model!=null) { if (model instanceof ProxyModel) { ((ProxyModel)model).unbind(); } model.removeThumbnailListener(thumblistener); } } protected abstract String getLabel(E elem); protected abstract Icon getIcon(E elem); public int getMaxFrame() { return maxframe; } public void setMaxFrame(int maxframe) { this.maxframe=maxframe; } public Dimension getMaxImage() { return maximage; } public M getModel() { return model; } public class TestRenderer extends JPanel implements ListCellRenderer { private JLabel label; TestRenderer() { super(false); add(label=new JLabel()); validate(); } public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { label.setText(getLabel((E)value)); validate(); return this; } } public class ItemPanel extends GBCPanel { protected JLabel label; protected ImagePanel panel; ItemPanel() { add(panel=new ImagePanel(), GBC(0, 0).setInsets(10, 10, 10, 10)); add(label=new JLabel(), GBC(0, 1).setInsets(0, 10, 10, 10)); } public void setData(E elem) { label.setText(getLabel(elem)); label.setIcon(getIcon(elem)); panel.setData(elem); } } public class ImagePanel extends JComponent { private E elem; private Border border; public ImagePanel() { border=(new BevelBorder(BevelBorder.LOWERED)); //setBorder(null); setBorder(new BevelBorder(BevelBorder.RAISED,4)); Insets insets=getInsets(); Dimension s=new Dimension((int)maximage.getWidth()+insets.right+insets.left+gap, (int)maximage.getHeight()+insets.top+insets.bottom+gap); setPreferredSize(s); setBackground(Color.GRAY); //setSize(s); //addMouseListener(new ML(elem)); } void setData(E elem) { this.elem=elem; } @Override public void paintComponent(Graphics g) { Rectangle r=g.getClipBounds(); // System.out.println(elem+" local insets: "+getInsets() // +" rect: "+Rectangles.toString(r)+": "+this.getBackground()); if (!r.isEmpty()) { int x,y; Insets insets=getInsets(); g.setColor(getBackground()); g.fillRect(insets.left, insets.top, (int)maximage.getWidth()+gap, (int)maximage.getHeight()+gap); BufferedImage image=model.getThumbnail(elem, maximage); x=(int)(maximage.getWidth()-image.getWidth()+gap)/2+insets.left; y=(int)(maximage.getHeight()-image.getHeight()+gap)/2+insets.top; g.drawImage(image, x, y, null); Insets binsets=border.getBorderInsets(this); border.paintBorder(this, g, x-binsets.left, y-binsets.top, image.getWidth()+binsets.left+binsets.right, image.getHeight()+binsets.top+binsets.bottom); } } } protected void requestListIndex(int index) { } public class Renderer extends ItemPanel implements ListCellRenderer { Renderer() { } public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { setData((E)value); requestListIndex(index); if (isSelected) { setBackground(list.getSelectionBackground()); setForeground(list.getSelectionForeground()); //panel.setBackground(list.getSelectionBackground()); } else { setBackground(list.getBackground()); setForeground(list.getForeground()); panel.setBackground(Color.GRAY); } //revalidate(); return this; } } /////////////////////////////////////////////////////////////////////////// private class ResizeListener extends ComponentAdapter implements PropertyChangeListener { @Override public void componentResized(ComponentEvent e) { adjustList(); } public void propertyChange(PropertyChangeEvent evt) { System.out.println("panel prop changed: "+evt.getPropertyName()); if (evt.getPropertyName().equals("size")) { adjustList(); } } private void adjustList() { Dimension n=getSize(); //System.out.println("panel size: "+Dimensions.toString(n)); int h=(int)(n.getHeight()/item.getHeight()); if (h<1) h=1; if (list.getVisibleRowCount()!=h) { list.setVisibleRowCount(h); list.revalidate(); } } } private class ThumbnailChangeListener implements ThumbnailListener<E> { public void thumbnailChanged(ThumbnailEvent<E> event) { repaint(); } } /////////////////////////////////////////////////////////////////////////// private boolean busy; private Cursor origcursor; protected void setBusy(boolean b) { if (b!=busy) { if (b) { System.out.println("-------------------------------------------------"); System.out.println("set busy"); origcursor=getCursor(); setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); } else { System.out.println("orig cursor"); setCursor(origcursor); } firePropertyChange("busy",!b,b); } busy=b; } ////////////////////////////////////////////////////////////////////////// // Context Menu outside of list ////////////////////////////////////////////////////////////////////////// private class Listener extends MouseAdapter { @Override public void mouseReleased(MouseEvent e) { handlePopup(e); } @Override public void mousePressed(MouseEvent e) { handlePopup(e); } public void handlePopup(MouseEvent e) { if (e.isPopupTrigger()&&ctxmenu!=null) { System.out.println("CTX POPUP at panel"); ctxmenu.handleContextMenu(panel, e, new ListSelection(list)); } } } private ContextMenuHandler ctxmenu; private Listener listener; synchronized public void setContextMenuHandler(DnDJList.ContextMenuHandler h) { if (h==null) { if (ctxmenu!=null) panel.removeMouseListener(listener); } else { if (ctxmenu==null) { if (listener==null) listener=new Listener(); panel.addMouseListener(listener); } } ctxmenu=h; list.setContextMenuHandler(h); } synchronized public ContextMenuHandler getContextMenuHandler() { return ctxmenu; } ////////////////////////////////////////////////////////////////////////// public class Ticker extends Timer implements ActionListener, ChangeListener { private Rectangle rect; private boolean inupdate; public Ticker() { super(100,null); rect=new Rectangle(0,0,1,1); this.addActionListener(this); scrollPane.getViewport().addChangeListener(this); } public void actionPerformed(ActionEvent e) { // System.out.println("tick..."); if (!scrollPane.getHorizontalScrollBar().isVisible()) { this.stop(); } Rectangle r=scrollPane.getViewport().getViewRect(); int w=(int)panel.getPreferredSize().getWidth(); // System.out.println(" vx="+r.getX()+", vw="+r.getWidth()+ // ", w="+w+", cur="+rect.getX()); if (r.getX()+r.getWidth()+1<w) { setX((int)rect.getX()+1); inupdate=true; try { list.scrollRectToVisible(rect); } finally { inupdate=false; } } else { this.stop(); } } @Override public void restart() { setX(1); super.restart(); } @Override public void start() { Rectangle r=scrollPane.getViewport().getViewRect(); // setX(0); // list.scrollRectToVisible(rect); setX((int)(r.getWidth()+r.getX())); this.setInitialDelay(this.getDelay()); super.start(); } public void start(int delay) { Rectangle r=scrollPane.getViewport().getViewRect(); // setX(0); // list.scrollRectToVisible(rect); setX((int)(r.getWidth()+r.getX())); this.setInitialDelay(delay); super.start(); } @Override public void stop() { System.out.println("stop ticker"); super.stop(); } private void setX(int x) { rect.setRect(x, 0, 1, 1); } public void stateChanged(ChangeEvent e) { if (!inupdate) stop(); } } }