0001: /*
0002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
0003: * for visualizing and manipulating spatial features with geometry and attributes.
0004: *
0005: * Copyright (C) 2003 Vivid Solutions
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License
0009: * as published by the Free Software Foundation; either version 2
0010: * of the License, or (at your option) any later version.
0011: *
0012: * This program is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0015: * GNU General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * along with this program; if not, write to the Free Software
0019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
0020: *
0021: * For more information, contact:
0022: *
0023: * Vivid Solutions
0024: * Suite #1A
0025: * 2328 Government Street
0026: * Victoria BC V8T 5G5
0027: * Canada
0028: *
0029: * (250)385-6040
0030: * www.vividsolutions.com
0031: */
0032: package com.vividsolutions.jump.workbench.ui.renderer.style;
0033:
0034: import com.vividsolutions.jump.I18N;
0035: import com.vividsolutions.jump.feature.Feature;
0036: import com.vividsolutions.jump.util.CollectionUtil;
0037: import com.vividsolutions.jump.util.Range;
0038: import com.vividsolutions.jump.util.StringUtil;
0039: import com.vividsolutions.jump.workbench.WorkbenchContext;
0040: import com.vividsolutions.jump.workbench.model.Layer;
0041: import com.vividsolutions.jump.workbench.model.LayerManager;
0042: import com.vividsolutions.jump.workbench.plugin.AbstractPlugIn;
0043: import com.vividsolutions.jump.workbench.plugin.EnableCheck;
0044: import com.vividsolutions.jump.workbench.plugin.MultiEnableCheck;
0045: import com.vividsolutions.jump.workbench.plugin.PlugIn;
0046: import com.vividsolutions.jump.workbench.plugin.PlugInContext;
0047: import com.vividsolutions.jump.workbench.ui.ColorPanel;
0048: import com.vividsolutions.jump.workbench.ui.EnableableToolBar;
0049: import com.vividsolutions.jump.workbench.ui.GUIUtil;
0050: import com.vividsolutions.jump.workbench.ui.OKCancelPanel;
0051: import com.vividsolutions.jump.workbench.ui.images.IconLoader;
0052: import com.vividsolutions.jump.workbench.ui.style.BasicStylePanel;
0053: import com.vividsolutions.jump.workbench.ui.style.StylePanel;
0054:
0055: import java.awt.BorderLayout;
0056: import java.awt.Color;
0057: import java.awt.Component;
0058: import java.awt.Dimension;
0059: import java.awt.GridBagConstraints;
0060: import java.awt.GridBagLayout;
0061: import java.awt.Insets;
0062: import java.awt.event.ActionEvent;
0063: import java.awt.event.ActionListener;
0064: import java.awt.event.MouseAdapter;
0065: import java.awt.event.MouseEvent;
0066: import java.util.Collection;
0067: import java.util.EventObject;
0068: import java.util.HashMap;
0069: import java.util.HashSet;
0070: import java.util.Iterator;
0071: import java.util.Map;
0072: import java.util.SortedSet;
0073: import java.util.TreeMap;
0074: import java.util.TreeSet;
0075: import java.util.Vector;
0076:
0077: import javax.swing.BorderFactory;
0078: import javax.swing.DefaultCellEditor;
0079: import javax.swing.DefaultComboBoxModel;
0080: import javax.swing.Icon;
0081: import javax.swing.JButton;
0082: import javax.swing.JCheckBox;
0083: import javax.swing.JComboBox;
0084: import javax.swing.JComponent;
0085: import javax.swing.JDialog;
0086: import javax.swing.JLabel;
0087: import javax.swing.JList;
0088: import javax.swing.JPanel;
0089: import javax.swing.JScrollPane;
0090: import javax.swing.JSlider;
0091: import javax.swing.JTable;
0092: import javax.swing.ScrollPaneConstants;
0093: import javax.swing.SwingUtilities;
0094: import javax.swing.event.CellEditorListener;
0095: import javax.swing.event.ChangeEvent;
0096: import javax.swing.event.ChangeListener;
0097: import javax.swing.event.ListSelectionEvent;
0098: import javax.swing.event.ListSelectionListener;
0099: import javax.swing.event.TableModelEvent;
0100: import javax.swing.event.TableModelListener;
0101: import javax.swing.table.DefaultTableCellRenderer;
0102: import javax.swing.table.TableCellEditor;
0103: import javax.swing.table.TableCellRenderer;
0104:
0105: public class ColorThemingStylePanel extends JPanel implements
0106: StylePanel {
0107: private static final String CUSTOM_ENTRY = I18N
0108: .get("ui.renderer.style.ColorThemingPanel.custom");
0109: public static final String TITLE = I18N
0110: .get("ui.renderer.style.ColorThemingPanel.colour-theming");
0111: public static final String COLOR_SCHEME_KEY = ColorThemingStylePanel.class
0112: .getName()
0113: + " - COLOR SCHEME";
0114: private WorkbenchContext workbenchContext;
0115: private Layer layer;
0116: private GridBagLayout gridBagLayout1 = new GridBagLayout();
0117: private JPanel jPanel1 = new JPanel();
0118: private JScrollPane scrollPane = new JScrollPane();
0119: private DefaultTableCellRenderer allOtherValuesRenderer = new DefaultTableCellRenderer();
0120: private JTable table = new JTable() {
0121: public TableCellRenderer getCellRenderer(int row, int column) {
0122: TableCellRenderer renderer = getCellRendererProper(row,
0123: column);
0124:
0125: if (renderer instanceof JComponent) {
0126: updateBackground((JComponent) renderer);
0127: }
0128:
0129: return renderer;
0130: }
0131:
0132: private TableCellRenderer getCellRendererProper(int row,
0133: int column) {
0134: if ((row == 0) && (column == attributeColumn())) {
0135: return allOtherValuesRenderer;
0136: }
0137: if ((row == 0) && (column == labelColumn())) {
0138: return allOtherValuesRenderer;
0139: }
0140: TableCellRenderer renderer = super .getCellRenderer(row,
0141: column);
0142:
0143: if (renderer instanceof JLabel) {
0144: ((JLabel) renderer).setHorizontalAlignment(JLabel.LEFT);
0145: }
0146:
0147: return renderer;
0148: }
0149: };
0150:
0151: private JPanel jPanel3 = new JPanel();
0152: private GridBagLayout gridBagLayout2 = new GridBagLayout();
0153: private GridBagLayout gridBagLayout4 = new GridBagLayout();
0154: private JComboBox attributeNameComboBox = new JComboBox();
0155: private JLabel attributeLabel = new JLabel();
0156: private JLabel statusLabel = new JLabel() {
0157: public void setText(String text) {
0158: super .setText(text);
0159: setToolTipText(text);
0160: }
0161: };
0162:
0163: private EnableableToolBar toolBar = new EnableableToolBar();
0164: private JPanel jPanel4 = new JPanel();
0165: private GridBagLayout gridBagLayout5 = new GridBagLayout();
0166: private JComboBox colorSchemeComboBox = new JComboBox();
0167: private JLabel colorSchemeLabel = new JLabel();
0168: private boolean updatingComponents = false;
0169: private boolean initializing = false;
0170: private BasicStyleListCellRenderer basicStyleListCellRenderer = new BasicStyleListCellRenderer();
0171: public BasicStylePanel basicStylePanel;
0172: private TableCellEditor basicStyleTableCellEditor = new TableCellEditor() {
0173: private DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel();
0174: private BasicStyle originalStyle;
0175: private DefaultCellEditor editor;
0176: private JComboBox comboBox = new JComboBox(comboBoxModel) {
0177: public void setSelectedItem(Object anObject) {
0178: if (anObject != CUSTOM_ENTRY) {
0179: super .setSelectedItem(anObject);
0180:
0181: return;
0182: }
0183:
0184: BasicStyle style = promptBasicStyle(originalStyle);
0185:
0186: if (style == null) {
0187: return;
0188: }
0189:
0190: comboBox.addItem(style);
0191: super .setSelectedItem(style);
0192: }
0193: };
0194:
0195: {
0196: comboBox.setRenderer(basicStyleListCellRenderer);
0197: editor = new DefaultCellEditor(comboBox);
0198: }
0199:
0200: public Component getTableCellEditorComponent(JTable table,
0201: Object value, boolean isSelected, int row, int column) {
0202: originalStyle = (BasicStyle) value;
0203: comboBoxModel.removeAllElements();
0204: comboBoxModel.addElement(CUSTOM_ENTRY);
0205:
0206: //Include the current BasicStyle; otherwise, the current BasicStyle
0207: //will not show up as the selected item in the combo box. [Jon Aquino]
0208: comboBoxModel.addElement(value);
0209:
0210: for (Iterator i = ColorScheme.create(
0211: (String) colorSchemeComboBox.getSelectedItem())
0212: .getColors().iterator(); i.hasNext();) {
0213: Color color = (Color) i.next();
0214: comboBoxModel.addElement(new BasicStyle(color));
0215: }
0216:
0217: comboBoxModel.setSelectedItem(value);
0218:
0219: return editor.getTableCellEditorComponent(table, value,
0220: isSelected, row, column);
0221: }
0222:
0223: public Object getCellEditorValue() {
0224: return editor.getCellEditorValue();
0225: }
0226:
0227: public boolean isCellEditable(EventObject anEvent) {
0228: return editor.isCellEditable(anEvent);
0229: }
0230:
0231: public boolean shouldSelectCell(EventObject anEvent) {
0232: return editor.shouldSelectCell(anEvent);
0233: }
0234:
0235: public boolean stopCellEditing() {
0236: return editor.stopCellEditing();
0237: }
0238:
0239: public void cancelCellEditing() {
0240: editor.cancelCellEditing();
0241: }
0242:
0243: public void addCellEditorListener(CellEditorListener l) {
0244: editor.addCellEditorListener(l);
0245: }
0246:
0247: public void removeCellEditorListener(CellEditorListener l) {
0248: editor.removeCellEditorListener(l);
0249: }
0250: };
0251:
0252: private DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel();
0253: private JPanel fillerPanel = new JPanel();
0254: private String lastAttributeName;
0255: private ColorScheme colorSchemeForInserts = null;
0256: private MyPlugIn insertPlugIn = new MyPlugIn() {
0257: public String getName() {
0258: return I18N
0259: .get("ui.renderer.style.ColorThemingPanel.insert-row");
0260: }
0261:
0262: public Icon getIcon() {
0263: return GUIUtil.toSmallIcon(IconLoader.icon("Plus.gif"));
0264: }
0265:
0266: public boolean execute(PlugInContext context) throws Exception {
0267: reportNothingToUndoYet(context);
0268: stopCellEditing();
0269: tableModel()
0270: .insertAttributeValue(
0271: (table.getSelectedRowCount() > 0) ? table
0272: .getSelectedRows()[0] : table
0273: .getRowCount(),
0274: getColorSchemeForInserts());
0275:
0276: if (table.getSelectedRowCount() == 0) {
0277: table.scrollRectToVisible(table.getCellRect(table
0278: .getRowCount() - 1, 0, true));
0279: }
0280:
0281: if (table.getSelectedRowCount() != 0) {
0282: int firstSelectedRow = table.getSelectedRows()[0];
0283: table.clearSelection();
0284: table.addRowSelectionInterval(firstSelectedRow,
0285: firstSelectedRow);
0286: }
0287:
0288: return true;
0289: }
0290: };
0291:
0292: private MyPlugIn deletePlugIn = new MyPlugIn() {
0293: public String getName() {
0294: return I18N
0295: .get("ui.renderer.style.ColorThemingPanel.delete-row");
0296: }
0297:
0298: public Icon getIcon() {
0299: return GUIUtil.toSmallIcon(IconLoader.icon("Delete.gif"));
0300: }
0301:
0302: public boolean execute(PlugInContext context) throws Exception {
0303: reportNothingToUndoYet(context);
0304: stopCellEditing();
0305: tableModel().removeAttributeValues(table.getSelectedRows());
0306:
0307: return true;
0308: }
0309: };
0310:
0311: //Maintain a blackboard for error messages instead of running all
0312: //validations whenever we need to check for messages -- some validations
0313: //may be expensive. [Jon Aquino]
0314: private HashSet errorMessages = new HashSet();
0315: private DiscreteColorThemingState discreteColorThemingState = new DiscreteColorThemingState(
0316: table);
0317: private RangeColorThemingState rangeColorThemingState;
0318: private State state = discreteColorThemingState;
0319: private JPanel jPanel5 = new JPanel();
0320: private GridBagLayout gridBagLayout3 = new GridBagLayout();
0321: private JCheckBox enableColorThemingCheckBox = new JCheckBox();
0322: private JCheckBox byRangeCheckBox = new JCheckBox();
0323: private JSlider transparencySlider = new JSlider();
0324:
0325: public ColorThemingStylePanel(Layer layer,
0326: WorkbenchContext workbenchContext) {
0327: initializing = true;
0328:
0329: try {
0330: basicStylePanel = new BasicStylePanel(workbenchContext
0331: .getWorkbench().getBlackboard(),
0332: ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
0333: this .layer = layer;
0334: this .workbenchContext = workbenchContext;
0335: rangeColorThemingState = new RangeColorThemingState(this );
0336: jbInit();
0337: byRangeCheckBox
0338: .setSelected(colorThemingStyleHasRanges(layer));
0339:
0340: //Must set state early so that #initTable can call #fromExternalFormat. [Jon Aquino]
0341: state = byRangeCheckBox.isSelected() ? (State) rangeColorThemingState
0342: : discreteColorThemingState;
0343:
0344: //Init the table before calling #setState, in which
0345: //RangeColorThemingState will add itself as a listener
0346: //to the table model. [Jon Aquino]
0347: initTable(layer);
0348: setState(state);
0349: initAttributeNameComboBox(layer);
0350: initColorSchemeComboBox(layer.getLayerManager());
0351: initTransparencySlider(layer);
0352: initToolBar();
0353: enableColorThemingCheckBox.setSelected(ColorThemingStyle
0354: .get(layer).isEnabled());
0355: updateComponents();
0356: GUIUtil.sync(basicStylePanel.getTransparencySlider(),
0357: transparencySlider);
0358: basicStylePanel.setSynchronizingLineColor(layer
0359: .isSynchronizingLineColor());
0360: } catch (Exception e) {
0361: e.printStackTrace();
0362: } finally {
0363: initializing = false;
0364: }
0365: }
0366:
0367: private void updateBackground(JComponent component) {
0368: component
0369: .setBackground(enableColorThemingCheckBox.isSelected() ? Color.white
0370: : jPanel1.getBackground());
0371: }
0372:
0373: private int attributeColumn() {
0374: return table
0375: .convertColumnIndexToView(ColorThemingTableModel.ATTRIBUTE_COLUMN);
0376: }
0377:
0378: private int labelColumn() {
0379: return table
0380: .convertColumnIndexToView(ColorThemingTableModel.LABEL_COLUMN);
0381: }
0382:
0383: private int colorColumn() {
0384: return table
0385: .convertColumnIndexToView(ColorThemingTableModel.COLOR_COLUMN);
0386: }
0387:
0388: public String getTitle() {
0389: return TITLE;
0390: }
0391:
0392: public void updateStyles() {
0393: boolean firingEvents = layer.getLayerManager().isFiringEvents();
0394: layer.getLayerManager().setFiringEvents(false);
0395:
0396: try {
0397: layer.removeStyle(ColorThemingStyle.get(layer));
0398: layer.addStyle(new ColorThemingStyle(getAttributeName(),
0399: state.toExternalFormat(tableModel()
0400: .getAttributeValueToBasicStyleMap()), state
0401: .toExternalFormat(tableModel()
0402: .getAttributeValueToLabelMap()),
0403: tableModel().getDefaultStyle()));
0404: ColorThemingStyle.get(layer).setAlpha(getAlpha());
0405: ColorThemingStyle.get(layer).setEnabled(
0406: enableColorThemingCheckBox.isSelected());
0407: layer.getBasicStyle().setEnabled(
0408: !enableColorThemingCheckBox.isSelected());
0409: } finally {
0410: layer.getLayerManager().setFiringEvents(firingEvents);
0411: }
0412: layer.fireAppearanceChanged();
0413: }
0414:
0415: private String getAttributeName() {
0416: return (String) attributeNameComboBox.getSelectedItem();
0417: }
0418:
0419: private void stopCellEditing() {
0420: if (table.getCellEditor() instanceof DefaultCellEditor) {
0421: ((DefaultCellEditor) table.getCellEditor())
0422: .stopCellEditing();
0423: }
0424: }
0425:
0426: public JCheckBox getSynchronizeCheckBox() {
0427: return basicStylePanel.getSynchronizeCheckBox();
0428: }
0429:
0430: public Layer getLayer() {
0431: return layer;
0432: }
0433:
0434: private void initTransparencySlider(Layer layer) {
0435: transparencySlider.setValue(transparencySlider.getMaximum()
0436: - ColorThemingStyle.get(layer).getDefaultStyle()
0437: .getAlpha());
0438: transparencySlider.addChangeListener(new ChangeListener() {
0439: public void stateChanged(ChangeEvent e) {
0440: basicStyleListCellRenderer.setAlpha(getAlpha());
0441: }
0442: });
0443: basicStyleListCellRenderer.setAlpha(getAlpha());
0444: }
0445:
0446: private boolean colorThemingStyleHasRanges(Layer layer) {
0447: return !ColorThemingStyle.get(layer)
0448: .getAttributeValueToBasicStyleMap().isEmpty()
0449: && ColorThemingStyle.get(layer)
0450: .getAttributeValueToBasicStyleMap().keySet()
0451: .iterator().next() instanceof Range;
0452: }
0453:
0454: private void initToolBar() {
0455: EnableCheck atLeast1RowMustBeSelectedCheck = new EnableCheck() {
0456: public String check(JComponent component) {
0457: return (table.getSelectedRowCount() == 0) ? I18N
0458: .get("ui.renderer.style.ColorThemingPanel.at-least-1-row-must-be-selected")
0459: : null;
0460: }
0461: };
0462:
0463: EnableCheck layerMustHaveAtLeast1AttributeCheck = new EnableCheck() {
0464: public String check(JComponent component) {
0465: return (attributeNameComboBox.getItemCount() == 0) ? I18N
0466: .get("ui.renderer.style.ColorThemingPanel.layer-must-have-at-least-1-attribute")
0467: : null;
0468: }
0469: };
0470:
0471: EnableCheck colorThemingMustBeEnabledCheck = new EnableCheck() {
0472: public String check(JComponent component) {
0473: return (!enableColorThemingCheckBox.isSelected()) ? I18N
0474: .get("ui.renderer.style.ColorThemingPanel.colour-theming-must-be-enabled")
0475: : null;
0476: }
0477: };
0478:
0479: addPlugIn(insertPlugIn, new MultiEnableCheck().add(
0480: layerMustHaveAtLeast1AttributeCheck).add(
0481: colorThemingMustBeEnabledCheck));
0482: addPlugIn(deletePlugIn, new MultiEnableCheck().add(
0483: layerMustHaveAtLeast1AttributeCheck).add(
0484: atLeast1RowMustBeSelectedCheck).add(
0485: colorThemingMustBeEnabledCheck));
0486: }
0487:
0488: private void addPlugIn(MyPlugIn plugIn, EnableCheck enableCheck) {
0489: JButton button = new JButton();
0490: toolBar.add(button, plugIn.getName(), plugIn.getIcon(),
0491: AbstractPlugIn.toActionListener(plugIn,
0492: workbenchContext, null), enableCheck);
0493: }
0494:
0495: private void updateComponents() {
0496: if (updatingComponents) {
0497: return;
0498: }
0499:
0500: updatingComponents = true;
0501:
0502: try {
0503: attributeLabel.setEnabled(enableColorThemingCheckBox
0504: .isSelected());
0505: attributeNameComboBox.setEnabled(enableColorThemingCheckBox
0506: .isSelected());
0507: state.getPanel()
0508: .setEnabled(
0509: enableColorThemingCheckBox.isSelected()
0510: && (attributeNameComboBox
0511: .getItemCount() > 0));
0512: colorSchemeLabel.setEnabled(enableColorThemingCheckBox
0513: .isSelected()
0514: && (attributeNameComboBox.getItemCount() > 0));
0515: byRangeCheckBox.setEnabled(enableColorThemingCheckBox
0516: .isSelected()
0517: && (attributeNameComboBox.getItemCount() > 0));
0518: colorSchemeComboBox.setEnabled(enableColorThemingCheckBox
0519: .isSelected()
0520: && (attributeNameComboBox.getItemCount() > 0));
0521: table.setEnabled(enableColorThemingCheckBox.isSelected()
0522: && (attributeNameComboBox.getItemCount() > 0));
0523: scrollPane.setEnabled(enableColorThemingCheckBox
0524: .isSelected()
0525: && (attributeNameComboBox.getItemCount() > 0));
0526: transparencySlider.setEnabled(enableColorThemingCheckBox
0527: .isSelected()
0528: && (attributeNameComboBox.getItemCount() > 0));
0529: statusLabel.setEnabled(enableColorThemingCheckBox
0530: .isSelected());
0531: toolBar.updateEnabledState();
0532:
0533: //Give the "Cannot colour-theme layer" message priority. [Jon Aquino]
0534: if (!setErrorMessage(
0535: new ErrorMessage(
0536: I18N
0537: .get("ui.renderer.style.ColorThemingPanel.cannot-colour-theme-layer-with-no-attributes")),
0538: attributeNameComboBox.getItemCount() == 0)) {
0539: setErrorMessage(
0540: new ErrorMessage(
0541: I18N
0542: .get("ui.renderer.style.ColorThemingPanel.table-must-not-be-empty")),
0543: table.getRowCount() == 0);
0544: }
0545:
0546: updateErrorDisplay();
0547:
0548: if (table.getColumnCount() > 0) {
0549: //Column count == 0 during initialization. [Jon Aquino]
0550: table
0551: .getColumnModel()
0552: .getColumn(
0553: table
0554: .convertColumnIndexToView(ColorThemingTableModel.ATTRIBUTE_COLUMN))
0555: .setHeaderValue(
0556: state.getAttributeValueColumnTitle());
0557: }
0558: } finally {
0559: updatingComponents = false;
0560: }
0561: }
0562:
0563: /**
0564: * @return null if user hits Cancel
0565: */
0566: private BasicStyle promptBasicStyle(BasicStyle basicStyle) {
0567: int originalTransparencySliderValue = transparencySlider
0568: .getValue();
0569: basicStylePanel.setBasicStyle(basicStyle);
0570: basicStylePanel.getTransparencySlider().setValue(
0571: originalTransparencySliderValue);
0572:
0573: OKCancelPanel okCancelPanel = new OKCancelPanel();
0574: final JDialog dialog = new JDialog((JDialog) SwingUtilities
0575: .windowForComponent(this ), I18N
0576: .get("ui.renderer.style.ColorThemingPanel.custom"),
0577: true);
0578: dialog.getContentPane().setLayout(new BorderLayout());
0579: dialog.getContentPane().add(basicStylePanel,
0580: BorderLayout.CENTER);
0581: dialog.getContentPane().add(okCancelPanel, BorderLayout.SOUTH);
0582: okCancelPanel.addActionListener(new ActionListener() {
0583: public void actionPerformed(ActionEvent e) {
0584: dialog.setVisible(false);
0585: }
0586: });
0587: dialog.pack();
0588: GUIUtil.centreOnWindow(dialog);
0589: dialog.setVisible(true);
0590:
0591: if (!okCancelPanel.wasOKPressed()) {
0592: transparencySlider
0593: .setValue(originalTransparencySliderValue);
0594: }
0595:
0596: return okCancelPanel.wasOKPressed() ? basicStylePanel
0597: .getBasicStyle() : null;
0598: }
0599:
0600: private void initTable(Layer layer) {
0601: table.setModel(new ColorThemingTableModel(ColorThemingStyle
0602: .get(layer).getDefaultStyle(), ColorThemingStyle.get(
0603: layer).getAttributeName(),
0604: attributeValueToBasicStyleMap(layer),
0605: attributeValueToLabelMap(layer), layer
0606: .getFeatureCollectionWrapper()
0607: .getFeatureSchema()) {
0608: public Object getValueAt(int rowIndex, int columnIndex) {
0609: //Don't use #attributeColumn() here because this is in the *model*,
0610: //not the view. Otherwise, an exception will be thrown when
0611: //the user tries to move the columns. [Jon Aquino]
0612: if ((rowIndex == 0)
0613: && (columnIndex == ColorThemingTableModel.ATTRIBUTE_COLUMN)) {
0614: return state.getAllOtherValuesDescription();
0615: }
0616: if ((rowIndex == 0)
0617: && (columnIndex == ColorThemingTableModel.LABEL_COLUMN)) {
0618: return "";
0619: }
0620: return super .getValueAt(rowIndex, columnIndex);
0621: }
0622: });
0623: table.createDefaultColumnsFromModel();
0624: table.setRowSelectionAllowed(true);
0625: table.getSelectionModel().addListSelectionListener(
0626: new ListSelectionListener() {
0627: public void valueChanged(ListSelectionEvent e) {
0628: updateComponents();
0629: }
0630: });
0631: table.getTableHeader().addMouseListener(new MouseAdapter() {
0632: public void mousePressed(MouseEvent e) {
0633: if (SwingUtilities.isLeftMouseButton(e)
0634: && (e.getClickCount() == 1)
0635: && table.isEnabled()) {
0636: tableModel().sort();
0637: }
0638: }
0639: });
0640: table.getColumnModel().getColumn(colorColumn())
0641: .setCellRenderer(new TableCellRenderer() {
0642: public Component getTableCellRendererComponent(
0643: JTable table, Object value,
0644: boolean isSelected, boolean hasFocus,
0645: int row, int column) {
0646: JComponent renderer = (JComponent) basicStyleListCellRenderer
0647: .getListCellRendererComponent(
0648: new JList(), value, row,
0649: isSelected, hasFocus);
0650:
0651: if (!isSelected) {
0652: updateBackground(renderer);
0653: }
0654:
0655: return renderer;
0656: }
0657: });
0658: table.getColumnModel().getColumn(colorColumn()).setCellEditor(
0659: basicStyleTableCellEditor);
0660: table.getModel().addTableModelListener(
0661: new TableModelListener() {
0662: public void tableChanged(TableModelEvent e) {
0663: updateComponents();
0664:
0665: Object duplicateAttributeValue = tableModel()
0666: .findDuplicateAttributeValue();
0667: setErrorMessage(
0668: new ErrorMessage(
0669: I18N
0670: .get("ui.renderer.style.ColorThemingPanel.table-must-not-have-duplicate-attribute-values"),
0671: "(" + duplicateAttributeValue
0672: + ")"),
0673: duplicateAttributeValue != null);
0674: setErrorMessage(
0675: new ErrorMessage(
0676: I18N
0677: .get("ui.renderer.style.ColorThemingPanel.table-must-not-have-null-attribute-values")),
0678: tableModel()
0679: .containsNullAttributeValues());
0680: }
0681: });
0682:
0683: int colorWidth = 10 + (int) basicStyleListCellRenderer
0684: .getListCellRendererComponent(new JList(),
0685: new BasicStyle(), 0, false, false)
0686: .getPreferredSize().getWidth();
0687: table.getColumnModel().getColumn(colorColumn())
0688: .setPreferredWidth(colorWidth);
0689: table.getColumnModel().getColumn(colorColumn()).setMinWidth(
0690: colorWidth);
0691: table.getColumnModel().getColumn(colorColumn()).setMaxWidth(
0692: colorWidth);
0693: }
0694:
0695: private Map attributeValueToBasicStyleMap(Layer layer) {
0696: if (!colorThemingAttributeValid(layer)) {
0697: return new TreeMap();
0698: }
0699: return state.fromExternalFormat(ColorThemingStyle.get(layer)
0700: .getAttributeValueToBasicStyleMap());
0701: }
0702:
0703: private Map attributeValueToLabelMap(Layer layer) {
0704: if (!colorThemingAttributeValid(layer)) {
0705: return new TreeMap();
0706: }
0707: return state.fromExternalFormat(ColorThemingStyle.get(layer)
0708: .getAttributeValueToLabelMap());
0709: }
0710:
0711: private boolean colorThemingAttributeValid(Layer layer) {
0712: if (ColorThemingStyle.get(layer).getAttributeName() == null) {
0713: return false;
0714: }
0715: //Schema won't have attribute name if user has deleted the attribute.
0716: //[Jon Aquino]
0717: if (!layer
0718: .getFeatureCollectionWrapper()
0719: .getFeatureSchema()
0720: .hasAttribute(
0721: ColorThemingStyle.get(layer).getAttributeName())) {
0722: return false;
0723: }
0724: return true;
0725: }
0726:
0727: private void initColorSchemeComboBox(LayerManager layerManager) {
0728: colorSchemeComboBox
0729: .setRenderer(new ColorSchemeListCellRenderer() {
0730: protected void color(ColorPanel colorPanel,
0731: Color fillColor, Color lineColor) {
0732: super .color(colorPanel, GUIUtil.alphaColor(
0733: fillColor, getAlpha()), GUIUtil
0734: .alphaColor(lineColor, getAlpha()));
0735: }
0736:
0737: protected ColorScheme colorScheme(String name) {
0738: return state.filterColorScheme(super
0739: .colorScheme(name));
0740: }
0741: });
0742: }
0743:
0744: private int getAlpha() {
0745: return transparencySlider.getMaximum()
0746: - transparencySlider.getValue();
0747: }
0748:
0749: private void initAttributeNameComboBox(Layer layer) {
0750: DefaultComboBoxModel model = new DefaultComboBoxModel();
0751:
0752: for (int i = 0; i < layer.getFeatureCollectionWrapper()
0753: .getFeatureSchema().getAttributeCount(); i++) {
0754: if (i == layer.getFeatureCollectionWrapper()
0755: .getFeatureSchema().getGeometryIndex()) {
0756: continue;
0757: }
0758:
0759: model.addElement(layer.getFeatureCollectionWrapper()
0760: .getFeatureSchema().getAttributeName(i));
0761: }
0762:
0763: attributeNameComboBox.setModel(model);
0764:
0765: if (model.getSize() == 0) {
0766: //Can get here if the only attribute is the geometry. [Jon Aquino]
0767: return;
0768: }
0769:
0770: attributeNameComboBox.setSelectedItem(ColorThemingStyle.get(
0771: layer).getAttributeName());
0772: }
0773:
0774: private void jbInit() throws Exception {
0775: this .setLayout(gridBagLayout1);
0776: jPanel1.setLayout(gridBagLayout2);
0777: jPanel3.setLayout(gridBagLayout4);
0778: attributeLabel.setText(I18N
0779: .get("ui.renderer.style.ColorThemingPanel.attribute")
0780: + " ");
0781: statusLabel.setBorder(BorderFactory.createLoweredBevelBorder());
0782: statusLabel.setText(" ");
0783: jPanel4.setLayout(gridBagLayout5);
0784: colorSchemeLabel
0785: .setText(I18N
0786: .get("ui.renderer.style.ColorThemingPanel.colour-scheme")
0787: + " ");
0788: attributeNameComboBox
0789: .addActionListener(new java.awt.event.ActionListener() {
0790: public void actionPerformed(ActionEvent e) {
0791: attributeNameComboBox_actionPerformed(e);
0792: }
0793: });
0794: colorSchemeComboBox
0795: .addActionListener(new java.awt.event.ActionListener() {
0796: public void actionPerformed(ActionEvent e) {
0797: colorSchemeComboBox_actionPerformed(e);
0798: }
0799: });
0800: jPanel5.setLayout(gridBagLayout3);
0801: enableColorThemingCheckBox
0802: .addActionListener(new java.awt.event.ActionListener() {
0803: public void actionPerformed(ActionEvent e) {
0804: enableColorThemingCheckBox_actionPerformed(e);
0805: }
0806: });
0807: enableColorThemingCheckBox
0808: .setText(I18N
0809: .get("ui.renderer.style.ColorThemingPanel.enable-colour-theming"));
0810: byRangeCheckBox.setText(I18N
0811: .get("ui.renderer.style.ColorThemingPanel.by-range"));
0812: byRangeCheckBox
0813: .addActionListener(new java.awt.event.ActionListener() {
0814: public void actionPerformed(ActionEvent e) {
0815: byRangeCheckBox_actionPerformed(e);
0816: }
0817: });
0818: transparencySlider.setMaximum(255);
0819: transparencySlider.setPreferredSize(new Dimension(75, 24));
0820:
0821: //Don't get squished by overlong status messages. [Jon Aquino]
0822: transparencySlider.setMinimumSize(new Dimension(75, 24));
0823: transparencySlider
0824: .setToolTipText(I18N
0825: .get("ui.renderer.style.ColorThemingPanel.transparency"));
0826: transparencySlider
0827: .addChangeListener(new javax.swing.event.ChangeListener() {
0828: public void stateChanged(ChangeEvent e) {
0829: transparencySlider_stateChanged(e);
0830: }
0831: });
0832: this .add(jPanel1, new GridBagConstraints(0, 2, 1, 1, 1.0, 0.0,
0833: GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
0834: new Insets(0, 0, 0, 0), 0, 0));
0835: jPanel1.add(attributeNameComboBox, new GridBagConstraints(1, 0,
0836: 1, 1, 0.0, 0.0, GridBagConstraints.WEST,
0837: GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0));
0838: jPanel1.add(attributeLabel, new GridBagConstraints(0, 0, 1, 1,
0839: 0.0, 0.0, GridBagConstraints.CENTER,
0840: GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0));
0841: jPanel1.add(fillerPanel, new GridBagConstraints(4, 0, 1, 1, 1,
0842: 0.0, GridBagConstraints.CENTER,
0843: GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2),
0844: 0, 0));
0845: this .add(scrollPane, new GridBagConstraints(0, 4, 1, 1, 1.0,
0846: 1.0, GridBagConstraints.CENTER,
0847: GridBagConstraints.BOTH, new Insets(2, 2, 2, 2), 0, 0));
0848: this .add(jPanel3, new GridBagConstraints(0, 5, 1, 1, 1.0, 0.0,
0849: GridBagConstraints.CENTER,
0850: GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0),
0851: 0, 0));
0852: jPanel3.add(statusLabel, new GridBagConstraints(1, 0, 1, 1,
0853: 1.0, 0.0, GridBagConstraints.CENTER,
0854: GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0),
0855: 0, 0));
0856: jPanel3.add(toolBar, new GridBagConstraints(2, 0, 1, 1, 0.0,
0857: 0.0, GridBagConstraints.CENTER,
0858: GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
0859: jPanel3.add(transparencySlider, new GridBagConstraints(3, 0, 1,
0860: 1, 0.0, 0.0, GridBagConstraints.CENTER,
0861: GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
0862: this .add(jPanel4, new GridBagConstraints(0, 3, 1, 1, 1.0, 0.0,
0863: GridBagConstraints.CENTER,
0864: GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0),
0865: 0, 0));
0866: jPanel4.add(colorSchemeComboBox, new GridBagConstraints(1, 0,
0867: 1, 1, 1.0, 0.0, GridBagConstraints.WEST,
0868: GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2),
0869: 0, 0));
0870: jPanel4.add(colorSchemeLabel, new GridBagConstraints(0, 0, 1,
0871: 1, 0.0, 0.0, GridBagConstraints.CENTER,
0872: GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0));
0873: this .add(jPanel5, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
0874: GridBagConstraints.WEST, GridBagConstraints.NONE,
0875: new Insets(0, 0, 0, 0), 0, 0));
0876: jPanel5.add(enableColorThemingCheckBox, new GridBagConstraints(
0877: 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
0878: GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0));
0879: jPanel5.add(byRangeCheckBox, new GridBagConstraints(1, 0, 1, 1,
0880: 0.0, 0.0, GridBagConstraints.CENTER,
0881: GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0));
0882: scrollPane.getViewport().add(table);
0883: }
0884:
0885: protected void enableColorThemingCheckBox_actionPerformed(
0886: ActionEvent e) {
0887: if (table.getRowCount() == 1) {
0888: //1 == just the "All other values" row. [Jon Aquino]
0889: populateTable();
0890: }
0891:
0892: updateComponents();
0893: }
0894:
0895: void attributeNameComboBox_actionPerformed(ActionEvent e) {
0896: try {
0897: if (initializing) {
0898: return;
0899: }
0900:
0901: if (attributeNameComboBox.getItemCount() == 0) {
0902: return;
0903: }
0904: Object selectedItem = attributeNameComboBox
0905: .getSelectedItem();
0906: if (selectedItem != null
0907: && selectedItem.equals(lastAttributeName)) {
0908: //Don't want to clear the table if the attribute hasn't changed. [Jon Aquino]
0909: return;
0910: }
0911:
0912: stopCellEditing();
0913: populateTable();
0914: } finally {
0915: //Want to set lastAttributeName even during initialization. Otherwise, the following
0916: //undesirable behaviour occurs: Open dialog box; press Get All Values
0917: // -- the table is populated; edit the table; click on attribute combo box but click
0918: //on the same attribute -- the table is repopulated! [Jon Aquino]
0919: lastAttributeName = getAttributeName();
0920:
0921: if (table.getModel() instanceof ColorThemingTableModel) {
0922: //Model will be DefaultTableModel during early initialization. [Jon Aquino]
0923: tableModel().setAttributeName(getAttributeName());
0924: }
0925: }
0926: }
0927:
0928: public ColorThemingTableModel tableModel() {
0929: return (ColorThemingTableModel) table.getModel();
0930: }
0931:
0932: private SortedSet getNonNullAttributeValues() {
0933: TreeSet values = new TreeSet();
0934:
0935: for (Iterator i = layer.getFeatureCollectionWrapper()
0936: .getFeatures().iterator(); i.hasNext();) {
0937: Feature feature = (Feature) i.next();
0938:
0939: if (feature.getAttribute(getAttributeName()) != null) {
0940: values.add(ColorThemingStyle.trimIfString(feature
0941: .getAttribute(getAttributeName())));
0942: }
0943: }
0944:
0945: return values;
0946: }
0947:
0948: public void populateTable() {
0949: if (!(enableColorThemingCheckBox.isSelected() && (attributeNameComboBox
0950: .getItemCount() > 0))) {
0951: return;
0952: }
0953: stopCellEditing();
0954: tableModel().clear();
0955: tableModel()
0956: .setMaps(
0957: toAttributeValueToBasicStyleMap(filteredAttributeValues()),
0958: toAttributeValueToLabelMap(filteredAttributeValues()));
0959: tableModel().sort(tableModel().wasLastSortAscending());
0960: applyColorScheme();
0961: }
0962:
0963: private Collection filteredAttributeValues() {
0964: return state.filterAttributeValues(getNonNullAttributeValues());
0965: }
0966:
0967: private Map toAttributeValueToLabelMap(Collection attributeValues) {
0968: Map attributeValueToAttributeValueMap = new TreeMap();
0969: for (Iterator i = attributeValues.iterator(); i.hasNext();) {
0970: Object attributeValue = i.next();
0971: attributeValueToAttributeValueMap.put(attributeValue,
0972: attributeValue);
0973: }
0974: Map attributeValueToLabelMap = CollectionUtil.inverse(state
0975: .toExternalFormat(attributeValueToAttributeValueMap));
0976: for (Iterator i = attributeValueToLabelMap.keySet().iterator(); i
0977: .hasNext();) {
0978: Object attributeValue = i.next();
0979: attributeValueToLabelMap.put(attributeValue,
0980: attributeValueToLabelMap.get(attributeValue)
0981: .toString());
0982: }
0983: return attributeValueToLabelMap;
0984: }
0985:
0986: private Map toAttributeValueToBasicStyleMap(
0987: Collection attributeValues) {
0988: Map attributeValueToBasicStyleMap = new TreeMap();
0989: for (Iterator i = attributeValues.iterator(); i.hasNext();) {
0990: Object attributeValue = i.next();
0991: attributeValueToBasicStyleMap.put(attributeValue,
0992: new BasicStyle());
0993: }
0994: return attributeValueToBasicStyleMap;
0995: }
0996:
0997: void colorSchemeComboBox_actionPerformed(ActionEvent e) {
0998: if (initializing) {
0999: return;
1000: }
1001:
1002: stopCellEditing();
1003: layer.getLayerManager().getBlackboard().put(COLOR_SCHEME_KEY,
1004: colorSchemeComboBox.getSelectedItem());
1005: applyColorScheme();
1006: colorSchemeForInserts = null;
1007: }
1008:
1009: private ColorScheme getColorSchemeForInserts() {
1010: //Cache the colour-scheme; otherwise the first color will always
1011: //be used for inserts. [Jon Aquino]
1012: if ((colorSchemeForInserts == null)
1013: || !colorSchemeForInserts.getName().equalsIgnoreCase(
1014: (String) colorSchemeComboBox.getSelectedItem())) {
1015: colorSchemeForInserts = ColorScheme
1016: .create((String) colorSchemeComboBox
1017: .getSelectedItem());
1018: }
1019:
1020: return colorSchemeForInserts;
1021: }
1022:
1023: public void applyColorScheme() {
1024: stopCellEditing();
1025: state.applyColorScheme(state
1026: .filterColorScheme(ColorScheme
1027: .create((String) colorSchemeComboBox
1028: .getSelectedItem())));
1029: }
1030:
1031: private void cancelCellEditing() {
1032: if (table.getCellEditor() instanceof DefaultCellEditor) {
1033: ((DefaultCellEditor) table.getCellEditor())
1034: .cancelCellEditing();
1035: }
1036: }
1037:
1038: public String validateInput() {
1039: stopCellEditing();
1040:
1041: //Split into #validateInput and #internalValidateInput to avoid
1042: //infinite recursion (#stopCellEditing causes #fireTableChanged
1043: //which causes #validateInput which causes #stopCellEditing...)
1044: //[Jon Aquino]
1045: return internalValidateInput();
1046: }
1047:
1048: private String internalValidateInput() {
1049: if (!enableColorThemingCheckBox.isSelected()) {
1050: return null;
1051: }
1052:
1053: if (errorMessages.isEmpty()) {
1054: return null;
1055: }
1056:
1057: return errorMessages.iterator().next().toString();
1058: }
1059:
1060: /**
1061: * @return enabled
1062: */
1063: private boolean setErrorMessage(ErrorMessage message,
1064: boolean enabled) {
1065: //Always remove the original, because #add doesn't replace, but is
1066: //ignored if the message already exists. Need to replace because the
1067: //"specific part" of the error message may be new. [Jon Aquino]
1068: errorMessages.remove(message);
1069:
1070: if (enabled) {
1071: errorMessages.add(message);
1072: }
1073:
1074: updateErrorDisplay();
1075:
1076: return enabled;
1077: }
1078:
1079: private void updateErrorDisplay() {
1080: String errorMessage = internalValidateInput();
1081:
1082: if (errorMessage != null) {
1083: statusLabel.setText(errorMessage);
1084: statusLabel.setIcon(GUIUtil.toSmallIcon(IconLoader
1085: .icon("Delete.gif")));
1086: } else {
1087: statusLabel.setText(" ");
1088: statusLabel.setIcon(null);
1089: }
1090: }
1091:
1092: private void setState(State state) {
1093: this .state.deactivate();
1094: jPanel1.remove(this .state.getPanel());
1095:
1096: //Need to call #revalidate; otherwise, the component won't get
1097: //removed. [Jon Aquino]
1098: this .state = state;
1099: jPanel1.add(state.getPanel(), new GridBagConstraints(3, 0, 1,
1100: 1, 0.0, 0.0, GridBagConstraints.CENTER,
1101: GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0));
1102: initializing = true;
1103:
1104: try {
1105: colorSchemeComboBox.setModel(new DefaultComboBoxModel(
1106: new Vector(state.getColorSchemeNames())));
1107:
1108: //The colour scheme may not be present if it was taken from the
1109: //set of discrete colour schemes but the combo box currently contains
1110: //the range colour schemes. [Jon Aquino]
1111: colorSchemeComboBox.setSelectedItem(layer.getLayerManager()
1112: .getBlackboard().get(COLOR_SCHEME_KEY,
1113: colorSchemeComboBox.getItemAt(0)));
1114: } finally {
1115: initializing = false;
1116: }
1117:
1118: updateComponents();
1119: this .state.activate();
1120: jPanel1.repaint();
1121: }
1122:
1123: void byRangeCheckBox_actionPerformed(ActionEvent e) {
1124: setState(byRangeCheckBox.isSelected() ? (State) rangeColorThemingState
1125: : discreteColorThemingState);
1126: populateTable();
1127: }
1128:
1129: void transparencySlider_stateChanged(ChangeEvent e) {
1130: repaint();
1131: }
1132:
1133: public JSlider getTransparencySlider() {
1134: return transparencySlider;
1135: }
1136:
1137: public JTable getTable() {
1138: return table;
1139: }
1140:
1141: public static interface State {
1142: public String getAllOtherValuesDescription();
1143:
1144: public ColorScheme filterColorScheme(ColorScheme colorScheme);
1145:
1146: public void activate();
1147:
1148: public void deactivate();
1149:
1150: public Collection getColorSchemeNames();
1151:
1152: public void applyColorScheme(ColorScheme scheme);
1153:
1154: public Collection filterAttributeValues(
1155: SortedSet attributeValues);
1156:
1157: public String getAttributeValueColumnTitle();
1158:
1159: public JComponent getPanel();
1160:
1161: /**
1162: * Performs any necessary modifications to the map before applying
1163: * it to the layer.
1164: */
1165: public Map toExternalFormat(Map attributeValueToObjectMap);
1166:
1167: public Map fromExternalFormat(Map attributeValueToObjectMap);
1168: }
1169:
1170: private abstract class MyPlugIn extends AbstractPlugIn {
1171: public abstract Icon getIcon();
1172: }
1173:
1174: private class ErrorMessage {
1175: private String commonPart;
1176: private String specificPart;
1177:
1178: public ErrorMessage(String commonPart) {
1179: this (commonPart, "");
1180: }
1181:
1182: public ErrorMessage(String commonPart, String specificPart) {
1183: this .commonPart = commonPart;
1184: this .specificPart = specificPart;
1185: }
1186:
1187: public int hashCode() {
1188: return commonPart.hashCode();
1189: }
1190:
1191: public boolean equals(Object obj) {
1192: return commonPart.equals(((ErrorMessage) obj).commonPart);
1193: }
1194:
1195: public String toString() {
1196: return commonPart + specificPart;
1197: }
1198: }
1199: }
|