001package jahuwaldt.js.unit; 002 003import jahuwaldt.swing.JButtonGroup; 004import java.awt.Component; 005import java.awt.Dimension; 006import java.awt.event.ActionEvent; 007import java.awt.event.ActionListener; 008import java.util.ResourceBundle; 009import java.util.logging.Level; 010import java.util.logging.Logger; 011import javax.measure.converter.ConversionException; 012import javax.measure.unit.Unit; 013import javax.swing.*; 014import javax.swing.table.AbstractTableModel; 015import javax.swing.table.TableCellRenderer; 016import javax.swing.table.TableColumn; 017 018/** 019 * A panel the allows the user to select a set of units to be used. 020 * 021 * <p> Modified by: Joseph A. Huwaldt </p> 022 * 023 * @author Joseph A. Huwaldt, Date: October 14, 2008 024 * @version March 17, 2017 025 */ 026@SuppressWarnings("serial") 027public class EditUnitSetPanel extends JPanel { 028 029 /** 030 * The resource bundle for use in this class. 031 */ 032 static final ResourceBundle RESOURCES = ResourceBundle.getBundle("jahuwaldt.js.unit.EditUnitSetResources", java.util.Locale.getDefault()); 033 034 // The column names in the derived units table. 035 private static final String[] derColumnNames = { 036 RESOURCES.getString("unitTypeHeaderLabel"), 037 RESOURCES.getString("unitValueHeaderLabel") 038 }; 039 040 // The combo-boxes that contain the fundamental units. 041 private JComboBox timeCBox, lengthCBox, massCBox; 042 043 // The data stored in the derived units table. 044 private final Object[][] derRowData = new Object[16][2]; 045 046 // The JTable displaying the derived units. 047 private final JTable derTable; 048 049 // The UnitSet being edited. 050 private UnitSet theUnits = null; 051 052 /** 053 * Construct a panel that allows the user to select a set of units to be used. 054 * 055 * @param message A message to display telling the user what to do. 056 * @param unitSet The unit set being edited. 057 * @param showCustom The option for custom units is shown if this is true. 058 */ 059 @SuppressWarnings("OverridableMethodCallInConstructor") 060 public EditUnitSetPanel(String message, UnitSet unitSet, boolean showCustom) { 061 super(); 062 063 theUnits = (UnitSet)unitSet.clone(); 064 065 // Create the data to display in the derivative units table. 066 setDerivedData(); 067 068 // Layout the panel. 069 this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 070 this.setAlignmentX(Component.CENTER_ALIGNMENT); 071 072 // Add instructions at the top. 073 Box topPanel = Box.createHorizontalBox(); 074 topPanel.setAlignmentX(Component.RIGHT_ALIGNMENT); 075 this.add(topPanel); 076 077 topPanel.add(Box.createHorizontalStrut(10)); 078 topPanel.add(new JLabel(message)); 079 080 // Create a panel to display in the center. 081 Box centerPanel = Box.createVerticalBox(); 082 centerPanel.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20)); 083 this.add(centerPanel); 084 085 // Create a set of radio buttons for selecting the unit set to use. 086 Box panel = Box.createVerticalBox(); 087 panel.setAlignmentX(Component.CENTER_ALIGNMENT); 088 centerPanel.add(panel); 089 panel.setBorder(BorderFactory.createTitledBorder(RESOURCES.getString("coherentUnitSetsTitle"))); 090 JButtonGroup btnGroup = new JButtonGroup(); 091 092 JRadioButton rb = new JRadioButton(RESOURCES.getString("siUnitSetTxt")); 093 panel.add(rb); 094 boolean found = false; 095 if (unitSet.equals(new UnitSet(UnitSet.UnitSystem.SI_MKS))) { 096 rb.setSelected(true); 097 found = true; 098 } 099 rb.addActionListener(new ActionListener() { 100 @Override 101 public void actionPerformed(ActionEvent e) { 102 theUnits = new UnitSet(UnitSet.UnitSystem.SI_MKS); 103 updateDerivativesTable(); 104 setEnableFundamentalUnitBoxes(false); 105 setFundamentalUnitSelections(); 106 } 107 }); 108 btnGroup.add(rb); 109 110 rb = new JRadioButton(RESOURCES.getString("cgsUnitSetTxt")); 111 panel.add(rb); 112 if (!found && unitSet.equals(new UnitSet(UnitSet.UnitSystem.CGS))) { 113 rb.setSelected(true); 114 found = true; 115 } 116 rb.addActionListener(new ActionListener() { 117 @Override 118 public void actionPerformed(ActionEvent e) { 119 theUnits = new UnitSet(UnitSet.UnitSystem.CGS); 120 updateDerivativesTable(); 121 setEnableFundamentalUnitBoxes(false); 122 setFundamentalUnitSelections(); 123 } 124 }); 125 btnGroup.add(rb); 126 127 rb = new JRadioButton(RESOURCES.getString("fssUnitSetTxt")); 128 panel.add(rb); 129 if (!found && unitSet.equals(new UnitSet(UnitSet.UnitSystem.US_FSS))) { 130 rb.setSelected(true); 131 found = true; 132 } 133 rb.addActionListener(new ActionListener() { 134 @Override 135 public void actionPerformed(ActionEvent e) { 136 theUnits = new UnitSet(UnitSet.UnitSystem.US_FSS); 137 updateDerivativesTable(); 138 setEnableFundamentalUnitBoxes(false); 139 setFundamentalUnitSelections(); 140 } 141 }); 142 btnGroup.add(rb); 143 144 rb = new JRadioButton(RESOURCES.getString("fpsUnitSetTxt")); 145 panel.add(rb); 146 if (!found && unitSet.equals(new UnitSet(UnitSet.UnitSystem.US_FPS))) { 147 rb.setSelected(true); 148 found = true; 149 } 150 rb.addActionListener(new ActionListener() { 151 @Override 152 public void actionPerformed(ActionEvent e) { 153 theUnits = new UnitSet(UnitSet.UnitSystem.US_FPS); 154 updateDerivativesTable(); 155 setEnableFundamentalUnitBoxes(false); 156 setFundamentalUnitSelections(); 157 } 158 }); 159 btnGroup.add(rb); 160 161 if (showCustom) { 162 rb = new JRadioButton(RESOURCES.getString("customUnitSetTxt")); 163 panel.add(rb); 164 if (!found) 165 rb.setSelected(true); 166 rb.addActionListener(new ActionListener() { 167 @Override 168 public void actionPerformed(ActionEvent e) { 169 setEnableFundamentalUnitBoxes(true); 170 } 171 }); 172 btnGroup.add(rb); 173 174 // Create a series of combo-boxes that allow the user to choose custom combinations. 175 centerPanel.add(Box.createVerticalStrut(10)); 176 panel = Box.createHorizontalBox(); 177 panel.setBorder(BorderFactory.createTitledBorder(RESOURCES.getString("fundamentalUnitOptionsLabel"))); 178 centerPanel.add(panel); 179 180 panel.add(new JLabel(RESOURCES.getString("timeLabel") + "=", JLabel.RIGHT)); 181 timeCBox = new JComboBox(UnitSet.getSet(UnitSet.SetType.TIME)); 182 timeCBox.setMaximumSize(timeCBox.getPreferredSize()); 183 timeCBox.addActionListener(new ActionListener() { 184 @Override 185 public void actionPerformed(ActionEvent e) { 186 JComboBox cbox = (JComboBox)e.getSource(); 187 int value = cbox.getSelectedIndex(); 188 if (value >= 0) { 189 handleUnitChange(0, (Unit)cbox.getSelectedItem()); 190 } 191 } 192 }); 193 panel.add(timeCBox); 194 195 panel.add(new JLabel(RESOURCES.getString("lengthLabel") + "=", JLabel.RIGHT)); 196 lengthCBox = new JComboBox(UnitSet.getSet(UnitSet.SetType.LENGTH)); 197 lengthCBox.setMaximumSize(lengthCBox.getPreferredSize()); 198 lengthCBox.addActionListener(new ActionListener() { 199 @Override 200 public void actionPerformed(ActionEvent e) { 201 JComboBox cbox = (JComboBox)e.getSource(); 202 int value = cbox.getSelectedIndex(); 203 if (value >= 0) { 204 handleUnitChange(1, (Unit)cbox.getSelectedItem()); 205 } 206 } 207 }); 208 panel.add(lengthCBox); 209 210 panel.add(new JLabel(RESOURCES.getString("massLabel") + "=", JLabel.RIGHT)); 211 massCBox = new JComboBox(UnitSet.getSet(UnitSet.SetType.MASS)); 212 massCBox.setMaximumSize(massCBox.getPreferredSize()); 213 massCBox.addActionListener(new ActionListener() { 214 @Override 215 public void actionPerformed(ActionEvent e) { 216 JComboBox cbox = (JComboBox)e.getSource(); 217 int value = cbox.getSelectedIndex(); 218 if (value >= 0) { 219 handleUnitChange(2, (Unit)cbox.getSelectedItem()); 220 } 221 } 222 }); 223 panel.add(massCBox); 224 } 225 226 // Create a display of the derived units. 227 centerPanel.add(Box.createVerticalStrut(10)); 228 panel = Box.createHorizontalBox(); 229 panel.setBorder(BorderFactory.createTitledBorder(RESOURCES.getString("derivedUnitsLabel"))); 230 centerPanel.add(panel); 231 232 // Create a table to display the derived units. 233 derTable = new JTable(new DerivativeTableModel()); 234 derTable.setCellSelectionEnabled(false); 235 derTable.setColumnSelectionAllowed(false); 236 derTable.setRowSelectionAllowed(false); 237 derTable.setCellEditor(null); 238 derTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); // Show scroll bars. 239 240 // Initialize the column widths to show all the data. 241 initColumnWidths(derTable); 242 243 // Determine the table preferred width. 244 int width = 0; 245 int numColumns = derTable.getColumnCount(); 246 for (int i = 0; i < numColumns; ++i) { 247 TableColumn column = derTable.getColumnModel().getColumn(i); 248 width += column.getPreferredWidth(); 249 } 250 derTable.setPreferredScrollableViewportSize(new Dimension(width, 110)); 251 252 // Put the table in a scroll pane so that we have scroll bars. 253 JScrollPane scrollPane = new JScrollPane(derTable); 254 scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); 255 scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); 256 scrollPane.setMaximumSize(new Dimension(width + 20, 250)); 257 258 // Add the list to the panel. 259 panel.add(scrollPane); 260 261 // Set the intially selected items. 262 setFundamentalUnitSelections(); 263 264 // Enable/disable the unit boxes as appropriate. 265 setEnableFundamentalUnitBoxes(!found); 266 } 267 268 /** 269 * Method that handles the user changing one of the fundamental unit types. This is 270 * called whenever the user selects a new unit. 271 */ 272 private void handleUnitChange(int idx, Unit newUnit) { 273 274 try { 275 276 switch (idx) { 277 case 0: // Change in time units. 278 if (!newUnit.equals(theUnits.time())) 279 theUnits.setTime(newUnit); 280 break; 281 282 case 1: // Change in length units. 283 if (!newUnit.equals(theUnits.length())) 284 theUnits.setLength(newUnit); 285 break; 286 287 case 2: // Change in mass units. 288 if (!newUnit.equals(theUnits.mass())) 289 theUnits.setMass(newUnit); 290 break; 291 } 292 293 // Create a coherent unit set. 294 theUnits.makeConsistent(); 295 296 // Update the derived units table. 297 updateDerivativesTable(); 298 299 } catch (ConversionException ex) { 300 Logger.getLogger(EditUnitSetPanel.class.getName()).log(Level.SEVERE, null, ex); 301 } 302 } 303 304 /** 305 * Method that updates the derived unit table display. 306 */ 307 private void updateDerivativesTable() { 308 // Change the derived table data set. 309 setDerivedData(); 310 311 // Notify the derived table that it's data has changed. 312 AbstractTableModel model = (AbstractTableModel)derTable.getModel(); 313 model.fireTableDataChanged(); 314 } 315 316 /** 317 * Enabled or disable all the fundamental unit combo-boxes. 318 */ 319 private void setEnableFundamentalUnitBoxes(boolean enable) { 320 if (timeCBox != null) 321 timeCBox.setEnabled(enable); 322 if (lengthCBox != null) 323 lengthCBox.setEnabled(enable); 324 if (massCBox != null) 325 massCBox.setEnabled(enable); 326 } 327 328 /** 329 * Set the currently selected item in the fundamental unit boxes. 330 */ 331 private void setFundamentalUnitSelections() { 332 if (timeCBox != null) 333 timeCBox.setSelectedItem(theUnits.time()); 334 if (lengthCBox != null) 335 lengthCBox.setSelectedItem(theUnits.length()); 336 if (massCBox != null) 337 massCBox.setSelectedItem(theUnits.mass()); 338 } 339 340 /** 341 * Method that fills in the derived row data table with the current units. 342 */ 343 private void setDerivedData() { 344 int row = 0; 345 derRowData[row][0] = RESOURCES.getString("areaLabel"); derRowData[row++][1] = theUnits.area(); 346 derRowData[row][0] = RESOURCES.getString("volumeLabel"); derRowData[row++][1] = theUnits.volume(); 347 derRowData[row][0] = RESOURCES.getString("forceLabel"); derRowData[row++][1] = theUnits.force(); 348 derRowData[row][0] = RESOURCES.getString("inertiaLabel"); derRowData[row++][1] = theUnits.inertia(); 349 derRowData[row][0] = RESOURCES.getString("densityLabel"); derRowData[row++][1] = theUnits.massDensity(); 350 } 351 352 /** 353 * Returns the unit set stored with this panel. 354 * 355 * @return The unit set stored with this panel. 356 */ 357 public UnitSet getUnits() { 358 return theUnits; 359 } 360 361 /** 362 * Method that resets the minimum column widths. 363 */ 364 private static void initColumnWidths(JTable table) { 365 int numColumns = table.getColumnCount(); 366 for (int i = 0; i < numColumns; ++i) { 367 // Get each column. 368 TableColumn column = table.getColumnModel().getColumn(i); 369 370 // Determine the header width. 371 int headerWidth = 0; 372 try { 373 TableCellRenderer renderer = column.getHeaderRenderer(); 374 if (renderer == null) 375 renderer = table.getTableHeader().getDefaultRenderer(); 376 377 Component comp = renderer. 378 getTableCellRendererComponent( 379 null, column.getHeaderValue(), 380 false, false, 0, 0); 381 headerWidth = comp.getPreferredSize().width; 382 } catch (NullPointerException e) { 383 e.printStackTrace(); 384 } 385 386 if (i == 0) 387 column.setPreferredWidth(headerWidth + 10); 388 else 389 // Set the minimum width to the larger of the header width 390 // and 100 pixels. 391 column.setPreferredWidth(Math.max(100, headerWidth) + 10); 392 } 393 } 394 395 /** 396 * Table model for the derivative unit table. 397 */ 398 private class DerivativeTableModel extends AbstractTableModel { 399 400 @Override 401 public String getColumnName(int col) { 402 return derColumnNames[col]; 403 } 404 405 @Override 406 public int getRowCount() { 407 return derRowData.length; 408 } 409 410 @Override 411 public int getColumnCount() { 412 return derColumnNames.length; 413 } 414 415 @Override 416 public Object getValueAt(int row, int col) { 417 return derRowData[row][col]; 418 } 419 420 @Override 421 public boolean isCellEditable(int row, int col) { 422 return false; 423 } 424 425 @Override 426 public Class getColumnClass(int c) { 427 return getValueAt(0, c).getClass(); 428 } 429 } 430}