001    /*
002     * Copyright (C) 2008-2010 by Holger Arndt
003     *
004     * This file is part of the Universal Java Matrix Package (UJMP).
005     * See the NOTICE file distributed with this work for additional
006     * information regarding copyright ownership and licensing.
007     *
008     * UJMP is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU Lesser General Public License as
010     * published by the Free Software Foundation; either version 2
011     * of the License, or (at your option) any later version.
012     *
013     * UJMP is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016     * GNU Lesser General Public License for more details.
017     *
018     * You should have received a copy of the GNU Lesser General Public
019     * License along with UJMP; if not, write to the
020     * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
021     * Boston, MA  02110-1301  USA
022     */
023    
024    package org.ujmp.gui.panels;
025    
026    import java.awt.AlphaComposite;
027    import java.awt.BasicStroke;
028    import java.awt.Color;
029    import java.awt.Dimension;
030    import java.awt.Graphics;
031    import java.awt.Graphics2D;
032    import java.awt.event.ComponentEvent;
033    import java.awt.event.ComponentListener;
034    import java.awt.event.MouseEvent;
035    import java.awt.event.MouseListener;
036    import java.awt.event.MouseMotionListener;
037    import java.awt.image.BufferedImage;
038    import java.io.File;
039    
040    import javax.swing.Action;
041    import javax.swing.BorderFactory;
042    import javax.swing.JComponent;
043    import javax.swing.JMenuItem;
044    import javax.swing.JPanel;
045    import javax.swing.JPopupMenu;
046    import javax.swing.ToolTipManager;
047    import javax.swing.UIManager;
048    import javax.swing.event.ListSelectionEvent;
049    import javax.swing.event.ListSelectionListener;
050    import javax.swing.event.TableModelEvent;
051    import javax.swing.event.TableModelListener;
052    
053    import org.ujmp.core.Coordinates;
054    import org.ujmp.core.interfaces.HasToolTip;
055    import org.ujmp.gui.MatrixGUIObject;
056    import org.ujmp.gui.actions.MatrixActions;
057    import org.ujmp.gui.interfaces.CanBeRepainted;
058    import org.ujmp.gui.io.ExportJPEG;
059    import org.ujmp.gui.io.ExportPDF;
060    import org.ujmp.gui.io.ExportPNG;
061    import org.ujmp.gui.menu.MatrixPopupMenu;
062    import org.ujmp.gui.renderer.MatrixRenderer;
063    import org.ujmp.gui.util.GraphicsExecutor;
064    import org.ujmp.gui.util.TooltipUtil;
065    
066    public class MatrixPaintPanel extends JPanel implements ComponentListener,
067                    TableModelListener, MouseListener, MouseMotionListener, CanBeRepainted,
068                    HasToolTip, ListSelectionListener {
069            private static final long serialVersionUID = 843653796010276950L;
070    
071            private MatrixGUIObject matrix = null;
072    
073            private final MatrixRenderer renderer = new MatrixRenderer();
074    
075            private BufferedImage bufferedImage = null;
076    
077            private static int PADDINGX = UIManager.getInt("Table.paddingX");
078    
079            private static int PADDINGY = UIManager.getInt("Table.paddingY");
080    
081            private int startRow = 0;
082    
083            private int startCol = 0;
084    
085            private String oldToolTip = "";
086    
087            public MatrixPaintPanel(MatrixGUIObject matrix, boolean showBorder) {
088                    if (showBorder) {
089                            setBorder(BorderFactory.createTitledBorder("Matrix Visualization"));
090                    }
091    
092                    addComponentListener(this);
093                    addMouseListener(this);
094                    addMouseMotionListener(this);
095    
096                    setPreferredSize(new Dimension(600, 400));
097    
098                    setMatrix(matrix);
099    
100                    ToolTipManager.sharedInstance().registerComponent(this);
101    
102                    registerKeyboardActions();
103            }
104    
105            private void registerKeyboardActions() {
106                    for (JComponent c : new MatrixActions(this, matrix, null)) {
107                            if (c instanceof JMenuItem) {
108                                    registerKeyboardAction(((JMenuItem) c).getAction());
109                            }
110                    }
111            }
112    
113            private void registerKeyboardAction(Action a) {
114                    // KeyStroke keyStroke = (KeyStroke) a.getValue(Action.ACCELERATOR_KEY);
115                    // getActionMap().put(a.getValue(Action.NAME), a);
116                    // getInputMap(WHEN_IN_FOCUSED_WINDOW).put(keyStroke,
117                    // a.getValue(Action.NAME));
118            }
119    
120            public void componentHidden(ComponentEvent e) {
121            }
122    
123            public void componentMoved(ComponentEvent e) {
124            }
125    
126            public void componentResized(ComponentEvent e) {
127                    renderer.setSize(getSize());
128                    GraphicsExecutor.scheduleUpdate(this);
129            }
130    
131            public void componentShown(ComponentEvent e) {
132            }
133    
134            public void tableChanged(TableModelEvent e) {
135                    GraphicsExecutor.scheduleUpdate(this);
136            }
137    
138            public void mouseClicked(MouseEvent e) {
139                    if (matrix != null) {
140    
141                            if (e.getButton() == MouseEvent.BUTTON3) {
142    
143                                    int newRow = getRowPos(e.getY());
144                                    int newCol = getColPos(e.getX());
145    
146                                    newRow = newRow < 0 ? 0 : newRow;
147                                    newCol = newCol < 0 ? 0 : newCol;
148                                    newRow = newRow >= matrix.getRowCount() ? matrix.getRowCount() - 1
149                                                    : newRow;
150                                    newCol = newCol >= matrix.getColumnCount() ? matrix
151                                                    .getColumnCount() - 1 : newCol;
152    
153                                    JPopupMenu popup = new MatrixPopupMenu(this, matrix, newRow,
154                                                    newCol);
155                                    popup.show(this, e.getX(), e.getY());
156                            }
157                    }
158            }
159    
160            public void mouseEntered(MouseEvent e) {
161            }
162    
163            public void mouseExited(MouseEvent e) {
164            }
165    
166            public void mousePressed(MouseEvent e) {
167                    if (matrix != null) {
168                            if (e.getButton() == MouseEvent.BUTTON1) {
169                                    startRow = getRowPos(e.getY());
170                                    startCol = getColPos(e.getX());
171                                    startRow = startRow < 0 ? 0 : startRow;
172                                    startCol = startCol < 0 ? 0 : startCol;
173                                    startRow = startRow >= matrix.getRowCount() ? matrix
174                                                    .getRowCount() - 1 : startRow;
175                                    startCol = startCol >= matrix.getColumnCount() ? matrix
176                                                    .getColumnCount() - 1 : startCol;
177                                    matrix.getRowSelectionModel().setValueIsAdjusting(true);
178                                    matrix.getColumnSelectionModel().setValueIsAdjusting(true);
179                                    matrix.getRowSelectionModel().setSelectionInterval(startRow,
180                                                    startRow);
181                                    matrix.getColumnSelectionModel().setSelectionInterval(startCol,
182                                                    startCol);
183                            }
184                            repaint(100);
185                    }
186            }
187    
188            private int getRowPos(int y) {
189                    return (int) Math.floor((double) matrix.getRowCount() * (double) y
190                                    / getHeight());
191            }
192    
193            private int getColPos(int x) {
194                    return (int) Math.floor((double) matrix.getColumnCount() * (double) x
195                                    / getWidth());
196            }
197    
198            public void mouseReleased(MouseEvent e) {
199                    if (matrix != null) {
200                            if (e.getButton() == MouseEvent.BUTTON1) {
201                                    int newRow = getRowPos(e.getY());
202                                    int newCol = getColPos(e.getX());
203                                    newRow = newRow < 0 ? 0 : newRow;
204                                    newCol = newCol < 0 ? 0 : newCol;
205                                    newRow = newRow >= matrix.getRowCount() ? matrix.getRowCount() - 1
206                                                    : newRow;
207                                    newCol = newCol >= matrix.getColumnCount() ? matrix
208                                                    .getColumnCount() - 1 : newCol;
209                                    matrix.getRowSelectionModel().setValueIsAdjusting(false);
210                                    matrix.getColumnSelectionModel().setValueIsAdjusting(false);
211                                    matrix.getRowSelectionModel().setSelectionInterval(startRow,
212                                                    newRow);
213                                    matrix.getColumnSelectionModel().setSelectionInterval(startCol,
214                                                    newCol);
215                                    repaint(100);
216                            }
217                    }
218            }
219    
220            public MatrixGUIObject getMatrix() {
221                    return matrix;
222            }
223    
224            public String getToolTipText(MouseEvent e) {
225                    // only generate tool text when there a matrix with size >0 is available
226                    if (matrix != null && Coordinates.product(matrix.getSize()) != 0) {
227                            int r = getRowPos(e.getY());
228                            int c = getColPos(e.getX());
229                            r = r < 0 ? 0 : r;
230                            c = c < 0 ? 0 : c;
231                            r = r >= matrix.getRowCount() ? matrix.getRowCount() - 1 : r;
232                            c = c >= matrix.getColumnCount() ? matrix.getColumnCount() - 1 : c;
233    
234                            String toolTip = TooltipUtil.getTooltip(matrix, r, c);
235                            if (("" + toolTip).length() == ("" + oldToolTip).length()) {
236                                    toolTip = toolTip + "\n";
237                            }
238                            oldToolTip = toolTip;
239                            return toolTip;
240                    } else {
241                            return null;
242                    }
243            }
244    
245            public void setMatrix(MatrixGUIObject m) {
246                    if (matrix != null) {
247                            matrix.removeTableModelListener(this);
248                            matrix.getRowSelectionModel().removeListSelectionListener(this);
249                            matrix.getColumnSelectionModel().removeListSelectionListener(this);
250                    }
251    
252                    if (m != null) {
253                            matrix = m;
254                            renderer.setMatrix(m);
255                    } else {
256                            matrix = null;
257                    }
258    
259                    if (matrix != null) {
260                            matrix.addTableModelListener(this);
261                            matrix.getRowSelectionModel().addListSelectionListener(this);
262                            matrix.getColumnSelectionModel().addListSelectionListener(this);
263                    }
264    
265                    GraphicsExecutor.scheduleUpdate(this);
266            }
267    
268            protected void finalize() throws Throwable {
269                    super.finalize();
270                    if (matrix != null) {
271                            matrix.removeTableModelListener(this);
272                    }
273                    ToolTipManager.sharedInstance().unregisterComponent(this);
274            }
275    
276            protected void paintComponent(Graphics g) {
277                    super.paintComponent(g);
278                    Graphics2D g2d = (Graphics2D) g;
279    
280                    if (matrix != null && bufferedImage != null) {
281                            g2d.drawImage(bufferedImage, 0, 0, getWidth(), getHeight(), null);
282    
283                            if (!matrix.getRowSelectionModel().isSelectionEmpty()) {
284                                    g2d.setColor(Color.BLUE);
285    
286                                    int x1 = matrix.getColumnSelectionModel()
287                                                    .getMinSelectionIndex();
288                                    int x2 = matrix.getColumnSelectionModel()
289                                                    .getMaxSelectionIndex();
290                                    int y1 = matrix.getRowSelectionModel().getMinSelectionIndex();
291                                    int y2 = matrix.getRowSelectionModel().getMaxSelectionIndex();
292                                    double sx = (double) (getWidth() - PADDINGX - PADDINGX)
293                                                    / (double) matrix.getColumnCount();
294                                    double sy = (double) (getHeight() - PADDINGY - PADDINGY)
295                                                    / (double) matrix.getRowCount();
296                                    g2d.setStroke(new BasicStroke(2.0f));
297                                    g2d
298                                                    .drawRect((int) Math.floor(PADDINGX + x1 * sx),
299                                                                    (int) Math.floor(PADDINGY + y1 * sy),
300                                                                    (int) Math.ceil(sx + (x2 - x1) * sx),
301                                                                    (int) Math.ceil(sy + (y2 - y1) * sy));
302                                    g2d.setComposite(AlphaComposite.getInstance(
303                                                    AlphaComposite.SRC_ATOP, 0.3f));
304                                    g2d
305                                                    .fillRect((int) Math.floor(PADDINGX + x1 * sx),
306                                                                    (int) Math.floor(PADDINGY + y1 * sy),
307                                                                    (int) Math.ceil(sx + (x2 - x1) * sx),
308                                                                    (int) Math.ceil(sy + (y2 - y1) * sy));
309                            }
310                    } else {
311                            g2d.setColor(Color.GRAY);
312                            g2d.drawLine(0, 0, getWidth(), getHeight());
313                            g2d.drawLine(0, getHeight(), getWidth(), 0);
314                    }
315            }
316    
317            public void repaintUI() {
318                    if (matrix != null && getWidth() > 0 && getHeight() > 0) {
319                            BufferedImage tempBufferedImage = new BufferedImage(getWidth(),
320                                            getHeight(), BufferedImage.TYPE_INT_RGB);
321                            renderer.setSize(getWidth(), getHeight());
322                            Graphics2D g2d = (Graphics2D) tempBufferedImage.getGraphics();
323                            renderer.paintComponent(g2d);
324                            g2d.dispose();
325                            bufferedImage = tempBufferedImage;
326                    }
327            }
328    
329            public void exportToPDF(File file) {
330                    ExportPDF.save(file, this);
331            }
332    
333            public void exportToJPEG(File file) {
334                    ExportJPEG.save(file, this);
335            }
336    
337            public void exportToPNG(File file) {
338                    ExportPNG.save(file, this);
339            }
340    
341            public void mouseDragged(MouseEvent e) {
342                    if (matrix != null) {
343                            if (matrix.getRowSelectionModel().getValueIsAdjusting()) {
344                                    int newRow = getRowPos(e.getY());
345                                    int newCol = getColPos(e.getX());
346                                    newRow = newRow < 0 ? 0 : newRow;
347                                    newCol = newCol < 0 ? 0 : newCol;
348                                    newRow = newRow >= matrix.getRowCount() ? matrix.getRowCount() - 1
349                                                    : newRow;
350                                    newCol = newCol >= matrix.getColumnCount() ? matrix
351                                                    .getColumnCount() - 1 : newCol;
352                                    matrix.getRowSelectionModel().setSelectionInterval(startRow,
353                                                    newRow);
354                                    matrix.getColumnSelectionModel().setSelectionInterval(startCol,
355                                                    newCol);
356                                    repaint(100);
357                            }
358                    }
359            }
360    
361            public void mouseMoved(MouseEvent e) {
362            }
363    
364            public void valueChanged(ListSelectionEvent e) {
365                    repaint(100);
366            }
367    }