/*
GeoGebra - Dynamic Mathematics for Everyone
http://www.geogebra.org
This file is part of GeoGebra.
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.
*/
package org.geogebra.desktop.gui.toolbar;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.SystemColor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import org.geogebra.common.gui.toolbar.ToolBar;
import org.geogebra.common.gui.toolbar.ToolbarItem;
import org.geogebra.desktop.gui.GuiManagerD;
import org.geogebra.desktop.gui.layout.DockPanelD;
import org.geogebra.desktop.main.AppD;
import org.geogebra.desktop.main.LocalizationD;
/**
* Toolbar configuration panel.
*
* @author Markus Hohenwarter, based on a dialog from geonext.de
*
*/
public class ToolbarConfigPanel extends JPanel
implements ActionListener, TreeExpansionListener {
private static final long serialVersionUID = 1L;
private static final int SCROLL_PANEL_WIDTH = 300;
private static final int SCROLL_PANEL_HEIGHT = 400;
private DockPanelD dockPanel;
private JButton insertButton;
private JButton moveUpButton;
private JButton moveDownButton;
private JButton deleteButton;
private JTree tree;
private JScrollPane configScrollPane;
private JScrollPane modeScrollPane;
private JPanel selectionPanel;
private JList toolList;
private DefaultListModel toolListModel;
private AppD app;
private LocalizationD loc;
/**
* Creates new toolbar config panel.
*
* @param app
* application
*/
public ToolbarConfigPanel(AppD app) {
super();
this.app = app;
this.loc = app.getLocalization();
selectionPanel = new JPanel();
selectionPanel.setLayout(new BorderLayout(5, 5));
selectionPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
setLayout(new BorderLayout(5, 5));
tree = generateTree();
toolListModel = new DefaultListModel();
toolList = new JList(toolListModel);
setToolbar(null,
((GuiManagerD) app.getGuiManager()).getToolbarDefinition());
configScrollPane = new JScrollPane(tree);
configScrollPane.setHorizontalScrollBarPolicy(
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
configScrollPane.setVerticalScrollBarPolicy(
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
// configScrollPane.setSize(150, 150);
JPanel scrollSpacePanel = new JPanel();
scrollSpacePanel.setLayout(new BorderLayout(0, 0));
scrollSpacePanel.setBorder(new EmptyBorder(3, 5, 3, 5));
scrollSpacePanel.add(configScrollPane, BorderLayout.CENTER); //
JPanel scrollPanel = new JPanel();
scrollPanel.setLayout(new BorderLayout(0, 0));
scrollPanel.setBorder(new TitledBorder(loc.getMenu("Toolbar")));
scrollPanel.add(scrollSpacePanel, BorderLayout.CENTER);
scrollPanel.setPreferredSize(
new Dimension(SCROLL_PANEL_WIDTH, SCROLL_PANEL_HEIGHT));
//
selectionPanel.add(scrollPanel, loc.borderWest());
//
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.Y_AXIS));
buttonPanel.add(Box.createVerticalGlue());
insertButton = new JButton("< " + loc.getPlain("Insert"));
insertButton.addActionListener(this);
insertButton.setAlignmentX(CENTER_ALIGNMENT);
buttonPanel.add(insertButton);
buttonPanel.add(Box.createVerticalStrut(10));
deleteButton = new JButton(loc.getPlain("Remove") + " >");
deleteButton.addActionListener(this);
deleteButton.setAlignmentX(CENTER_ALIGNMENT);
buttonPanel.add(deleteButton);
buttonPanel.add(Box.createVerticalGlue());
selectionPanel.add(buttonPanel, BorderLayout.CENTER);
//
JPanel upDownPanel = new JPanel();
moveUpButton = new JButton("\u25b2 " + loc.getPlain("Up"));
moveUpButton.addActionListener(this);
upDownPanel.add(moveUpButton);
//
moveDownButton = new JButton("\u25bc " + loc.getPlain("Down"));
moveDownButton.addActionListener(this);
upDownPanel.add(moveDownButton);
scrollPanel.add(upDownPanel, BorderLayout.SOUTH);
//
JPanel buttonAllPanel = new JPanel(new BorderLayout());
buttonAllPanel.add(buttonPanel, BorderLayout.NORTH);
JPanel tempPanel = new JPanel();
tempPanel.setLayout(new BoxLayout(tempPanel, BoxLayout.Y_AXIS));
tempPanel.add(Box.createRigidArea(new Dimension(10, 150)));
tempPanel.add(buttonAllPanel);
tempPanel.add(Box.createVerticalGlue());
selectionPanel.add(tempPanel, BorderLayout.CENTER);
JPanel modePanel = new JPanel();
modePanel.setLayout(new BorderLayout(0, 0));
modePanel.setBorder(new TitledBorder(loc.getMenu("Tools")));
ListSelectionModel lsm = toolList.getSelectionModel();
lsm.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
toolList.setBackground(SystemColor.text);
modeScrollPane = new JScrollPane(toolList);
modeScrollPane.setHorizontalScrollBarPolicy(
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
modeScrollPane.setVerticalScrollBarPolicy(
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
toolList.setCellRenderer(new ModeCellRenderer(app));
toolList.setSelectedIndex(0);
//
//
JPanel modeSpacePanel = new JPanel();
modeSpacePanel.setLayout(new BorderLayout(0, 0));
modeSpacePanel.setBorder(new EmptyBorder(3, 5, 3, 5));
modeSpacePanel.add("Center", modeScrollPane);
modePanel.add("Center", modeSpacePanel);
modePanel.setPreferredSize(
new Dimension(SCROLL_PANEL_WIDTH, SCROLL_PANEL_HEIGHT));
selectionPanel.add("East", modePanel);
add("Center", selectionPanel);
try {
tree.setSelectionRow(1);
} catch (Exception exc) {
tree.setSelectionRow(0);
}
}
/**
* Handles remove, add and up, down buttons.
*/
@Override
public void actionPerformed(ActionEvent e) {
// get selected node in tree
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
TreePath selPath = tree.getSelectionPath();
if (selPath == null) {
tree.setSelectionRow(0); // take root if nothing is selected
selPath = tree.getSelectionPath();
}
DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
DefaultMutableTreeNode selNode = (DefaultMutableTreeNode) selPath
.getLastPathComponent();
// remember row number
int selRow = tree.getRowForPath(selPath);
DefaultMutableTreeNode parentNode;
if (selNode == root) { // root is selected
parentNode = selNode;
} else {
parentNode = (DefaultMutableTreeNode) selNode.getParent();
}
int childIndex = parentNode.getIndex(selNode);
Object src = e.getSource();
// DELETE
if (src == deleteButton) {
if (selRow > 0) { // not root
if (selNode.isLeaf()) {
Object userOb = selNode.getUserObject();
if (userOb == null) {
userOb = ((DefaultMutableTreeNode) selNode
.getFirstChild()).getUserObject();
} else {
toolListModel.addElement(userOb);
}
} else {
for (int i = 0; i < selNode.getChildCount(); i++) {
Integer mode = (Integer) ((DefaultMutableTreeNode) selNode
.getChildAt(i)).getUserObject();
if (mode != null
&& mode.intValue() != ToolBar.SEPARATOR) {
toolListModel.addElement(mode);
}
}
}
// not move mode: delete node
model.removeNodeFromParent(selNode);
// remove empty menu too
if (parentNode.getChildCount() == 0) {
if (!parentNode.isRoot()) {
model.removeNodeFromParent(parentNode);
selRow--;
}
}
sortToolList();
// select node at same row or above
if (selRow >= tree.getRowCount()) {
selRow--;
}
tree.setSelectionRow(selRow);
}
}
// INSERT
else if (src == insertButton) {
childIndex++;
boolean didInsert = false;
Object[] tools = toolList.getSelectedValuesList().toArray();
for (int i = 0; i < tools.length; i++) {
// check if too is already there
Integer modeInt = (Integer) tools[i];
if (modeInt.intValue() > -1
&& containsTool(root, (Integer) tools[i])) {
continue;
}
DefaultMutableTreeNode newNode;
if (parentNode == root) {
if (modeInt.intValue() > -1) {
// parent is root: create new submenu
newNode = new DefaultMutableTreeNode();
newNode.add(new DefaultMutableTreeNode(modeInt));
} else {
continue; // no seperator in root
}
} else {
// parent is submenu
newNode = new DefaultMutableTreeNode(modeInt);
}
model.insertNodeInto(newNode, parentNode, childIndex++);
didInsert = true;
// remove node from list of unused tools if the node is not a
// separator
if (modeInt.intValue() > -1) {
toolListModel.removeElement(modeInt);
}
}
if (didInsert) {
// make sure that root is expanded
tree.expandPath(new TreePath(model.getRoot()));
// select first inserted node
tree.setSelectionRow(++selRow);
tree.scrollRowToVisible(selRow);
configScrollPane.getHorizontalScrollBar().setValue(0); // scroll
// to
// left
// sort tool list
sortToolList();
}
}
// UP
else if (src == moveUpButton) {
if (selNode == root) {
return;
}
if (parentNode.getChildBefore(selNode) != null) {
model.removeNodeFromParent(selNode);
model.insertNodeInto(selNode, parentNode, --childIndex);
tree.setSelectionRow(--selRow);
}
}
// DOWN
else if (src == moveDownButton) {
if (selNode == root) {
return;
}
if (parentNode.getChildAfter(selNode) != null) {
model.removeNodeFromParent(selNode);
model.insertNodeInto(selNode, parentNode, ++childIndex);
tree.setSelectionRow(++selRow);
}
}
}
private boolean containsTool(DefaultMutableTreeNode node, Integer mode) {
// compare modes
Object ob = node.getUserObject();
if (ob != null && mode.compareTo((Integer) ob) == 0) {
return true;
}
if (node.getChildCount() >= 0) {
for (Enumeration<DefaultMutableTreeNode> e = node.children(); e
.hasMoreElements();) {
DefaultMutableTreeNode n = e.nextElement();
if (containsTool(n, mode)) {
return true;
}
}
}
return false;
}
/**
* Inits the toolbar tree in this panel to show the given toolbar definition
* string.
*
* @param dockPanel
* dock panel
* @param toolbarDefinition
* toolbar as string (sequence of numbers and delimiters)
*/
public void setToolbar(DockPanelD dockPanel, String toolbarDefinition) {
this.dockPanel = dockPanel;
// create new tree model
Vector<ToolbarItem> toolVec;
try {
toolVec = ToolBar.parseToolbarString(toolbarDefinition);
} catch (Exception e) {
return;
}
DefaultTreeModel model = new DefaultTreeModel(
generateRootNode(toolVec));
tree.setModel(model);
collapseAllRows();
tree.setRowHeight(-1);
Vector<Integer> allTools = generateToolsVector(
ToolbarD.getAllTools(app));
Vector<Integer> usedTools = generateToolsVector(toolbarDefinition);
toolListModel.clear();
toolListModel.addElement(ToolBar.SEPARATOR); // always display the
// separator in the
// tools list
for (Iterator<Integer> iter = allTools.iterator(); iter.hasNext();) {
Integer next = iter.next();
if (!usedTools.contains(next)) {
toolListModel.addElement(next);
}
}
}
public void apply() {
if (dockPanel != null) {
String current = getToolBarString();
dockPanel.setToolbarString(current);
// GuiManagerD gm = ((GuiManagerD) app.getGuiManager());
// gm.setToolBarDefinition(current);
// gm.updateToolbar();
} else {
((GuiManagerD) app.getGuiManager())
.setToolBarDefinition(getToolBarString());
}
}
/**
* Reset the current toolbar to its default state.
*/
public void resetDefaultToolbar() {
if (dockPanel != null) {
setToolbar(dockPanel, dockPanel.getDefaultToolbarString());
} else {
setToolbar(null, ((GuiManagerD) app.getGuiManager())
.getDefaultToolbarString());
}
}
/**
* Returns the custom toolbar created with this panel as a String. Separator
* ("||" between menus, "," in menu), New menu starts with "|"
*
* @return toolbar as string
*/
public String getToolBarString() {
StringBuilder sb = new StringBuilder();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel()
.getRoot();
for (int i = 0; i < root.getChildCount(); i++) {
DefaultMutableTreeNode menu = (DefaultMutableTreeNode) root
.getChildAt(i);
if (menu.getChildCount() == 0) { // new menu with separator
sb.append("|| ");
} else if (i > 0 && !sb.toString().endsWith("|| ")) {
sb.append("| ");
}
for (int j = 0; j < menu.getChildCount(); j++) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) menu
.getChildAt(j);
int mode = ((Integer) node.getUserObject()).intValue();
if (mode < 0) {
sb.append(", ");
} else { // mode number
sb.append(mode);
sb.append(" ");
}
}
}
return sb.toString().trim();
}
/**
* Collapses all rows
*/
public void collapseAllRows() {
int z = tree.getRowCount();
for (int i = z; i > 0; i--) {
tree.collapseRow(i);
}
}
/**
* @param toolbarDefinition
* toolbar definition string (see EuclidianConstants)
* @return vector of menus (vectors of ints) and separators (ints)
*
*/
public Vector<Integer> generateToolsVector(String toolbarDefinition) {
Vector<Integer> vector = new Vector<Integer>();
// separator
vector.add(ToolBar.SEPARATOR);
// get default toolbar as nested vectors
Vector<ToolbarItem> defTools = null;
try {
defTools = ToolBar.parseToolbarString(toolbarDefinition);
} catch (Exception e) {
return new Vector<Integer>();
}
for (int i = 0; i < defTools.size(); i++) {
ToolbarItem element = defTools.get(i);
if (element.getMenu() != null) {
Vector<Integer> menu = element.getMenu();
for (int j = 0; j < menu.size(); j++) {
Integer modeInt = menu.get(j);
int mode = modeInt.intValue();
if (mode != -1) {
vector.add(modeInt);
}
}
} else {
Integer modeInt = element.getMode();
int mode = modeInt.intValue();
if (mode != -1) {
vector.add(modeInt);
}
}
}
return vector;
}
/**
*
*/
private JTree generateTree() {
final JTree jTree = new JTree() {
private static final long serialVersionUID = 1L;
@Override
protected void setExpandedState(TreePath path, boolean state) {
// Ignore all collapse requests of root
if (path != getPathForRow(0)) {
super.setExpandedState(path, state);
}
}
};
jTree.setCellRenderer(new ModeCellRenderer(app));
jTree.getSelectionModel()
.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
jTree.putClientProperty("JTree.lineStyle", "Angled");
jTree.addTreeExpansionListener(this);
return jTree;
}
/**
* @param toolbarModes
* list of menus and separators
* @return toolbar as DefaultMutableTreeNode
*
*/
public DefaultMutableTreeNode generateRootNode(
Vector<ToolbarItem> toolbarModes) {
DefaultMutableTreeNode node = new DefaultMutableTreeNode();
for (int i = 0; i < toolbarModes.size(); i++) {
ToolbarItem ob = toolbarModes.get(i);
if (ob.getMenu() != null) {
Vector<Integer> menu = ob.getMenu();
DefaultMutableTreeNode sub = new DefaultMutableTreeNode();
for (int j = 0; j < menu.size(); j++) {
sub.add(new DefaultMutableTreeNode(menu.get(j)));
}
node.add(sub);
} else {
node.add(new DefaultMutableTreeNode(ob.getMode()));
}
}
return node;
}
/**
* Add an item to the tool list
*
* TODO Rename method TODO Use this method to insert new items into the
* model TODO Use the default toolbar vector to keep the standard sorting
*
* @author Florian Sonner
* @version 2008-10-22
*/
private void sortToolList() {
/*
* int numItems = toolListModel.getSize();
*
* if (numItems < 2) return;
*
* // copy list data into an array Integer[] a = new Integer[numItems];
* for (int i = 0; i < numItems; ++i) { a[i] = (Integer)
* toolListModel.getElementAt(i); }
*
* // sort array.. Arrays.sort(a);
*
* // copy the sorted array back into the model for (int i = 0; i <
* numItems; ++i) { toolListModel.setElementAt(a[i], i); }
*/
}
/**
*
*/
@Override
public void treeCollapsed(TreeExpansionEvent event) {/*
* do nothing
*/
}
/**
*
*/
@Override
public void treeExpanded(TreeExpansionEvent event) {
/*
* tabbed.invalidate(); tabbed.validateTree();
*/
}
/**
* @param e
* list selection event
*/
public void valueChanged(ListSelectionEvent e) {/*
* do nothing
*/
}
}