/*
* Copyright (C) 2011 Alasdair C. Hamilton
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package ketUI;
import java.awt.GridLayout;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.util.*;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.border.*;
import geom.Offset;
import geom.Position;
import ket.MathCollection;
import ket.Selection;
import ket.display.ImageTools;
import ket.display.box.Box;
import ket.math.Argument;
import ket.math.Branch;
import ket.math.Equation;
import ket.math.Function;
import ket.math.Symbol;
import ket.math.SymbolicFunction;
import ket.math.Token;
import ket.math.purpose.Word;
import ketUI.chord.Chord;
import ketUI.chord.KeyPress;
import ketUI.chord.KeyboardEventHandler;
import ketUI.modes.AddMode;
import ketUI.modes.Cycle;
import ketUI.modes.DocumentState;
import ketUI.modes.NormalMode;
import ketUI.panel.KetPanel;
import ketUI.responder.MouseResponder;
/**
* Display simliar expressions to current.
*/
public class Album extends JFrame implements WindowFocusListener, Runnable, ActionListener, KeyListener { //! Not related to ButtonMenu.
Document document;
Vector<Argument> family;
int rows;
int columns;
Map<JButton,Argument> button2Arg;
Map<Argument,JButton> arg2Button;
Map<Index,Argument> index2Argument;
Map<Argument,Index> argument2Index;
Map<Index,JButton> index2Button;
private JButton getButton(Index sought) {
for (Map.Entry<Index,JButton> entry : index2Button.entrySet()) { // HACK
Index i = entry.getKey();
if (sought.equals(i)) {
return entry.getValue();
}
}
return null;
// BUG: Why doesn't the get method not work here?
//> JButton button = index2Button.get(sought); // BUG: Doesn't work
}
class Index {
public final int r;
public final int c;
public Index(int r, int c) {
this.r = r;
this.c = c;
}
public Index bound() {
int area = rows*columns;
int a = (clen() + area) % area;
return new Index(a/columns, a%columns);
}
public int clen() {
int len = family!=null ? family.size() : 1000;
return (r*columns + c + len) % len;
}
public int rlen() {
int len = family!=null ? family.size() : 1000;
return (c*rows + r + len) % len;
}
public Index wrap() {
//-? return new Index((r+rows)%rows, (c+columns)%columns);
int area = rows*columns;
int a = (rlen() + area) % area;
return new Index(a%rows, a/rows);
}
public Index up() {
return new Index(r-1, c);
}
public Index down() {
return new Index(r+1, c);
}
public Index left() {
return new Index(r, c-1);
}
public Index right() {
return new Index(r, c+1);
}
public boolean equals(Index that) {
return this.r==that.r && this.c==that.c;
}
public String toString() {
return String.format("[r=%d,c=%d]", r, c);
}
public int hashCode() {
return r + 1000 * c;
}
}
public Album(Document document, Cycle cycle) {
this.document = document;
setUndecorated(true);
addWindowFocusListener(this);
button2Arg = new HashMap<JButton,Argument>();
arg2Button = new HashMap<Argument,JButton>();
index2Argument = new HashMap<Index,Argument>();
index2Button = new HashMap<Index,JButton>();
argument2Index = new HashMap<Argument,Index>();
family = cycle.refresh(document.getSelection().getCurrent());
if (family.size()>1) {
rows = (int) Math.sqrt(family.size()); // Wide
columns = (int) Math.ceil(family.size()*1.0 / rows);
Ket.out.println(family.size() + "::rows, cols = " + rows + ", " + columns);
setLayout(new GridLayout(rows, columns));
buttonSetup();
new Thread(this).start();
}
}
@Override
public void run() {
KetPanel ketPanel = document.getKetPanel();
setLocationRelativeTo(ketPanel);
setVisible(true);
}
public void buttonSetup() {
// current -> {family}
//- int count = 0;
JPanel panel = null;
//- for (Argument a : family) {
for (int count=0; count<family.size(); count++) {
Argument a = family.get(count);
int i = count / columns;
int j = count % columns;
Box box = a.toBox(0L, document.getColourScheme());
box.setup(Box.DEFAULT_BOX_FONT_SIZE, new Offset(20, 20)); // Use a fixed icon size?
BufferedImage image = ImageTools.paintInnerBoxToImage(box, false, document.getColourScheme());
JButton button = new JButton();
button.setIcon(new ImageIcon(image));
//> button.setRolloverIcon(new ImageIcon()); // Highlighted or bold version?
button2Arg.put(button, a);
arg2Button.put(a, button);
Index index = new Index(i, j);
index2Argument.put(index, a);
argument2Index.put(a, index);
index2Button.put(index, button);
button.addActionListener(this);
button.addKeyListener(this);
if (j==0) {
if (panel!=null) {
add(panel);
}
panel = new JPanel();
panel.setLayout(new GridLayout(1, columns));
panel.add(button);
} else {
panel.add(button);
}
//- count += 1;
}
if (panel!=null) {
add(panel);
}
pack();
}
@Override
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
select(button2Arg.get(source));
}
public void select(Argument a) {
document.getKeyboardEventHandler().setToInitialState();
document.getSelection().replace(a);
document.getMathCollection().updateUndoStack();
document.updateAndRepaint();
done();
}
public void done() {
setVisible(false);
setHold(false);
dispose();
}
public void setHold(boolean hold) {
document.getKetPanel().setHold(hold);
}
/*
* Invoked when a key has been released.
*/
@Override
public void keyReleased(KeyEvent keyEvent) { }
/*
* Invoked when a key has been typed.
*/
@Override
public void keyTyped(KeyEvent keyEvent) { }
/*
* Invoked when a key has been pressed.
*/
@Override
public void keyPressed(KeyEvent keyEvent) {
int codeNumber = keyEvent.getKeyCode();
int modifier = keyEvent.getModifiers();
boolean ctrl = (modifier&InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK;
if ((codeNumber==KeyEvent.VK_C && ctrl) || codeNumber==KeyEvent.VK_ESCAPE) {
Ket.out.println("[cancel]");
done();
}
Argument selected = button2Arg.get((JButton) getFocusOwner()); // selected -> arg.
if (selected==null) {
Ket.out.println("[unknown selection]");
return;
}
Index index = argument2Index.get(selected);
switch (codeNumber) {
case KeyEvent.VK_ENTER:
case KeyEvent.VK_M: // Synonyms of enter as used elsewhere.
select(selected);
return;
case KeyEvent.VK_ESCAPE:
case KeyEvent.VK_Q:
case KeyEvent.VK_Z:
done();
return;
case KeyEvent.VK_J:
case KeyEvent.VK_DOWN:
JButton down = getButton(index.down().wrap());
if (down!=null) {
down.requestFocusInWindow();
}
return;
case KeyEvent.VK_K:
case KeyEvent.VK_UP:
JButton up = getButton(index.up().wrap());
if (up!=null) {
up.requestFocusInWindow();
}
return;
case KeyEvent.VK_H: // left
case KeyEvent.VK_LEFT:
case KeyEvent.VK_B:
case KeyEvent.VK_P: // previous
JButton left = getButton(index.left().bound());
if (left!=null) {
left.requestFocusInWindow();
}
return;
case KeyEvent.VK_L: // right
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_N: // next
case KeyEvent.VK_W:
JButton next = getButton(index.right().bound());
if (next!=null) {
next.requestFocusInWindow();
}
return;
default:
Ket.out.println("[unknown key]");
}
}
public void windowGainedFocus(WindowEvent e) {
// Do nothing.
}
public void windowLostFocus(WindowEvent e) {
done();
}
}