package net.sourceforge.fidocadj.macropicker;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.tree.*;
import net.sourceforge.fidocadj.circuit.CircuitPanel;
import net.sourceforge.fidocadj.circuit.controllers.ElementsEdtActions;
import net.sourceforge.fidocadj.export.ExportGraphic;
import net.sourceforge.fidocadj.geom.DrawingSize;
import net.sourceforge.fidocadj.geom.MapCoordinates;
import net.sourceforge.fidocadj.globals.Globals;
import net.sourceforge.fidocadj.toolbars.ChangeSelectionListener;
import net.sourceforge.fidocadj.librarymodel.LibraryModel;
import net.sourceforge.fidocadj.librarymodel.Library;
import net.sourceforge.fidocadj.librarymodel.Category;
import net.sourceforge.fidocadj.layermodel.LayerModel;
import net.sourceforge.fidocadj.librarymodel.event.LibraryListenerAdapter;
import net.sourceforge.fidocadj.librarymodel.event.LibraryListener;
import net.sourceforge.fidocadj.macropicker.model.MacroTreeModel;
import net.sourceforge.fidocadj.macropicker.model.MacroTreeNode;
import net.sourceforge.fidocadj.primitives.MacroDesc;
/** Library view component.<br>
Features:<BR>
Shows macros of libraries as tree and previews.<BR>
Notice selected macro to related components.<BR>
Provides interfaces of renaming, removing, moving and changing key for
library.<BR>
<pre>
This file is part of FidoCadJ.
FidoCadJ 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.
FidoCadJ 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 FidoCadJ. If not,
@see <a href=http://www.gnu.org/licenses/>http://www.gnu.org/licenses/</a>.
Copyright 2014 Kohta Ozaki, Davide Bucci
</pre>
@author Kohta Ozaki, Davide Bucci
*/
public class MacroTree extends JPanel
{
/** Indicates library */
public static final int LIBRARY = 0;
/** Indicates category */
public static final int CATEGORY = 1;
/** Indicates macro */
public static final int MACRO = 2;
// View components.
private ExpandableJTree treeComponent;
private SearchField searchField;
private CircuitPanel previewPanel;
private JScrollPane treeScrollPane;
// Models.
private LibraryModel libraryModel;
private LayerModel layerModel;
private MacroTreeModel macroTreeModel;
// A Listener for sending selected macro to CircuitPanel.
private ChangeSelectionListener selectionListener;
private ArrayList<ChangeListener> changeListeners;
private TreePath copyTarget = null;
private OperationPermissions permissionObject;
/** Constructor.
@param libraryModel library model. not null.
@param layerModel layer model. not null.
*/
public MacroTree(LibraryModel libraryModel, LayerModel layerModel)
{
this.libraryModel = libraryModel;
this.layerModel = layerModel;
initComponents();
}
/** Initialize view components and relate models.
*/
private void initComponents()
{
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
Box topBox = Box.createVerticalBox();
setLayout(new GridLayout(1, 0));
createListenerArray();
createTreeView();
createPreviewPanel();
createSearchField();
createPermissionObject();
createPopupMenu();
bindSearchField();
bindLibraryModel();
bindPreviewPanel();
topBox.add(searchField);
topBox.add(treeScrollPane);
splitPane.setTopComponent(topBox);
splitPane.setBottomComponent(previewPanel);
splitPane.setResizeWeight(0.9);
add(splitPane);
}
/** Create the array of change listeners.
*/
private void createListenerArray()
{
changeListeners = new ArrayList<ChangeListener>();
}
/** Add the provided change listener to the current pipeline.
@param l the change listener to add.
*/
public void addChangeListener(ChangeListener l)
{
changeListeners.add(l);
}
/** Remove a change listener from the pipeline.
@param l the change listener to remove.
*/
public void removeChangeListener(ChangeListener l)
{
changeListeners.remove(l);
}
/** Returns node type of selected.
@return int A constant of LIBRARY or CATEGORY or MACRO or -1(other).
*/
public int getSelectedType()
{
TreePath path = treeComponent.getSelectionPath();
int type;
if(path==null) return -1;
type = macroTreeModel.getNodeType(path);
switch(type) {
case MacroTreeModel.LIBRARY:
return LIBRARY;
case MacroTreeModel.CATEGORY:
return CATEGORY;
case MacroTreeModel.MACRO:
return MACRO;
default:
return -1;
}
}
/** Removes library.
@param library Library to remove.
*/
public void remove(Library library)
{
int result;
if(library==null) {
return;
}
result = JOptionPane.showConfirmDialog(null,
Globals.messages.getString("remove_library_confirm")+
library.getName() + "?",
Globals.messages.getString("remove_library"),
JOptionPane.YES_NO_OPTION);
if(result==JOptionPane.YES_OPTION) {
try {
libraryModel.remove(library);
} catch (LibraryModel.IllegalLibraryAccessException e) {
JOptionPane.showMessageDialog(null, e.getMessage(),
Globals.messages.getString("error"),
JOptionPane.ERROR_MESSAGE);
}
}
}
/** Removes category.
@param category Category to remove.
*/
public void remove(Category category)
{
int result;
if(category==null) {
return;
}
result=JOptionPane.showConfirmDialog(null,
Globals.messages.getString("remove_category_confirm")+
category.getName() + "?",
Globals.messages.getString("remove_category"),
JOptionPane.YES_NO_OPTION);
if(result==JOptionPane.YES_OPTION) {
try {
libraryModel.remove(category);
} catch (LibraryModel.IllegalLibraryAccessException e) {
JOptionPane.showMessageDialog(null, e.getMessage(),
Globals.messages.getString("error"),
JOptionPane.ERROR_MESSAGE);
}
}
}
/** Remove macro.
@param macro MacroDesc to remove.
*/
public void remove(MacroDesc macro)
{
int result;
if(macro==null) {
return;
}
result=JOptionPane.showConfirmDialog(null,
Globals.messages.getString("remove_macro_confirm")+
macro.name + "?",
Globals.messages.getString("remove_macro"),
JOptionPane.YES_NO_OPTION);
if(result==JOptionPane.YES_OPTION) {
try {
libraryModel.remove(macro);
} catch (LibraryModel.IllegalLibraryAccessException e) {
JOptionPane.showMessageDialog(null, e.getMessage(),
Globals.messages.getString("error"),
JOptionPane.ERROR_MESSAGE);
}
}
}
/** Renames macro.
@param macro MacroDesc to rename.
*/
public void rename(MacroDesc macro)
{
String newName;
if(macro==null) {
return;
}
newName = JOptionPane.showInputDialog(null,
Globals.messages.getString("new_macro_name"),
macro.name);
if(newName==null || newName.equals(macro.name)) {
return;
}
try {
libraryModel.rename(macro,newName);
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e.getMessage(),
Globals.messages.getString("error"),
JOptionPane.ERROR_MESSAGE);
}
}
/** Rename a category.
@param category Category to rename.
*/
public void rename(Category category)
{
String newName;
if(category==null) {
return;
}
newName = JOptionPane.showInputDialog(null,
Globals.messages.getString("new_category_name"),
category.getName());
if(newName==null || newName.equals(category.getName())) {
return;
}
try {
libraryModel.rename(category,newName);
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e.getMessage(),
Globals.messages.getString("error"),
JOptionPane.ERROR_MESSAGE);
}
}
/** Renames a library.
@param library Library to rename.
*/
public void rename(Library library)
{
String newName;
if(library==null) {
return;
}
newName = JOptionPane.showInputDialog(null,
Globals.messages.getString("new_library_name"),
library.getName());
if(newName==null || newName.equals(library.getName())) {
return;
}
try {
libraryModel.rename(library,newName);
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e.getMessage(),
Globals.messages.getString("error"),
JOptionPane.ERROR_MESSAGE);
}
}
/** Ask to the user to change the key of the given macro.
@param macro the macro to be modified.
*/
public void changeKey(MacroDesc macro)
{
String oldKey;
String newKey;
if(macro==null) {
return;
}
int n = JOptionPane.showConfirmDialog(null,
Globals.messages.getString("ChangeKeyWarning"),
Globals.messages.getString("RenKey"),
JOptionPane.YES_NO_OPTION);
if(n==JOptionPane.NO_OPTION) {
return;
}
oldKey = LibraryModel.getPlainMacroKey(macro);
newKey = JOptionPane.showInputDialog(null,
Globals.messages.getString("Key"),
oldKey);
if(newKey==null || newKey.equals(oldKey)) {
return;
}
try {
libraryModel.changeKey(macro,newKey);
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e.getMessage(),
Globals.messages.getString("error"),
JOptionPane.ERROR_MESSAGE);
}
}
/** Called during a copy operation. Makes sort that the currently selected
node will become the target for the copy.
*/
public void setSelectedNodeToCopyTarget()
{
copyTarget = treeComponent.getSelectionPath();
}
/** Paste into the currently selected node.
*/
public void pasteIntoSelectedNode()
{
if(copyTarget==null){
return;
}
int copyTargetType = macroTreeModel.getNodeType(copyTarget);
int selectedNodeType = getSelectedType();
if(copyTargetType==MacroTreeModel.CATEGORY &&
selectedNodeType==LIBRARY)
{
copyCategoryIntoLibrary();
} else if(copyTargetType==MacroTreeModel.MACRO &&
selectedNodeType==CATEGORY)
{
copyMacroIntoCategory();
}
copyTarget = null;
updateOperationPermission();
}
/** Copy a category (which has been previously selected).
*/
private void copyCategoryIntoLibrary()
{
Category targetCategory;
Library destLibrary;
destLibrary = getSelectedLibrary();
targetCategory = macroTreeModel.getCategory(copyTarget);
libraryModel.copy(targetCategory,destLibrary);
}
/** Copy a macro into a selected category.
*/
private void copyMacroIntoCategory()
{
MacroDesc targetMacro;
Category destCategory;
destCategory = getSelectedCategory();
targetMacro = macroTreeModel.getMacro(copyTarget);
libraryModel.copy(targetMacro,destCategory);
}
/** Create the object describing permissions.
*/
private void createPermissionObject()
{
permissionObject = new OperationPermissions();
}
/** Get a permission description object.
@return the permission descriptor.
*/
public OperationPermissions getOperationPermission()
{
return permissionObject;
}
/** Update the permission descriptor.
*/
private void updateOperationPermission()
{
TreePath selectedPath;
int selectedType;
int copyTargetType;
Library lib = getSelectedLibrary();
Category cat = getSelectedCategory();
MacroDesc macro = getSelectedMacro();
selectedPath = treeComponent.getSelectionPath();
permissionObject.disableAll();
selectedType = getSelectedType();
//copy permission
if(selectedType==CATEGORY || selectedType==MACRO) {
permissionObject.copyAvailable = true;
}
if(!macroTreeModel.isSearchMode()) {
//paste permission
if(copyTarget!=null && lib!=null && !lib.isStdLib()){
copyTargetType = macroTreeModel.getNodeType(copyTarget);
if(copyTargetType==MacroTreeModel.CATEGORY &&
selectedType==LIBRARY)
{
permissionObject.pasteAvailable = true;
} else if (copyTargetType==MacroTreeModel.MACRO &&
selectedType==CATEGORY)
{
permissionObject.pasteAvailable = true;
}
}
//rename/renkey permission
if(lib!=null && !lib.isStdLib()) {
permissionObject.renameAvailable = true;
permissionObject.removeAvailable = true;
if(selectedType==MACRO) {
permissionObject.renKeyAvailable = true;
}
}
}
}
/** Create the popup menu.
*/
private void createPopupMenu()
{
MacroTreePopupMenu popupMenu = new MacroTreePopupMenu(this);
treeComponent.setComponentPopupMenu(popupMenu);
addChangeListener(popupMenu);
}
/** Relate preview panel and JTree selection model.<br>
Relate preview panel and library model.
*/
private void bindPreviewPanel()
{
// Relate with JTree.
treeComponent.getSelectionModel().
addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e)
{
MacroDesc md;
md = macroTreeModel.getMacro(e.getPath());
if(md!=null) {
previewPanel.getParserActions().parseString(
new StringBuffer(md.description));
MapCoordinates m =
DrawingSize.calculateZoomToFit(
previewPanel.dmp,
previewPanel.getSize().width*85/100,
previewPanel.getSize().height*85/100, true);
m.setXCenter(-m.getXCenter()+10);
m.setYCenter(-m.getYCenter()+10);
previewPanel.setMapCoordinates(m);
previewPanel.repaint();
}
}
});
// Relate with library model.
LibraryListener l = new LibraryListenerAdapter() {
public void libraryLoaded()
{
previewPanel.dmp.setLibrary(libraryModel.getAllMacros());
}
};
libraryModel.addLibraryListener(l);
}
/** Sets the listener for selecting macro.
@param l the new listener. It should not be null.
*/
public void setSelectionListener(ChangeSelectionListener l)
{
selectionListener=l;
treeComponent.getSelectionModel().
addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e)
{
MacroDesc md;
if (selectionListener!=null) {
md = macroTreeModel.getMacro(e.getPath());
if(md==null){
selectionListener.setSelectionState(
ElementsEdtActions.SELECTION, "");
} else {
selectionListener.setSelectionState(
ElementsEdtActions.MACRO, md.key);
}
}
}
});
}
/**
* Relate tree model and library model.<br>
*/
private void bindLibraryModel()
{
macroTreeModel = new MacroTreeModel(libraryModel);
treeComponent.setModel((TreeModel)macroTreeModel);
libraryModel.addLibraryListener(macroTreeModel);
}
/**
* Create JTree with scroll pane.
*/
private void createTreeView()
{
treeComponent = new ExpandableJTree();
treeComponent.setCellRenderer(new MacroTreeCellRenderer());
treeComponent.getSelectionModel().setSelectionMode(
TreeSelectionModel.SINGLE_TREE_SELECTION);
treeScrollPane = new JScrollPane(treeComponent);
treeScrollPane.setMinimumSize(new Dimension(150, 100));
treeScrollPane.setPreferredSize(new Dimension(350, 600));
treeComponent.getSelectionModel().addTreeSelectionListener(
new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e)
{
updateOperationPermission();
for(ChangeListener l:changeListeners) {
l.stateChanged(new ChangeEvent(this));
}
}
});
}
/**
* Create search bar.
*/
private void createSearchField()
{
// I think this must be initialized with localized label string
// means search.
// searchField = new SearchField(Globals.messages.getString("Search"));
searchField = new SearchField();
}
/**
* Create preview panel.
*/
private void createPreviewPanel()
{
previewPanel = new CircuitPanel(false);
previewPanel.dmp.setLayers(layerModel.getAllLayers());
previewPanel.dmp.setLibrary(libraryModel.getAllMacros());
previewPanel.setGridVisibility(false);
previewPanel.setMinimumSize(new Dimension(150, 100));
previewPanel.setPreferredSize(new Dimension(350, 300));
}
/**
* Relate document model of search bar and tree model.
*/
private void bindSearchField()
{
DocumentListener searchFieldListener = new DocumentListener() {
@Override
public void changedUpdate(DocumentEvent e)
{
// Nothing to do
}
public void removeUpdate(DocumentEvent e)
{
setWord(e);
}
public void insertUpdate(DocumentEvent e)
{
setWord(e);
}
private void setWord(DocumentEvent e)
{
String word = null;
Document d = e.getDocument();
try {
word = d.getText(0, d.getLength());
} catch (BadLocationException ex) {
word = "";
System.out.println(
"[SearchFieldListener] BadLocationException");
} finally {
if("".equals(word)) {
treeComponent.collapseOnce();
} else {
treeComponent.expandOnce();
}
macroTreeModel.setFilterWord(word);
}
}
};
searchField.getDocument().addDocumentListener(searchFieldListener);
searchField.addKeyListener(new KeyListener() {
@Override
public void keyPressed(KeyEvent e)
{
// Nothing to do
}
@Override
public void keyReleased(KeyEvent e)
{
if(e.getKeyCode() == KeyEvent.VK_ENTER) {
if(e.isShiftDown()) {
treeComponent.selectPrevLeaf();
} else {
treeComponent.selectNextLeaf();
}
}
}
@Override
public void keyTyped(KeyEvent e)
{
// Nothing to do
}
});
}
/** Set the current model for the library.
@param libraryModel the library model to employ.
*/
public void setLibraryModel(LibraryModel libraryModel)
{
this.libraryModel = libraryModel;
previewPanel.dmp.setLibrary(libraryModel.getAllMacros());
bindSearchField();
}
/** Set the current layer model.
@param layerModel the layer model.
*/
public void setLayerModel(LayerModel layerModel)
{
this.layerModel = layerModel;
previewPanel.dmp.setLayers(layerModel.getAllLayers());
}
/** Get the currently selected macro.
@return the selected macro, or null if no macro is selected.
*/
public MacroDesc getSelectedMacro()
{
TreePath path = treeComponent.getSelectionPath();
if(path==null) return null;
return macroTreeModel.getMacro(path);
}
/** Get the currently selected category.
@return the selected category, or null if no category is selected.
*/
public Category getSelectedCategory()
{
TreePath path = treeComponent.getSelectionPath();
if(path==null) return null;
return macroTreeModel.getCategory(path);
}
/** Get the currently selected library.
@return the selected library, or null if no library is selected.
*/
public Library getSelectedLibrary()
{
TreePath path = treeComponent.getSelectionPath();
if(path==null) return null;
return macroTreeModel.getLibrary(path);
}
}