blob: 858a07c83db9e1aa5f6aba038243198289d8c523 [file] [log] [blame]
/*****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* https://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 org.apache.cayenne.modeler.editor;
import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
import org.apache.cayenne.map.CallbackDescriptor;
import org.apache.cayenne.map.CallbackMap;
import org.apache.cayenne.map.LifecycleEvent;
import org.apache.cayenne.modeler.Application;
import org.apache.cayenne.modeler.ProjectController;
import org.apache.cayenne.modeler.action.CopyCallbackMethodAction;
import org.apache.cayenne.modeler.action.CreateCallbackMethodAction;
import org.apache.cayenne.modeler.action.CutCallbackMethodAction;
import org.apache.cayenne.modeler.action.PasteAction;
import org.apache.cayenne.modeler.action.RemoveCallbackMethodAction;
import org.apache.cayenne.modeler.event.CallbackMethodEvent;
import org.apache.cayenne.modeler.event.CallbackMethodListener;
import org.apache.cayenne.modeler.event.TablePopupHandler;
import org.apache.cayenne.modeler.pref.TableColumnPreferences;
import org.apache.cayenne.modeler.util.CayenneAction;
import org.apache.cayenne.modeler.util.CayenneTable;
import org.apache.cayenne.modeler.util.ModelerUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.ListSelectionModel;
import javax.swing.TransferHandler;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.table.JTableHeader;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.List;
/**
* Base abstract class for all callback methods editing tabs Contains logic for callback
* methods displaying, creating, removing, editing, reordering
*
*/
public abstract class AbstractCallbackMethodsTab extends JPanel {
private static Logger logger = LoggerFactory.getLogger(AbstractCallbackMethodsTab.class);
/**
* mediator instance
*/
ProjectController mediator;
/**
* toolbar for actions
*/
protected JToolBar toolBar;
/**
* panel for displaying callback method tables
*/
protected JPanel auxPanel;
/**
* preferences for the callback methods table
*/
protected TableColumnPreferences tablePreferences;
/**
* list of callback method types
*/
protected CallbackType[] callbackTypes = {
new CallbackType(LifecycleEvent.POST_ADD),
new CallbackType(LifecycleEvent.PRE_PERSIST),
new CallbackType(LifecycleEvent.POST_PERSIST),
new CallbackType(LifecycleEvent.PRE_UPDATE),
new CallbackType(LifecycleEvent.POST_UPDATE),
new CallbackType(LifecycleEvent.PRE_REMOVE),
new CallbackType(LifecycleEvent.POST_REMOVE),
new CallbackType(LifecycleEvent.POST_LOAD),
};
/**
* list of tables
*/
protected CayenneTable[] tables = new CayenneTable[callbackTypes.length];
protected JPopupMenu popupMenu;
/**
* constructor
*
* @param mediator mediator instance
*/
public AbstractCallbackMethodsTab(ProjectController mediator) {
this.mediator = mediator;
init();
initController();
}
/**
* @return CallbackMap with callback methods
*/
protected abstract CallbackMap getCallbackMap();
/**
* @return create callback method action
*/
protected CayenneAction getCreateCallbackMethodAction() {
Application app = Application.getInstance();
return app.getActionManager().getAction(CreateCallbackMethodAction.class);
}
/**
* @return remove callback method action
*/
protected RemoveCallbackMethodAction getRemoveCallbackMethodAction() {
Application app = Application.getInstance();
return app.getActionManager().getAction(RemoveCallbackMethodAction.class);
}
/**
* @return copy callback method action
*/
protected CopyCallbackMethodAction getCopyCallbackMethodAction() {
Application app = Application.getInstance();
return app.getActionManager().getAction(CopyCallbackMethodAction.class);
}
/**
* @return cut callback method action
*/
protected CutCallbackMethodAction getCutCallbackMethodAction() {
Application app = Application.getInstance();
return app.getActionManager().getAction(CutCallbackMethodAction.class);
}
/**
* @return paste callback method action
*/
protected PasteAction getPasteCallbackMethodAction() {
Application app = Application.getInstance();
return app.getActionManager().getAction(PasteAction.class);
}
/**
* GUI components initialization
*/
protected void init() {
this.setLayout(new BorderLayout());
toolBar = new JToolBar();
toolBar.setFloatable(false);
toolBar.add(getRemoveCallbackMethodAction().buildButton());
toolBar.addSeparator(new Dimension(10, 0));
toolBar.add(getCopyCallbackMethodAction().buildButton(1));
toolBar.add(getCutCallbackMethodAction().buildButton(2));
toolBar.add(getPasteCallbackMethodAction().buildButton(3));
add(toolBar, BorderLayout.NORTH);
auxPanel = new JPanel();
auxPanel.setOpaque(false);
auxPanel.setLayout(new BorderLayout());
popupMenu = createPopup();
createTables();
add(new JScrollPane(auxPanel), BorderLayout.CENTER);
}
/**
* Inits the {@link TableColumnPreferences} object according to callback table name.
*/
protected abstract void initTablePreferences();
/**
* listeners initialization
*/
protected void initController() {
mediator.addCallbackMethodListener(new CallbackMethodListener() {
public void callbackMethodChanged(CallbackMethodEvent e) {
rebuildTables();
}
public void callbackMethodAdded(CallbackMethodEvent e) {
rebuildTables();
selectAdded();
}
public void callbackMethodRemoved(CallbackMethodEvent e) {
int row = -1, i;
for(i = 0; i < callbackTypes.length; i++) {
if(callbackTypes[i] == getSelectedCallbackType()) {
row = tables[i].getSelectedRow();
break;
}
}
rebuildTables();
if(row == tables[i].getRowCount()) {
row--;
}
if (row < 0) {
return;
}
tables[i].changeSelection(row, 0, false, false);
}
});
for (CayenneTable table : tables) {
mediator.getApplication().getActionManager().setupCutCopyPaste(
table,
CutCallbackMethodAction.class,
CopyCallbackMethodAction.class);
}
}
/**
* rebuilds table content
*/
protected void rebuildTables() {
CallbackMap callbackMap = getCallbackMap();
for(int i = 0; i < callbackTypes.length; i++) {
CallbackType callbackType = callbackTypes[i];
List<String> methods = new ArrayList<>();
CallbackDescriptor descriptor = null;
if (callbackMap != null && callbackType != null) {
descriptor = callbackMap.getCallbackDescriptor(callbackType.getType());
methods.addAll(descriptor.getCallbackMethods());
}
CallbackDescriptorTableModel model = new CallbackDescriptorTableModel(
mediator,
this,
methods,
descriptor,
callbackType);
tables[i].setModel(model);
}
for (CayenneTable table : tables) {
tablePreferences.bind(table, null, null, null);
}
}
private void createTables() {
FormLayout formLayout = new FormLayout("left:pref");
DefaultFormBuilder builder = new DefaultFormBuilder(formLayout);
int index = 0;
for(CallbackType callbackType : callbackTypes) {
tables[index] = createTable(callbackType);
builder.append(createTablePanel(tables[index++]));
}
initTablePreferences();
auxPanel.add(builder.getPanel(), BorderLayout.CENTER);
validate();
}
private CayenneTable createTable(CallbackType callbackType) {
final CayenneTable cayenneTable = new CayenneTable();
// drag-and-drop initialization
cayenneTable.setDragEnabled(true);
cayenneTable.setSortable(false);
cayenneTable.setRowHeight(25);
cayenneTable.setRowMargin(3);
cayenneTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
cayenneTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
cayenneTable.setTransferHandler(new CallbackImportableHandler(cayenneTable));
cayenneTable.getSelectionModel().addListSelectionListener(new CallbackListSelectionListener(cayenneTable));
cayenneTable.getColumnModel().addColumnModelListener(new CallbackTableColumnModelListener(cayenneTable));
cayenneTable.getTableHeader().addMouseListener(new CallbackMouseAdapter(cayenneTable));
cayenneTable.getTableHeader().addMouseMotionListener(new CallbackMouseMotionListener(cayenneTable));
TablePopupHandler.install(cayenneTable, popupMenu);
addButtonAtHeader(
cayenneTable,
getCreateCallbackMethodAction().buildButton(),
new ButtonListener(callbackType),
ModelerUtil.buildIcon("icon-create-method.png"));
return cayenneTable;
}
private JPopupMenu createPopup() {
JPopupMenu popup = new JPopupMenu();
popup.add(getRemoveCallbackMethodAction().buildMenu());
popup.addSeparator();
popup.add(getCopyCallbackMethodAction().buildMenu());
popup.add(getCutCallbackMethodAction().buildMenu());
popup.add(getPasteCallbackMethodAction().buildMenu());
return popup;
}
private JPanel createTablePanel(final CayenneTable cayenneTable) {
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add(cayenneTable.getTableHeader(), BorderLayout.NORTH);
panel.add(cayenneTable, BorderLayout.CENTER);
return panel;
}
private void addButtonAtHeader(JTable table, JButton button, ActionListener buttonListener, ImageIcon buttonIcon){
PanelBuilder builder = new PanelBuilder(new FormLayout("left:10dlu, 2dlu", "center:14dlu"));
CellConstraints cc = new CellConstraints();
button.setIcon(buttonIcon);
button.setOpaque(false);
button.setBorderPainted(false);
button.setContentAreaFilled(false);
button.addActionListener(buttonListener);
builder.add(button, cc.xy(1, 1));
JPanel buttonPanel = builder.getPanel();
buttonPanel.setOpaque(false);
JTableHeader header = table.getTableHeader();
header.setLayout(new BorderLayout());
header.setReorderingAllowed(false);
header.setPreferredSize(new Dimension(150, 22));
header.add(buttonPanel, BorderLayout.EAST);
}
class ButtonListener implements ActionListener {
private CallbackType callbackType;
public ButtonListener(CallbackType callbackType) {
this.callbackType = callbackType;
}
public void actionPerformed(ActionEvent e) {
mediator.setCurrentCallbackType(callbackType);
}
}
protected final CallbackType getSelectedCallbackType() {
return mediator.getCurrentCallbackType();
}
private void selectAdded() {
for(int i = 0; i < callbackTypes.length; i++) {
if(callbackTypes[i] == getSelectedCallbackType()) {
if (tables[i].editCellAt(
tables[i].getRowCount() - 1,
CallbackDescriptorTableModel.METHOD_NAME)
&& tables[i].getEditorComponent() != null) {
tables[i].changeSelection(tables[i].getRowCount() - 1, 0, false, false);
tables[i].editCellAt(tables[i].getRowCount() - 1, 0);
tables[i].getCellEditor().stopCellEditing();
return;
}
}
}
}
private void unselectAll() {
for(int i = 0; i < callbackTypes.length; i++) {
if(tables[i].getCellEditor() != null) {
tables[i].getCellEditor().stopCellEditing();
}
tables[i].clearSelection();
}
}
private class CallbackImportableHandler extends TransferHandler {
private CayenneTable table;
public CallbackImportableHandler(CayenneTable table) {
this.table = table;
}
protected Transferable createTransferable(JComponent c) {
int rowIndex = table.getSelectedRow();
String result = null;
if (rowIndex >= 0 && rowIndex < table.getModel().getRowCount()) {
result = String.valueOf(table.getModel().getValueAt(
rowIndex,
CallbackDescriptorTableModel.METHOD_NAME));
}
return new StringSelection(result);
}
public int getSourceActions(JComponent c) {
return COPY_OR_MOVE;
}
public boolean importData(JComponent comp, Transferable t) {
if (canImport(comp, t.getTransferDataFlavors())) {
String callbackMethod;
try {
callbackMethod = (String) t.getTransferData(DataFlavor.stringFlavor);
} catch (Exception e) {
logger.warn("Error transferring", e);
return false;
}
int rowIndex = table.getSelectedRow();
CallbackDescriptor callbackDescriptor =
((CallbackDescriptorTableModel)table.getCayenneModel()).getCallbackDescriptor();
mediator.setDirty(callbackDescriptor.moveMethod(callbackMethod, rowIndex));
rebuildTables();
return true;
}
return false;
}
public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
for (DataFlavor flavor : transferFlavors) {
if (DataFlavor.stringFlavor.equals(flavor)) {
return true;
}
}
return false;
}
}
private class CallbackListSelectionListener implements ListSelectionListener {
private CayenneTable table;
public CallbackListSelectionListener(CayenneTable table){
this.table = table;
}
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
ObjCallbackMethod[] methods = new ObjCallbackMethod[0];
if(!((ListSelectionModel)e.getSource()).isSelectionEmpty()) {
for (CayenneTable nextTable : tables) {
if (!nextTable.equals(table)) {
nextTable.clearSelection();
if (nextTable.getCellEditor() != null) {
nextTable.getCellEditor().stopCellEditing();
}
}
}
mediator.setCurrentCallbackType(((CallbackDescriptorTableModel)table.getCayenneModel()).getCallbackType());
}
if (table.getSelectedRow() != -1) {
int[] sel = table.getSelectedRows();
CallbackType callbackType = mediator.getCurrentCallbackType();
methods = new ObjCallbackMethod[sel.length];
for (int i = 0; i < sel.length; i++) {
String methodName = (String) table.getValueAt(
sel[i],
table.convertColumnIndexToView(CallbackDescriptorTableModel.METHOD_NAME));
methods[i] = new ObjCallbackMethod(methodName, callbackType);
}
}
mediator.setCurrentCallbackMethods(methods);
boolean enabled = methods.length > 0;
boolean multiple = methods.length > 1;
getRemoveCallbackMethodAction().setEnabled(enabled);
getRemoveCallbackMethodAction().setName(getRemoveCallbackMethodAction().getActionName(multiple));
getCopyCallbackMethodAction().setEnabled(enabled);
getCopyCallbackMethodAction().setName(getCopyCallbackMethodAction().getActionName(multiple));
getCutCallbackMethodAction().setEnabled(enabled);
getCutCallbackMethodAction().setName(getCutCallbackMethodAction().getActionName(multiple));
}
}
}
private class CallbackTableColumnModelListener implements TableColumnModelListener {
private CayenneTable table;
public CallbackTableColumnModelListener(CayenneTable table){
this.table = table;
}
public void columnMarginChanged(ChangeEvent e) {
if(!table.getColumnWidthChanged() && table.getTableHeader().getResizingColumn() != null) {
table.setColumnWidthChanged(true);
}
}
public void columnMoved(TableColumnModelEvent e) {
}
public void columnAdded(TableColumnModelEvent e) {
}
public void columnRemoved(TableColumnModelEvent e) {
}
public void columnSelectionChanged(ListSelectionEvent e) {
}
}
private class CallbackMouseAdapter extends MouseAdapter {
CayenneTable table;
public CallbackMouseAdapter(CayenneTable table) {
this.table = table;
}
@Override
public void mouseReleased(MouseEvent e) {
if(table.getColumnWidthChanged()) {
for (CayenneTable nextTable : tables) {
nextTable.getColumnModel().getColumn(0).setPreferredWidth(table.getWidth());
}
initTablePreferences();
table.setColumnWidthChanged(false);
}
}
@Override
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger() && e.getComponent() instanceof JTableHeader ) {
unselectAll();
mediator.setCurrentCallbackType(((CallbackDescriptorTableModel)table.getCayenneModel()).getCallbackType());
popupMenu.show(e.getComponent(), e.getX(), e.getY());
}
}
}
private class CallbackMouseMotionListener implements MouseMotionListener {
CayenneTable table;
public CallbackMouseMotionListener(CayenneTable table) {
this.table = table;
}
public void mouseMoved(MouseEvent e) {
}
public void mouseDragged(MouseEvent e) {
if(table.getColumnWidthChanged()) {
tablePreferences.bind(table, null, null, null);
for (CayenneTable nextTable : tables) {
if (!table.equals(nextTable)) {
nextTable.getColumnModel().getColumn(0).setPreferredWidth(table.getWidth());
}
}
}
}
}
}