001: /*
002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
003: * for visualizing and manipulating spatial features with geometry and attributes.
004: *
005: * Copyright (C) 2003 Vivid Solutions
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: *
021: * For more information, contact:
022: *
023: * Vivid Solutions
024: * Suite #1A
025: * 2328 Government Street
026: * Victoria BC V8T 5G5
027: * Canada
028: *
029: * (250)385-6040
030: * www.vividsolutions.com
031: */
032: package com.vividsolutions.jump.workbench.ui.renderer.style;
033:
034: import java.awt.GridBagConstraints;
035: import java.awt.GridBagLayout;
036: import java.awt.Insets;
037: import java.awt.event.ActionEvent;
038: import java.awt.event.ActionListener;
039: import java.util.ArrayList;
040: import java.util.Collection;
041: import java.util.Collections;
042: import java.util.Iterator;
043: import java.util.List;
044: import java.util.Map;
045: import java.util.Set;
046: import java.util.SortedMap;
047: import java.util.SortedSet;
048: import java.util.TreeMap;
049: import java.util.TreeSet;
050:
051: import javax.swing.DefaultComboBoxModel;
052: import javax.swing.JButton;
053: import javax.swing.JComboBox;
054: import javax.swing.JComponent;
055: import javax.swing.JLabel;
056: import javax.swing.JPanel;
057: import javax.swing.event.TableModelEvent;
058: import javax.swing.event.TableModelListener;
059:
060: import com.vividsolutions.jts.util.Assert;
061: import com.vividsolutions.jump.I18N;
062: import com.vividsolutions.jump.util.CollectionUtil;
063: import com.vividsolutions.jump.util.Range;
064:
065: public class RangeColorThemingState implements
066: ColorThemingStylePanel.State {
067: private ColorThemingStylePanel stylePanel;
068: private static final String RANGE_COUNT_KEY = RangeColorThemingState.class
069: .getName()
070: + " - RANGE COUNT";
071:
072: public String getAllOtherValuesDescription() {
073: return I18N
074: .get("ui.renderer.style.RangeColorThemingState.values-below-these-values");
075: }
076:
077: public String getAttributeValueColumnTitle() {
078: return I18N
079: .get("ui.renderer.style.RangeColorThemingState.minimum-attribute-values");
080: }
081:
082: private int getRangeCount() {
083: return ((Integer) comboBox.getSelectedItem()).intValue();
084: }
085:
086: public Collection filterAttributeValues(SortedSet attributeValues) {
087: //-1 because one row in the table is reserved for "all other values". [Jon Aquino]
088: int maxFilteredSize = getRangeCount() - 1;
089: //Obtain even distribution. [Jon Aquino]
090: ArrayList attributeValueList = new ArrayList(attributeValues);
091: Set filteredValues = new TreeSet();
092: CollectionUtil.stretch(attributeValueList, filteredValues,
093: maxFilteredSize);
094: return filteredValues;
095: }
096:
097: private JPanel panel = new JPanel(new GridBagLayout()) {
098: public void setEnabled(boolean enabled) {
099: comboBox.setEnabled(enabled);
100: label.setEnabled(enabled);
101: reverseButton.setEnabled(enabled);
102: super .setEnabled(enabled);
103: }
104: };
105:
106: public RangeColorThemingState(
107: final ColorThemingStylePanel stylePanel) {
108: this .stylePanel = stylePanel;
109: addComboBoxItems();
110: comboBox.setSelectedItem(stylePanel.getLayer()
111: .getLayerManager().getBlackboard().get(RANGE_COUNT_KEY,
112: new Integer(5)));
113: //Don't add action listeners until items have been added to the
114: //combo box. [Jon Aquino]
115: comboBox.addActionListener(new ActionListener() {
116: public void actionPerformed(ActionEvent e) {
117: stylePanel.populateTable();
118: stylePanel.getLayer().getLayerManager().getBlackboard()
119: .put(RANGE_COUNT_KEY,
120: comboBox.getSelectedItem());
121: }
122: });
123: panel.add(label, new GridBagConstraints(1, 0, 1, 1, 0, 0,
124: GridBagConstraints.WEST, GridBagConstraints.NONE,
125: new Insets(2, 2, 2, 2), 0, 0));
126: panel.add(comboBox, new GridBagConstraints(2, 0, 1, 1, 0, 0,
127: GridBagConstraints.WEST, GridBagConstraints.NONE,
128: new Insets(2, 2, 2, 2), 0, 0));
129: panel.add(reverseButton, new GridBagConstraints(3, 0, 1, 1, 0,
130: 0, GridBagConstraints.WEST, GridBagConstraints.NONE,
131: new Insets(2, 2, 2, 2), 0, 0));
132: reverseButton.addActionListener(new ActionListener() {
133: public void actionPerformed(ActionEvent e) {
134: reversingColorScheme = !reversingColorScheme;
135: stylePanel.applyColorScheme();
136: }
137: });
138: }
139:
140: private JButton reverseButton = new JButton(
141: I18N
142: .get("ui.renderer.style.RangeColorThemingState.reverse-colors"));
143:
144: private void addComboBoxItems() {
145: int maxColorSchemeSize = -1;
146: for (Iterator i = ColorScheme.rangeColorSchemeNames()
147: .iterator(); i.hasNext();) {
148: String rangeColorSchemeName = (String) i.next();
149: maxColorSchemeSize = Math.max(maxColorSchemeSize,
150: ColorScheme.create(rangeColorSchemeName)
151: .getColors().size());
152: }
153: for (int i = 3; i <= maxColorSchemeSize; i++) {
154: comboBoxModel.addElement(new Integer(i));
155: }
156: }
157:
158: private DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel();
159: private JComboBox comboBox = new JComboBox(comboBoxModel);
160: private JLabel label = new JLabel(
161: I18N
162: .get("ui.renderer.style.RangeColorThemingState.range-count"));
163:
164: public JComponent getPanel() {
165: return panel;
166: }
167:
168: public Map fromExternalFormat(Map attributeValueToObjectMap) {
169: //Table takes values, not ranges. [Jon Aquino]
170: TreeMap newMap = new TreeMap();
171: for (Iterator i = attributeValueToObjectMap.keySet().iterator(); i
172: .hasNext();) {
173: Range range = (Range) i.next();
174: newMap.put(range.getMin(), attributeValueToObjectMap
175: .get(range));
176: }
177: return newMap;
178: }
179:
180: public Map toExternalFormat(Map attributeValueToObjectMap) {
181: if (attributeValueToObjectMap.isEmpty()) {
182: return attributeValueToObjectMap;
183: }
184: //Turn the values into ranges. Validations have already ensured that
185: //the values are unique and contain no nulls. [Jon Aquino]
186: Assert.isTrue(attributeValueToObjectMap instanceof SortedMap);
187: TreeMap newMap = new Range.RangeTreeMap();
188: Object previousValue = null;
189: for (Iterator i = attributeValueToObjectMap.keySet().iterator(); i
190: .hasNext();) {
191: Object value = i.next();
192: try {
193: if (previousValue == null) {
194: //Let the default style handle values from negative infinity to
195: //the first value. [Jon Aquino]
196: continue;
197: }
198: //Make one side inclusive and the other exclusive to ensure no
199: //overlaps. [Jon Aquino]
200: newMap.put(
201: new Range(previousValue, true, value, false),
202: attributeValueToObjectMap.get(previousValue));
203: } finally {
204: previousValue = value;
205: }
206: }
207: newMap.put(new Range(previousValue, true,
208: new Range.PositiveInfinity(), false),
209: attributeValueToObjectMap.get(previousValue));
210: return newMap;
211: }
212:
213: public void applyColorScheme(ColorScheme colorScheme) {
214: stylePanel.tableModel().apply(
215: new ColorScheme(null, CollectionUtil.stretch(
216: colorScheme.getColors(), new ArrayList(),
217: stylePanel.tableModel().getRowCount())), false);
218: }
219:
220: public Collection getColorSchemeNames() {
221: return ColorScheme.rangeColorSchemeNames();
222: }
223:
224: private TableModelListener tableModelListener = new TableModelListener() {
225: public void tableChanged(TableModelEvent e) {
226: if (e instanceof ColorThemingTableModel.AttributeValueTableModelEvent) {
227: stylePanel.tableModel().sort(
228: stylePanel.tableModel().wasLastSortAscending());
229: //I'd like to scroll to the row at this point, but the user probably
230: //finished the edit by clicking on another cell, so even if I scroll
231: //to the row, it scrolls back to where the user clicked. [Jon Aquino]
232: }
233: }
234: };
235:
236: private int row(Object attributeValue) {
237: for (int i = 0; i < stylePanel.tableModel().getRowCount(); i++) {
238: Object otherAttributeValue = stylePanel.tableModel()
239: .getValueAt(i,
240: ColorThemingTableModel.ATTRIBUTE_COLUMN);
241: if (attributeValue == null && otherAttributeValue == null) {
242: return i;
243: }
244: if (attributeValue != null
245: && attributeValue.equals(otherAttributeValue)) {
246: return i;
247: }
248: }
249: Assert.shouldNeverReachHere();
250: return -1;
251: }
252:
253: public void activate() {
254: stylePanel.tableModel().addTableModelListener(
255: tableModelListener);
256: }
257:
258: public void deactivate() {
259: stylePanel.tableModel().removeTableModelListener(
260: tableModelListener);
261: }
262:
263: private boolean reversingColorScheme = false;
264:
265: public ColorScheme filterColorScheme(ColorScheme colorScheme) {
266: if (!reversingColorScheme) {
267: return colorScheme;
268: }
269: List colors = new ArrayList(colorScheme.getColors());
270: Collections.reverse(colors);
271: return new ColorScheme(colorScheme.getName(), colors);
272: }
273:
274: }
|