001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2016 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see http://www.gnu.org/licenses. 027 */ 028 029package edu.wisc.ssec.mcidasv.control; 030 031import java.awt.Color; 032import java.awt.Container; 033import java.awt.FlowLayout; 034import java.awt.event.ActionEvent; 035import java.awt.event.ActionListener; 036import java.rmi.RemoteException; 037import java.util.ArrayList; 038import java.util.HashMap; 039import java.util.Hashtable; 040import java.util.List; 041import java.util.Map; 042 043import javax.swing.JButton; 044import javax.swing.JComboBox; 045import javax.swing.JComponent; 046import javax.swing.JLabel; 047import javax.swing.JPanel; 048import javax.swing.JTabbedPane; 049import javax.swing.JTextField; 050import javax.swing.border.LineBorder; 051 052import org.python.core.PyObject; 053 054import visad.ConstantMap; 055import visad.Data; 056import visad.VisADException; 057import visad.georef.MapProjection; 058 059import ucar.unidata.data.DataChoice; 060import ucar.unidata.data.DataSource; 061import ucar.unidata.data.DirectDataChoice; 062import ucar.unidata.idv.MapViewManager; 063import ucar.unidata.util.GuiUtils; 064import ucar.unidata.util.LogUtil; 065import ucar.unidata.view.geoloc.MapProjectionDisplay; 066 067import edu.wisc.ssec.mcidasv.Constants; 068import edu.wisc.ssec.mcidasv.McIDASV; 069import edu.wisc.ssec.mcidasv.control.LinearCombo.Combination; 070import edu.wisc.ssec.mcidasv.control.LinearCombo.Selector; 071import edu.wisc.ssec.mcidasv.data.hydra.MultiDimensionSubset; 072import edu.wisc.ssec.mcidasv.data.hydra.MultiSpectralData; 073import edu.wisc.ssec.mcidasv.data.hydra.MultiSpectralDataSource; 074import edu.wisc.ssec.mcidasv.display.hydra.MultiSpectralDisplay; 075import edu.wisc.ssec.mcidasv.jython.Console; 076import edu.wisc.ssec.mcidasv.jython.ConsoleCallback; 077 078public class HydraCombo extends HydraControl { 079 080 081 private MultiSpectralDisplay display; 082 083 private DataChoice dataChoice = null; 084 085 private CombinationPanel comboPanel; 086 087 private final Hashtable<String, Object> persistable = new Hashtable<String, Object>(); 088 089 private MultiSpectralDataSource source; 090 091 float init_wavenumber; 092 093 private Map<String, Selector> selectorMap = new HashMap<String, Selector>(); 094 095 private static final String defaultButtonLabel = "Compute New Field"; 096 private JButton computeButton = new JButton(defaultButtonLabel); 097 098 public HydraCombo() { 099 super(); 100 setHelpUrl("idv.controls.hydra.channelcombinationcontrol"); 101 } 102 103 @Override public boolean init(final DataChoice choice) 104 throws VisADException, RemoteException { 105 ((McIDASV)getIdv()).getMcvDataManager().setHydraControl(choice, this); 106 dataChoice = choice; 107 List<DataSource> sources = new ArrayList<DataSource>(); 108 choice.getDataSources(sources); 109 source = ((MultiSpectralDataSource)sources.get(0)); 110 111 Float fieldSelectorChannel = (Float)getDataSelection().getProperty(Constants.PROP_CHAN); 112 if (fieldSelectorChannel == null) 113 fieldSelectorChannel = 0f; 114 init_wavenumber = fieldSelectorChannel; 115 116 display = new MultiSpectralDisplay((DirectDataChoice)choice); 117 display.setWaveNumber(fieldSelectorChannel); 118 display.setDisplayControl(this); 119 120 ((McIDASV)getIdv()).getMcvDataManager().setHydraDisplay(choice, display); 121 122 comboPanel = new CombinationPanel(this); 123 if (!persistable.isEmpty()) { 124 comboPanel.unpersistData(persistable); 125 persistable.clear(); 126 } 127 return true; 128 } 129 130 @Override public void initDone() { 131 MapViewManager viewManager = (MapViewManager) getViewManager(); 132 MapProjectionDisplay dispMaster = 133 (MapProjectionDisplay) viewManager.getMaster(); 134 try { 135 dispMaster.setMapProjection(getDataProjection()); 136 } catch (Exception e) { 137 logException("problem setting MapProjection", e); 138 } 139 140 getIdv().getIdvUIManager().showDashboard(); 141 } 142 143 public Hashtable<String, Object> getPersistable() { 144 return comboPanel.persistData(); 145 } 146 147 public void setPersistable(final Hashtable<String, Object> table) { 148 persistable.clear(); 149 persistable.putAll(table); 150 } 151 152 @Override public MapProjection getDataProjection() { 153 MapProjection mp = null; 154 Map<String, double[]> subset = null; 155 Hashtable table = dataChoice.getProperties(); 156 MultiDimensionSubset dataSel = 157 (MultiDimensionSubset) table.get(MultiDimensionSubset.key); 158 if (dataSel != null) { 159 subset = dataSel.getSubset(); 160 } 161 mp = source.getDataProjection(subset); 162 return mp; 163 } 164 165 @Override public Container doMakeContents() { 166 JTabbedPane pane = new JTabbedPane(); 167 pane.add("Channel Combination Tool", GuiUtils.inset(getComboTab(), 5)); 168 GuiUtils.handleHeavyWeightComponentsInTabs(pane); 169 return pane; 170 } 171 172 public void updateComboPanel(final String id, final float value) { 173 if (comboPanel == null) 174 return; 175 comboPanel.updateSelector(id, value); 176 } 177 178 protected void enableSelectorForWrapper(final SelectorWrapper wrapper) { 179 if (comboPanel == null) 180 return; 181 comboPanel.enableSelector(wrapper, false); 182 } 183 184 protected void disableSelectorForWrapper(final SelectorWrapper wrapper) { 185 if (comboPanel == null) 186 return; 187 comboPanel.disableSelector(wrapper, false); 188 } 189 190 protected MultiSpectralDisplay getMultiSpectralDisplay() { 191 return display; 192 } 193 194 protected JButton getComputeButton() { 195 return computeButton; 196 } 197 198 private JComponent getComboTab() { 199 computeButton.addActionListener(new ActionListener() { 200 public void actionPerformed(final ActionEvent e) { 201 comboPanel.queueCombination(); 202 computeButton.setEnabled(false); 203 showWaitCursor(); 204 } 205 }); 206 // wrap compute button in JPanel to retain preferred size 207 JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); 208 buttonPanel.add(computeButton); 209 JPanel tmp = GuiUtils.topCenterBottom(display.getDisplayComponent(), comboPanel.getPanel(), buttonPanel); 210 return tmp; 211 } 212 213 public void addCombination(final String name, final Data combination) { 214 if (combination != null) 215 source.addChoice(name, combination); 216 } 217 218 public void addCombination(final Combination combination) { 219 if (combination != null) 220 source.addChoice(combination.getName(), combination.getData()); 221 } 222 223 protected void addSelector(final Selector selector) throws Exception { 224 ConstantMap[] mapping = selector.getColor(); 225 float r = new Double(mapping[0].getConstant()).floatValue(); 226 float g = new Double(mapping[1].getConstant()).floatValue(); 227 float b = new Double(mapping[2].getConstant()).floatValue(); 228 Color javaColor = new Color(r, g, b); 229 display.createSelector(selector.getId(), javaColor); 230 display.setSelectorValue(selector.getId(), selector.getWaveNumber()); 231 selectorMap.put(selector.getId(), selector); 232 } 233 234 public void moveSelector(final String id, final float wavenum) { 235 if (!selectorMap.containsKey(id)) 236 return; 237 display.updateControlSelector(id, wavenum); 238 } 239 240 public void updateSelector(final String id, final float wavenum) { 241 if (!selectorMap.containsKey(id)) 242 return; 243 244 selectorMap.get(id).setWaveNumber(wavenum); 245 } 246 247 248 249 public enum DataType { HYPERSPECTRAL, MULTISPECTRAL }; 250 251 public enum WrapperState { ENABLED, DISABLED }; 252 253 public static class CombinationPanel implements ConsoleCallback { 254 private final SelectorWrapper a; 255 private final SelectorWrapper b; 256 private final SelectorWrapper c; 257 private final SelectorWrapper d; 258 259 private final OperationXY ab; 260 private final OperationXY cd; 261 262 private final CombineOperations abcd; 263 264 private final MultiSpectralDisplay display; 265 266 private final HydraCombo control; 267 268 private final Console console; 269 270 private final Map<String, Selector> selectorMap = new HashMap<String, Selector>(); 271 private final Map<String, SelectorWrapper> wrapperMap = new HashMap<String, SelectorWrapper>(); 272 273 private final DataType dataType; 274 275 public CombinationPanel(final HydraCombo control) { 276 this.control = control; 277 display = control.getMultiSpectralDisplay(); 278 if (display == null) 279 throw new NullPointerException("Display hasn't been initialized"); 280 281 MultiSpectralData data = display.getMultiSpectralData(); 282 if (data.hasBandNames()) 283 dataType = DataType.MULTISPECTRAL; 284 else 285 dataType = DataType.HYPERSPECTRAL; 286 287 this.console = new Console(); 288 console.setCallbackHandler(this); 289 290 a = makeWrapper("a", Color.RED); 291 b = makeWrapper("b", Color.GREEN); 292 c = makeWrapper("c", Color.BLUE); 293 d = makeWrapper("d", Color.MAGENTA); 294 295 enableSelector(a, true); 296 297 ab = new OperationXY(this, a, b); 298 cd = new OperationXY(this, c, d); 299 300 abcd = new CombineOperations(this, ab, cd); 301 } 302 303// public Console getConsole() { 304// return console; 305// } 306 307 public void ranBlock(final String line) { 308 PyObject jythonObj = console.getJythonObject("combo"); 309// if (jythonObj instanceof PyJavaInstance) { 310 Object combination = jythonObj.__tojava__(Object.class); 311 if (combination instanceof Combination) { 312 control.addCombination((Combination)combination); 313 control.getComputeButton().setEnabled(true); 314 control.showNormalCursor(); 315 } 316// } 317 } 318 319 public void updateSelector(final String id, final float channel) { 320 if (!selectorMap.containsKey(id)) 321 return; 322 323 Selector selector = selectorMap.get(id); 324 selector.setWaveNumber(channel); 325 wrapperMap.get(id).update(); 326 } 327 328 protected void addSelector(final Selector selector, final boolean enabled) throws Exception { 329 String id = selector.getId(); 330 if (enabled) { 331 display.createSelector(id, selector.getColor()); 332 display.setSelectorValue(id, selector.getWaveNumber()); 333 } 334 selectorMap.put(id, selector); 335 } 336 337 protected void disableSelector(final SelectorWrapper wrapper, final boolean disableWrapper) { 338 if (disableWrapper) 339 wrapper.disable(); 340 341 try { 342 display.removeSelector(wrapper.getSelector().getId()); 343 } catch (Exception e) { 344 LogUtil.logException("HydraCombo.disableSelector", e); 345 } 346 } 347 348 protected void enableSelector(final SelectorWrapper wrapper, final boolean enableWrapper) { 349 if (enableWrapper) 350 wrapper.enable(); 351 352 try { 353 Selector selector = wrapper.getSelector(); 354 String id = selector.getId(); 355 display.createSelector(id, selector.getColor()); 356 display.setSelectorValue(id, selector.getWaveNumber()); 357 } catch (Exception e) { 358 LogUtil.logException("HydraCombo.disableSelector", e); 359 } 360 } 361 362 private SelectorWrapper makeWrapper(final String var, final Color color) { 363 try { 364 ConstantMap[] mappedColor = MultiSpectralDisplay.makeColorMap(color); 365 366 SelectorWrapper tmp; 367 if (dataType == DataType.HYPERSPECTRAL) 368 tmp = new HyperspectralSelectorWrapper(var, mappedColor, control, console); 369 else 370 tmp = new MultispectralSelectorWrapper(var, mappedColor, control, console); 371 addSelector(tmp.getSelector(), false); 372 wrapperMap.put(tmp.getSelector().getId(), tmp); 373// console.injectObject(var, new PyJavaInstance(tmp.getSelector())); 374 console.injectObject(var, tmp.getSelector()); 375 return tmp; 376 } catch (Exception e) { 377 LogUtil.logException("HydraCombo.makeWrapper", e); 378 } 379 return null; 380 } 381 382 public JPanel getPanel() { 383 JPanel panel = new JPanel(new FlowLayout()); 384 panel.add(new JLabel("(")); 385 panel.add(a.getPanel()); 386 panel.add(ab.getBox()); 387 panel.add(b.getPanel()); 388 panel.add(new JLabel(")")); 389 panel.add(abcd.getBox()); 390 panel.add(new JLabel("(")); 391 panel.add(c.getPanel()); 392 panel.add(cd.getBox()); 393 panel.add(d.getPanel()); 394 panel.add(new JLabel(")")); 395 return panel; 396 } 397 398 public void queueCombination() { 399 String jy = "combo="+abcd.getJython(); 400 System.err.println("jython=" + jy); 401 console.queueLine(jy); 402 } 403 404 public Hashtable<String, Object> persistData() { 405 Hashtable<String, Object> table = new Hashtable<String, Object>(); 406 407 table.put("a", a.persistSelectorWrapper()); 408 table.put("b", b.persistSelectorWrapper()); 409 table.put("c", c.persistSelectorWrapper()); 410 table.put("d", d.persistSelectorWrapper()); 411 table.put("ab", ab.getOperation()); 412 table.put("cd", cd.getOperation()); 413 table.put("abcd", abcd.getOperation()); 414 return table; 415 } 416 417 public void unpersistData(final Hashtable<String, Object> table) { 418 Hashtable<String, String> tableA = (Hashtable<String, String>)table.get("a"); 419 Hashtable<String, String> tableB = (Hashtable<String, String>)table.get("b"); 420 Hashtable<String, String> tableC = (Hashtable<String, String>)table.get("c"); 421 Hashtable<String, String> tableD = (Hashtable<String, String>)table.get("d"); 422 423 a.unpersistSelectorWrapper(tableA); 424 b.unpersistSelectorWrapper(tableB); 425 c.unpersistSelectorWrapper(tableC); 426 d.unpersistSelectorWrapper(tableD); 427 428 String opAb = (String)table.get("ab"); 429 String opCd = (String)table.get("cd"); 430 String opAbcd = (String)table.get("abcd"); 431 432 ab.setOperation(opAb); 433 cd.setOperation(opCd); 434 abcd.setOperation(opAbcd); 435 } 436 } 437 438 private static class OperationXY { 439 private static final String[] OPERATIONS = { "+", "-", "/", "*", " " }; 440 private static final String INVALID_OP = " "; 441 private final JComboBox combo = new JComboBox(OPERATIONS); 442 443 private CombinationPanel comboPanel; 444 private SelectorWrapper x; 445 private SelectorWrapper y; 446 447 public OperationXY(final CombinationPanel comboPanel, final SelectorWrapper x, final SelectorWrapper y) { 448 this.x = x; 449 this.y = y; 450 this.comboPanel = comboPanel; 451 combo.setSelectedItem(" "); 452 combo.addActionListener(new ActionListener() { 453 public void actionPerformed(final ActionEvent e) { 454 if (getOperation().equals(" ")) { 455 comboPanel.disableSelector(y, true); 456 } else { 457 comboPanel.enableSelector(y, true); 458 } 459 } 460 }); 461 } 462 463 public void disable() { 464 comboPanel.disableSelector(x, true); 465 combo.setSelectedItem(INVALID_OP); 466 comboPanel.disableSelector(y, true); 467 } 468 469 public void enable() { 470 comboPanel.enableSelector(x, true); 471 } 472 473 public String getOperation() { 474 return (String)combo.getSelectedItem(); 475 } 476 477 public void setOperation(final String operation) { 478 combo.setSelectedItem(operation); 479 } 480 481 public JComboBox getBox() { 482 return combo; 483 } 484 485 public String getJython() { 486 String operation = getOperation(); 487 if (operation.equals(INVALID_OP)) 488 operation = ""; 489 490 String jython = x.getJython() + operation + y.getJython(); 491 if (x.isValid() && y.isValid()) 492 return "(" + jython + ")"; 493 return jython; 494 } 495 } 496 497 private static class CombineOperations { 498 private static final String[] OPERATIONS = { "+", "-", "/", "*", " "}; 499 private static final String INVALID_OP = " "; 500 private final JComboBox combo = new JComboBox(OPERATIONS); 501 502 private OperationXY x; 503 private OperationXY y; 504 505 public CombineOperations(final CombinationPanel comboPanel, final OperationXY x, final OperationXY y) { 506 this.x = x; 507 this.y = y; 508 combo.setSelectedItem(INVALID_OP); 509 combo.addActionListener(new ActionListener() { 510 public void actionPerformed(final ActionEvent e) { 511 if (getOperation().equals(" ")) { 512 y.disable(); 513 } else { 514 y.enable(); 515 } 516 } 517 }); 518 } 519 520 public String getOperation() { 521 return (String)combo.getSelectedItem(); 522 } 523 524 public void setOperation(final String operation) { 525 combo.setSelectedItem(operation); 526 } 527 528 public JComboBox getBox() { 529 return combo; 530 } 531 532 public String getJython() { 533 String operation = getOperation(); 534 if (operation.equals(INVALID_OP)) 535 operation = ""; 536 537 String jython = x.getJython() + operation + y.getJython(); 538 return jython; 539 } 540 } 541 542 private static abstract class SelectorWrapper { 543 protected static final String BLANK = "-----"; 544 private String variable; 545 private final ConstantMap[] color; 546 protected final Selector selector; 547 protected final MultiSpectralDisplay display; 548 protected final MultiSpectralData data; 549 private final JTextField scale = new JTextField(String.format("%3.1f", 1.0)); 550 protected WrapperState currentState = WrapperState.DISABLED; 551 552 public SelectorWrapper(final String variable, final ConstantMap[] color, final HydraCombo control, final Console console) { 553 this.display = control.getMultiSpectralDisplay(); 554 this.data = control.getMultiSpectralDisplay().getMultiSpectralData(); 555 this.variable = variable; 556 this.color = color; 557 this.selector = new Selector(control.init_wavenumber, color, control, console); 558 this.selector.addName(variable); 559 } 560 561 public Selector getSelector() { 562 return selector; 563 } 564 565 public JPanel getPanel() { 566 JPanel panel = new JPanel(new FlowLayout()); 567 JComponent comp = getWavenumberComponent(); 568 float r = new Double(color[0].getConstant()).floatValue(); 569 float g = new Double(color[1].getConstant()).floatValue(); 570 float b = new Double(color[2].getConstant()).floatValue(); 571 comp.setBorder(new LineBorder(new Color(r, g, b), 2)); 572 panel.add(scale); 573 panel.add(comp); 574 return panel; 575 } 576 577 public String getJython() { 578 if (!isValid()) 579 return ""; 580 return "(" + scale.getText() + "*" + variable + ")"; 581 } 582 583 public boolean isValid() { 584 return !getValue().equals(BLANK); 585 } 586 587 public void enable() { 588 setValue(Float.toString(selector.getWaveNumber())); 589 currentState = WrapperState.ENABLED; 590 } 591 592 public void disable() { 593 setValue(BLANK); 594 currentState = WrapperState.DISABLED; 595 } 596 597 public void update() { 598 setValue(Float.toString(selector.getWaveNumber())); 599 } 600 601 public Hashtable<String, String> persistSelectorWrapper() { 602 Hashtable<String, String> table = new Hashtable<String, String>(3); 603 String scaleText = scale.getText(); 604 String waveText = getValue(); 605 606 if (scaleText == null || scaleText.length() == 0) 607 scaleText = "1.0"; 608 if (waveText == null || waveText.length() == 0 || !isValid()) 609 waveText = BLANK; 610 611 table.put("variable", variable); 612 table.put("scale", scale.getText()); 613 table.put("wave", getValue()); 614 return table; 615 } 616 617 public void unpersistSelectorWrapper(final Hashtable<String, String> table) { 618 variable = table.get("variable"); 619 selector.addName(variable); 620 scale.setText(String.format("%3.1f", new Float(table.get("scale")))); 621 622 String waveText = table.get("wave"); 623 if (waveText.equals(BLANK)) { 624 disable(); 625 } else { 626 float wave = new Float(table.get("wave")); 627 selector.setWaveNumber(wave); 628 if (isValid()) 629 update(); 630 } 631 } 632 633 public abstract JComponent getWavenumberComponent(); 634 635 public abstract void setValue(final String value); 636 637 public abstract String getValue(); 638 } 639 640 private static class HyperspectralSelectorWrapper extends SelectorWrapper { 641 private final JTextField wavenumber; 642 public HyperspectralSelectorWrapper(final String variable, final ConstantMap[] color, final HydraCombo control, final Console console) { 643 super(variable, color, control, console); 644 wavenumber = new JTextField(BLANK, 7); 645 646 wavenumber.addActionListener(new ActionListener() { 647 public void actionPerformed(final ActionEvent e) { 648 String textVal = wavenumber.getText(); 649 if (textVal.equals(BLANK)) 650 return; 651 652 float wave = Float.parseFloat(textVal.trim()); 653 control.updateComboPanel(getSelector().getId(), wave); 654 } 655 }); 656 } 657 658 @Override public JComponent getWavenumberComponent() { 659 return wavenumber; 660 } 661 662 @Override public void setValue(final String value) { 663 if (value == null) 664 throw new NullPointerException(""); 665 666 if (!value.equals(BLANK)) { 667 float wave = Float.parseFloat(value); 668 String fmt = String.format("%7.2f", wave); 669 wavenumber.setText(fmt); 670 } 671 } 672 673 public String getValue() { 674 return wavenumber.getText(); 675 } 676 } 677 678 private static class MultispectralSelectorWrapper extends SelectorWrapper { 679 680 private final JComboBox bands; 681 682 public MultispectralSelectorWrapper(final String variable, final ConstantMap[] color, final HydraCombo control, final Console console) { 683 super(variable, color, control, console); 684 removeMSDListeners(); 685 bands = bigBadBox(control); 686 } 687 688 private void removeMSDListeners() { 689 JComboBox box = display.getBandSelectComboBox(); 690 for (ActionListener l : box.getActionListeners()) 691 box.removeActionListener(l); 692 } 693 694 private JComboBox bigBadBox(final HydraCombo control) { 695 final JComboBox box = new JComboBox(); 696 box.addItem(BLANK); 697 698 for (String name : data.getBandNames()) 699 box.addItem(name); 700 701 final SelectorWrapper wrapper = this; 702 box.addActionListener(new ActionListener() { 703 public void actionPerformed(final ActionEvent e) { 704 String selected = (String)box.getSelectedItem(); 705 float wave = Float.NaN; 706 707 if (!selected.equals(BLANK)) { 708 Map<String, Float> map = data.getBandNameMap(); 709 if (map.containsKey(selected)) 710 wave = map.get(selected); 711 712 if (currentState == WrapperState.DISABLED) 713 control.enableSelectorForWrapper(wrapper); 714 control.updateComboPanel(getSelector().getId(), wave); 715 } else { 716 control.disableSelectorForWrapper(wrapper); 717 } 718 } 719 }); 720 721 return box; 722 } 723 724 @Override public JComponent getWavenumberComponent() { 725 return bands; 726 } 727 728 @Override public void setValue(final String value) { 729 if (value == null) 730 throw new NullPointerException(); 731 732 if (!value.equals(BLANK)) { 733 String name = data.getBandNameFromWaveNumber(Float.parseFloat(value)); 734 bands.setSelectedItem(name); 735 } else { 736 bands.setSelectedItem(BLANK); 737 } 738 } 739 740 @Override public String getValue() { 741 return (String)bands.getSelectedItem(); 742 } 743 } 744}