/*
Copyright (C) 2006 EBI
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the itmplied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.biomart.builder.view.gui.diagrams.components;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Iterator;
import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import org.biomart.builder.model.Key;
import org.biomart.builder.model.Relation;
import org.biomart.builder.model.Schema;
import org.biomart.builder.model.Table;
import org.biomart.builder.view.gui.diagrams.Diagram;
import org.biomart.common.resources.Resources;
/**
* A diagram component that represents a schema. It usually only has a label in
* it, but if the schema has any external relations, then the tables with those
* relations will appear in full using {@link TableComponent}s.
*
* @author Richard Holland <holland@ebi.ac.uk>
* @version $Revision: 1.35 $, $Date: 2008-02-19 13:27:29 $, modified by
* $Author: rh4 $
* @since 0.5
*/
public class SchemaComponent extends BoxShapedComponent {
private static final long serialVersionUID = 1;
private static final Color TRANSPARENT_COLOR = new Color(0, 0, 0, 0);
/**
* Normal background colour.
*/
public static final Color BACKGROUND_COLOUR = Color.YELLOW;
/**
* Background for masked schemas.
*/
public static Color MASKED_BACKGROUND = Color.LIGHT_GRAY;
private static final Font BOLD_FONT = Font.decode("SansSerif-BOLD-10");
private GridBagConstraints constraints;
private GridBagLayout layout;
private final PropertyChangeListener repaintListener = new PropertyChangeListener() {
public void propertyChange(final PropertyChangeEvent e) {
SchemaComponent.this.needsRepaint = true;
}
};
private final PropertyChangeListener recalcListener = new PropertyChangeListener() {
public void propertyChange(final PropertyChangeEvent e) {
SchemaComponent.this.needsRecalc = true;
}
};
/**
* Constructs a schema diagram component in the given diagram that displays
* details of a particular schema.
*
* @param schema
* the schema to display details of.
* @param diagram
* the diagram to display the details in.
*/
public SchemaComponent(final Schema schema, final Diagram diagram) {
super(schema, diagram);
// Schema components are set out in a vertical list.
this.layout = new GridBagLayout();
this.setLayout(this.layout);
// Constraints for each part of the schema component.
this.constraints = new GridBagConstraints();
this.constraints.gridwidth = GridBagConstraints.REMAINDER;
this.constraints.fill = GridBagConstraints.HORIZONTAL;
this.constraints.anchor = GridBagConstraints.CENTER;
this.constraints.insets = new Insets(5, 5, 5, 5);
// Set the background colour.
this.setBackground(SchemaComponent.BACKGROUND_COLOUR);
// Calculate the components and add them to the list.
this.recalculateDiagramComponent();
// Repaint events.
schema
.addPropertyChangeListener("directModified",
this.repaintListener);
// Recalc events.
schema.addPropertyChangeListener("name", this.recalcListener);
schema.getRelations().addPropertyChangeListener(this.recalcListener);
}
private Schema getSchema() {
return (Schema) this.getObject();
}
protected void processMouseEvent(final MouseEvent evt) {
boolean eventProcessed = false;
// Is it a right-click?
if (evt.getButton() == MouseEvent.BUTTON1 && evt.getClickCount() >= 2) {
final int index = SchemaComponent.this.getDiagram().getMartTab()
.getSchemaTabSet().indexOfTab(
SchemaComponent.this.getSchema().getName());
SchemaComponent.this.getDiagram().getMartTab().getSchemaTabSet()
.setSelectedIndex(index);
// Mark as handled.
eventProcessed = true;
}
// Pass it on up if we're not interested.
if (!eventProcessed)
super.processMouseEvent(evt);
}
public JPopupMenu getContextMenu() {
// First of all, work out what would have been shown by default.
final JPopupMenu contextMenu = super.getContextMenu();
// Add the 'show tables' option, which opens the tab representing
// this schema.
final JMenuItem showTables = new JMenuItem(Resources
.get("showTablesTitle"));
showTables.setMnemonic(Resources.get("showTablesMnemonic").charAt(0));
showTables.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent evt) {
final int index = SchemaComponent.this.getDiagram()
.getMartTab().getSchemaTabSet().indexOfTab(
SchemaComponent.this.getSchema().getName());
SchemaComponent.this.getDiagram().getMartTab()
.getSchemaTabSet().setSelectedIndex(index);
}
});
contextMenu.add(showTables);
// Separator
contextMenu.addSeparator();
// Update menu option.
final JMenuItem update = new JMenuItem(Resources
.get("updateSchemaTitle"), new ImageIcon(Resources
.getResourceAsURL("refresh.gif")));
update.setMnemonic(Resources.get("updateSchemaMnemonic").charAt(0));
update.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent evt) {
SchemaComponent.this.getDiagram().getMartTab()
.getSchemaTabSet().requestModifySchema(
SchemaComponent.this.getSchema());
}
});
contextMenu.add(update);
// Add a separator.
contextMenu.addSeparator();
// Add an option to rename this dataset.
final JMenuItem rename = new JMenuItem(Resources
.get("renameSchemaTitle"));
rename.setMnemonic(Resources.get("renameSchemaMnemonic").charAt(0));
rename.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent evt) {
SchemaComponent.this.getDiagram().getMartTab()
.getSchemaTabSet().requestRenameSchema(
SchemaComponent.this.getSchema());
}
});
contextMenu.add(rename);
// Option to remove the dataset from the mart.
final JMenuItem remove = new JMenuItem(Resources
.get("removeSchemaTitle"), new ImageIcon(Resources
.getResourceAsURL("cut.gif")));
remove.setMnemonic(Resources.get("removeSchemaMnemonic").charAt(0));
remove.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent evt) {
SchemaComponent.this.getDiagram().getMartTab()
.getSchemaTabSet().requestRemoveSchema(
SchemaComponent.this.getSchema());
}
});
contextMenu.add(remove);
// Separator
contextMenu.addSeparator();
// Masked menu option.
final JMenuItem masked = new JCheckBoxMenuItem(Resources
.get("maskedSchemaTitle"));
masked.setMnemonic(Resources.get("maskedSchemaMnemonic").charAt(0));
masked.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent evt) {
SchemaComponent.this.getDiagram().getMartTab()
.getSchemaTabSet().requestMaskSchema(
SchemaComponent.this.getSchema(),
masked.isSelected());
}
});
masked.setSelected(this.getSchema().isMasked());
contextMenu.add(masked);
// Return it. Will be further adapted by a listener elsewhere.
return contextMenu;
}
protected void doRecalculateDiagramComponent() {
// Clear subcomponents.
this.getSubComponents().clear();
// Add the label for the schema name,
final JTextField name = new JTextField() {
private static final long serialVersionUID = 1L;
private Color opaqueBackground;
// work around transparency issue in OS X 10.5
public void setOpaque(boolean opaque) {
if (opaque != isOpaque()) {
if (opaque) {
super.setBackground(opaqueBackground);
} else if (opaqueBackground != null) {
opaqueBackground = getBackground();
super.setBackground(TRANSPARENT_COLOR);
}
}
super.setOpaque(opaque);
}
// work around transparency issue in OS X 10.5
public void setBackground(Color color) {
if (isOpaque()) {
super.setBackground(color);
} else {
opaqueBackground = color;
}
}
};
name.setFont(SchemaComponent.BOLD_FONT);
this.setRenameTextField(name);
this.layout.setConstraints(name, this.constraints);
this.add(name);
// Now add any tables with external relations. Loop through the
// external keys to identify the tables to do this.
for (final Iterator i = this.getSchema().getRelations().iterator(); i
.hasNext();) {
final Relation rel = (Relation) i.next();
if (!rel.isExternal())
continue;
final Key key = rel.getKeyForSchema(this.getSchema());
final Table table = key.getTable();
// Only add the table if it's not already added!
if (!this.getSubComponents().containsKey(table)) {
// Create the table component that represents this table.
final TableComponent tableComponent = new TableComponent(table,
this.getDiagram());
// Remember, internally, the subcomponents of this table, as
// well as the table itself as a subcomponent.
this.addSubComponent(table, tableComponent);
this.getSubComponents().putAll(
tableComponent.getSubComponents());
// Add the table component to our layout.
this.layout.setConstraints(tableComponent, this.constraints);
this.add(tableComponent);
}
}
}
public void performRename(final String newName) {
this.getDiagram().getMartTab().getSchemaTabSet().requestRenameSchema(
this.getSchema(), newName);
}
public String getEditableName() {
return this.getSchema().getName();
}
public String getDisplayName() {
final StringBuffer name = new StringBuffer();
if (this.getSchema().getPartitionRegex() != null
&& !this.getSchema().getPartitionRegex().equals("")) {
name.append(" (");
name.append(Resources.get("partitionedSchemaMessage"));
name.append(')');
}
name.append(this.getEditableName());
return name.toString();
}
}