From 15785cf43c4b31057cc5d83ebe6974dfdde81296 Mon Sep 17 00:00:00 2001 From: rusEfi Date: Fri, 6 Mar 2015 10:08:33 -0600 Subject: [PATCH] auto-sync --- java_console/.idea/libraries/jep.xml | 9 + .../.idea/libraries/swing_layout_1_0.xml | 9 + java_console/lib/jep.jar | Bin 0 -> 80355 bytes java_console/lib/swing-layout-1.0.jar | Bin 0 -> 140545 bytes .../romraider/resources/graphics/1d.gif | Bin 0 -> 304 bytes .../romraider/resources/graphics/2d.gif | Bin 0 -> 604 bytes .../romraider/resources/graphics/3d.gif | Bin 0 -> 641 bytes .../resources/graphics/3d_render.png | Bin 0 -> 1005 bytes .../resources/graphics/icon-close.png | Bin 0 -> 7423 bytes .../resources/graphics/icon-deccoarse.png | Bin 0 -> 4123 bytes .../resources/graphics/icon-decfine.png | Bin 0 -> 4103 bytes .../resources/graphics/icon-inccoarse.png | Bin 0 -> 4111 bytes .../resources/graphics/icon-incfine.png | Bin 0 -> 4124 bytes .../resources/graphics/icon-open.png | Bin 0 -> 7782 bytes .../resources/graphics/icon-palette.png | Bin 0 -> 2444 bytes .../resources/graphics/icon-save.png | Bin 0 -> 7406 bytes .../resources/graphics/icon-smooth.ico | Bin 0 -> 4414 bytes .../resources/graphics/icon-smooth.png | Bin 0 -> 1572 bytes .../resources/graphics/logger_blue.png | Bin 0 -> 707 bytes .../resources/graphics/logger_green.png | Bin 0 -> 769 bytes .../resources/graphics/logger_log_to_file.png | Bin 0 -> 542 bytes .../resources/graphics/logger_recording.png | Bin 0 -> 725 bytes .../resources/graphics/logger_red.png | Bin 0 -> 745 bytes .../resources/graphics/logger_restart.png | Bin 0 -> 766 bytes .../resources/graphics/logger_stop.png | Bin 0 -> 468 bytes .../graphics/romraider-ico-large.gif | Bin 0 -> 7906 bytes .../resources/graphics/romraider-ico.gif | Bin 0 -> 1022 bytes .../resources/graphics/romraider-ico.ico | Bin 0 -> 7358 bytes .../romraider/resources/graphics/splash.bmp | Bin 0 -> 207056 bytes .../romraider/resources/graphics/switch.gif | Bin 0 -> 193 bytes java_console/romraider/romraider.iml | 2 + .../projects/JFontChooser/JFontChooser.java | 271 +++ .../romraider/src/com/romraider/ECUExec.java | 146 ++ .../romraider/src/com/romraider/Settings.java | 873 ++++++++++ .../src/com/romraider/Version.java.template | 41 + .../com/romraider/editor/ecu/ECUEditor.java | 742 +++++++++ .../editor/ecu/ECUEditorManager.java | 46 + .../io/connection/ConnectionManager.java | 32 + .../io/connection/ConnectionProperties.java | 36 + .../connection/ConnectionPropertiesImpl.java | 82 + .../port/SerialPortRefreshListener.java | 28 + .../romraider/logger/car/util/Constants.java | 40 + .../logger/car/util/SpeedCalculator.java | 44 + .../logger/car/util/TorqueCalculator.java | 39 + .../romraider/logger/ecu/EcuLoggerExec.java | 47 + .../comms/controller/LoggerController.java | 41 + .../controller/LoggerControllerImpl.java | 111 ++ .../comms/learning/LearningTableValues.java | 26 + .../flkctable/FlkcTableQueryBuilder.java | 108 ++ .../comms/learning/parameter/Parameter.java | 88 + .../parameter/ParameterCrossReference.java | 64 + .../parameter/ParameterIdComparator.java | 35 + .../ecu/comms/manager/PollingState.java | 44 + .../ecu/comms/manager/PollingStateImpl.java | 89 + .../ecu/comms/manager/QueryManager.java | 41 + .../ecu/comms/manager/QueryManagerImpl.java | 459 +++++ .../comms/manager/TransmissionManager.java | 35 + .../manager/TransmissionManagerImpl.java | 76 + .../logger/ecu/comms/query/EcuInit.java | 28 + .../ecu/comms/query/EcuInitCallback.java | 26 + .../logger/ecu/comms/query/EcuQuery.java | 31 + .../logger/ecu/comms/query/EcuQueryImpl.java | 75 + .../logger/ecu/comms/query/ExternalQuery.java | 26 + .../ecu/comms/query/ExternalQueryImpl.java | 46 + .../logger/ecu/comms/query/Query.java | 30 + .../logger/ecu/comms/query/Response.java | 34 + .../logger/ecu/comms/query/ResponseImpl.java | 52 + .../logger/ecu/comms/query/SSMEcuInit.java | 46 + .../ecu/comms/readcodes/ReadCodesManager.java | 25 + .../logger/ecu/comms/reset/ResetManager.java | 24 + .../definition/ConvertorUpdateListener.java | 26 + .../logger/ecu/definition/EcuAddress.java | 31 + .../logger/ecu/definition/EcuAddressImpl.java | 123 ++ .../logger/ecu/definition/EcuData.java | 26 + .../ecu/definition/EcuDataConvertor.java | 39 + .../logger/ecu/definition/EcuDataLoader.java | 47 + .../logger/ecu/definition/EcuDataType.java | 26 + .../logger/ecu/definition/EcuDefinition.java | 35 + .../ecu/definition/EcuDefinitionImpl.java | 67 + .../EcuDerivedParameterConvertor.java | 26 + .../definition/EcuDerivedParameterImpl.java | 129 ++ .../logger/ecu/definition/EcuParameter.java | 26 + .../ecu/definition/EcuParameterImpl.java | 109 ++ .../logger/ecu/definition/EcuSwitch.java | 24 + .../logger/ecu/definition/EcuSwitchImpl.java | 101 ++ .../ecu/definition/EvaluateEcuDefinition.java | 51 + .../logger/ecu/definition/ExternalData.java | 26 + .../logger/ecu/definition/LoggerData.java | 41 + .../plugin/PluginFilenameFilter.java | 30 + .../xml/EcuDefinitionDocumentLoader.java | 70 + .../definition/xml/EcuDefinitionHandler.java | 256 +++ .../xml/EcuDefinitionInheritanceList.java | 119 ++ .../xml/EcuTableDefinitionHandler.java | 113 ++ .../ecu/exception/ConfigurationException.java | 41 + .../ecu/exception/FileLoggerException.java | 41 + .../exception/InvalidResponseException.java | 41 + .../ecu/exception/NotConnectedException.java | 39 + .../ecu/exception/PortNotFoundException.java | 40 + .../SerialCommunicationException.java | 41 + .../UnsupportedPortTypeException.java | 40 + .../UnsupportedProtocolException.java | 41 + .../logger/ecu/profile/UserProfile.java | 40 + .../ecu/profile/UserProfileFileFilter.java | 38 + .../logger/ecu/profile/UserProfileImpl.java | 151 ++ .../logger/ecu/profile/UserProfileItem.java | 32 + .../ecu/profile/UserProfileItemImpl.java | 51 + .../logger/ecu/profile/UserProfileLoader.java | 26 + .../ecu/profile/UserProfileLoaderImpl.java | 56 + .../ecu/profile/xml/UserProfileHandler.java | 79 + .../logger/ecu/ui/DataRegistrationBroker.java | 32 + .../ecu/ui/DataRegistrationBrokerImpl.java | 93 ++ .../logger/ecu/ui/EcuDataComparator.java | 31 + .../logger/ecu/ui/MessageListener.java | 35 + .../logger/ecu/ui/SerialPortComboBox.java | 103 ++ .../logger/ecu/ui/StatusChangeListener.java | 31 + .../logger/ecu/ui/StatusIndicator.java | 73 + .../ecu/ui/handler/DataUpdateHandler.java | 37 + .../ui/handler/DataUpdateHandlerManager.java | 36 + .../handler/DataUpdateHandlerManagerImpl.java | 57 + .../ecu/ui/handler/file/FileLogger.java | 32 + .../FileLoggerControllerSwitchHandler.java | 25 + .../FileLoggerControllerSwitchMonitor.java | 29 + ...FileLoggerControllerSwitchMonitorImpl.java | 42 + .../ecu/ui/handler/file/FileLoggerImpl.java | 146 ++ .../ui/handler/file/FileUpdateHandler.java | 32 + .../handler/file/FileUpdateHandlerImpl.java | 212 +++ .../ecu/ui/handler/graph/SpringUtilities.java | 222 +++ .../injector/InjectorUpdateHandler.java | 187 +++ .../ecu/ui/handler/livedata/LiveDataRow.java | 79 + .../handler/livedata/LiveDataTableModel.java | 108 ++ .../livedata/LiveDataUpdateHandler.java | 65 + .../ui/handler/table/TableUpdateHandler.java | 122 ++ .../ecu/ui/paramlist/ParameterListTable.java | 97 ++ .../ui/paramlist/ParameterListTableModel.java | 127 ++ .../logger/ecu/ui/paramlist/ParameterRow.java | 46 + .../ecu/ui/paramlist/UnitsComboBoxEditor.java | 67 + .../ui/paramlist/UnitsComboBoxRenderer.java | 44 + .../ecu/ui/playback/PlaybackManager.java | 38 + .../ecu/ui/playback/PlaybackManagerImpl.java | 83 + .../ecu/ui/swing/layout/BetterFlowLayout.java | 79 + .../ecu/ui/swing/menubar/util/FileHelper.java | 91 + .../AirFuelLearningTableModel.java | 79 + ...FineLearningKnockCorrectionTableModel.java | 92 + .../tablemodels/ReadCodesTableModel.java | 83 + .../VehicleInformationTableModel.java | 68 + .../tablemodels/renderers/CentreRenderer.java | 50 + .../renderers/LtvCellRenderer.java | 89 + .../ui/swing/vertical/VerticalLabelUI.java | 99 ++ .../ui/swing/vertical/VerticalTextIcon.java | 316 ++++ .../com/romraider/logger/ecu/ui/tab/Tab.java | 46 + .../logger/ecu/ui/tab/TableFinder.java | 33 + .../ecu/ui/tab/injector/InjectorTab.java | 46 + .../src/com/romraider/maps/DataCell.java | 535 ++++++ .../romraider/src/com/romraider/maps/Rom.java | 419 +++++ .../src/com/romraider/maps/RomChecksum.java | 81 + .../src/com/romraider/maps/RomID.java | 219 +++ .../src/com/romraider/maps/Scale.java | 215 +++ .../src/com/romraider/maps/Table.java | 1478 +++++++++++++++++ .../src/com/romraider/maps/Table1D.java | 306 ++++ .../src/com/romraider/maps/Table2D.java | 509 ++++++ .../src/com/romraider/maps/Table3D.java | 1142 +++++++++++++ .../src/com/romraider/maps/TableSwitch.java | 388 +++++ .../src/com/romraider/net/BrowserControl.java | 89 + .../romraider/src/com/romraider/net/URL.java | 68 + .../command/executor/CommandExecutor.java | 27 + .../RamTuneTestAppConnectionProperties.java | 75 + .../com/romraider/swing/AbstractFrame.java | 65 + .../com/romraider/swing/CategoryTreeNode.java | 31 + .../romraider/swing/CompareImagesForm.java | 392 +++++ .../romraider/swing/CustomToolbarLayout.java | 110 ++ .../src/com/romraider/swing/DebugPanel.java | 54 + .../romraider/swing/DefinitionManager.java | 263 +++ .../com/romraider/swing/ECUEditorMenuBar.java | 623 +++++++ .../com/romraider/swing/ECUEditorToolBar.java | 157 ++ .../com/romraider/swing/ECUImageFilter.java | 36 + .../romraider/swing/GenericFileFilter.java | 103 ++ .../com/romraider/swing/JProgressPane.java | 80 + .../com/romraider/swing/JTableChooser.java | 122 ++ .../romraider/swing/LookAndFeelManager.java | 69 + .../com/romraider/swing/MDIDesktopPane.java | 295 ++++ .../swing/ParameterIdsTableModel.java | 79 + .../com/romraider/swing/RomCellRenderer.java | 151 ++ .../com/romraider/swing/RomPropertyPanel.java | 305 ++++ .../src/com/romraider/swing/RomTree.java | 142 ++ .../com/romraider/swing/RomTreeRootNode.java | 31 + .../com/romraider/swing/ScalesTableModel.java | 95 ++ .../src/com/romraider/swing/SetFont.java | 90 + .../src/com/romraider/swing/SettingsForm.java | 1144 +++++++++++++ .../swing/SwitchStateTableModel.java | 81 + .../romraider/swing/TableChooserTreeNode.java | 38 + .../src/com/romraider/swing/TableFrame.java | 234 +++ .../src/com/romraider/swing/TableMenuBar.java | 407 +++++ .../com/romraider/swing/TableMenuItem.java | 12 + .../romraider/swing/TablePropertyPanel.java | 362 ++++ .../src/com/romraider/swing/TableToolBar.java | 794 +++++++++ .../com/romraider/swing/TableTreeNode.java | 57 + .../src/com/romraider/swing/XMLFilter.java | 106 ++ .../src/com/romraider/swing/menubar/Menu.java | 32 + .../com/romraider/swing/menubar/MenuItem.java | 46 + .../src/com/romraider/util/AxisRange.java | 39 + .../src/com/romraider/util/ByteUtil.java | 118 ++ .../src/com/romraider/util/ColorScaler.java | 62 + .../com/romraider/util/FormatFilename.java | 68 + .../src/com/romraider/util/HexUtil.java | 104 ++ .../src/com/romraider/util/JEPUtil.java | 48 + .../src/com/romraider/util/JREChecker.java | 59 + .../src/com/romraider/util/LogManager.java | 33 + .../src/com/romraider/util/MD5Checksum.java | 53 + .../src/com/romraider/util/ObjectCloner.java | 96 ++ .../src/com/romraider/util/ParamChecker.java | 99 ++ .../src/com/romraider/util/Platform.java | 31 + .../src/com/romraider/util/RomServer.java | 96 ++ .../com/romraider/util/SaxParserFactory.java | 57 + .../com/romraider/util/SettingsManager.java | 103 ++ .../util/ThreadCheckingRepaintManager.java | 96 ++ .../src/com/romraider/util/ThreadUtil.java | 50 + .../com/romraider/util/proxy/Proxifier.java | 46 + .../romraider/util/proxy/TimerWrapper.java | 52 + .../src/com/romraider/util/proxy/Wrapper.java | 25 + .../src/com/romraider/xml/DOMHelper.java | 65 + .../com/romraider/xml/DOMRomUnmarshaller.java | 617 +++++++ .../com/romraider/xml/DOMSettingsBuilder.java | 430 +++++ .../xml/DOMSettingsUnmarshaller.java | 402 +++++ .../xml/InvalidTableNameException.java | 30 + .../com/romraider/xml/RomAttributeParser.java | 202 +++ .../romraider/xml/RomNotFoundException.java | 28 + .../xml/TableIsOmittedException.java | 32 + .../romraider/xml/TableNotFoundException.java | 29 + 228 files changed, 25314 insertions(+) create mode 100644 java_console/.idea/libraries/jep.xml create mode 100644 java_console/.idea/libraries/swing_layout_1_0.xml create mode 100644 java_console/lib/jep.jar create mode 100644 java_console/lib/swing-layout-1.0.jar create mode 100644 java_console/romraider/resources/graphics/1d.gif create mode 100644 java_console/romraider/resources/graphics/2d.gif create mode 100644 java_console/romraider/resources/graphics/3d.gif create mode 100644 java_console/romraider/resources/graphics/3d_render.png create mode 100644 java_console/romraider/resources/graphics/icon-close.png create mode 100644 java_console/romraider/resources/graphics/icon-deccoarse.png create mode 100644 java_console/romraider/resources/graphics/icon-decfine.png create mode 100644 java_console/romraider/resources/graphics/icon-inccoarse.png create mode 100644 java_console/romraider/resources/graphics/icon-incfine.png create mode 100644 java_console/romraider/resources/graphics/icon-open.png create mode 100644 java_console/romraider/resources/graphics/icon-palette.png create mode 100644 java_console/romraider/resources/graphics/icon-save.png create mode 100644 java_console/romraider/resources/graphics/icon-smooth.ico create mode 100644 java_console/romraider/resources/graphics/icon-smooth.png create mode 100644 java_console/romraider/resources/graphics/logger_blue.png create mode 100644 java_console/romraider/resources/graphics/logger_green.png create mode 100644 java_console/romraider/resources/graphics/logger_log_to_file.png create mode 100644 java_console/romraider/resources/graphics/logger_recording.png create mode 100644 java_console/romraider/resources/graphics/logger_red.png create mode 100644 java_console/romraider/resources/graphics/logger_restart.png create mode 100644 java_console/romraider/resources/graphics/logger_stop.png create mode 100644 java_console/romraider/resources/graphics/romraider-ico-large.gif create mode 100644 java_console/romraider/resources/graphics/romraider-ico.gif create mode 100644 java_console/romraider/resources/graphics/romraider-ico.ico create mode 100644 java_console/romraider/resources/graphics/splash.bmp create mode 100644 java_console/romraider/resources/graphics/switch.gif create mode 100644 java_console/romraider/src/ZoeloeSoft/projects/JFontChooser/JFontChooser.java create mode 100644 java_console/romraider/src/com/romraider/ECUExec.java create mode 100644 java_console/romraider/src/com/romraider/Settings.java create mode 100644 java_console/romraider/src/com/romraider/Version.java.template create mode 100644 java_console/romraider/src/com/romraider/editor/ecu/ECUEditor.java create mode 100644 java_console/romraider/src/com/romraider/editor/ecu/ECUEditorManager.java create mode 100644 java_console/romraider/src/com/romraider/io/connection/ConnectionManager.java create mode 100644 java_console/romraider/src/com/romraider/io/connection/ConnectionProperties.java create mode 100644 java_console/romraider/src/com/romraider/io/connection/ConnectionPropertiesImpl.java create mode 100644 java_console/romraider/src/com/romraider/io/serial/port/SerialPortRefreshListener.java create mode 100644 java_console/romraider/src/com/romraider/logger/car/util/Constants.java create mode 100644 java_console/romraider/src/com/romraider/logger/car/util/SpeedCalculator.java create mode 100644 java_console/romraider/src/com/romraider/logger/car/util/TorqueCalculator.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/EcuLoggerExec.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/controller/LoggerController.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/controller/LoggerControllerImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/learning/LearningTableValues.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/learning/flkctable/FlkcTableQueryBuilder.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/learning/parameter/Parameter.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/learning/parameter/ParameterCrossReference.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/learning/parameter/ParameterIdComparator.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/manager/PollingState.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/manager/PollingStateImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/manager/QueryManager.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/manager/QueryManagerImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/manager/TransmissionManager.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/manager/TransmissionManagerImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/query/EcuInit.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/query/EcuInitCallback.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/query/EcuQuery.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/query/EcuQueryImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/query/ExternalQuery.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/query/ExternalQueryImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/query/Query.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/query/Response.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/query/ResponseImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/query/SSMEcuInit.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/readcodes/ReadCodesManager.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/comms/reset/ResetManager.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/ConvertorUpdateListener.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/EcuAddress.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/EcuAddressImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/EcuData.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDataConvertor.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDataLoader.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDataType.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDefinition.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDefinitionImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDerivedParameterConvertor.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDerivedParameterImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/EcuParameter.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/EcuParameterImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/EcuSwitch.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/EcuSwitchImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/EvaluateEcuDefinition.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/ExternalData.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/LoggerData.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/plugin/PluginFilenameFilter.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/xml/EcuDefinitionDocumentLoader.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/xml/EcuDefinitionHandler.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/xml/EcuDefinitionInheritanceList.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/definition/xml/EcuTableDefinitionHandler.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/exception/ConfigurationException.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/exception/FileLoggerException.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/exception/InvalidResponseException.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/exception/NotConnectedException.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/exception/PortNotFoundException.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/exception/SerialCommunicationException.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/exception/UnsupportedPortTypeException.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/exception/UnsupportedProtocolException.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfile.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileFileFilter.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileItem.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileItemImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileLoader.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileLoaderImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/profile/xml/UserProfileHandler.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/DataRegistrationBroker.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/DataRegistrationBrokerImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/EcuDataComparator.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/MessageListener.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/SerialPortComboBox.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/StatusChangeListener.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/StatusIndicator.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/handler/DataUpdateHandler.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/handler/DataUpdateHandlerManager.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/handler/DataUpdateHandlerManagerImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileLogger.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileLoggerControllerSwitchHandler.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileLoggerControllerSwitchMonitor.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileLoggerControllerSwitchMonitorImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileLoggerImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileUpdateHandler.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileUpdateHandlerImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/handler/graph/SpringUtilities.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/handler/injector/InjectorUpdateHandler.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/handler/livedata/LiveDataRow.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/handler/livedata/LiveDataTableModel.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/handler/livedata/LiveDataUpdateHandler.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/handler/table/TableUpdateHandler.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/paramlist/ParameterListTable.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/paramlist/ParameterListTableModel.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/paramlist/ParameterRow.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/paramlist/UnitsComboBoxEditor.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/paramlist/UnitsComboBoxRenderer.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/playback/PlaybackManager.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/playback/PlaybackManagerImpl.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/swing/layout/BetterFlowLayout.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/swing/menubar/util/FileHelper.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/AirFuelLearningTableModel.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/FineLearningKnockCorrectionTableModel.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/ReadCodesTableModel.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/VehicleInformationTableModel.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/renderers/CentreRenderer.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/renderers/LtvCellRenderer.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/swing/vertical/VerticalLabelUI.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/swing/vertical/VerticalTextIcon.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/tab/Tab.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/tab/TableFinder.java create mode 100644 java_console/romraider/src/com/romraider/logger/ecu/ui/tab/injector/InjectorTab.java create mode 100644 java_console/romraider/src/com/romraider/maps/DataCell.java create mode 100644 java_console/romraider/src/com/romraider/maps/Rom.java create mode 100644 java_console/romraider/src/com/romraider/maps/RomChecksum.java create mode 100644 java_console/romraider/src/com/romraider/maps/RomID.java create mode 100644 java_console/romraider/src/com/romraider/maps/Scale.java create mode 100644 java_console/romraider/src/com/romraider/maps/Table.java create mode 100644 java_console/romraider/src/com/romraider/maps/Table1D.java create mode 100644 java_console/romraider/src/com/romraider/maps/Table2D.java create mode 100644 java_console/romraider/src/com/romraider/maps/Table3D.java create mode 100644 java_console/romraider/src/com/romraider/maps/TableSwitch.java create mode 100644 java_console/romraider/src/com/romraider/net/BrowserControl.java create mode 100644 java_console/romraider/src/com/romraider/net/URL.java create mode 100644 java_console/romraider/src/com/romraider/ramtune/test/command/executor/CommandExecutor.java create mode 100644 java_console/romraider/src/com/romraider/ramtune/test/io/RamTuneTestAppConnectionProperties.java create mode 100644 java_console/romraider/src/com/romraider/swing/AbstractFrame.java create mode 100644 java_console/romraider/src/com/romraider/swing/CategoryTreeNode.java create mode 100644 java_console/romraider/src/com/romraider/swing/CompareImagesForm.java create mode 100644 java_console/romraider/src/com/romraider/swing/CustomToolbarLayout.java create mode 100644 java_console/romraider/src/com/romraider/swing/DebugPanel.java create mode 100644 java_console/romraider/src/com/romraider/swing/DefinitionManager.java create mode 100644 java_console/romraider/src/com/romraider/swing/ECUEditorMenuBar.java create mode 100644 java_console/romraider/src/com/romraider/swing/ECUEditorToolBar.java create mode 100644 java_console/romraider/src/com/romraider/swing/ECUImageFilter.java create mode 100644 java_console/romraider/src/com/romraider/swing/GenericFileFilter.java create mode 100644 java_console/romraider/src/com/romraider/swing/JProgressPane.java create mode 100644 java_console/romraider/src/com/romraider/swing/JTableChooser.java create mode 100644 java_console/romraider/src/com/romraider/swing/LookAndFeelManager.java create mode 100644 java_console/romraider/src/com/romraider/swing/MDIDesktopPane.java create mode 100644 java_console/romraider/src/com/romraider/swing/ParameterIdsTableModel.java create mode 100644 java_console/romraider/src/com/romraider/swing/RomCellRenderer.java create mode 100644 java_console/romraider/src/com/romraider/swing/RomPropertyPanel.java create mode 100644 java_console/romraider/src/com/romraider/swing/RomTree.java create mode 100644 java_console/romraider/src/com/romraider/swing/RomTreeRootNode.java create mode 100644 java_console/romraider/src/com/romraider/swing/ScalesTableModel.java create mode 100644 java_console/romraider/src/com/romraider/swing/SetFont.java create mode 100644 java_console/romraider/src/com/romraider/swing/SettingsForm.java create mode 100644 java_console/romraider/src/com/romraider/swing/SwitchStateTableModel.java create mode 100644 java_console/romraider/src/com/romraider/swing/TableChooserTreeNode.java create mode 100644 java_console/romraider/src/com/romraider/swing/TableFrame.java create mode 100644 java_console/romraider/src/com/romraider/swing/TableMenuBar.java create mode 100644 java_console/romraider/src/com/romraider/swing/TableMenuItem.java create mode 100644 java_console/romraider/src/com/romraider/swing/TablePropertyPanel.java create mode 100644 java_console/romraider/src/com/romraider/swing/TableToolBar.java create mode 100644 java_console/romraider/src/com/romraider/swing/TableTreeNode.java create mode 100644 java_console/romraider/src/com/romraider/swing/XMLFilter.java create mode 100644 java_console/romraider/src/com/romraider/swing/menubar/Menu.java create mode 100644 java_console/romraider/src/com/romraider/swing/menubar/MenuItem.java create mode 100644 java_console/romraider/src/com/romraider/util/AxisRange.java create mode 100644 java_console/romraider/src/com/romraider/util/ByteUtil.java create mode 100644 java_console/romraider/src/com/romraider/util/ColorScaler.java create mode 100644 java_console/romraider/src/com/romraider/util/FormatFilename.java create mode 100644 java_console/romraider/src/com/romraider/util/HexUtil.java create mode 100644 java_console/romraider/src/com/romraider/util/JEPUtil.java create mode 100644 java_console/romraider/src/com/romraider/util/JREChecker.java create mode 100644 java_console/romraider/src/com/romraider/util/LogManager.java create mode 100644 java_console/romraider/src/com/romraider/util/MD5Checksum.java create mode 100644 java_console/romraider/src/com/romraider/util/ObjectCloner.java create mode 100644 java_console/romraider/src/com/romraider/util/ParamChecker.java create mode 100644 java_console/romraider/src/com/romraider/util/Platform.java create mode 100644 java_console/romraider/src/com/romraider/util/RomServer.java create mode 100644 java_console/romraider/src/com/romraider/util/SaxParserFactory.java create mode 100644 java_console/romraider/src/com/romraider/util/SettingsManager.java create mode 100644 java_console/romraider/src/com/romraider/util/ThreadCheckingRepaintManager.java create mode 100644 java_console/romraider/src/com/romraider/util/ThreadUtil.java create mode 100644 java_console/romraider/src/com/romraider/util/proxy/Proxifier.java create mode 100644 java_console/romraider/src/com/romraider/util/proxy/TimerWrapper.java create mode 100644 java_console/romraider/src/com/romraider/util/proxy/Wrapper.java create mode 100644 java_console/romraider/src/com/romraider/xml/DOMHelper.java create mode 100644 java_console/romraider/src/com/romraider/xml/DOMRomUnmarshaller.java create mode 100644 java_console/romraider/src/com/romraider/xml/DOMSettingsBuilder.java create mode 100644 java_console/romraider/src/com/romraider/xml/DOMSettingsUnmarshaller.java create mode 100644 java_console/romraider/src/com/romraider/xml/InvalidTableNameException.java create mode 100644 java_console/romraider/src/com/romraider/xml/RomAttributeParser.java create mode 100644 java_console/romraider/src/com/romraider/xml/RomNotFoundException.java create mode 100644 java_console/romraider/src/com/romraider/xml/TableIsOmittedException.java create mode 100644 java_console/romraider/src/com/romraider/xml/TableNotFoundException.java diff --git a/java_console/.idea/libraries/jep.xml b/java_console/.idea/libraries/jep.xml new file mode 100644 index 0000000000..70023f8067 --- /dev/null +++ b/java_console/.idea/libraries/jep.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/java_console/.idea/libraries/swing_layout_1_0.xml b/java_console/.idea/libraries/swing_layout_1_0.xml new file mode 100644 index 0000000000..4893bfcb0c --- /dev/null +++ b/java_console/.idea/libraries/swing_layout_1_0.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/java_console/lib/jep.jar b/java_console/lib/jep.jar new file mode 100644 index 0000000000000000000000000000000000000000..926b295a92cfd4e8a932784de027793b647549a8 GIT binary patch literal 80355 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 YT5^EynT5p#{CfR63(K@L=o?x74`N6$MF0Q* literal 0 HcmV?d00001 diff --git a/java_console/lib/swing-layout-1.0.jar b/java_console/lib/swing-layout-1.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..50f159011aa045405fff8e21decd747e259749ec GIT binary patch literal 140545 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 literal 0 HcmV?d00001 diff --git a/java_console/romraider/resources/graphics/1d.gif b/java_console/romraider/resources/graphics/1d.gif new file mode 100644 index 0000000000000000000000000000000000000000..9b263a08ca97d8bbb1f08eeca78187af00d591b3 GIT binary patch literal 304 zcmZ?wbhEHb6k-r!xXQrr|B=!E$HxC}8vnm#{Qtbk{|hGn51afy0z{_&51anqX8M0S zkTmB)jNMhiIi@gmGqgVLYW2v zt&9(x|5TVsakD-Okl|=%sH|{}R^vZ@$U&gzw5*JzxQI|3XF+{xNRNMC|AdK?+#DIK E0otQo4gdfE literal 0 HcmV?d00001 diff --git a/java_console/romraider/resources/graphics/2d.gif b/java_console/romraider/resources/graphics/2d.gif new file mode 100644 index 0000000000000000000000000000000000000000..f925371dae1507abf4ffb35debd4811cdd904186 GIT binary patch literal 604 zcmbu+$xBsX7{~ErIW;+4%}kq|7NwQlbI$vo;YNdGN>)f24HBh`qph@phK=)FmPMHt znxsK1)53-uWl=VuMM!XqFd`R5ySBCYcKjQ9c=+&JJv=w#EmIF^v(8QH}Q+uQY}=Uc2hN(Rr)$T<3+(Q=Mlz zLpl$19_j>K1&;)e1p&c5!F|D?;EteAa97YT7;v+sU(zG!72Fc^NO~o=B{v1vB-bT3 zB$p*uCEac?xMJX!T$K0?&Kq3y|M?i$mZo-!B{6&d!V%-OnOXvMN63*uvDM!a*jZP}FL zTa~#eIc_+(yQm<4y{#?(J~ukJcURHopSE1OFv%0#_j`L`-nxqMwKX5VCT158Q~A>0 z49OTaGQki*pN#i1`epPHyd`)?&`aPYct!A<;0?iZf*yhwh64NGGcV$nA zuh6L{vxN0g<`sd97wPK)dfj`HwPx#1ZP1_K>z@;j|==^1poj532;bRa{vGf5&!@T5&_cPe*6Fc00(qQO+^RR2oDo8 z1^U5)lK=n%Z%IT!R9HvtmP=?8Q51%gOw&%%hQvIY+&f8H6sf4FP^gHwP>X_~Ru?YC zt!^r~aU)hLh)W+xsUY|Q!78{CTu4QzSZiBr(}}HG7jB9{r50@MWBgB=DL0eMbW#Of zByeb&d+tBqdE9fy;V6H~8Td~#FkV+Drxj(B8jtTyGuAT7*dCSh2F>A+{wZ>r^Rw?b z?*-i4J4MloSO#lXhU4*IF;MtVne3az?-^Jh?5e6LJ69B_iFg4h0vQ`hCRZ6BVgXnz z*yp2(#F~;o4Se5&ww^AWH2fe46R*JP)Ys$r|tPB?~88ZIn3cj+SW_Gl;g)Di;qW^{K};WX`dhN(#_|VLN~TRs^Uh*F->!EU~BSO#%BF z8!@s9K3Twe%rRr%gk7#TSc{iZV!Gy#!MwqL$m_KS2!M~+d`gW*#VBen1%vB{IDaMr z_QYbk=+or@*SXz|bYo!uMR6jKUKjcqmx8WE}|Lndj+z5sArq+ccr2@W)MhBQAQN~2TW0rszEnS0* z{WJqjG4}qr&!-9{A@Kc|0jepCLe_#duG*7m0eRh+o0|F8Gjkon`if#kLZ}pO`dtDS&jy zl6K+9uL;%#`(^=}#@Y#sl9&Nf&t+hJu*@chuR{tlW<-O$$ b2pRYd)BOYPyby}N00000NkvXXu0mjfh>Xra literal 0 HcmV?d00001 diff --git a/java_console/romraider/resources/graphics/icon-close.png b/java_console/romraider/resources/graphics/icon-close.png new file mode 100644 index 0000000000000000000000000000000000000000..0fcb3075b46c4bac054ef8ca8adabb20de75d61f GIT binary patch literal 7423 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 literal 0 HcmV?d00001 diff --git a/java_console/romraider/resources/graphics/icon-deccoarse.png b/java_console/romraider/resources/graphics/icon-deccoarse.png new file mode 100644 index 0000000000000000000000000000000000000000..a19369127da2ad311400d1d3bafa7bc6768ca3dd GIT binary patch literal 4123 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 FV1nIv%BBDS literal 0 HcmV?d00001 diff --git a/java_console/romraider/resources/graphics/icon-inccoarse.png b/java_console/romraider/resources/graphics/icon-inccoarse.png new file mode 100644 index 0000000000000000000000000000000000000000..20feaee6e7cb318393f0365cd60f80d113a1bc3f GIT binary patch literal 4111 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 literal 0 HcmV?d00001 diff --git a/java_console/romraider/resources/graphics/icon-incfine.png b/java_console/romraider/resources/graphics/icon-incfine.png new file mode 100644 index 0000000000000000000000000000000000000000..4badc746c4a5693301ada476ad41ed6376b9edec GIT binary patch literal 4124 zcmV+%5aaKOP)KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C1vyDXK~#9!te4A+990y?f4AyZb@lXXx@S6*WRi)A27(ZU zY<%HDR0xQ=5LA$$_!s!dxRMBhOG#iBl7$Xg4MAMQkX0mL5=bK1X^dn>2B!1Qym}s8 z-CbREUDTbVERQ37iKiz!soihyW}B(*hdERPpOZZA3tK3ET&4 z2X+8eAS%ezEUtwZm;>~^g_8kFyrIaI4u+o4Dk;f|E0C*=b0F*9W zy7XMFR(o>z*h#P)F4iJS^LIZ~N;QFDAe6O7DHW|30o5h&R-knG^5y5Jrl$71H+&Rq z7i7 zzjp1~OUJLaVujfSFk3_yEmFVA4vW2nz#pqQbQ)o5GM?nEs39p)Mx>VAOVj`;JNmjS*6ZUf^$O^Y9Q zq*?R0zsMIrV;&RaUQ5oky<1aiDC=Sg-> zYpv-t>#^#n15rc}8vG!{_E*9?3;Zp_?*nxq5QyqUdB)eNeb{n}pe1XIbb`u+QL$bPPAfNfqdgWP?I%oq;OhE=+oe#gxHeMhd|pHqoovGV@%x5+#bjj zP+oy@PLua+ETz)mz9^mn?ujrXbH!@)svAM7ziZj4y0SrN*XwoIQ-=H&$Zuh1#bbM~ zM=oO%{;S-i6gC8SX|uamlMa=XBb0VRc{jVamf1UyqgrrrwW8ID*zDK{JQN?uiv2?0 zQ|#C_z|-6F4E1FxWi_^?Xa?c`3$Bz3CG;g}v1d8?!Rt5IGM{+NOWq{e{ZI?-8eDc8 zT7D4nGEV}30MAM%v{t<~ohmP12$=t(TuGwYsq9~p`fZ6Fuilj=MshDPl2jkx-0xDV z)|bVZnsU@@sAerASr1%opxhGM^fR&c ap8)_x>2IvC&OONh0000KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C6N5=aK~#9!#F}}Gq-S~HfA4qIS66pcbypuVeN1=H(c|&h zbA%a>!N!JwP3*8Fh#*Kp0$G%Qax7YD4-vEySp`rMk`S~JYyt@+3L;9RKw>Y1#5OjT zhcWipW6$`St7oR~?mD`vzT>_2kEtFNy<>auvP(&?zOPlSufE^&JnwV9f}iY1^elR= z7BD^cZ}{Oq2_OI$@OqZh{mud!NCDZse?x%Y4NBIzj+8g0+k3r{XL*1R1VGqbnJ%o; z1&V-;?sMO;2EA8#31^FB{VjhRKy_av?Jjj4$X?-WJ}2y1*)4De=ybK^c9-rh@uLEI z*MahTesFX7-Tw``_p196QFp~a(uLiWw9_Muty&6tu0f$t2;TFa_iVf4jyp!G)vEiE zk9_1W>-BoteU{UG-jA%oUO1=wg9RYpmFiYFAHefGsg$xkCTV&EW4rf3&*~M_wSBf@ z$Bx3U{reB^KYZxO;hCA4*=?hvyR6mg5B&2tU%LIxcl=ZL|3p`Fs=IC&Q1pO>K(Py# z>#i&i6#M#vJ$v>SfAzz^dC$_yMpYWj_*muQU;X(XeD(XsA7AO3<)-OuYN=P-n}YE( znT&JSU3U)L|L*$_9y|8tyT->S4&+M%p4678)25IMu`*eG^s(=~@BVka>vUIe$!nPY z>u&O(3)SBPmVd{6A3V7K;E_X9GY9WkX{x-F&4qq06p8j|tky}E&#icJ=|6t{v;XPw z?hkrht>?Nho2KdBd++_*4julfnRniM_w5rCqqi0N%Rw0Si#YYE$C6mftDDSfs-+y4 z&Y%Cv!{7P#mwxZ}K6!d+X{p@SS(50#;63Bl>S17Vm5<|U3~tz=RSDnoo{`j z$HF!pi|qllJ&xzvcbiTm2b6#^(D(8G{zvaf#N;QY553)?*hkw+(lo_Nfsj7JYK&)a z!W<@vIDhg4qh4oeI2V8S%KW_Y`--JZY5UuvW-T*O&2j9ueN0bpM;k*cGHgVaSSqB6 zNp*+3Ra6RDf{Y-W2V*;^BqE=!(5$Cld+4FZKKX(7|I)&yY4@1C=xQ_VI=D{v-U3#1 z-E9u20Oeo!rH{^5_uTiDY;~7Ez3(P!?KaxRC^8t6!P8frn6?7rf)J3;RB;1NyQ73o^&|7kw^Xe2#bAx`0=> zf=OHSySo_6ALHWL<&Vwo+xNF!hm`ADSGKFUxEmAeK(o6)4Pd=3>ea_?zkA0c&m{it z$zf`dMi~cb0#YQ72(Y*aBQVzBT7$8$+-{Qb9S+f$MzwVn`N=lqSmNGtSNYcor5_LIp*6X{^3;2 zLoaKNuWByK6nqbC#O~4=?z=g~R}B(br0MyHViVuFz*u=^GTu-VZ+qvvem-_4`nHel zKDyTM_s=h_kqwj8V}~oh@~iK=_g}5GqN`nHt<_#nfK)*I+^MJjmqYQl7w6U)8QV^f zNs&q+OcqCMpyCcDw)Fcg?$~ZPu&bZ`!7}SgaB?-lY%Ed|nu%hTaHPQHjg&8Z`(++_ zW}WAk1dB-#+h2uJ0jDG|UIOh5vtxoCg8wubZ%|!?ygyGh6?R%NmS`X>x;Xl2bPbYt_{_fG}Ube zp*14%QF?`;zK}yZ18&|?#>*F}HDlH(tOIP7iuh} z1sHL0!ZxlCS``t5gkF>VyAtxb8qc4Cryl8W>WstkVv$r<2#3cRo}6TMs!xoM<;j*j zeBa_|5Vbu#{^03tbFZu)89!2aqMI=#y`jME23`+vzWg^2{VQlxq?F4NxPtXavYI+XQWFS;b_B(|rjqeFdnx2E9$Mg= zXO>u(E?#9Aoei)k4)kB-H|~ph`Wyeu_x`R#>K>rHca+K5aYlv&!&OPSID)HVoWQ4p zK^uwNPC5P9ahg}2S^Trl{`Sx2bGZi4*mNvjPYR_rGnW2RczmGRRP~h$-e|joirUzG zgT1?_xOCwq3b_)Q@(?+Enr+R>l^P?1eJnRxbhL*TX0SrwTL(u9UTRBDx*i99c8rPJ z$~^e^Do@Tygy<&}0Tn&Rmp*lxD=*x{>`h0R*k7PnjTsoI;k#{Ikt2!*@r;9G1O^A0 zrqt%=IQ{e^{O8~Jcc07Wa%M}_f_t5S?tbUn4_vzQt@r)n`75%oJTygLpUy==s?xTW z)%phRl^0lg_&J=70ruX0D^tfp$gJaN4XFn0A{-yb(Ky1z7zeEkB5L!4M;~PW&dR_3 z`mg-_7rP-L?V4l)Xy-bWuUK1MF?)7Ro=jS6)N1QUn?+=1c=RzMxd@ z$2b|R8;~kPsV{?T6-K8RYmu1@&({^Nv_h;5aXT5L2nh4=o&CgLr@%E^tAZb@ z!$t=AmhVURHJS@lq73=I3C_%2;?P=?&`3JX8WV&4EY{m-BWSi#1_}iN*Cn+A3oNUU zm)AE)@G+iG&a;@L#q*~Zc={VB&`;JlxZ@z>2aDu~HI5A^I0NL0DMDmXo*>c*o{fkT zaGWeEmADd~f9eEpy?y^b&P+~R>U7$~JE12yQ zbn*^yzRD9%U*MfTHNo=2DieFkR6@gQWN9Q4;R?dgCzXO+E@WBwEZ5pZYY{J;Z1U(U z&(S(JPc#pi+$b||c@tx|6zMM*9HsF503ie&>0nW0oEC0s!Es3(g{Le!R@4@kk?p0< z-FLk80mpHiTrQvX6t-$pQClnV&DzZT@uz?1k-L8G6F)fn-Ic?gNRr6}WWoVnyl{oX zvlc6B%m4$$BDK~!aoj+v0@+-aXP;kW?%WxUzj%QsPtGHk0$iD)Fj%BCxu1yx6AW#y zGF(bQo~ORlBv+{5$g8q#G-`OBOD>sRZcY{F^4-ChkJEr#j6t!9lu zokygAU*^U0XV^C~z*?0xcJ@pU5SFUcti^^Y)>Bv; z<9AYgV~{3dT{h@o32`8i1VKQWCU~BU#^U<{N@=vVSgS#|Sf5*EVr1}z1N&!Q?5WO^ zE|}cZ8a@4qUhQ2YfB2z)e6WxcvbA0(?L?$X;`(_O*Ih0w#;mQa(htOq2A})f-%{Vm zbNk!g&VeISl&cvKEp)t&Y;BOVC2``>R4z>{F%qE?L~8?2OI$~gBr!6T1VM(t_em0o zR2r=nNs^ExhQ%wduyeX{raD-vb>DR}X;!^p*MsZT9xL^x-8nOyrnMTKh9WkKPNcB@ z2&c}~Xw+-OvF5M-`~fb`MeI9p8{?CM#K{tIMMhNL+q{8+3xV)sfyzcVZFMOMCe&;kh_w8lZ>ZQ^TEbI$)VL~AtSI#BXxub`gr)!%hcKhW)B=B6Xwvd#nT!iTd34x5`m)y z4jyS@kf|U^Em#ku1xkslS~3o4qL7KfDsXK;KIo$_7@*IqLMLS9Vw2W-jh#EjzA{+p zk9)v+8%H;+IRn^*^QXQyT*)n6SZEA)(gZhSagAnup+>#sFg&`Q@zE+;rzm5PI>lOp zb~IKPgy#}Cf+$f)9CY_JTnoZVJe}gZK7pGh7Z&lI9IgmZiDhY{LlW1KamvQ(JQ0%ltiZ~zA*SmOx#4s7?UQLR1=6SePM=0Rh0NoC#EhfyJUFYoCRTfq|)Z2zg zIz+K#uoS*<&)W~4*wUoc*E7l2(p+yM&EQ}qam)g7t4W`qXKiJj)!I4(gB2_;?M?^Z z_fgW|x*kS3-2m)jg~f3-dC%b5lt5UrUWPEp;u??}2}^B5oW$rfMOX`-k8wS$by3FP zSc6e1wfb5kmkXmUaP~)?^4J~?>gr(jgzu%ds;Gu2_{?8f!1Xfp70Xv$b)@jT3|bqE zkzlTtR0@OV2^<5(AWJdGqLe0SNj9PmNi304p$Nfo@f;T+EJlLW4mcoWgzGe^ufD`x zcaF7t+Ii}=_S@@?u&y;XvqAE7U%@3!8f?@&+8Z4TeHC0UM=DcVQ9`y*01HZ`xQ>hO zd$^v(b3vt&#zsUGY2sLuDvL&d=isuK(qQ(0bYnrb=6>9ciO0{l!7H`B5EK$oH3U92aFYLIgz8a%r)Fk_xR1##n?15W?WN z3Cb9Z=Bl%>3e)Lek~XScC+XA?W|>kkWu)w=U3*LOyJl`#-aS2jVs_8Y)1`8Mtx_4d z)M_;gxm*DugzUc8Ywouj(VPbK?|$Sc5PcEhExs0~xKN z;uT!mq~LcLFFWkMrLug}?7rvA<>Hm2w;g?P{r1*d_y6(K)bz_+BrLZ;Y=x0qGK_05b8|~}c&JDw^srb28UCNZb)iJhp_q)( zy5zWSUw5|P_Q2@toKH!v1N1{MaoV(D;P9P?Pg1K%jvl^=UN{U0?RDp*ATsWc?F7;P zVWy7jZl*tRk0a>aE4^Cj?Er5M1>f*lt{?01C?SNk*3vU1*({NwEgn_(!0Fx@+5bV!Z07*qoM6N<$f{V{XdjJ3c literal 0 HcmV?d00001 diff --git a/java_console/romraider/resources/graphics/icon-palette.png b/java_console/romraider/resources/graphics/icon-palette.png new file mode 100644 index 0000000000000000000000000000000000000000..035c927376010c486a2d663226970e57361267a9 GIT binary patch literal 2444 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 literal 0 HcmV?d00001 diff --git a/java_console/romraider/resources/graphics/icon-smooth.ico b/java_console/romraider/resources/graphics/icon-smooth.ico new file mode 100644 index 0000000000000000000000000000000000000000..fee26db5c0212dfdbabe5901d39737d45bace25f GIT binary patch literal 4414 zcmd^CS!`5g7@eiFbkBy15Q ziwguJB!IqX2%w3O81zAS(f9<>#KZ@TZze`GJ`y;7-@n{@JJTB9G_mB;E$O-ETh2M3 z&1R>6iL{OWPugZjY_-u2W@jpR9uCL=xVk85AF$ z58o>-@E>SLU~ds9AmqwH;RxU-}UeZ8wozcpItg7f8>!i~7tr5ZGrG_8SWO2w@*0?1L=`Xzbm+ z2<=#llJOx#2s+`C9CY^UH|s(2?* zOxV7|S%EEh?;+m1`wVR69T^=(*=EMxMcDfd-cdK1aMSB2L(Xx4^!Qq~e`! z$m4C)ypd|Iv2zIQS%DB?hj$Y1i6K$*$k-@KN5-k0Y zm%QW8Ts8YmvnFcJ{njx8su5qN=i-N0eZlYfy>58Iuq%9bD1-PC)|MMobB%54 zT0zZKzxNDe#NYld9>+^Pi!f`9cAWNG>Kg4B?RW0k7xAE~vEUsx7kNef?GBINEo&}y zEj9O^&^2SfPt$%g@8hKBj7@spGQI$t)@OyDv$hmyD(M>U_u|!C3$X8bOH}V22Da8z ztu5M1iVQE2nriMh>l$mS)N`#Zq4h)XwRXT=lR~-6Q-J-aFDkH|?otc3)>P_u!_2eh zV;>W`){Y?in8FSY_oFCYE&nd{bOr6%UE-KOe~)SISPT5Il~P;UZU`?Cx+dnF^_+A~ z_?W8s2{q?5Yku~zmo@J`1V*}%_INQ_RE$q)o*ogmd`$iQ&&6eMdZVaba$e3kYpUU) za?Tx$t#!>h=d7thTL_zYXUCS~vq%M={>`3iT>g9^oICUnj8ZfAFPQa@aGwzF z&15ICUB?RjoBGZBEi{$;ZSgU|Tl+U*>sbqshyE6c&4~R+ForwHX1um&5G_4>$-i}W zkUmz^aRoK4mxYI#+9LOxXH9F1D^F9o-<+8q2MTrkbDFhIn(=URr^rk)uW@!T^IEiN zKU{$nBB?DRud$C!$J@pIw(vG`3(fgYu|i!>Sp3TsOCiuin%6dr@WNpP(mn9_s}Z)@ z<(`$VkXcb_>XfXg=X?tGe6eGk*WO!FsF}NI&t~%SWmJ!Ey%9_(U3K}AhMpU|g@0RS zEq})j%2~^7>%gN(;hf*1x^JgP6Kzp6QViPgs%MuxDh zy9ei*a=4ah!|iknK1nadGklD(jocz@Zr*~NTj&nWy9H;yL+Ku4-qU%B%8Imq|H}^mPybEgjL*+s DTZY&~ literal 0 HcmV?d00001 diff --git a/java_console/romraider/resources/graphics/icon-smooth.png b/java_console/romraider/resources/graphics/icon-smooth.png new file mode 100644 index 0000000000000000000000000000000000000000..87fda8b2e08575ce03b1a36132f46a139957d1ec GIT binary patch literal 1572 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 literal 0 HcmV?d00001 diff --git a/java_console/romraider/resources/graphics/logger_log_to_file.png b/java_console/romraider/resources/graphics/logger_log_to_file.png new file mode 100644 index 0000000000000000000000000000000000000000..da20009c6ebc28d8286577735f0a2aed9402fdf5 GIT binary patch literal 542 zcmV+(0^$9MP)ipsC{NwHXhk^T^8M@b{_OJo?ehKY^ZoAg{_gbs?)3ie^!@Mk{qXhu@b&)i_Wtqq z{_*$y^7sDp_x%TEqudB$nZInuwrvMP0JG9BOzzh0ezk6zs>pvBoiAh|OLc&u8Fey` ziY$o~K;Jx&g(y@3dIv*?e@PVfWq^*Mlf`~l1ZXGs9iIniYARXqTo!;y?%Xy7(4c9m gRw)&7nRFQb04&@fraZ$Zy#N3J07*qoM6N<$g3-nx`~Uy| literal 0 HcmV?d00001 diff --git a/java_console/romraider/resources/graphics/logger_recording.png b/java_console/romraider/resources/graphics/logger_recording.png new file mode 100644 index 0000000000000000000000000000000000000000..7abf4250a2c9e373e4ad259e450d5b6ef4765b1b GIT binary patch literal 725 zcmV;`0xJE9P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf0%}P_K~y+Tm6T6N z6k!;~f8WfmJL@j${&7u(=w@}uE=F{XGEvmTE(H}KiFAnwDzJlx2#N}VN{}Fmhz=Em zv}O{~B8YV=NIG>B(cN{|U0r8qXQyvwF_m4A9vEi6ciwrv_kG^)84-)ctk&ohs@DsK z0r@;A0_-*NUI9X1XRV{1xncpaGCX4Gx9@_P!#dc8Xg`XpV4~lh3P8`g&-vbgXn5;qxK<{<6z0V>T3Yo&3cCyl2lif$AfN z$$p$7eyo$Qm4n5lifwFkr&y|r=f_VcY3xNAug&QI_`vxJGZbp zJHWCqS65M8GF?%&(QK|F5fc7-*>@gGjwm_%oyNfd#G7|4O7i+_9BY2wA=`>g0y%r( z_mz}=KTe^&oAk^q;l5rx&&NodK2OLNH6=CKf>H|`ka{EoO@x-`NnX1`@yQtJUvt!6 zI7h0wL>|m8k&f(^PoC0dWxB{J5?X-Z^{eDvDq8{$lxtX})V?p+AS75_PrBF{Cmm}SYz|OWwni{E!PLlA{ zu{`mK+~^qDU3<_ENzX_>I2s7qk8O^MSFtEvs-XqyP`dlcynKzZuLVV(Qnxgmvri&6 zZT;yzg=3D&?)@j2us@RLz-<WdKBPAUGgOVQwHYFfcSAF)=zZH###kAS*C2Ffd5B`I-O#00(qQO+^RP z0SF2b2Ue(+U;qFB32;bRa{vGf5&!@T5&_cPe*6Fc00d`2O+f$vv5yP0)}X5iR&B2U1bHIq)tu5k4_a(Jn;?> z9&4zs0G0tnf)3~~j?)Y@D~v(Ea#?TX4u9yJ!O;N#j`AEgW{$z2tY!o7Lx553oDqls zoM1wvP#!@$d#>1s@oX1xBtL`klSr)ZSoQ)SAV|0ai~5iVxqJ>>&6K26O8_JJDVTqN zc9)PDvL2^`kfi`QOGqRk5u%Vo==u~b`hb8bu;_Li%B-kq-O?`WI|!r;Sx)LC^g^0? z%B};WTA{0&POM9S?4GP!z5&XXfFwYMVDRh!uFoq2fM}SD>hINQ1w6gk9sxw0T28V` z`%=u&X}NIQ9y~W*95gW#;uZV$)k?CuUK&0{;8O%?X6gf{25)cKXw3MQ z)@VMle=G~c(r%CnLjtfcSv#hUTD6UO)e_lmG<(yAx#ua1^SEaW54|0ooEXUsWg$Xp z7&XwLnjOo>=4Pjz>$2JFKIZrROl!w6-U42TLt07I^b1kkgXgecju$UvGkAH+Mf0FP b_@D6?MCDH@7f5>;00000NkvXXu0mjfTn;`P literal 0 HcmV?d00001 diff --git a/java_console/romraider/resources/graphics/logger_restart.png b/java_console/romraider/resources/graphics/logger_restart.png new file mode 100644 index 0000000000000000000000000000000000000000..383e3579d2eaf0c00ee791e1074a49fd74ae4253 GIT binary patch literal 766 zcmVhe4!|s zFQne_DWBAT72Vf%rb5@7gImw@aCblj!(b`=r9P;nuY~`RZ08>FdAchZ3^hIJXn0wG zmJtow$7r;UUWDOQ5lBP)worK|j(xzJ`bllS0-fX6&^vh>T_1FKI9dUHUnZ1Ia>!$2 z#iVvBC`6KEQx+zZ<(EH;MC03B+#E>;pAvntj9{pKK#At}`Opr=Lf@Z(bEX&+7^7Io z`d53iL*rjNw$Sh3Mg+ufK=83GjPSdynn1C5?S7s6jEseL!e7P7WHLF1t5-l= z=7!J$y0TkeD-9NQtV-O>vl}`e_ObLxu1biiT|o8{TB#`s+lb z8HFg(>6XqZplNzpfl&sA!nP>Ty~lBHScUEhJpv^`Fq_RT)?d>;Sq6hS8;3Ri_IEj} zDVT}ONQ}+{iH)03+#^NZXbM_Bs?a=smd$6_URmww$2&liQzJjc)_=01=v#GK9R6*Z=?k07*qoM6N<$f&m$1O8@`> literal 0 HcmV?d00001 diff --git a/java_console/romraider/resources/graphics/logger_stop.png b/java_console/romraider/resources/graphics/logger_stop.png new file mode 100644 index 0000000000000000000000000000000000000000..dc05ac60e40bc4f09ada711288ad778c96deec3c GIT binary patch literal 468 zcmV;_0W1EAP)e0L*v!)EKhXyL|No!mYE#qyqhVoqv4g$CKcWo0*3j_(cyKU5d?F;| zzk?kfLxC=1x?WfJ|AfCkN!ZlV8oT#z1HF0L+Z&Gm{`m192F?IAf-nsHe*Ye!*2m|+ zzKQ7+Oap6c|DSbtN5NRZ-@h2@^i0jBVHkL?s_Op*S62*-0{;EOss^ipw<06|Uv+lI zz#v25j{EuZ|0z*XIKJZO2v&pLg)uSzFGxuIzhP(h|0Yo34O`p)|9=0*@bT~0ufgiz zSV{`JfsfQK_C_bVreLrgJKw86o>_&j$ox}ISK#(s{@cf0l&-u0000< KMNUMnLSTYui{bhJ literal 0 HcmV?d00001 diff --git a/java_console/romraider/resources/graphics/romraider-ico-large.gif b/java_console/romraider/resources/graphics/romraider-ico-large.gif new file mode 100644 index 0000000000000000000000000000000000000000..c926be8fef68124cee2fab38679d0c4d8570433a GIT binary patch literal 7906 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 literal 0 HcmV?d00001 diff --git a/java_console/romraider/resources/graphics/romraider-ico.gif b/java_console/romraider/resources/graphics/romraider-ico.gif new file mode 100644 index 0000000000000000000000000000000000000000..d379c14b6e6580b8f6e97c51fbb8df4d01baa2a7 GIT binary patch literal 1022 zcmZ?wbhEHb6krfw_|5i`Sa30Cgo9DK((_)c;PT;mnF%O`f5PxL;&#C-wrCqhzR z#T34X$$yqq{3WgOLssptyv7tEu~m|C|22(^^^Fd)^8HiR`>$pGU(fQdp>>w7!G8<4 zP#ZfHHcoMFURz5mb0d@7oTBB+);Bh`IyyQT8JpHMciB5S>l+xZ*}5k!J3lfZH8CZv zw5m2RG`y&?CNZZVqoks5`mBPMuEOSyqSh{L14CnLo6v;Bh~$*0%&evvGj&Z&!jh77 zjg9?6LagoW!y+OaJv|J}OdZ|bbPe>m`FVNxc+D*>xcPW^_<0SC4Fv@G`FObm1$c!7 z`M5aQxw$w5_<02d_=E)cdAT{bIoU-7`G8Cz0X|_tei0!7K>>bgDM=1iW?n815kY<} zbyYnb?Gxcj;sU&!Y^>sf`~ti@DH`J90(^fHobAlbij}4QC;I6M3&@I!{PTB_<>pdj zWl`r~SLWjM6&JM^7W^L(pvc2*AR^qsz|h3Nki)=`$-t1#z>vVe5XZm}z`)SL$mqbr zV!*~~%E@8D!(%TXV5*}7j7@N?juJ$LfZ|UUMh1r23_2jypgh6Aaht)MQ^sS1!aud)VmdK9asop*j&a-bS#1#5;e2|AQP#B`HM?vOM}722Gstlleb zSj%>8#wXT7N#n#*I}D4Ta~_lrJaEL*N6K%y8f&}G&P{55GA8M(BpPoxcL-?*tuWAJ Onz(>pj*Xd#!5RRfjl{VC literal 0 HcmV?d00001 diff --git a/java_console/romraider/resources/graphics/romraider-ico.ico b/java_console/romraider/resources/graphics/romraider-ico.ico new file mode 100644 index 0000000000000000000000000000000000000000..9fb49316010fa8a3cfedc541db4909883d6e4247 GIT binary patch literal 7358 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 aLAABDzp4MHY~Q{e{@(;(|KI)(75E?Aw7S~> literal 0 HcmV?d00001 diff --git a/java_console/romraider/resources/graphics/splash.bmp b/java_console/romraider/resources/graphics/splash.bmp new file mode 100644 index 0000000000000000000000000000000000000000..71345c70c0f7cb581cf585e59f3301aa26bf713d GIT binary patch literal 207056 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 literal 0 HcmV?d00001 diff --git a/java_console/romraider/resources/graphics/switch.gif b/java_console/romraider/resources/graphics/switch.gif new file mode 100644 index 0000000000000000000000000000000000000000..09f5a096616f7bd0be75a0cba1b1e72adca4c226 GIT binary patch literal 193 zcmZ?wbhEHb6k-r!IKsg2|B=!E$HxC}8vnm#{Qtbk{|hGn51afy0z{_&51an4v;1FY z^}o>Se}VP?LhJu2KxFej0*GuGfO`M`2a@=K;!hSv1_luZ9gs4RoeZpY3aWi6ne#GM zt;=~mE7)e@Cm(nI%{PRDco=49C{|st65C~G+p?uy!fF*yzvAh`(ahXO1Z(Q|e LpU(*9V6X-N8Ja@Y literal 0 HcmV?d00001 diff --git a/java_console/romraider/romraider.iml b/java_console/romraider/romraider.iml index 015c172575..ea38c34862 100644 --- a/java_console/romraider/romraider.iml +++ b/java_console/romraider/romraider.iml @@ -9,5 +9,7 @@ + + \ No newline at end of file diff --git a/java_console/romraider/src/ZoeloeSoft/projects/JFontChooser/JFontChooser.java b/java_console/romraider/src/ZoeloeSoft/projects/JFontChooser/JFontChooser.java new file mode 100644 index 0000000000..0039a45efc --- /dev/null +++ b/java_console/romraider/src/ZoeloeSoft/projects/JFontChooser/JFontChooser.java @@ -0,0 +1,271 @@ +/******************************************************************************** + * ********************************************************* * + * * $Package: ZoeloeSoft.projects.JFontChooser * * + * * $Id: JFontChooser.java * * + * * $Date: 23:39 19/04/2004 * * + * * * * + * * $Creator: Tim Eeckhaut * * + * * $Alias: zoeloeboeloe * * + * * * * + * * $Company: ZoeloeSoft * * + * * $Website: http://users.pandora.be/ZoeloeSof * * + * ********************************************************* * + * * + * Copyright (c) 2004, Tim Eeckhaut * + * All rights reserved. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the following disclaimer. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the following disclaimer in the * + * documentation and/or other materials provided with the distribution. * + * * + * 3. Neither the name of the company nor the names of its * + * contributors may be used to endorse or promote products derived from this * + * software without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR * + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + * * + ********************************************************************************/ + + +package ZoeloeSoft.projects.JFontChooser; + +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Frame; +import java.awt.GraphicsEnvironment; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JDialog; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.ListSelectionModel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +public class JFontChooser extends JDialog +{ + public static int OK_OPTION = 0; + public static int CANCEL_OPTION = 1; + + private JList fontList, sizeList; + private JCheckBox cbBold, cbItalic; + private JTextArea txtSample; + + private int OPTION; + + private final String[] sizes = new String[] + { "2","4","6","8","10","11","12","13","14","16","18","20","22","24","30","36","48","72" }; + + public int showDialog(Font font) + { + setFont(font); + return showDialog(); + } + + public int showDialog() + { + setVisible(true); + + return OPTION; + } + + public JFontChooser(Frame parent) + { + super(parent, true); + setTitle("JFontChooser"); + + OPTION = JFontChooser.CANCEL_OPTION; + + + // create all components + + JButton btnOK = new JButton("OK"); + btnOK.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + JFontChooser.this.OPTION = JFontChooser.OK_OPTION; + JFontChooser.this.setVisible(false); + } + }); + + + JButton btnCancel = new JButton("Cancel"); + btnCancel.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + JFontChooser.this.OPTION = JFontChooser.CANCEL_OPTION; + JFontChooser.this.setVisible(false); + } + }); + + + fontList = new JList(GraphicsEnvironment.getLocalGraphicsEnvironment(). + getAvailableFontFamilyNames()) + { + @Override + public Dimension getPreferredScrollableViewportSize() + { return new Dimension(150, 144); } + }; + fontList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + + sizeList = new JList(sizes) + { + @Override + public Dimension getPreferredScrollableViewportSize() + { return new Dimension(25, 144); } + }; + sizeList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + + cbBold = new JCheckBox("Bold"); + + cbItalic = new JCheckBox("Italic"); + + + txtSample = new JTextArea() + { + @Override + public Dimension getPreferredScrollableViewportSize() + { return new Dimension(385, 80); } + }; + txtSample.setFont(new Font("Monospaced", Font.PLAIN, 12)); + txtSample.setText("The quick brown fox jumped over the fence"); + + // set the default font + + setFont(null); + + + // add the listeners + + ListSelectionListener listListener = new ListSelectionListener() + { + @Override + public void valueChanged(ListSelectionEvent e) + { txtSample.setFont(getCurrentFont()); } + }; + + fontList.addListSelectionListener(listListener); + sizeList.addListSelectionListener(listListener); + + + ActionListener cbListener = new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { txtSample.setFont(getCurrentFont()); } + }; + + cbBold.addActionListener(cbListener); + cbItalic.addActionListener(cbListener); + + // build the container + + getContentPane().setLayout(new java.awt.BorderLayout()); + + JPanel leftPanel = new JPanel(); + leftPanel.setLayout(new java.awt.BorderLayout()); + + leftPanel.add(new JScrollPane(fontList), java.awt.BorderLayout.CENTER); + leftPanel.add(new JScrollPane(sizeList), java.awt.BorderLayout.EAST); + + getContentPane().add(leftPanel, java.awt.BorderLayout.CENTER); + + + JPanel rightPanel = new JPanel(); + rightPanel.setLayout(new java.awt.BorderLayout()); + + JPanel rightPanelSub1 = new JPanel(); + rightPanelSub1.setLayout(new java.awt.FlowLayout()); + + rightPanelSub1.add(cbBold); + rightPanelSub1.add(cbItalic); + + rightPanel.add(rightPanelSub1, java.awt.BorderLayout.NORTH); + + JPanel rightPanelSub2 = new JPanel(); + rightPanelSub2.setLayout(new java.awt.GridLayout(2, 1)); + + rightPanelSub2.add(btnOK); + rightPanelSub2.add(btnCancel); + + rightPanel.add(rightPanelSub2, java.awt.BorderLayout.SOUTH); + + getContentPane().add(rightPanel, java.awt.BorderLayout.EAST); + + getContentPane().add(new JScrollPane(txtSample), java.awt.BorderLayout.SOUTH); + + setSize(200, 200); + setResizable(false); + + pack(); + } + + @Override + public void setFont(Font font) + { + if (font == null) font = txtSample.getFont(); + + fontList.setSelectedValue(font.getName(), true); + fontList.ensureIndexIsVisible(fontList.getSelectedIndex()); + sizeList.setSelectedValue("" + font.getSize(), true); + sizeList.ensureIndexIsVisible(sizeList.getSelectedIndex()); + + cbBold.setSelected(font.isBold()); + cbItalic.setSelected(font.isItalic()); + } + + @Override + public Font getFont() + { + if (OPTION == OK_OPTION) + { + return getCurrentFont(); + } + else return null; + } + + private Font getCurrentFont() + { + try { + String fontFamily = (String)fontList.getSelectedValue(); + int fontSize = Integer.parseInt((String)sizeList.getSelectedValue()); + + int fontType = Font.PLAIN; + + if (cbBold.isSelected()) fontType += Font.BOLD; + if (cbItalic.isSelected()) fontType += Font.ITALIC; + return new Font(fontFamily, fontType, fontSize); + } catch (Exception ex) { + // if error return current sample font. + return txtSample.getFont(); + } + } +} diff --git a/java_console/romraider/src/com/romraider/ECUExec.java b/java_console/romraider/src/com/romraider/ECUExec.java new file mode 100644 index 0000000000..a0f4d439c5 --- /dev/null +++ b/java_console/romraider/src/com/romraider/ECUExec.java @@ -0,0 +1,146 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider; + +import static com.romraider.Version.BUILDNUMBER; +import static com.romraider.Version.PRODUCT_NAME; +import static com.romraider.Version.SUPPORT_URL; +import static com.romraider.Version.VERSION; +import static com.romraider.editor.ecu.ECUEditorManager.getECUEditor; +import static com.romraider.swing.LookAndFeelManager.initLookAndFeel; +import static com.romraider.util.LogManager.initDebugLogging; +import static com.romraider.util.RomServer.isRunning; +import static com.romraider.util.RomServer.sendRomToOpenInstance; +import static com.romraider.util.RomServer.waitForRom; +import static javax.swing.JOptionPane.INFORMATION_MESSAGE; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static javax.swing.SwingUtilities.invokeLater; +import static javax.swing.WindowConstants.EXIT_ON_CLOSE; +import static org.apache.log4j.Logger.getLogger; + +import java.io.File; +import java.text.DateFormat; + +import org.apache.log4j.Logger; + +import com.romraider.editor.ecu.ECUEditor; +import com.romraider.util.JREChecker; + +public class ECUExec { + private static final Logger LOGGER = getLogger(ECUExec.class); + private static final String START_LOGGER_ARG = "-logger"; + private static final String START_LOGGER_FULLSCREEN_ARG = "-logger.fullscreen"; + + private ECUExec() { + throw new UnsupportedOperationException(); + } + + public static void main(String args[]) { + // init debug logging + initDebugLogging(); + // dump the system properties to the log file as early as practical to + // help debugging/support + LOGGER.info(PRODUCT_NAME + " " + VERSION + " Build: " + BUILDNUMBER); + LOGGER.info("When requesting assistance at " + SUPPORT_URL + " please include the System Properties information below:"); + LOGGER.info(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.LONG).format(System.currentTimeMillis())); + LOGGER.info("System Properties: \n\t" + + System.getProperties().toString().replace(",", "\n\t")); + + // 64-bit won't work with the native libs (e.g. serial rxtx) but won't + // fail until we actually try to use them we'll just warn here + if (!JREChecker.is32bit() && + !containsLoggerArg(args)) { + showMessageDialog(null, + "Incompatible JRE detected.\n" + + PRODUCT_NAME + + " requires a 32-bit JRE for some operations.\nSome features may be unavailable.", + "JRE Incompatibility Warning", + WARNING_MESSAGE); + } + + // check for dodgy threading - dev only + //RepaintManager.setCurrentManager(new ThreadCheckingRepaintManager(true)); + + // set look and feel + initLookAndFeel(); + + // check if already running + if (isRunning()) { + if (args.length == 0 || containsLoggerArg(args)) { + showAlreadyRunningMessage(); + } else { + sendRomToOpenInstance(args[0]); + } + } else { + openEditor(args); + } + } + + private static void showAlreadyRunningMessage() { + showMessageDialog(null, PRODUCT_NAME + " is already running.", PRODUCT_NAME, INFORMATION_MESSAGE); + } + + private static boolean containsLoggerArg(String[] args) { + for (String arg : args) { + if (arg.equalsIgnoreCase(START_LOGGER_ARG) || arg.equalsIgnoreCase(START_LOGGER_FULLSCREEN_ARG)) { + return true; + } + } + return false; + } + + + private static void openRom(final ECUEditor editor, final String rom) { + invokeLater(new Runnable() { + @Override + public void run() { + try { + File file = new File(rom); + editor.openImage(file); + } catch (Exception ex) { + LOGGER.error("Error opening rom", ex); + } + } + }); + } + + private static void openEditor(String[] args) { + ECUEditor editor = getECUEditor(); + editor.initializeEditorUI(); + editor.checkDefinitions(); + + if (args.length > 0) { + openRom(editor, args[0]); + } + startRomListener(editor); + } + + private static void startRomListener(ECUEditor editor) { + try { + while (true) { + String rom = waitForRom(); + openRom(editor, rom); + } + } catch (Throwable e) { + LOGGER.error("Error occurred", e); + } + } +} diff --git a/java_console/romraider/src/com/romraider/Settings.java b/java_console/romraider/src/com/romraider/Settings.java new file mode 100644 index 0000000000..4ab85a14ab --- /dev/null +++ b/java_console/romraider/src/com/romraider/Settings.java @@ -0,0 +1,873 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2014 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider; + +import static com.romraider.Version.RELEASE_NOTES; +import static com.romraider.Version.ROM_REVISION_URL; +import static com.romraider.Version.SUPPORT_URL; +import static com.romraider.util.ParamChecker.checkNotNullOrEmpty; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Point; +import java.io.File; +import java.io.Serializable; +import java.util.Locale; +import java.util.Map; +import java.util.Vector; + +import com.romraider.io.connection.ConnectionProperties; +import com.romraider.logger.ecu.definition.EcuDefinition; +//import com.romraider.logger.external.phidget.interfacekit.io.IntfKitSensor; + +public class Settings implements Serializable { + + private static final long serialVersionUID = 1026542922680475190L; + + /* String Format Settings */ + public static final String NEW_LINE = System.getProperty("line.separator"); + public static final String TAB = "\t"; + public static final String BLANK = ""; + + /* Clipboard Settings */ + public static final String TABLE_CLIPBOARD_FORMAT_ELEMENT = "table-clipboard-format"; + public static final String TABLE_CLIPBOARD_FORMAT_ATTRIBUTE = "format-string"; + public static final String TABLE_ELEMENT = "table"; + public static final String TABLE1D_ELEMENT = "table1D"; + public static final String TABLE2D_ELEMENT = "table2D"; + public static final String TABLE3D_ELEMENT = "table3D"; + public static final String TABLE_HEADER_ATTRIBUTE = "table-header"; + + public static final String DEFAULT_CLIPBOARD_FORMAT = "Default"; + public static final String DEFAULT_TABLE_HEADER = "[Table1D]" + NEW_LINE; + public static final String DEFAULT_TABLE1D_HEADER = ""; + public static final String DEFAULT_TABLE2D_HEADER = "[Table2D]" + NEW_LINE; + public static final String DEFAULT_TABLE3D_HEADER = "[Table3D]" + NEW_LINE; + + public static final String AIRBOYS_CLIPBOARD_FORMAT = "Airboys"; + public static final String AIRBOYS_TABLE_HEADER = ""; + public static final String AIRBOYS_TABLE1D_HEADER = ""; + public static final String AIRBOYS_TABLE2D_HEADER = "[Table2D]" + NEW_LINE; + public static final String AIRBOYS_TABLE3D_HEADER = "[Table3D]" + TAB; + + public static final String CUSTOM_CLIPBOARD_FORMAT = "Custom"; + + /* XML Settings */ + public static final String REPOSITORY_ELEMENT_NAME = "repository-dir"; + public static final String REPOSITORY_ATTRIBUTE_NAME = "path"; + + public static final String ICONS_ELEMENT_NAME = "icons"; + public static final String EDITOR_ICONS_ELEMENT_NAME = "editor-toolbar"; + public static final String EDITOR_ICONS_SCALE_ATTRIBUTE_NAME = "scale"; + public static final String TABLE_ICONS_ELEMENT_NAME = "table-toolbar"; + public static final String TABLE_ICONS_SCALE_ATTRIBUTE_NAME = "scale"; + + /* UI Settings */ + public static final int DEFAULT_EDITOR_ICON_SCALE = 50; + public static final int DEFAULT_TABLE_ICON_SCALE = 70; + + /* Table Settings */ + public static final String defaultTableToolBarName = "Table Tools"; + + public static final int ENDIAN_LITTLE = 1; + public static final int ENDIAN_BIG = 2; + + public static final int TABLE_1D = 1; + public static final int TABLE_2D = 2; + public static final int TABLE_3D = 3; + public static final int TABLE_X_AXIS = 4; + public static final int TABLE_Y_AXIS = 5; + public static final int TABLE_SWITCH = 6; + + public static final int DATA_TYPE_ORIGINAL = 0; + public static final int DATA_TYPE_BIN = 1; + public static final int DATA_TYPE_REAL = 3; + + public static final int COMPARE_DISPLAY_PERCENT = 1; + public static final int COMPARE_DISPLAY_ABSOLUTE = 2; + + public static final int STORAGE_TYPE_FLOAT = 99; + public static final boolean STORAGE_DATA_SIGNED = false; + + public static final Color UNCHANGED_VALUE_COLOR = new Color(160, 160, 160); + + public static final String INVALID_ATTRIBUTE_TEXT = "invalid"; + + public static final String DEFAULT_TABLE_NAME = "Unknown"; + + /* Rom Settings */ + public static final int CHECK_TOTAL = 0x5AA5A55A; + public static final int SIXTEENBIT_START_ADDRESS = 0x20000; + public static final int SIXTEENBIT_END_ADDRESS = 0x28000; + public static final int SIXTEENBIT_SMALL_SIZE = 0x28000; + public static final int SIXTEENBIT_LARGE_SIZE = 0x30000; + public static final int SIXTEENBIT_SEGMENT_SIZE = SIXTEENBIT_SMALL_SIZE - SIXTEENBIT_START_ADDRESS; // 0x8000 + public static final int SIXTEENBIT_SEGMENT_VALUE = 0x00; + + /* Scale Settings */ + public static final int LINEAR = 1; + public static final int INVERSE = 2; + + /* Compare Image Settings */ + public static Color TABLE_EQUAL_COLOR = new Color(52,114,53); + public static Color TABLE_DIFFERENT_COLOR = new Color(193, 27, 23); + public static Color TABLE_MISSING_COLOR = new Color(251,185,23); + + /* Compare DTC Foreground Colors */ + public static Color TABLESWITCH_DEFAULT_COLOR = Color.black; + public static Color TABLESWITCH_DIFFERENT_COLOR = new Color(193, 27, 23); + + /* MDI Desktop Settings*/ + public static int FRAME_OFFSET = 20; + + /* Scale Settings */ + public static String DEFAULT_SCALE = "Default"; + public static String METRIC_SCALE = "Metric"; + public static String STANDARD_SCALE = "Standard"; + + /* DataCell Colors */ + public static final Color scaleTextColor = new Color(0, 0, 0); + public static final Color highlightTextColor = new Color(255, 255, 255); + public static final Color selectTextColor = new Color(0, 0, 0); + public static final Color liveDataTraceTextColor = new Color(50, 50, 50); + + private static final String ISO15765 = "ISO15765"; + private static final String ISO9141 = "ISO9141"; + private static final String SYSTEM_NUMFORMAT = "system"; + private static final String USER_LANGUAGE = "user.language"; + private static final String USER_COUNTRY = "user.country"; + private static final String EN_US = "en_US"; + private static final String SSM = "SSM"; + private static final String OBD = "OBD"; + + private final Dimension windowSize = new Dimension(800, 600); + private final Point windowLocation = new Point(); + private int splitPaneLocation = 150; + private boolean windowMaximized; + + private String recentVersion = "x"; + + private Vector ecuDefinitionFiles = new Vector(); + private File lastImageDir = new File("images"); + private File lastRepositoryDir = new File("repositories"); + private boolean obsoleteWarning = true; + private boolean calcConflictWarning = true; + private boolean debug; + private int userLevel = 1; + private boolean saveDebugTables = true; + private boolean displayHighTables = true; + private boolean valueLimitWarning = true; + + private Font tableFont = new Font("Arial", Font.BOLD, 11); + private Dimension cellSize = new Dimension(42, 18); + private Color maxColor = new Color(255, 102, 102); + private Color minColor = new Color(153, 153, 255); + + private Color selectColor = new Color(204, 204, 204); + private Color highlightColor = new Color(27, 161, 226); + private Color liveValueColor = new Color (0, 255, 0); + + private Color decreaseBorder = new Color(0, 0, 255); + private Color increaseBorder = new Color(255, 0, 0); + + private Color axisColor = new Color(255, 255, 255); + private Color warningColor = new Color(255, 0, 0); + private int tableClickCount = 1; // number of clicks to open table + private int tableClickBehavior = 0; // TableTreeNode click behavior. 0=open/close frame, 1=open/focus frame + private boolean colorAxis = false; + + private String loggerPort; + private String loggerPortDefault; + private static String loggerProtocol = SSM; + private static String loggerDefinitionFilePath; + private static String loggerProfileFilePath; + private static String loggerOutputDirPath = System.getProperty("user.home"); + private String fileLoggingControllerSwitchId = "S20"; // defogger switch by default + private boolean fileLoggingControllerSwitchActive = true; + private boolean fileLoggingAbsoluteTimestamp; + private String logfileNameText; + private boolean logExternalsOnly; + private static String userLocale = SYSTEM_NUMFORMAT; + + private Dimension loggerWindowSize = new Dimension(1000, 600); + private Point loggerWindowLocation = new Point(); + private boolean loggerWindowMaximized; + private int loggerSelectedTabIndex; + private boolean loggerParameterListState = true; + private ConnectionProperties loggerConnectionProperties; + private Map loggerEcuDefinitionMap; + private Map loggerPluginPorts; + private boolean loggerRefreshMode; + private static byte loggerDestinationId = 0x10; + private boolean fastPoll = true; + private double loggerDividerLocation = 400; + private String loggerDebuggingLevel = "info"; + private static String j2534Device; + private static String transportProtocol = ISO9141; + + private String tableClipboardFormat = DEFAULT_CLIPBOARD_FORMAT; // Currently 2 options. Default and Airboy. Custom is not hooked up. + private String tableHeader = DEFAULT_TABLE_HEADER; + private String table1DHeader = DEFAULT_TABLE1D_HEADER; + private String table2DHeader = DEFAULT_TABLE2D_HEADER; + private String table3DHeader = DEFAULT_TABLE3D_HEADER; + + private int editorIconScale = DEFAULT_EDITOR_ICON_SCALE; + private int tableIconScale = DEFAULT_TABLE_ICON_SCALE; + + private boolean openExpanded = true; + private boolean showTableToolbarBorder = false; + private boolean alwaysOpenTableAtZero = false; + private boolean scaleHeadersAndData = true; + + private String defaultScale = "Metric"; + +// private Map phidgetSensors; + + public Settings() { + //center window by default + Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize(); + windowLocation.move(((int) (screenSize.getWidth() - windowSize.getWidth()) / 2), + ((int) (screenSize.getHeight() - windowSize.getHeight()) / 2)); + } + + public Dimension getWindowSize() { + return windowSize; + } + + public Point getWindowLocation() { + return windowLocation; + } + + public void setWindowSize(Dimension size) { + windowSize.setSize(size); + } + + public void setWindowLocation(Point location) { + windowLocation.setLocation(location); + } + + public Vector getEcuDefinitionFiles() { + return ecuDefinitionFiles; + } + + public void addEcuDefinitionFile(File ecuDefinitionFile) { + ecuDefinitionFiles.add(ecuDefinitionFile); + } + + public void setEcuDefinitionFiles(Vector ecuDefinitionFiles) { + this.ecuDefinitionFiles = ecuDefinitionFiles; + } + + public File getLastImageDir() { + return lastImageDir; + } + + public void setLastImageDir(File lastImageDir) { + this.lastImageDir = lastImageDir; + } + + public File getLastRepositoryDir() { + return lastRepositoryDir; + } + + public void setLastRepositoryDir(File lastRepositoryDir) { + this.lastRepositoryDir = lastRepositoryDir; + } + + public int getSplitPaneLocation() { + return splitPaneLocation; + } + + public void setSplitPaneLocation(int splitPaneLocation) { + this.splitPaneLocation = splitPaneLocation; + } + + public boolean isWindowMaximized() { + return windowMaximized; + } + + public void setWindowMaximized(boolean windowMaximized) { + this.windowMaximized = windowMaximized; + } + + public String getRomRevisionURL() { + return ROM_REVISION_URL; + } + + public String getSupportURL() { + return SUPPORT_URL; + } + + public Font getTableFont() { + return tableFont; + } + + public void setTableFont(Font tableFont) { + this.tableFont = tableFont; + } + + public boolean isObsoleteWarning() { + return obsoleteWarning; + } + + public void setObsoleteWarning(boolean obsoleteWarning) { + this.obsoleteWarning = obsoleteWarning; + } + + public boolean isDebug() { + return debug; + } + + public void setDebug(boolean debug) { + this.debug = debug; + } + + public Dimension getCellSize() { + return cellSize; + } + + public void setCellSize(Dimension cellSize) { + this.cellSize = cellSize; + } + + public Color getMaxColor() { + return maxColor; + } + + public void setMaxColor(Color maxColor) { + this.maxColor = maxColor; + } + + public Color getMinColor() { + return minColor; + } + + public void setMinColor(Color minColor) { + this.minColor = minColor; + } + + public Color getHighlightColor() { + return highlightColor; + } + + public void setHighlightColor(Color highlightColor) { + this.highlightColor = highlightColor; + } + + public Color getliveValueColor() { + return this.liveValueColor; + } + + public void setLiveValueColor(Color liveValueColor) { + this.liveValueColor = liveValueColor; + } + + public Color getSelectColor() { + return selectColor; + } + + public void setSelectColor(Color selectColor) { + this.selectColor = selectColor; + } + + public boolean isCalcConflictWarning() { + return calcConflictWarning; + } + + public void setCalcConflictWarning(boolean calcConflictWarning) { + this.calcConflictWarning = calcConflictWarning; + } + + public Color getIncreaseBorder() { + return increaseBorder; + } + + public void setIncreaseBorder(Color increaseBorder) { + this.increaseBorder = increaseBorder; + } + + public Color getDecreaseBorder() { + return decreaseBorder; + } + + public void setDecreaseBorder(Color decreaseBorder) { + this.decreaseBorder = decreaseBorder; + } + + public Color getAxisColor() { + return axisColor; + } + + public void setAxisColor(Color axisColor) { + this.axisColor = axisColor; + } + + public int getUserLevel() { + return userLevel; + } + + public void setUserLevel(int userLevel) { + if (userLevel > 5) { + this.userLevel = 5; + } else if (userLevel < 1) { + this.userLevel = 1; + } else { + this.userLevel = userLevel; + } + } + + public int getTableClickCount() { + return tableClickCount; + } + + public void setTableClickCount(int tableClickCount) { + this.tableClickCount = tableClickCount; + } + + public int getTableClickBehavior() { + return tableClickBehavior; + } + + public void setTableClickBehavior(int clickBehavior) { + // 0 = open/close + // 1 = open/focus + this.tableClickBehavior = clickBehavior; + } + + public String getRecentVersion() { + return recentVersion; + } + + public void setRecentVersion(String recentVersion) { + this.recentVersion = recentVersion; + } + + public String getReleaseNotes() { + return RELEASE_NOTES; + } + + public boolean isSaveDebugTables() { + return saveDebugTables; + } + + public void setSaveDebugTables(boolean saveDebugTables) { + this.saveDebugTables = saveDebugTables; + } + + public boolean isDisplayHighTables() { + return displayHighTables; + } + + public void setDisplayHighTables(boolean displayHighTables) { + this.displayHighTables = displayHighTables; + } + + public boolean isValueLimitWarning() { + return valueLimitWarning; + } + + public void setValueLimitWarning(boolean valueLimitWarning) { + this.valueLimitWarning = valueLimitWarning; + } + + public Color getWarningColor() { + return warningColor; + } + + public void setWarningColor(Color warningColor) { + this.warningColor = warningColor; + } + + public String getLoggerPort() { + return loggerPort; + } + + public void setLoggerPort(String loggerPort) { + this.loggerPort = loggerPort; + } + + public String getLoggerPortDefault() { + return loggerPortDefault; + } + + public void setLoggerPortDefault(String loggerPortDefault) { + this.loggerPortDefault = loggerPortDefault; + } + + public void setLoggerProtocol(String protocol) { + Settings.loggerProtocol = protocol; + } + + public String getLoggerProtocol() { + return loggerProtocol; + } + + public String getLoggerDefinitionFilePath() { + return loggerDefinitionFilePath; + } + + public void setLoggerDefinitionFilePath(String loggerDefinitionFilePath) { + Settings.loggerDefinitionFilePath = loggerDefinitionFilePath; + } + + public String getLoggerOutputDirPath() { + return loggerOutputDirPath; + } + + public Point getLoggerWindowLocation() { + return loggerWindowLocation; + } + + public void setLoggerWindowLocation(Point loggerWindowLocation) { + this.loggerWindowLocation = loggerWindowLocation; + } + + public boolean isLoggerWindowMaximized() { + return loggerWindowMaximized; + } + + public void setLoggerWindowMaximized(boolean loggerWindowMaximized) { + this.loggerWindowMaximized = loggerWindowMaximized; + } + + public Dimension getLoggerWindowSize() { + return loggerWindowSize; + } + + public void setLoggerWindowSize(Dimension loggerWindowSize) { + this.loggerWindowSize = loggerWindowSize; + } + + public double getDividerLocation() { + return loggerDividerLocation; + } + + public void setLoggerDividerLocation(double dividerLocation) { + this.loggerDividerLocation = dividerLocation; + } + + public String getLoggerProfileFilePath() { + return loggerProfileFilePath; + } + + public void setLoggerProfileFilePath(String loggerProfileFilePath) { + Settings.loggerProfileFilePath = loggerProfileFilePath; + } + + public void setLoggerOutputDirPath(String loggerOutputDirPath) { + Settings.loggerOutputDirPath = loggerOutputDirPath; + } + + public String getFileLoggingControllerSwitchId() { + return fileLoggingControllerSwitchId; + } + + public void setFileLoggingControllerSwitchId(String fileLoggingControllerSwitchId) { + checkNotNullOrEmpty(fileLoggingControllerSwitchId, "fileLoggingControllerSwitchId"); + this.fileLoggingControllerSwitchId = fileLoggingControllerSwitchId; + } + + public boolean isFileLoggingControllerSwitchActive() { + return fileLoggingControllerSwitchActive; + } + + public void setFileLoggingControllerSwitchActive(boolean fileLoggingControllerSwitchActive) { + this.fileLoggingControllerSwitchActive = fileLoggingControllerSwitchActive; + } + + public boolean isFileLoggingAbsoluteTimestamp() { + return fileLoggingAbsoluteTimestamp; + } + + public void setFileLoggingAbsoluteTimestamp(boolean fileLoggingAbsoluteTimestamp) { + this.fileLoggingAbsoluteTimestamp = fileLoggingAbsoluteTimestamp; + } + + public ConnectionProperties getLoggerConnectionProperties() { + return loggerConnectionProperties; + } + + public void setLoggerConnectionProperties(ConnectionProperties loggerConnectionProperties) { + this.loggerConnectionProperties = loggerConnectionProperties; + } + + public Map getLoggerEcuDefinitionMap() { + return loggerEcuDefinitionMap; + } + + public void setLoggerEcuDefinitionMap(Map loggerEcuDefinitionMap) { + this.loggerEcuDefinitionMap = loggerEcuDefinitionMap; + } + + public void setLoggerSelectedTabIndex(int loggerSelectedTabIndex) { + this.loggerSelectedTabIndex = loggerSelectedTabIndex; + } + + public int getLoggerSelectedTabIndex() { + return loggerSelectedTabIndex; + } + + public Map getLoggerPluginPorts() { + return loggerPluginPorts; + } + + public void setLoggerPluginPorts(Map loggerPluginPorts) { + this.loggerPluginPorts = loggerPluginPorts; + } + + public void setLoggerParameterListState(boolean ShowListState) { + this.loggerParameterListState = ShowListState; + } + + public boolean getLoggerParameterListState() { + return loggerParameterListState; + } + + public void setRefreshMode(boolean selected) { + this.loggerRefreshMode = selected; + } + + public boolean getRefreshMode() { + return loggerRefreshMode; + } + + public void setDestinationId(byte id) { + loggerDestinationId = id; + } + + public byte getDestinationId() { + return loggerDestinationId; + } + + public void setFastPoll(boolean state) { + this.fastPoll = state; + } + + public boolean isFastPoll() { + return fastPoll; + } + + public void setLogfileNameText(String text) { + this.logfileNameText = text; + } + + public String getLogfileNameText() { + return logfileNameText; + } + + public void setLoggerDebuggingLevel(String level) { + this.loggerDebuggingLevel = level; + } + + public String getLoggerDebuggingLevel() { + return loggerDebuggingLevel; + } + + public void setJ2534Device(String j2534Device) { + Settings.j2534Device = j2534Device; + } + + public String getJ2534Device() { + return j2534Device; + } + + public void setTransportProtocol(String transport) { + Settings.transportProtocol = transport; + } + + public String getTransportProtocol() { + return transportProtocol; + } + + public void setTableClipboardFormat(String formatString) { + this.tableClipboardFormat = formatString; + } + + public String getTableClipboardFormat() { + return this.tableClipboardFormat; + } + + public void setTableHeader(String header) { + this.tableHeader = header; + } + + public String getTableHeader() { + return this.tableHeader; + } + + public void setTable1DHeader(String header) { + this.table1DHeader = header; + } + + public String getTable1DHeader() { + return this.table1DHeader; + } + + public void setTable2DHeader(String header) { + this.table2DHeader = header; + } + + public String getTable2DHeader() { + return this.table2DHeader; + } + + public void setTable3DHeader(String header) { + this.table3DHeader = header; + } + + public String getTable3DHeader() { + return this.table3DHeader; + } + + public void setDefaultFormat() { + this.tableClipboardFormat = DEFAULT_CLIPBOARD_FORMAT; + this.tableHeader = DEFAULT_TABLE_HEADER; + this.table1DHeader = DEFAULT_TABLE1D_HEADER; + this.table2DHeader = DEFAULT_TABLE2D_HEADER; + this.table3DHeader = DEFAULT_TABLE3D_HEADER; + } + + public void setAirboysFormat() { + this.tableClipboardFormat = AIRBOYS_CLIPBOARD_FORMAT; + this.tableHeader = AIRBOYS_TABLE_HEADER; + this.table1DHeader = AIRBOYS_TABLE1D_HEADER; + this.table2DHeader = AIRBOYS_TABLE2D_HEADER; + this.table3DHeader = AIRBOYS_TABLE3D_HEADER; + } + + public int getEditorIconScale() { + return this.editorIconScale; + } + + public void setEditorIconScale(int scale) { + this.editorIconScale = scale; + } + + public int getTableIconScale() { + return this.tableIconScale; + } + + public void setTableIconScale(int scale) { + this.tableIconScale = scale; + } + + public void setLogExternalsOnly(boolean state) { + this.logExternalsOnly = state; + } + + public boolean isLogExternalsOnly() { + return logExternalsOnly; + } + + public boolean isCanBus() { + if (transportProtocol.equals(ISO15765)) { + return true; + } + return false; + } + + public boolean isObdProtocol() { + if (loggerProtocol.equals(OBD)) { + return true; + } + return false; + } + + public final boolean isUsNumberFormat() { + if (userLocale.equalsIgnoreCase(EN_US)) { + return true; + } + return false; + } + + public final String getLocale() { + return userLocale; + } + + public final void setLocale(String locale) { + userLocale = locale; + if (!locale.equalsIgnoreCase(SYSTEM_NUMFORMAT)) { + final String[] language = locale.split("_"); + System.setProperty(USER_LANGUAGE, language[0]); + System.setProperty(USER_COUNTRY, language[1]); + final Locale lc = new Locale(language[0], language[1]); + Locale.setDefault(lc); + } + } + + public boolean isOpenExpanded() { + return openExpanded; + } + + public void setOpenExpanded(boolean expanded) { + this.openExpanded = expanded; + } + + public boolean isShowTableToolbarBorder() { + return showTableToolbarBorder; + } + + public void setShowTableToolbarBorder(boolean showBorder) { + this.showTableToolbarBorder = showBorder; + } + + public boolean isAlwaysOpenTableAtZero() { + return alwaysOpenTableAtZero; + } + + public void setAlwaysOpenTableAtZero(boolean openAtZero) { + this.alwaysOpenTableAtZero = openAtZero; + } + + public boolean isScaleHeadersAndData() { + return this.scaleHeadersAndData; + } + + public void setScaleHeadersAndData(boolean headersAndData) { + this.scaleHeadersAndData = headersAndData; + } + + public boolean isColorAxis() { + return this.colorAxis; + } + + public void setColorAxis(boolean colorAxis) { + this.colorAxis = colorAxis; + } + + public String getDefaultScale() { + return this.defaultScale; + } + + public void setDefaultScale(String defaultScale) { + this.defaultScale = defaultScale; + } + +// public Map getPhidgetSensors() { +// return this.phidgetSensors; +// } +// +// public void setPhidgetSensors( +// Map phidgetSensors) { +// +// this.phidgetSensors = phidgetSensors; +// } +} diff --git a/java_console/romraider/src/com/romraider/Version.java.template b/java_console/romraider/src/com/romraider/Version.java.template new file mode 100644 index 0000000000..88732ede8d --- /dev/null +++ b/java_console/romraider/src/com/romraider/Version.java.template @@ -0,0 +1,41 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// @warning.generated-file@ + +package com.romraider; + +import javax.swing.ImageIcon; + +public final class Version { + public static final String PRODUCT_NAME = "@name.package@"; + public static final String VERSION = "@version.major@.@version.minor@.@version.patch@ @version.extra@ @version.extra1@"; + public static final String BUILDNUMBER = "@version.buildnumber@"; + public static final String SUPPORT_URL = "@supporturl@"; + public static final String ROM_REVISION_URL = "@romrevisionurl@"; + public static final String ECU_DEFS_URL = "@ecudefsurl@"; + public static final String LOGGER_DEFS_URL = "@loggerdefsurl@"; + public static final String CARS_DEFS_URL = "@carsdefsurl@"; + public static final String RELEASE_NOTES = "@release_notes@"; + public static final ImageIcon ABOUT_ICON = new ImageIcon(Version.class.getClass().getResource("/graphics/romraider-ico-large.gif")); + public static final int MIN_LOG_DEF_VERSION = @min.logger.def.version@; + + private Version() { + } +} diff --git a/java_console/romraider/src/com/romraider/editor/ecu/ECUEditor.java b/java_console/romraider/src/com/romraider/editor/ecu/ECUEditor.java new file mode 100644 index 0000000000..04560311f5 --- /dev/null +++ b/java_console/romraider/src/com/romraider/editor/ecu/ECUEditor.java @@ -0,0 +1,742 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2014 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.editor.ecu; + +import static com.romraider.Version.ECU_DEFS_URL; +import static com.romraider.Version.PRODUCT_NAME; +import static com.romraider.Version.VERSION; +import static javax.swing.JOptionPane.DEFAULT_OPTION; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.INFORMATION_MESSAGE; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static javax.swing.JOptionPane.showOptionDialog; +import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED; +import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER; +import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS; +import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyVetoException; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.IOException; +import java.util.Vector; + +import javax.swing.ImageIcon; +import javax.swing.JCheckBox; +import javax.swing.JInternalFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTextArea; +import javax.swing.SwingWorker; +import javax.swing.tree.TreePath; + +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import org.xml.sax.SAXParseException; + +import com.romraider.net.BrowserControl; +import com.romraider.Settings; +import com.romraider.maps.Rom; +import com.romraider.net.URL; +import com.romraider.swing.AbstractFrame; +import com.romraider.swing.CustomToolbarLayout; +import com.romraider.swing.ECUEditorMenuBar; +import com.romraider.swing.ECUEditorToolBar; +import com.romraider.swing.JProgressPane; +import com.romraider.swing.MDIDesktopPane; +import com.romraider.swing.RomTree; +import com.romraider.swing.RomTreeRootNode; +import com.romraider.swing.TableFrame; +import com.romraider.swing.TableToolBar; +import com.romraider.util.SettingsManager; +import com.romraider.xml.DOMRomUnmarshaller; +import com.romraider.xml.RomNotFoundException; +import com.sun.org.apache.xerces.internal.parsers.DOMParser; + +public class ECUEditor extends AbstractFrame { + private static final long serialVersionUID = -7826850987392016292L; + + private final String titleText = PRODUCT_NAME + " v" + VERSION + " | ECU Editor"; + + private final RomTreeRootNode imageRoot = new RomTreeRootNode("Open Images"); + private final RomTree imageList = new RomTree(imageRoot); + public MDIDesktopPane rightPanel = new MDIDesktopPane(); + public JProgressPane statusPanel = new JProgressPane(); + private JSplitPane splitPane = new JSplitPane(); + private Rom lastSelectedRom = null; + private ECUEditorToolBar toolBar; + private ECUEditorMenuBar menuBar; + private TableToolBar tableToolBar; + private final JPanel toolBarPanel = new JPanel(); + private OpenImageWorker openImageWorker; + private CloseImageWorker closeImageWorker; + private SetUserLevelWorker setUserLevelWorker; + private LaunchLoggerWorker launchLoggerWorker; + private final ImageIcon editorIcon = new ImageIcon(getClass().getResource("/graphics/romraider-ico.gif"), "RomRaider ECU Editor"); + + public ECUEditor() { + Settings settings = SettingsManager.getSettings(); + if (!settings.getRecentVersion().equalsIgnoreCase(VERSION)) { + showReleaseNotes(); + } + + setSize(settings.getWindowSize()); + setLocation(settings.getWindowLocation()); + if (settings.isWindowMaximized()) { + setExtendedState(MAXIMIZED_BOTH); + } + + JScrollPane rightScrollPane = new JScrollPane(rightPanel, + VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED); + JScrollPane leftScrollPane = new JScrollPane(imageList, + VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED); + + splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftScrollPane, rightScrollPane); + splitPane.setDividerSize(3); + splitPane.setDividerLocation(settings.getSplitPaneLocation()); + splitPane.addPropertyChangeListener(this); + splitPane.setContinuousLayout(true); + getContentPane().add(splitPane); + + rightPanel.setBackground(Color.BLACK); + imageList.setScrollsOnExpand(true); + + this.add(statusPanel, BorderLayout.SOUTH); + + //set remaining window properties + setIconImage(editorIcon.getImage()); + + setDefaultCloseOperation(EXIT_ON_CLOSE); + addWindowListener(this); + setTitle(titleText); + setVisible(true); + toFront(); + } + + public void initializeEditorUI() { + //create menubar + menuBar = new ECUEditorMenuBar(); + this.setJMenuBar(menuBar); + + // create toolbars + toolBar = new ECUEditorToolBar("Editor Tools"); + + tableToolBar = new TableToolBar(); + tableToolBar.updateTableToolBar(); + + CustomToolbarLayout toolBarLayout = new CustomToolbarLayout(FlowLayout.LEFT, 0, 0); + + toolBarPanel.setLayout(toolBarLayout); + toolBarPanel.add(toolBar); + toolBarPanel.add(tableToolBar); + toolBarPanel.setVisible(true); + + this.add(toolBarPanel, BorderLayout.NORTH); + validate(); + } + + public void checkDefinitions() { + if (SettingsManager.getSettings().getEcuDefinitionFiles().size() <= 0) { + // no ECU definitions configured - let user choose to get latest or configure later + Object[] options = {"Yes", "No"}; + int answer = showOptionDialog(null, + "ECU definitions not configured.\nGo online to download the latest definition files?", + "Editor Configuration", + DEFAULT_OPTION, + WARNING_MESSAGE, + null, + options, + options[0]); + if (answer == 0) { + BrowserControl.displayURL(ECU_DEFS_URL); + } else { + showMessageDialog(this, + "ECU definition files need to be configured before ROM images can be opened.\nMenu: ECU Definitions > ECU Definition Manager...", + "Editor Configuration", + INFORMATION_MESSAGE); + } + } + } + + private void showReleaseNotes() { + try { + BufferedReader br = new BufferedReader(new FileReader(SettingsManager.getSettings().getReleaseNotes())); + try { + // new version being used, display release notes + JTextArea releaseNotes = new JTextArea(); + releaseNotes.setEditable(false); + releaseNotes.setWrapStyleWord(true); + releaseNotes.setLineWrap(true); + releaseNotes.setFont(new Font("Tahoma", Font.PLAIN, 12)); + + StringBuffer sb = new StringBuffer(); + while (br.ready()) { + sb.append(br.readLine()).append(Settings.NEW_LINE); + } + releaseNotes.setText(sb.toString()); + releaseNotes.setCaretPosition(0); + + JScrollPane scroller = new JScrollPane(releaseNotes, VERTICAL_SCROLLBAR_ALWAYS, HORIZONTAL_SCROLLBAR_NEVER); + scroller.setPreferredSize(new Dimension(600, 500)); + + showMessageDialog(this, scroller, + PRODUCT_NAME + VERSION + " Release Notes", INFORMATION_MESSAGE); + } finally { + br.close(); + } + } catch (Exception e) { + /* Ignore */ + } + } + + public void handleExit() { + Settings settings = SettingsManager.getSettings(); + settings.setSplitPaneLocation(splitPane.getDividerLocation()); + settings.setWindowMaximized(getExtendedState() == MAXIMIZED_BOTH); + settings.setWindowSize(getSize()); + settings.setWindowLocation(getLocation()); + + // Save when exit to save file settings. + SettingsManager.save(settings, statusPanel); + statusPanel.update("Ready...", 0); + repaint(); + } + + @Override + public void windowClosing(WindowEvent e) { + handleExit(); + } + + @Override + public void windowOpened(WindowEvent e) { + } + + @Override + public void windowClosed(WindowEvent e) { + } + + @Override + public void windowIconified(WindowEvent e) { + } + + @Override + public void windowDeiconified(WindowEvent e) { + } + + @Override + public void windowActivated(WindowEvent e) { + } + + @Override + public void windowDeactivated(WindowEvent e) { + } + + public String getVersion() { + return VERSION; + } + + public void addRom(Rom input) { + Settings settings = SettingsManager.getSettings(); + input.refreshDisplayedTables(); + + // add to ecu image list pane + getImageRoot().add(input); + + getImageList().setVisible(true); + getImageList().expandPath(new TreePath(getImageRoot())); + + getImageList().expandPath(new TreePath(input.getPath())); + + if(!settings.isOpenExpanded()) { + imageList.collapsePath(new TreePath(input.getPath())); + } + + getImageList().setRootVisible(false); + getImageList().repaint(); + + // Only set if no other rom has been selected. + if(null == getLastSelectedRom()) { + setLastSelectedRom(input); + } + + if (input.getRomID().isObsolete() && settings.isObsoleteWarning()) { + JPanel infoPanel = new JPanel(); + infoPanel.setLayout(new GridLayout(3, 1)); + infoPanel.add(new JLabel("A newer version of this ECU revision exists. " + + "Please visit the following link to download the latest revision:")); + infoPanel.add(new URL(settings.getRomRevisionURL())); + + JCheckBox check = new JCheckBox("Always display this message", true); + check.setHorizontalAlignment(JCheckBox.RIGHT); + + check.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + SettingsManager.getSettings().setObsoleteWarning(((JCheckBox) e.getSource()).isSelected()); + } + }); + + infoPanel.add(check); + showMessageDialog(this, infoPanel, "ECU Revision is Obsolete", INFORMATION_MESSAGE); + } + } + + public void displayTable(TableFrame frame) { + try { + // check if frame has been added. + for(JInternalFrame curFrame : getRightPanel().getAllFrames()) { + if(curFrame.equals(frame)) { + // table is already open. + if(1 == SettingsManager.getSettings().getTableClickBehavior()) { // open/focus frame + // table is already open, so set focus on the frame. + boolean selected = true; + frame.toFront(); + try { + frame.setSelected(true); + } catch (PropertyVetoException e) { + frame.toBack(); + selected = false; + } + if(selected) { + frame.requestFocusInWindow(); + } + } else { // default to open/close frame + // table is already open, so close the frame. + rightPanel.remove(frame); + frame.setVisible(false); + try { + frame.setClosed(true); + } catch (PropertyVetoException e) { + ; // Do nothing. + } + frame.dispose(); + } + frame.pack(); + rightPanel.repaint(); + return; + } + } + + // frame not added. Draw table and add the frame. + frame.getTable().drawTable(); + rightPanel.add(frame); + } catch (IllegalArgumentException ex) { + ;// Do nothing. + } + frame.pack(); + rightPanel.repaint(); + } + + public void removeDisplayTable(TableFrame frame) { + frame.setVisible(false); + this.getTableToolBar().updateTableToolBar(); + rightPanel.remove(frame); + rightPanel.validate(); + refreshUI(); + } + + public void closeImage() { + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + closeImageWorker = new CloseImageWorker(getLastSelectedRom()); + closeImageWorker.addPropertyChangeListener(getStatusPanel()); + closeImageWorker.execute(); + } + + public void closeAllImages() { + while (imageRoot.getChildCount() > 0) { + closeImage(); + } + } + + public Rom getLastSelectedRom() { + return lastSelectedRom; + } + + public String getLastSelectedRomFileName() { + Rom lastSelRom = getLastSelectedRom(); + return lastSelRom == null ? "" : lastSelRom.getFileName() + " "; + } + + public void setLastSelectedRom(Rom lastSelectedRom) { + this.lastSelectedRom = lastSelectedRom; + if (lastSelectedRom == null) { + setTitle(titleText); + } else { + setTitle(titleText + " - " + lastSelectedRom.getFileName()); + } + } + + public ECUEditorToolBar getToolBar() { + return toolBar; + } + + public void setToolBar(ECUEditorToolBar toolBar) { + this.toolBar = toolBar; + } + + public ECUEditorMenuBar getEditorMenuBar() { + return menuBar; + } + + public TableToolBar getTableToolBar() { + return tableToolBar; + } + + public void redrawVisableTables(Settings settings) { + + } + + public void setUserLevel(int userLevel) { + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + SettingsManager.getSettings().setUserLevel(userLevel); + setUserLevelWorker = new SetUserLevelWorker(); + setUserLevelWorker.addPropertyChangeListener(getStatusPanel()); + setUserLevelWorker.execute(); + } + + public Vector getImages() { + Vector images = new Vector(); + for (int i = 0; i < imageRoot.getChildCount(); i++) { + if(imageRoot.getChildAt(i) instanceof Rom) { + Rom rom = (Rom) imageRoot.getChildAt(i); + if(null != rom) { + images.add(rom); + } + } + } + return images; + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + imageList.updateUI(); + imageList.repaint(); + rightPanel.updateUI(); + rightPanel.repaint(); + } + + public void refreshUI() + { + getToolBar().updateButtons(); + getEditorMenuBar().updateMenu(); + getTableToolBar().updateTableToolBar(); + imageList.updateUI(); + imageList.repaint(); + rightPanel.updateUI(); + rightPanel.repaint(); + } + + public void refreshTableCompareMenus() { + for (int i = 0; i < imageRoot.getChildCount(); i++) { + if(imageRoot.getChildAt(i) instanceof Rom) { + Rom rom = (Rom) imageRoot.getChildAt(i); + if(null != rom) { + rom.refreshTableCompareMenus(); + } + } + } + } + + public void openImage(File inputFile) throws Exception { + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + openImageWorker = new OpenImageWorker(inputFile); + openImageWorker.addPropertyChangeListener(getStatusPanel()); + openImageWorker.execute(); + } + + public void openImages(File[] inputFiles) throws Exception { + if(inputFiles.length < 1) { + showMessageDialog(this, "Image Not Found", "Error Loading Image(s)", ERROR_MESSAGE); + return; + } + for(int j = 0; j < inputFiles.length; j++) { + openImage(inputFiles[j]); + } + } + + public byte[] readFile(File inputFile) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + FileInputStream fis = new FileInputStream(inputFile); + try { + byte[] buf = new byte[8192]; + int bytesRead; + while ((bytesRead = fis.read(buf)) != -1) { + baos.write(buf, 0, bytesRead); + } + } finally { + fis.close(); + } + return baos.toByteArray(); + } + + public void launchLogger() { + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); +// launchLoggerWorker = new LaunchLoggerWorker(); +// launchLoggerWorker.addPropertyChangeListener(getStatusPanel()); +// launchLoggerWorker.execute(); + } + + public RomTreeRootNode getImageRoot() { + return imageRoot; + } + + public RomTree getImageList() { + return imageList; + } + + public JProgressPane getStatusPanel() { + return this.statusPanel; + } + + public MDIDesktopPane getRightPanel() { + return this.rightPanel; + } +} + +class LaunchLoggerWorker extends SwingWorker { + public LaunchLoggerWorker() { + } + + @Override + protected Void doInBackground() throws Exception { + ECUEditor editor = ECUEditorManager.getECUEditor(); + editor.getStatusPanel().setStatus("Launching Logger..."); + setProgress(10); +// EcuLogger.startLogger(javax.swing.WindowConstants.DISPOSE_ON_CLOSE, editor); + return null; + } + + public void propertyChange(PropertyChangeEvent evnt) + { + SwingWorker source = (SwingWorker) evnt.getSource(); + if (null != source && "state".equals( evnt.getPropertyName() ) + && (source.isDone() || source.isCancelled() ) ) + { + source.removePropertyChangeListener(ECUEditorManager.getECUEditor().getStatusPanel()); + } + } + + @Override + public void done() { + ECUEditor editor = ECUEditorManager.getECUEditor(); + editor.getStatusPanel().setStatus("Ready..."); + setProgress(0); + editor.setCursor(null); + editor.refreshUI(); + } +} + +class SetUserLevelWorker extends SwingWorker { + public SetUserLevelWorker() { + } + + @Override + protected Void doInBackground() throws Exception { + for(Rom rom : ECUEditorManager.getECUEditor().getImages()) { + rom.refreshDisplayedTables(); + } + return null; + } + + public void propertyChange(PropertyChangeEvent evnt) + { + SwingWorker source = (SwingWorker) evnt.getSource(); + if (null != source && "state".equals( evnt.getPropertyName() ) + && (source.isDone() || source.isCancelled() ) ) + { + source.removePropertyChangeListener(ECUEditorManager.getECUEditor().getStatusPanel()); + } + } + + @Override + public void done() { + ECUEditor editor = ECUEditorManager.getECUEditor(); + editor.getStatusPanel().setStatus("Ready..."); + setProgress(0); + editor.setCursor(null); + editor.refreshUI(); + } +} + +class CloseImageWorker extends SwingWorker { + Rom rom; + + public CloseImageWorker(Rom romToRemove) { + this.rom = romToRemove; + } + + @Override + protected Void doInBackground() throws Exception { + ECUEditor editor = ECUEditorManager.getECUEditor(); + RomTreeRootNode imageRoot = editor.getImageRoot(); + + rom.clearData(); + rom.removeFromParent(); + rom = null; + + if (imageRoot.getChildCount() > 0) { + editor.setLastSelectedRom((Rom) imageRoot.getChildAt(0)); + } else { + // no other images open + editor.setLastSelectedRom(null); + } + + editor.refreshTableCompareMenus(); + + return null; + } + + @Override + public void done() { + ECUEditor editor = ECUEditorManager.getECUEditor(); + editor.getStatusPanel().setStatus("Ready..."); + setProgress(0); + editor.setCursor(null); + editor.refreshUI(); + System.gc(); + } +} + +class OpenImageWorker extends SwingWorker { + private final File inputFile; + + public OpenImageWorker(File inputFile) { + this.inputFile = inputFile; + } + + @Override + protected Void doInBackground() throws Exception { + ECUEditor editor = ECUEditorManager.getECUEditor(); + Settings settings = SettingsManager.getSettings(); + + DOMParser parser = new DOMParser(); + Document doc; + FileInputStream fileStream; + + try { + editor.getStatusPanel().setStatus("Parsing ECU definitions..."); + setProgress(0); + + byte[] input = editor.readFile(inputFile); + + editor.getStatusPanel().setStatus("Finding ECU definition..."); + setProgress(10); + + // parse ecu definition files until result found + for (int i = 0; i < settings.getEcuDefinitionFiles().size(); i++) { + fileStream = new FileInputStream(settings.getEcuDefinitionFiles().get(i)); + InputSource src = new InputSource(fileStream); + + parser.parse(src); + doc = parser.getDocument(); + + Rom rom; + + try { + rom = new DOMRomUnmarshaller().unmarshallXMLDefinition(doc.getDocumentElement(), input, editor.getStatusPanel()); + } catch (RomNotFoundException rex) { + // rom was not found in current file, skip to next + continue; + } catch (Exception ex) { + ex.printStackTrace(); + showMessageDialog(editor, "Error Loading. Unknown Exception.", "Error Loading " + inputFile.getName(), ERROR_MESSAGE); + return null; + } finally { + // Release mem after unmarshall. + parser.reset(); + doc.removeChild(doc.getDocumentElement()); + doc = null; + fileStream.close(); + System.gc(); + } + + editor.getStatusPanel().setStatus("Populating tables..."); + setProgress(50); + + rom.setFullFileName(inputFile); + rom.populateTables(input, editor.getStatusPanel()); + + editor.getStatusPanel().setStatus("Finalizing..."); + setProgress(90); + + editor.addRom(rom); + editor.refreshTableCompareMenus(); + + editor.getStatusPanel().setStatus("Done loading image..."); + setProgress(100); + return null; + } + + // if code executes to this point, no ROM was found, report to user + showMessageDialog(editor, "ECU Definition Not Found", "Error Loading " + inputFile.getName(), ERROR_MESSAGE); + + } catch (SAXParseException spe) { + // catch general parsing exception - enough people don't unzip the defs that a better error message is in order + showMessageDialog(editor, "Unable to read XML definitions. Please make sure the definition file is correct. If it is in a ZIP archive, unzip the file and try again.", "Error Loading " + inputFile.getName(), ERROR_MESSAGE); + + } catch (StackOverflowError ex) { + // handles looped inheritance, which will use up all available memory + showMessageDialog(editor, "Looped \"base\" attribute in XML definitions.", "Error Loading " + inputFile.getName(), ERROR_MESSAGE); + + } catch (OutOfMemoryError ome) { + // handles Java heap space issues when loading multiple Roms. + showMessageDialog(editor, "Error loading Image. Out of memeory.", "Error Loading " + inputFile.getName(), ERROR_MESSAGE); + + } + return null; + } + + public void propertyChange(PropertyChangeEvent evnt) + { + SwingWorker source = (SwingWorker) evnt.getSource(); + if (null != source && "state".equals( evnt.getPropertyName() ) + && (source.isDone() || source.isCancelled() ) ) + { + source.removePropertyChangeListener(ECUEditorManager.getECUEditor().getStatusPanel()); + } + } + + @Override + public void done() { + ECUEditor editor = ECUEditorManager.getECUEditor(); + editor.getStatusPanel().setStatus("Ready..."); + setProgress(0); + editor.setCursor(null); + editor.refreshUI(); + System.gc(); + } +} diff --git a/java_console/romraider/src/com/romraider/editor/ecu/ECUEditorManager.java b/java_console/romraider/src/com/romraider/editor/ecu/ECUEditorManager.java new file mode 100644 index 0000000000..a04d9a3216 --- /dev/null +++ b/java_console/romraider/src/com/romraider/editor/ecu/ECUEditorManager.java @@ -0,0 +1,46 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.editor.ecu; + +import javax.swing.SwingUtilities; + + +public class ECUEditorManager { + private static ECUEditor editor = null; + + private ECUEditorManager() { + throw new UnsupportedOperationException(); + } + + public static ECUEditor getECUEditor() { + if (editor == null) { + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + editor = new ECUEditor(); + } + }); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return editor; + } +} diff --git a/java_console/romraider/src/com/romraider/io/connection/ConnectionManager.java b/java_console/romraider/src/com/romraider/io/connection/ConnectionManager.java new file mode 100644 index 0000000000..2083c6c3b3 --- /dev/null +++ b/java_console/romraider/src/com/romraider/io/connection/ConnectionManager.java @@ -0,0 +1,32 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.io.connection; + +import com.romraider.logger.ecu.comms.manager.PollingState; + +public interface ConnectionManager { + void send(byte[] request, byte[] response, PollingState pollState); + + byte[] send(byte[] bytes); + + void clearLine(); + + void close(); +} diff --git a/java_console/romraider/src/com/romraider/io/connection/ConnectionProperties.java b/java_console/romraider/src/com/romraider/io/connection/ConnectionProperties.java new file mode 100644 index 0000000000..27606f3a8f --- /dev/null +++ b/java_console/romraider/src/com/romraider/io/connection/ConnectionProperties.java @@ -0,0 +1,36 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.io.connection; + +public interface ConnectionProperties { + int getBaudRate(); + + void setBaudRate(int b); + + int getDataBits(); + + int getStopBits(); + + int getParity(); + + int getConnectTimeout(); + + int getSendTimeout(); +} diff --git a/java_console/romraider/src/com/romraider/io/connection/ConnectionPropertiesImpl.java b/java_console/romraider/src/com/romraider/io/connection/ConnectionPropertiesImpl.java new file mode 100644 index 0000000000..7c00378a8d --- /dev/null +++ b/java_console/romraider/src/com/romraider/io/connection/ConnectionPropertiesImpl.java @@ -0,0 +1,82 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.io.connection; + +public final class ConnectionPropertiesImpl implements ConnectionProperties { + private final int baudRate; + private final int dataBits; + private final int stopBits; + private final int parity; + private final int connectTimeout; + private final int sendTimeout; + + + public ConnectionPropertiesImpl(int baudRate, int dataBits, int stopBits, int parity, int connectTimeout, int sendTimeout) { + this.baudRate = baudRate; + this.dataBits = dataBits; + this.stopBits = stopBits; + this.parity = parity; + this.connectTimeout = connectTimeout; + this.sendTimeout = sendTimeout; + } + + public int getBaudRate() { + return baudRate; + } + + public void setBaudRate(int b) { + + } + + public int getDataBits() { + return dataBits; + } + + public int getStopBits() { + return stopBits; + } + + public int getParity() { + return parity; + } + + public int getConnectTimeout() { + return connectTimeout; + } + + public int getSendTimeout() { + return sendTimeout; + } + + public String toString() { + final String properties = String.format( + "%s[baudRate=%d, dataBits=%d, stopBits=%d, parity=%d, " + + "connectTimeout=%d, sendTimeout=%d]", + getClass().getSimpleName(), + getBaudRate(), + getDataBits(), + getStopBits(), + getParity(), + getConnectTimeout(), + getSendTimeout() + ); + return properties; + } +} diff --git a/java_console/romraider/src/com/romraider/io/serial/port/SerialPortRefreshListener.java b/java_console/romraider/src/com/romraider/io/serial/port/SerialPortRefreshListener.java new file mode 100644 index 0000000000..3260c917c6 --- /dev/null +++ b/java_console/romraider/src/com/romraider/io/serial/port/SerialPortRefreshListener.java @@ -0,0 +1,28 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.io.serial.port; + +import java.util.Set; + +public interface SerialPortRefreshListener { + + void refreshPortList(Set ports, String defaultSelectedPort); + +} diff --git a/java_console/romraider/src/com/romraider/logger/car/util/Constants.java b/java_console/romraider/src/com/romraider/logger/car/util/Constants.java new file mode 100644 index 0000000000..577286c399 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/car/util/Constants.java @@ -0,0 +1,40 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.car.util; + +public enum Constants { + IMPERIAL ("imperial"), + IMPERIAL_UNIT ("mph"), + METRIC ("metric"), + METRIC_UNIT ("km/h"), + KPH_2_MPH ("1.609344"), + TQ_CONSTANT_I ("5252.113122"), + TQ_CONSTANT_M ("9549.296748"); + + private final String value; + + Constants (String value) { + this.value = value; + } + + public String value(){ + return value; + } +} diff --git a/java_console/romraider/src/com/romraider/logger/car/util/SpeedCalculator.java b/java_console/romraider/src/com/romraider/logger/car/util/SpeedCalculator.java new file mode 100644 index 0000000000..06441e5571 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/car/util/SpeedCalculator.java @@ -0,0 +1,44 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.car.util; + +import static com.romraider.logger.car.util.Constants.IMPERIAL_UNIT; +import static com.romraider.logger.car.util.Constants.KPH_2_MPH; +import static com.romraider.logger.car.util.Constants.METRIC_UNIT; + +public class SpeedCalculator { + private static final double K2M = Double.parseDouble(KPH_2_MPH.value()); + + public static double calculateMph(double rpm, double ratio) { + return (rpm / ratio); + } + + public static double calculateKph(double rpm, double ratio) { + return calculateMph(rpm, ratio) * K2M; + } + + public static double calculateRpm(double vs, double ratio, String units) { + double rpm = 0; + if (units.equalsIgnoreCase(IMPERIAL_UNIT.value())) rpm = (vs * ratio); + if (units.equalsIgnoreCase(METRIC_UNIT.value())) rpm = (vs * ratio / K2M); + return rpm; + } + +} diff --git a/java_console/romraider/src/com/romraider/logger/car/util/TorqueCalculator.java b/java_console/romraider/src/com/romraider/logger/car/util/TorqueCalculator.java new file mode 100644 index 0000000000..8a4fcd8cb2 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/car/util/TorqueCalculator.java @@ -0,0 +1,39 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.car.util; + +import static com.romraider.logger.car.util.Constants.IMPERIAL; +import static com.romraider.logger.car.util.Constants.METRIC; +import static com.romraider.logger.car.util.Constants.TQ_CONSTANT_I; +import static com.romraider.logger.car.util.Constants.TQ_CONSTANT_M; + +public class TorqueCalculator { + + public static double calculateTorque(double rpm, double hp, String units) { + double tq = 0; + if (units.equalsIgnoreCase(IMPERIAL.value())) { + tq = hp / rpm * Double.parseDouble(TQ_CONSTANT_I.value()); + } + if (units.equalsIgnoreCase(METRIC.value())) { + tq = hp / rpm * Double.parseDouble(TQ_CONSTANT_M.value()); + } + return tq; + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/EcuLoggerExec.java b/java_console/romraider/src/com/romraider/logger/ecu/EcuLoggerExec.java new file mode 100644 index 0000000000..074c424397 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/EcuLoggerExec.java @@ -0,0 +1,47 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu; + +import static javax.swing.WindowConstants.EXIT_ON_CLOSE; + +import com.romraider.swing.LookAndFeelManager; +import com.romraider.util.LogManager; + +public final class EcuLoggerExec { + + private EcuLoggerExec() { + throw new UnsupportedOperationException(); + } + + public static void main(String... args) { + // init debug loging + LogManager.initDebugLogging(); + + // check for dodgy threading - dev only + // RepaintManager.setCurrentManager(new ThreadCheckingRepaintManager(true)); + + // set look and feel + LookAndFeelManager.initLookAndFeel(); + + // start logger + // EcuLogger.startLogger(EXIT_ON_CLOSE, args); + } + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/controller/LoggerController.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/controller/LoggerController.java new file mode 100644 index 0000000000..9ade304f21 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/controller/LoggerController.java @@ -0,0 +1,41 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.controller; + +import com.romraider.logger.ecu.definition.LoggerData; +import com.romraider.logger.ecu.ui.StatusChangeListener; +import com.romraider.logger.ecu.ui.handler.file.FileLoggerControllerSwitchMonitor; + +public interface LoggerController { + + void setFileLoggerSwitchMonitor(FileLoggerControllerSwitchMonitor monitor); + + void addLogger(String callerId, LoggerData loggerData); + + void removeLogger(String callerId, LoggerData loggerData); + + boolean isStarted(); + + void start(); + + void stop(); + + void addListener(StatusChangeListener listener); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/controller/LoggerControllerImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/controller/LoggerControllerImpl.java new file mode 100644 index 0000000000..1ac78986a8 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/controller/LoggerControllerImpl.java @@ -0,0 +1,111 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.controller; + +import static com.romraider.util.ParamChecker.checkNotNull; +import static com.romraider.util.ThreadUtil.runAsDaemon; + +import org.apache.log4j.Logger; + +import com.romraider.logger.ecu.comms.manager.QueryManager; +import com.romraider.logger.ecu.comms.manager.QueryManagerImpl; +import com.romraider.logger.ecu.comms.query.EcuInitCallback; +import com.romraider.logger.ecu.definition.LoggerData; +import com.romraider.logger.ecu.ui.MessageListener; +import com.romraider.logger.ecu.ui.StatusChangeListener; +import com.romraider.logger.ecu.ui.handler.DataUpdateHandler; +import com.romraider.logger.ecu.ui.handler.file.FileLoggerControllerSwitchMonitor; + +public final class LoggerControllerImpl implements LoggerController { + private static final Logger LOGGER = Logger.getLogger(LoggerControllerImpl.class); + private final QueryManager queryManager; + + public LoggerControllerImpl(EcuInitCallback ecuInitCallback, MessageListener messageListener, + DataUpdateHandler... dataUpdateHandlers) { + checkNotNull(ecuInitCallback, messageListener, dataUpdateHandlers); + queryManager = new QueryManagerImpl(ecuInitCallback, messageListener, dataUpdateHandlers); + } + + @Override + public synchronized void addListener(StatusChangeListener listener) { + checkNotNull(listener, "listener"); + queryManager.addListener(listener); + } + + @Override + public void setFileLoggerSwitchMonitor(FileLoggerControllerSwitchMonitor monitor) { + checkNotNull(monitor); + LOGGER.debug("Setting file logger switch monitor: [" + monitor.getEcuSwitch().getId() + "] " + monitor.getEcuSwitch().getName()); + queryManager.setFileLoggerSwitchMonitor(monitor); + } + + @Override + public void addLogger(String callerId, LoggerData loggerData) { + checkNotNull(loggerData); + LOGGER.debug("Adding logger: [" + loggerData.getId() + "] " + loggerData.getName()); + queryManager.addQuery(callerId, loggerData); + } + + @Override + public void removeLogger(String callerId, LoggerData loggerData) { + checkNotNull(loggerData, "ecuParam"); + LOGGER.debug("Removing logger: [" + loggerData.getId() + "] " + loggerData.getName()); + queryManager.removeQuery(callerId, loggerData); + } + + @Override + public synchronized boolean isStarted() { + return queryManager.isRunning(); + } + + @Override + public synchronized void start() { + if (!isStarted()) runAsDaemon(queryManager); + } + + @Override + public synchronized void stop() { + if (isStarted() && queryManager.getThread().isAlive()) { + queryManager.stop(); + try { + LOGGER.debug(String.format( + "%s - Stopping QueryManager: %s", + this.getClass().getSimpleName(), + queryManager.getThread().getName())); + queryManager.getThread().interrupt(); + LOGGER.debug(String.format( + "%s - Waiting for QueryManager %s to terminate", + this.getClass().getSimpleName(), + queryManager.getThread().getName())); + queryManager.getThread().join(5000); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + finally { + LOGGER.debug(String.format( + "%s - QueryManager %s state: %s", + this.getClass().getSimpleName(), + queryManager.getThread().getName(), + queryManager.getThread().getState())); + } + } + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/learning/LearningTableValues.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/learning/LearningTableValues.java new file mode 100644 index 0000000000..8d51cbbec3 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/learning/LearningTableValues.java @@ -0,0 +1,26 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.learning; + + +public interface LearningTableValues { + + void execute(); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/learning/flkctable/FlkcTableQueryBuilder.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/learning/flkctable/FlkcTableQueryBuilder.java new file mode 100644 index 0000000000..47db997e19 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/learning/flkctable/FlkcTableQueryBuilder.java @@ -0,0 +1,108 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.learning.flkctable; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import com.romraider.logger.ecu.comms.learning.parameter.Parameter; +import com.romraider.logger.ecu.comms.query.EcuQuery; +import com.romraider.logger.ecu.comms.query.EcuQueryImpl; +import com.romraider.logger.ecu.definition.EcuAddress; +import com.romraider.logger.ecu.definition.EcuAddressImpl; +import com.romraider.logger.ecu.definition.EcuData; +import com.romraider.logger.ecu.definition.EcuDataConvertor; +import com.romraider.logger.ecu.definition.EcuParameterImpl; +import com.romraider.logger.ecu.ui.paramlist.ParameterRow; +import com.romraider.util.HexUtil; + +/** + * Build an EcuQuery for each of the cells in the FLKC RAM table. + */ +public class FlkcTableQueryBuilder { + private static final Logger LOGGER = + Logger.getLogger(FlkcTableQueryBuilder.class); + + public FlkcTableQueryBuilder() { + } + + /** + * Build an EcuQuery for each cell of the FLKC RAM table. Note this + * returns an extra null query for column 0 of each row which is later + * populated with the row header (RPM Ranges) data. + * @param flkc - a ParameterRow item that helps to identify the + * ECU bitness and provide a Converter for the raw data. + * @param flkcAddr - the address in RAM of the start of the table. + * @param rows - the number of rows in the table. + * @param columns - the number of columns in the table. + * @return EcuQueries divided into groups to query each row separately to + * avoid maxing out the ECU send/receive buffer. + */ + public final List> build( + ParameterRow flkc, + int flkcAddr, + int rows, + int columns) { + + final List> flkcQueryRows = new ArrayList>(); + int checksummed = 1; + int dataSize = 1; + if (Parameter.fromValue(flkc.getLoggerData().getId()) == Parameter.E41 || + Parameter.fromValue(flkc.getLoggerData().getId()) == Parameter.E173) { + checksummed = 2; + dataSize = 4; + } + LOGGER.debug( + String.format( + "FLKC Data format rows:%d col:%d checksummed:%d " + + "dataSize:%d FLKC:%s", + rows, columns, checksummed, dataSize, + flkc.getLoggerData().getId())); + + int i = 0; + for (int j = 0; j < rows; j++) { + final List flkcQueryCols = new ArrayList(); + flkcQueryCols.add(null); + for (int k = 0; k < columns; k++) { + String id = "flkc-r" + j + "c" + k; + final String addrStr = + HexUtil.intToHexString( + flkcAddr + (i * dataSize * checksummed)); + LOGGER.debug( + String.format( + "FLKC Data row:%d col:%d addr:%s", + j, k, addrStr)); + final EcuAddress ea = new EcuAddressImpl(addrStr, dataSize, -1); + final EcuParameterImpl epi = + new EcuParameterImpl(id, addrStr, id, ea, + new EcuDataConvertor[] { + flkc.getLoggerData().getSelectedConvertor() + } + ); + flkcQueryCols.add(new EcuQueryImpl((EcuData) epi)); + i++; + } + flkcQueryRows.add(flkcQueryCols); + } + return flkcQueryRows; + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/learning/parameter/Parameter.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/learning/parameter/Parameter.java new file mode 100644 index 0000000000..28300fae84 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/learning/parameter/Parameter.java @@ -0,0 +1,88 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.learning.parameter; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +/** + * This Emun defines all the possible parameters used to query the Learning + * Table Values of an ECU. + */ +public enum Parameter { + E1("E1"), // IAM 16 bit + E31("E31"), // IAM 32 bit + E12("E12"), // Fine Learning Knock Correction* + E41("E41"), // Fine Learning Knock Correction (4-byte)* + E13("E13"), // AF Learning 1A 16 bit + E14("E14"), // AF Learning 1B 16 bit + E15("E15"), // AF Learning 1C 16 bit + E16("E16"), // AF Learning 1D 16 bit + E44("E44"), // AF Learning 1A 32 bit + E45("E45"), // AF Learning 1B 32 bit + E46("E46"), // AF Learning 1C 32 bit + E47("E47"), // AF Learning 1D 32 bit + E62("E62"), // AF Learning 2A 32 bit + E63("E63"), // AF Learning 2B 32 bit + E64("E64"), // AF Learning 2C 32 bit + E65("E65"), // AF Learning 2D 32 bit + P2("P2"), // ECT + P11("P11"), // IAT + P17("P17"), // Battery Volts + P24("P24"), // ATM + P66("P66"), // AF Learning 3 + P115("P115"), // Learned Throttle Closed Voltage + P118("P118"), // AF Learning 4 + P153("P153"), // Whole Learning value in timing + E173("E173"); // Fine Learning Knock Correction Table Start (4-byte)* + + private static final Map lookup + = new HashMap(); + + static { + for(Parameter s : EnumSet.allOf(Parameter.class)) + lookup.put(s.toString(), s); + } + + private Parameter(final String text) { + this.text = text; + } + + private final String text; + + @Override + public final String toString() { + return text; + } + + /** + * Retrieve the Parameter that has the given value. + * @param value - the value of the Parameter in String format + * @return the Parameter that has the given value or null if undefined. + */ + public static Parameter fromValue(String value) { + Parameter result = null; + if (lookup.containsKey(value)) { + result = lookup.get(value); + } + return result; + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/learning/parameter/ParameterCrossReference.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/learning/parameter/ParameterCrossReference.java new file mode 100644 index 0000000000..94a9a60f6d --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/learning/parameter/ParameterCrossReference.java @@ -0,0 +1,64 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.learning.parameter; + +import static com.romraider.logger.ecu.comms.learning.parameter.Parameter.E1; +import static com.romraider.logger.ecu.comms.learning.parameter.Parameter.E31; +import static com.romraider.logger.ecu.comms.learning.parameter.Parameter.P11; +import static com.romraider.logger.ecu.comms.learning.parameter.Parameter.P115; +import static com.romraider.logger.ecu.comms.learning.parameter.Parameter.P118; +import static com.romraider.logger.ecu.comms.learning.parameter.Parameter.P153; +import static com.romraider.logger.ecu.comms.learning.parameter.Parameter.P17; +import static com.romraider.logger.ecu.comms.learning.parameter.Parameter.P2; +import static com.romraider.logger.ecu.comms.learning.parameter.Parameter.P24; +import static com.romraider.logger.ecu.comms.learning.parameter.Parameter.P66; + +import java.util.HashMap; +import java.util.Map; + +/** + * A Map of Parameter and value specific to the Vehicle Information Table. + */ +public class ParameterCrossReference { + final Map map; + + public ParameterCrossReference() { + map = new HashMap(); + map.put(P17, "Battery"); + map.put(P24, "ATM"); + map.put(P11, "IAT"); + map.put(P2, "ECT"); + map.put(E1, "IAM"); + map.put(E31, "IAM"); + map.put(P66, "A/F #3"); + map.put(P118, "A/F #4"); + map.put(P115, "TPS VDC"); + map.put(P153, "Lrn Timing"); + } + +/** + * Retrieve the string value associated with the supplied Parameter. + * @param parameter - Parameter to lookup value for. + * @return the value of the Parameter. + */ + public final String getValue(Parameter parameter) { + return map.get(parameter); + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/learning/parameter/ParameterIdComparator.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/learning/parameter/ParameterIdComparator.java new file mode 100644 index 0000000000..6e8ec7ceb1 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/learning/parameter/ParameterIdComparator.java @@ -0,0 +1,35 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.learning.parameter; + +import java.util.Comparator; + +import com.romraider.logger.ecu.comms.query.EcuQuery; + +/** + * A custom comparator to sort EcuQuery items based on an encapsulated ID field. + */ +public final class ParameterIdComparator implements Comparator { + + public int compare(EcuQuery ecuQuery1, EcuQuery ecuQuery2) { + return ecuQuery1.getLoggerData().getId().compareTo( + ecuQuery2.getLoggerData().getId()); + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/manager/PollingState.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/manager/PollingState.java new file mode 100644 index 0000000000..b96253a172 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/manager/PollingState.java @@ -0,0 +1,44 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.manager; + + +public interface PollingState { + + int getCurrentState(); + + void setCurrentState(int i); + + int getLastState(); + + void setLastState(int i); + + boolean isNewQuery(); + + void setNewQuery(boolean state); + + boolean isLastQuery(); + + void setLastQuery(boolean state); + + boolean isFastPoll(); + + void setFastPoll(boolean state); + } diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/manager/PollingStateImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/manager/PollingStateImpl.java new file mode 100644 index 0000000000..ecc7c347ad --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/manager/PollingStateImpl.java @@ -0,0 +1,89 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.manager; + +public final class PollingStateImpl implements PollingState { + private static int currentState; + private static int lastpollState; + private static boolean newQuery; + private static boolean lastQuery; + private static boolean fastPoll; + + public PollingStateImpl() { + setCurrentState(0); + setLastState(0); + setNewQuery(true); + setLastQuery(false); + setFastPoll(false); + } + + public int getCurrentState() { + return currentState; + } + + public void setCurrentState(int i) { + currentState = i; + } + + public int getLastState() { + return lastpollState; + } + + public void setLastState(int i) { + lastpollState = i; + } + + public boolean isNewQuery() { + return newQuery; + } + + public void setNewQuery(boolean state) { + newQuery = state; + } + + public boolean isLastQuery() { + return lastQuery; + } + + public void setLastQuery(boolean state) { + lastQuery = state; + } + + public boolean isFastPoll() { + return fastPoll; + } + + public void setFastPoll(boolean state) { + fastPoll = state; + } + + public String toString() { + final String state = String.format( + "Polling State [isFastPoll=%s, CurrentState=%d, LastState=%d, " + + "isNewQuery=%s, isLastQuery=%s]", + isFastPoll(), + getCurrentState(), + getLastState(), + isNewQuery(), + isLastQuery() + ); + return state; + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/manager/QueryManager.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/manager/QueryManager.java new file mode 100644 index 0000000000..6cb67ac0cc --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/manager/QueryManager.java @@ -0,0 +1,41 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.manager; + +import com.romraider.logger.ecu.definition.LoggerData; +import com.romraider.logger.ecu.ui.StatusChangeListener; +import com.romraider.logger.ecu.ui.handler.file.FileLoggerControllerSwitchMonitor; + +public interface QueryManager extends Runnable { + + void setFileLoggerSwitchMonitor(FileLoggerControllerSwitchMonitor monitor); + + void addQuery(String callerId, LoggerData loggerData); + + void removeQuery(String callerId, LoggerData loggerData); + + boolean isRunning(); + + void stop(); + + void addListener(StatusChangeListener listener); + + Thread getThread(); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/manager/QueryManagerImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/manager/QueryManagerImpl.java new file mode 100644 index 0000000000..6bedbc70e3 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/manager/QueryManagerImpl.java @@ -0,0 +1,459 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2014 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.manager; + +//import static com.romraider.logger.ecu.comms.io.connection.LoggerConnectionFactory.getConnection; +import static com.romraider.logger.ecu.definition.EcuDataType.EXTERNAL; +import static com.romraider.util.ParamChecker.checkNotNull; +import static com.romraider.util.ThreadUtil.runAsDaemon; +import static com.romraider.util.ThreadUtil.sleep; +import static java.lang.System.currentTimeMillis; +import static java.util.Collections.synchronizedList; +import static java.util.Collections.synchronizedMap; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.SwingUtilities; + +import org.apache.log4j.Logger; + +import com.romraider.Settings; +//import com.romraider.logger.ecu.comms.io.connection.LoggerConnection; +import com.romraider.logger.ecu.comms.query.EcuInitCallback; +import com.romraider.logger.ecu.comms.query.EcuQuery; +import com.romraider.logger.ecu.comms.query.EcuQueryImpl; +import com.romraider.logger.ecu.comms.query.ExternalQuery; +import com.romraider.logger.ecu.comms.query.ExternalQueryImpl; +import com.romraider.logger.ecu.comms.query.Query; +import com.romraider.logger.ecu.comms.query.Response; +import com.romraider.logger.ecu.comms.query.ResponseImpl; +import com.romraider.logger.ecu.definition.EcuData; +import com.romraider.logger.ecu.definition.ExternalData; +import com.romraider.logger.ecu.definition.LoggerData; +import com.romraider.logger.ecu.ui.MessageListener; +import com.romraider.logger.ecu.ui.StatusChangeListener; +import com.romraider.logger.ecu.ui.handler.DataUpdateHandler; +import com.romraider.logger.ecu.ui.handler.file.FileLoggerControllerSwitchMonitor; +import com.romraider.util.SettingsManager; + +public final class QueryManagerImpl implements QueryManager { + private static final Logger LOGGER = Logger.getLogger(QueryManagerImpl.class); + private final List listeners = + synchronizedList(new ArrayList()); + private final Map queryMap = + synchronizedMap(new HashMap()); + private final Map addList = new HashMap(); + private final List removeList = new ArrayList(); + private static final PollingState pollState = new PollingStateImpl(); + private static final Settings settings = SettingsManager.getSettings(); + private static final String ECU = "ECU"; + private static final String TCU = "TCU"; + private static final String EXT = "Externals"; + private final EcuInitCallback ecuInitCallback; + private final MessageListener messageListener; + private final DataUpdateHandler[] dataUpdateHandlers; + private FileLoggerControllerSwitchMonitor monitor; + private EcuQuery fileLoggerQuery; + private Thread queryManagerThread; + private static boolean started; + private static boolean stop; + + public QueryManagerImpl(EcuInitCallback ecuInitCallback, + MessageListener messageListener, + DataUpdateHandler... dataUpdateHandlers) { + checkNotNull(ecuInitCallback, + messageListener, + dataUpdateHandlers); + this.ecuInitCallback = ecuInitCallback; + this.messageListener = messageListener; + this.dataUpdateHandlers = dataUpdateHandlers; + stop = true; + } + + @Override + public synchronized void addListener(StatusChangeListener listener) { + checkNotNull(listener, "listener"); + listeners.add(listener); + } + + @Override + public void setFileLoggerSwitchMonitor(FileLoggerControllerSwitchMonitor monitor) { + checkNotNull(monitor); + this.monitor = monitor; + fileLoggerQuery = new EcuQueryImpl(monitor.getEcuSwitch()); + } + + @Override + public synchronized void addQuery(String callerId, LoggerData loggerData) { + checkNotNull(callerId, loggerData); + //FIXME: This is a hack!! + String queryId = buildQueryId(callerId, loggerData); + if (loggerData.getDataType() == EXTERNAL) { + addList.put(queryId, new ExternalQueryImpl((ExternalData) loggerData)); + } else { + addList.put(queryId, new EcuQueryImpl((EcuData) loggerData)); + pollState.setLastQuery(false); + pollState.setNewQuery(true); + } + } + + @Override + public synchronized void removeQuery(String callerId, LoggerData loggerData) { + checkNotNull(callerId, loggerData); + removeList.add(buildQueryId(callerId, loggerData)); + if (loggerData.getDataType() != EXTERNAL) { + pollState.setNewQuery(true); + } + } + + @Override + public Thread getThread() { + return queryManagerThread; + } + + @Override + public boolean isRunning() { + return started && !stop; + } + + @Override + public void run() { + started = true; + queryManagerThread = Thread.currentThread(); + LOGGER.debug("QueryManager started."); + + try { + stop = false; + while (!stop) { + notifyConnecting(); + if (!settings.isLogExternalsOnly() && + doEcuInit(settings.getDestinationId())) { + + notifyReading(); + runLogger(settings.getDestinationId()); + } else if (settings.isLogExternalsOnly()) { + notifyReading(); + runLogger((byte) -1); + } else { + sleep(1000L); + } + } + } catch (Exception e) { + messageListener.reportError(e); + } finally { + notifyStopped(); + messageListener.reportMessage("Disconnected."); + LOGGER.debug("QueryManager stopped."); + } + } + + private boolean doEcuInit(byte id) { + String target = null; + if (id == 0x10){ + target = ECU; + } + if (id == 0x18){ + target = TCU; + } + + try { +// LoggerConnection connection = +// getConnection(settings.getLoggerProtocol(), +// settings.getLoggerPort(), +// settings.getLoggerConnectionProperties()); + try { + messageListener.reportMessage("Sending " + target + " Init..."); + //connection.ecuInit(ecuInitCallback, id); + messageListener.reportMessage("Sending " + target + " Init...done."); + return true; + } finally { + //connection.close(); + } + } catch (Exception e) { + messageListener.reportMessage("Unable to send " + target + + " init - check cable is connected and ignition is on."); + logError(e); + return false; + } + } + + private void logError(Exception e) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Error sending init", e); + } else { + LOGGER.info("Error sending init: " + e.getMessage()); + } + } + + private void runLogger(byte id) { + String target = null; + if (id == -1){ + target = EXT; + } + if (id == 0x10){ + target = ECU; + } + if (id == 0x18){ + target = TCU; + } + TransmissionManager txManager = new TransmissionManagerImpl(); + long start = currentTimeMillis(); + long end = currentTimeMillis(); + int count = 0; + try { + txManager.start(); + boolean lastPollState = settings.isFastPoll(); + while (!stop) { + pollState.setFastPoll(settings.isFastPoll()); + updateQueryList(); + if (queryMap.isEmpty()) { + if (pollState.isLastQuery() && + pollState.getCurrentState() == 0) { + endEcuQueries(txManager); + pollState.setLastState(0); + } + start = System.currentTimeMillis(); + count = 0; + messageListener.reportMessage("Select parameters to be logged..."); + sleep(1000L); + } else { + end = currentTimeMillis() + 1L; // update once every 1msec + final List ecuQueries = + filterEcuQueries(queryMap.values()); + + if (!settings.isLogExternalsOnly()) { + if (!ecuQueries.isEmpty()) { + sendEcuQueries(txManager); + if (!pollState.isFastPoll() && lastPollState) { + endEcuQueries(txManager); + } + if (pollState.isFastPoll()) { + if (pollState.getCurrentState() == 0 && + pollState.isNewQuery()) { + pollState.setCurrentState(1); + pollState.setNewQuery(false); + } + if (pollState.getCurrentState() == 0 && + !pollState.isNewQuery()) { + pollState.setCurrentState(1); + } + if (pollState.getCurrentState() == 1 && + pollState.isNewQuery()) { + pollState.setCurrentState(0); + pollState.setLastState(1); + pollState.setNewQuery(false); + } + if (pollState.getCurrentState() == 1 && + !pollState.isNewQuery()) { + pollState.setLastState(1); + } + pollState.setLastQuery(true); + } + else { + pollState.setCurrentState(0); + pollState.setLastState(0); + pollState.setNewQuery(false); + } + lastPollState = pollState.isFastPoll(); + } + else { + if (pollState.isLastQuery() && + pollState.getLastState() == 1) { + endEcuQueries(txManager); + pollState.setLastState(0); + pollState.setCurrentState(0); + pollState.setNewQuery(true); + } + } + } + sendExternalQueries(); + // waiting until at least 1msec has passed since last query set + while (currentTimeMillis() < end) { + sleep(1L); + } + handleQueryResponse(); + count++; + messageListener.reportMessage("Querying " + target + "..."); + messageListener.reportStats(buildStatsMessage(start, count)); + } + } + } catch (Exception e) { + messageListener.reportError(e); + } finally { + txManager.stop(); + pollState.setCurrentState(0); + pollState.setNewQuery(true); + } + } + + private void sendEcuQueries(TransmissionManager txManager) { + final List ecuQueries = filterEcuQueries(queryMap.values()); + if (fileLoggerQuery != null + && settings.isFileLoggingControllerSwitchActive()) + ecuQueries.add(fileLoggerQuery); + txManager.sendQueries(ecuQueries, pollState); + } + + private void sendExternalQueries() { + final List externalQueries = + filterExternalQueries(queryMap.values()); + for (ExternalQuery externalQuery : externalQueries) { + //FIXME: This is a hack!! + externalQuery.setResponse( + externalQuery.getLoggerData().getSelectedConvertor().convert(null)); + } + } + + private void endEcuQueries(TransmissionManager txManager) { + txManager.endQueries(); + pollState.setLastQuery(false); + } + + private void handleQueryResponse() { + if (settings.isFileLoggingControllerSwitchActive()) + monitor.monitorFileLoggerSwitch(fileLoggerQuery.getResponse()); + final Response response = buildResponse(queryMap.values()); + for (final DataUpdateHandler dataUpdateHandler : dataUpdateHandlers) { + runAsDaemon(new Runnable() { + @Override + public void run() { + dataUpdateHandler.handleDataUpdate(response); + } + }); + } + } + + private Response buildResponse(Collection queries) { + final Response response = new ResponseImpl(); + for (final Query query : queries) { + response.setDataValue(query.getLoggerData(), query.getResponse()); + } + return response; + } + + //FIXME: This is a hack!! + private List filterEcuQueries(Collection queries) { + List filtered = new ArrayList(); + for (Query query : queries) { + if (EcuQuery.class.isAssignableFrom(query.getClass())) { + filtered.add((EcuQuery) query); + } + } + return filtered; + } + + //FIXME: This is a hack!! + private List filterExternalQueries(Collection queries) { + List filtered = new ArrayList(); + for (Query query : queries) { + if (ExternalQuery.class.isAssignableFrom(query.getClass())) { + filtered.add((ExternalQuery) query); + } + } + return filtered; + } + + @Override + public void stop() { + stop = true; + } + + private String buildQueryId(String callerId, LoggerData loggerData) { + return callerId + "_" + loggerData.getName(); + } + + private synchronized void updateQueryList() { + addQueries(); + removeQueries(); + } + + private void addQueries() { + for (String queryId : addList.keySet()) { + queryMap.put(queryId, addList.get(queryId)); + } + addList.clear(); + } + + private void removeQueries() { + for (String queryId : removeList) { + queryMap.remove(queryId); + } + removeList.clear(); + } + + private String buildStatsMessage(long start, int count) { + String state = "Slow-K:"; + if (pollState.isFastPoll()) { + state = "Fast-K:"; + } + if (settings.getTransportProtocol().equals("ISO15765")) { + state = "CAN bus:"; + } + if (settings.isLogExternalsOnly()) { + state = "Externals:"; + } + double duration = (System.currentTimeMillis() - start) / 1000.0; + String result = String.format( + "%s[ %.2f queries/sec, %.2f sec/query ]", + state, + (count) / duration, + duration / (count) + ); + return result; + } + + private void notifyConnecting() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + for (StatusChangeListener listener : listeners) { + listener.connecting(); + } + } + }); + } + + private void notifyReading() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + for (StatusChangeListener listener : listeners) { + listener.readingData(); + } + } + }); + } + + private void notifyStopped() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + for (StatusChangeListener listener : listeners) { + listener.stopped(); + } + } + }); + } + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/manager/TransmissionManager.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/manager/TransmissionManager.java new file mode 100644 index 0000000000..9aa91e0bdf --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/manager/TransmissionManager.java @@ -0,0 +1,35 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.manager; + +import com.romraider.logger.ecu.comms.query.EcuQuery; +import java.util.Collection; + +public interface TransmissionManager { + + void start(); + + void sendQueries(Collection queries, PollingState pollMode); + + void endQueries(); + + void stop(); + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/manager/TransmissionManagerImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/manager/TransmissionManagerImpl.java new file mode 100644 index 0000000000..5518d02ff6 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/manager/TransmissionManagerImpl.java @@ -0,0 +1,76 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.manager; + +//import static com.romraider.logger.ecu.comms.io.connection.LoggerConnectionFactory.getConnection; +import static com.romraider.util.ParamChecker.checkNotNull; +import static org.apache.log4j.Logger.getLogger; + +import java.util.Collection; + +import org.apache.log4j.Logger; + +import com.romraider.Settings; +//import com.romraider.logger.ecu.comms.io.connection.LoggerConnection; +import com.romraider.logger.ecu.comms.query.EcuQuery; +import com.romraider.logger.ecu.exception.NotConnectedException; +import com.romraider.util.SettingsManager; + +public final class TransmissionManagerImpl implements TransmissionManager { + private static final Logger LOGGER = getLogger(TransmissionManagerImpl.class); + // private LoggerConnection connection; + + public TransmissionManagerImpl() { + } + + @Override + public void start() { + try { + Settings settings = SettingsManager.getSettings(); + // connection = getConnection(settings.getLoggerProtocol(), settings.getLoggerPort(), settings.getLoggerConnectionProperties()); + LOGGER.info("TX Manager Started."); + } catch (Throwable e) { + stop(); + } + } + + @Override + public void sendQueries(Collection queries, PollingState pollState) { + checkNotNull(queries, "queries"); + checkNotNull(pollState, "pollState"); +// if (connection == null) throw new NotConnectedException("TransmissionManager must be started before queries can be sent!"); +// connection.sendAddressReads(queries, SettingsManager.getSettings().getDestinationId(), pollState); + } + + @Override + public void endQueries() { +// if (connection == null) throw new NotConnectedException("TransmissionManager must be started before ending queries!"); +// connection.clearLine(); + } + + @Override + public void stop() { +// if (connection != null) { +// endQueries(); +//// connection.close(); +// } + LOGGER.info("TX Manager Stopped."); + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/query/EcuInit.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/query/EcuInit.java new file mode 100644 index 0000000000..b7d70cf2e5 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/query/EcuInit.java @@ -0,0 +1,28 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.query; + +public interface EcuInit { + + String getEcuId(); + + byte[] getEcuInitBytes(); + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/query/EcuInitCallback.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/query/EcuInitCallback.java new file mode 100644 index 0000000000..e8dc0687dc --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/query/EcuInitCallback.java @@ -0,0 +1,26 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.query; + +public interface EcuInitCallback { + + void callback(EcuInit ecuInit); + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/query/EcuQuery.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/query/EcuQuery.java new file mode 100644 index 0000000000..a0aad1ab67 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/query/EcuQuery.java @@ -0,0 +1,31 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.query; + +public interface EcuQuery extends Query { + + String[] getAddresses(); + + byte[] getBytes(); + + String getHex(); + + void setResponse(byte[] bytes); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/query/EcuQueryImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/query/EcuQueryImpl.java new file mode 100644 index 0000000000..a337777ba9 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/query/EcuQueryImpl.java @@ -0,0 +1,75 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.query; + +import com.romraider.logger.ecu.definition.EcuData; +import com.romraider.logger.ecu.definition.LoggerData; +import static com.romraider.util.HexUtil.asHex; +import static com.romraider.util.ParamChecker.checkNotNull; + +public final class EcuQueryImpl implements EcuQuery { + private final EcuData ecuData; + private final byte[] bytes; + private final String hex; + private double response; + + public EcuQueryImpl(EcuData ecuData) { + checkNotNull(ecuData); + this.ecuData = ecuData; + bytes = ecuData.getAddress().getBytes(); + hex = asHex(bytes); + } + + public LoggerData getLoggerData() { + return ecuData; + } + + public String[] getAddresses() { + return ecuData.getAddress().getAddresses(); + } + + public byte[] getBytes() { + return bytes; + } + + public String getHex() { + return hex; + } + + public double getResponse() { + return response; + } + + public void setResponse(byte[] bytes) { + this.response = ecuData.getSelectedConvertor().convert(bytes); + } + + public boolean equals(Object object) { + return object instanceof EcuQueryImpl && getHex().equals(((EcuQueryImpl) object).getHex()); + } + + public int hashCode() { + return getHex().hashCode(); + } + + public String toString() { + return "0x" + getHex(); + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/query/ExternalQuery.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/query/ExternalQuery.java new file mode 100644 index 0000000000..3a5fd63c5f --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/query/ExternalQuery.java @@ -0,0 +1,26 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.query; + +public interface ExternalQuery extends Query { + + void setResponse(double response); + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/query/ExternalQueryImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/query/ExternalQueryImpl.java new file mode 100644 index 0000000000..fa753b2f2f --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/query/ExternalQueryImpl.java @@ -0,0 +1,46 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.query; + +import com.romraider.logger.ecu.definition.ExternalData; +import com.romraider.logger.ecu.definition.LoggerData; +import static com.romraider.util.ParamChecker.checkNotNull; + +public final class ExternalQueryImpl implements ExternalQuery { + private final ExternalData externalData; + private double response; + + public ExternalQueryImpl(ExternalData externalData) { + checkNotNull(externalData); + this.externalData = externalData; + } + + public LoggerData getLoggerData() { + return externalData; + } + + public void setResponse(double response) { + this.response = response; + } + + public double getResponse() { + return response; + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/query/Query.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/query/Query.java new file mode 100644 index 0000000000..b63a20fd2f --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/query/Query.java @@ -0,0 +1,30 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.query; + +import com.romraider.logger.ecu.definition.LoggerData; + +public interface Query { + + LoggerData getLoggerData(); + + double getResponse(); + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/query/Response.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/query/Response.java new file mode 100644 index 0000000000..a87bda83ba --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/query/Response.java @@ -0,0 +1,34 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.query; + +import com.romraider.logger.ecu.definition.LoggerData; +import java.util.Set; + +public interface Response { + + void setDataValue(LoggerData data, double value); + + Set getData(); + + double getDataValue(LoggerData data); + + long getTimestamp(); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/query/ResponseImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/query/ResponseImpl.java new file mode 100644 index 0000000000..b1111565ad --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/query/ResponseImpl.java @@ -0,0 +1,52 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.query; + +import com.romraider.logger.ecu.definition.LoggerData; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +public class ResponseImpl implements Response { + private static final double ZERO = 0.0; + private final Map dataValues = new LinkedHashMap(); + private final long timestamp; + + public ResponseImpl() { + timestamp = System.currentTimeMillis(); + } + + public void setDataValue(LoggerData data, double value) { + dataValues.put(data, value); + } + + public Set getData() { + return dataValues.keySet(); + } + + public double getDataValue(LoggerData data) { + final Double value = dataValues.get(data); + return value == null ? ZERO : value; + } + + public long getTimestamp() { + return timestamp; + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/query/SSMEcuInit.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/query/SSMEcuInit.java new file mode 100644 index 0000000000..abf578c840 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/query/SSMEcuInit.java @@ -0,0 +1,46 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.query; + +import static com.romraider.util.HexUtil.asHex; +import static com.romraider.util.ParamChecker.checkNotNullOrEmpty; +import static java.lang.System.arraycopy; + +public final class SSMEcuInit implements EcuInit { + private byte[] ecuInitBytes; + private String ecuId; + + public SSMEcuInit(byte[] ecuInitBytes) { + checkNotNullOrEmpty(ecuInitBytes, "ecuInitBytes"); + this.ecuInitBytes = ecuInitBytes; + byte[] ecuIdBytes = new byte[5]; + arraycopy(ecuInitBytes, 3, ecuIdBytes, 0, 5); + ecuId = asHex(ecuIdBytes); + } + + public String getEcuId() { + return ecuId; + } + + public byte[] getEcuInitBytes() { + return ecuInitBytes; + } + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/readcodes/ReadCodesManager.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/readcodes/ReadCodesManager.java new file mode 100644 index 0000000000..93d0aa08b5 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/readcodes/ReadCodesManager.java @@ -0,0 +1,25 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.readcodes; + + +public interface ReadCodesManager { + int readCodes(); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/comms/reset/ResetManager.java b/java_console/romraider/src/com/romraider/logger/ecu/comms/reset/ResetManager.java new file mode 100644 index 0000000000..081c08f654 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/comms/reset/ResetManager.java @@ -0,0 +1,24 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.comms.reset; + +public interface ResetManager { + boolean resetEcu(); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/ConvertorUpdateListener.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/ConvertorUpdateListener.java new file mode 100644 index 0000000000..27807af226 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/ConvertorUpdateListener.java @@ -0,0 +1,26 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition; + +public interface ConvertorUpdateListener { + + void notifyConvertorUpdate(LoggerData updatedLoggerData); + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuAddress.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuAddress.java new file mode 100644 index 0000000000..4fcfe94515 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuAddress.java @@ -0,0 +1,31 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition; + +public interface EcuAddress { + + String[] getAddresses(); + + byte[] getBytes(); + + int getBit(); + + int getLength(); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuAddressImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuAddressImpl.java new file mode 100644 index 0000000000..4d0f85cb3f --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuAddressImpl.java @@ -0,0 +1,123 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition; + +import static com.romraider.util.HexUtil.asBytes; +import static com.romraider.util.HexUtil.hexToInt; +import static com.romraider.util.HexUtil.intToHexString; +import static com.romraider.util.ParamChecker.checkGreaterThanZero; +import static com.romraider.util.ParamChecker.checkNotNullOrEmpty; +import java.util.LinkedList; +import java.util.List; + +public final class EcuAddressImpl implements EcuAddress { + private final String[] addresses; + private final byte[] bytes; + private final int bit; + + + public EcuAddressImpl(String address, int length, int bit) { + checkNotNullOrEmpty(address, "address"); + checkGreaterThanZero(length, "length"); + final String[] addrSting = {address}; + this.addresses = buildAddresses(addrSting, length); + this.bytes = getAddressBytes(addresses); + this.bit = bit; + } + + public EcuAddressImpl(String[] address, int length, int bit) { + checkNotNullOrEmpty(address, "address"); + checkGreaterThanZero(length, "length"); + this.addresses = buildAddresses(address, length); + this.bytes = getAddressBytes(addresses); + this.bit = bit; + } + + public EcuAddressImpl(String[] addresses) { + checkNotNullOrEmpty(addresses, "addresses"); + this.addresses = addresses; + this.bytes = getAddressBytes(addresses); + this.bit = -1; + } + + public String[] getAddresses() { + return addresses; + } + + public byte[] getBytes() { + return bytes; + } + + public int getBit() { + return bit; + } + + public int getLength() { + return addresses.length; + } + + private String[] buildAddresses(String[] startAddress, int addressLength) { + final List addresses = new LinkedList(); + if (startAddress.length > addressLength) { + for (int i = 0; i < startAddress.length; i++) { + final int address = hexToInt(startAddress[i]); + addresses.add( + padAddress(intToHexString(address), + startAddress[i].length())); + } + } + else { + int start = hexToInt(startAddress[0]); + for (int i = 0; i < addressLength; i++) { + addresses.add( + padAddress(intToHexString(start + i), + startAddress[0].length())); + } + } + return addresses.toArray(new String[addresses.size()]); + } + + private String padAddress(String address, int length) { + if (address.length() == length) { + return address; + } else { + StringBuilder builder = new StringBuilder(length); + builder.append("0x"); + String s = address.substring(2); + for (int i = 0; i < length - s.length() - 2; i++) { + builder.append('0'); + } + builder.append(s); + return builder.toString(); + } + } + + private byte[] getAddressBytes(String[] addresses) { + byte[] bytes = new byte[0]; + for (String address : addresses) { + byte[] tmp1 = asBytes(address); + byte[] tmp2 = new byte[bytes.length + tmp1.length]; + System.arraycopy(bytes, 0, tmp2, 0, bytes.length); + System.arraycopy(tmp1, 0, tmp2, bytes.length, tmp1.length); + bytes = tmp2; + } + return bytes; + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuData.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuData.java new file mode 100644 index 0000000000..f08260f65a --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuData.java @@ -0,0 +1,26 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition; + +public interface EcuData extends LoggerData { + + EcuAddress getAddress(); + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDataConvertor.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDataConvertor.java new file mode 100644 index 0000000000..0d7b696baf --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDataConvertor.java @@ -0,0 +1,39 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2014 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition; + + + +public interface EcuDataConvertor { + + double convert(byte[] bytes); + + String format(double value); + + String getUnits(); + + //GaugeMinMax getGaugeMinMax(); + + String getFormat(); + + String getExpression(); + + String getDataType(); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDataLoader.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDataLoader.java new file mode 100644 index 0000000000..843996248c --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDataLoader.java @@ -0,0 +1,47 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition; + +import com.romraider.io.connection.ConnectionProperties; +import com.romraider.logger.ecu.comms.query.EcuInit; +import java.io.File; +import java.util.List; +import java.util.Map; + +public interface EcuDataLoader { + + void loadEcuDefsFromXml(File ecuDefsFile); + + void loadConfigFromXml(String loggerConfigFilePath, String protocol, String fileLoggingControllerSwitchId, EcuInit ecuInit); + + Map getEcuDefinitionMap(); + + List getEcuParameters(); + + List getEcuSwitches(); + + EcuSwitch getFileLoggingControllerSwitch(); + + ConnectionProperties getConnectionProperties(); + + String getDefVersion(); + + List getEcuCodes(); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDataType.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDataType.java new file mode 100644 index 0000000000..3393360fed --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDataType.java @@ -0,0 +1,26 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition; + +public enum EcuDataType { + PARAMETER, + SWITCH, + EXTERNAL +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDefinition.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDefinition.java new file mode 100644 index 0000000000..b92f43ac1a --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDefinition.java @@ -0,0 +1,35 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition; + +import java.io.File; + +public interface EcuDefinition { + + String getEcuId(); + + String getCalId(); + + String getCarString(); + + String getInherits(); + + File getEcuDefFile(); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDefinitionImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDefinitionImpl.java new file mode 100644 index 0000000000..c0a36fe752 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDefinitionImpl.java @@ -0,0 +1,67 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition; + +import static com.romraider.util.ParamChecker.checkNotNullOrEmpty; + +import java.io.File; + + +public final class EcuDefinitionImpl implements EcuDefinition { + private final String ecuId; + private final String calId; + private final String carString; + private final String inherits; + private final File ecuDefFile; + + public EcuDefinitionImpl( + String ecuId, String calId, String carString, + String inherits, File ecuDefFile) { + + checkNotNullOrEmpty(ecuId, "ecuId"); + checkNotNullOrEmpty(calId, "calId"); + checkNotNullOrEmpty(carString, "carString"); + this.ecuId = ecuId; + this.calId = calId; + this.carString = carString; + this.inherits = inherits; + this.ecuDefFile = ecuDefFile; + } + + public String getEcuId() { + return ecuId; + } + + public String getCalId() { + return calId; + } + + public String getCarString() { + return carString; + } + + public String getInherits() { + return inherits; + } + + public File getEcuDefFile() { + return ecuDefFile; + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDerivedParameterConvertor.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDerivedParameterConvertor.java new file mode 100644 index 0000000000..2e2ed84c61 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDerivedParameterConvertor.java @@ -0,0 +1,26 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition; + +public interface EcuDerivedParameterConvertor extends EcuDataConvertor { + + void setEcuDatas(EcuData[] ecuDatas); + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDerivedParameterImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDerivedParameterImpl.java new file mode 100644 index 0000000000..13cd8974ef --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuDerivedParameterImpl.java @@ -0,0 +1,129 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition; + +import static com.romraider.logger.ecu.definition.EcuDataType.PARAMETER; +import static com.romraider.util.ParamChecker.checkNotNull; +import static com.romraider.util.ParamChecker.checkNotNullOrEmpty; +import java.util.HashSet; +import java.util.Set; + +public final class EcuDerivedParameterImpl implements EcuParameter { + private final String id; + private final String name; + private final String description; + private final EcuDerivedParameterConvertor[] convertors; + private final EcuAddress address; + private final Set listeners = new HashSet(); + private int selectedConvertorIndex; + private boolean selected; + + public EcuDerivedParameterImpl(String id, String name, String description, EcuData[] ecuDatas, + EcuDerivedParameterConvertor[] convertors) { + checkNotNullOrEmpty(id, "id"); + checkNotNullOrEmpty(name, "name"); + checkNotNull(description, "description"); + checkNotNullOrEmpty(ecuDatas, "ecuDatas"); + checkNotNullOrEmpty(convertors, "convertors"); + this.id = id; + this.name = name; + this.description = description; + this.convertors = convertors; + this.address = buildCombinedAddress(ecuDatas); + setEcuDatas(ecuDatas); + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public EcuAddress getAddress() { + return address; + } + + public EcuDataConvertor getSelectedConvertor() { + return convertors[selectedConvertorIndex]; + } + + public EcuDataConvertor[] getConvertors() { + return convertors; + } + + public void selectConvertor(EcuDataConvertor convertor) { + if (convertor != getSelectedConvertor()) { + for (int i = 0; i < convertors.length; i++) { + EcuDerivedParameterConvertor parameterConvertor = convertors[i]; + if (convertor == parameterConvertor) { + selectedConvertorIndex = i; + } + } + notifyUpdateListeners(); + } + } + + public EcuDataType getDataType() { + return PARAMETER; + } + + public boolean isSelected() { + return selected; + } + + public void setSelected(boolean selected) { + this.selected = selected; + } + + public void addConvertorUpdateListener(ConvertorUpdateListener listener) { + checkNotNull(listener, "listener"); + listeners.add(listener); + } + + private EcuAddress buildCombinedAddress(EcuData[] ecuDatas) { + String[] addresses = new String[0]; + for (EcuData ecuData : ecuDatas) { + String[] newAddresses = ecuData.getAddress().getAddresses(); + String[] tmp = new String[addresses.length + newAddresses.length]; + System.arraycopy(addresses, 0, tmp, 0, addresses.length); + System.arraycopy(newAddresses, 0, tmp, addresses.length, newAddresses.length); + addresses = tmp; + } + return new EcuAddressImpl(addresses); + } + + private void setEcuDatas(EcuData[] ecuDatas) { + for (EcuDerivedParameterConvertor convertor : convertors) { + convertor.setEcuDatas(ecuDatas); + } + } + + private void notifyUpdateListeners() { + for (ConvertorUpdateListener listener : listeners) { + listener.notifyConvertorUpdate(this); + } + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuParameter.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuParameter.java new file mode 100644 index 0000000000..1e9d450ae6 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuParameter.java @@ -0,0 +1,26 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition; + +public interface EcuParameter extends EcuData { + + void addConvertorUpdateListener(ConvertorUpdateListener listener); + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuParameterImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuParameterImpl.java new file mode 100644 index 0000000000..c5c12564aa --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuParameterImpl.java @@ -0,0 +1,109 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition; + +import static com.romraider.logger.ecu.definition.EcuDataType.PARAMETER; +import static com.romraider.util.ParamChecker.checkNotNull; +import static com.romraider.util.ParamChecker.checkNotNullOrEmpty; +import java.util.HashSet; +import java.util.Set; + +public final class EcuParameterImpl implements EcuParameter { + private final String id; + private final String name; + private final String description; + private final EcuAddress address; + private final EcuDataConvertor[] convertors; + private final Set listeners = new HashSet(); + private int selectedConvertorIndex; + private boolean selected; + + public EcuParameterImpl(String id, String name, String description, EcuAddress address, EcuDataConvertor[] convertors) { + checkNotNullOrEmpty(id, "id"); + checkNotNullOrEmpty(name, "name"); + checkNotNull(description, "description"); + checkNotNull(address, "address"); + checkNotNullOrEmpty(convertors, "convertors"); + this.id = id; + this.name = name; + this.description = description; + this.address = address; + this.convertors = convertors; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public EcuAddress getAddress() { + return address; + } + + public EcuDataConvertor getSelectedConvertor() { + return convertors[selectedConvertorIndex]; + } + + public EcuDataConvertor[] getConvertors() { + return convertors; + } + + public void selectConvertor(EcuDataConvertor convertor) { + if (convertor != getSelectedConvertor()) { + for (int i = 0; i < convertors.length; i++) { + EcuDataConvertor dataConvertor = convertors[i]; + if (convertor == dataConvertor) { + selectedConvertorIndex = i; + } + } + notifyUpdateListeners(); + } + } + + public EcuDataType getDataType() { + return PARAMETER; + } + + public boolean isSelected() { + return selected; + } + + public void setSelected(boolean selected) { + this.selected = selected; + } + + public void addConvertorUpdateListener(ConvertorUpdateListener listener) { + checkNotNull(listener, "listener"); + listeners.add(listener); + } + + private void notifyUpdateListeners() { + for (ConvertorUpdateListener listener : listeners) { + listener.notifyConvertorUpdate(this); + } + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuSwitch.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuSwitch.java new file mode 100644 index 0000000000..703b7f254e --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuSwitch.java @@ -0,0 +1,24 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition; + +public interface EcuSwitch extends EcuData { + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuSwitchImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuSwitchImpl.java new file mode 100644 index 0000000000..90647aa59a --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/EcuSwitchImpl.java @@ -0,0 +1,101 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition; + +import static com.romraider.logger.ecu.definition.EcuDataType.SWITCH; +import static com.romraider.util.ParamChecker.checkNotNull; +import static com.romraider.util.ParamChecker.checkNotNullOrEmpty; + +public final class EcuSwitchImpl implements EcuSwitch { + private final String id; + private final String name; + private final String description; + private final EcuAddress address; + private final EcuDataConvertor[] convertors; + private int selectedConvertorIndex; + private boolean fileLogController; + private boolean selected; + + public EcuSwitchImpl(String id, String name, String description, EcuAddress address, EcuDataConvertor[] convertors) { + checkNotNullOrEmpty(id, "id"); + checkNotNullOrEmpty(name, "name"); + checkNotNull(description, "description"); + checkNotNull(address, "address"); + checkNotNullOrEmpty(convertors, "convertors"); + this.id = id; + this.name = name; + this.description = description; + this.address = address; + this.convertors = convertors; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public EcuAddress getAddress() { + return address; + } + + public EcuDataConvertor getSelectedConvertor() { + return convertors[selectedConvertorIndex]; + } + + public EcuDataConvertor[] getConvertors() { + return convertors; + } + + public void selectConvertor(EcuDataConvertor convertor) { + for (int i = 0; i < convertors.length; i++) { + EcuDataConvertor dataConvertor = convertors[i]; + if (convertor == dataConvertor) { + selectedConvertorIndex = i; + } + } + } + + public EcuDataType getDataType() { + return SWITCH; + } + + public boolean isSelected() { + return selected; + } + + public void setSelected(boolean selected) { + this.selected = selected; + } + + public void setFileLogController(boolean fileLogController) { + this.fileLogController = fileLogController; + } + + public boolean isFileLogController() { + return fileLogController; + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/EvaluateEcuDefinition.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/EvaluateEcuDefinition.java new file mode 100644 index 0000000000..ef44acdb83 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/EvaluateEcuDefinition.java @@ -0,0 +1,51 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition; + +import java.util.Map; + +import com.romraider.util.ParamChecker; + +/** + * From the ECU Definition Map retrieve the definition for the current ECU ID. + */ +public final class EvaluateEcuDefinition { + public EvaluateEcuDefinition() { + } + + /** + * Retrieve the definition for the current ECU ID. + * @param ecuDefMap - a map of ECU Definitions keyed by ECU ID. + * @param ecuId - the ECU ID to find the ECU Definition for. + * @return an ECU Definition or a New ECU Definition with all but the ECU ID set + * to "unknown". + */ + public final EcuDefinition getDef( + Map ecuDefMap, String ecuId) { + + ParamChecker.checkNotNull(ecuDefMap, "EcuDefinitionMap"); + EcuDefinition ecuDef = ecuDefMap.get(ecuId); + if (ecuDef == null) { + ecuDef = new EcuDefinitionImpl( + ecuId, "unknown", "unknown", "unknown", null); + } + return ecuDef; + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/ExternalData.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/ExternalData.java new file mode 100644 index 0000000000..88e2b61841 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/ExternalData.java @@ -0,0 +1,26 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition; + +public interface ExternalData extends LoggerData { + + void addConvertorUpdateListener(ConvertorUpdateListener listener); + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/LoggerData.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/LoggerData.java new file mode 100644 index 0000000000..6073096810 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/LoggerData.java @@ -0,0 +1,41 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition; + +public interface LoggerData { + + String getId(); + + String getName(); + + String getDescription(); + + EcuDataConvertor getSelectedConvertor(); + + EcuDataConvertor[] getConvertors(); + + void selectConvertor(EcuDataConvertor convertor); + + EcuDataType getDataType(); + + boolean isSelected(); + + void setSelected(boolean selected); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/plugin/PluginFilenameFilter.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/plugin/PluginFilenameFilter.java new file mode 100644 index 0000000000..72cdf6b02f --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/plugin/PluginFilenameFilter.java @@ -0,0 +1,30 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition.plugin; + +import java.io.File; +import java.io.FilenameFilter; + +public final class PluginFilenameFilter implements FilenameFilter { + + public boolean accept(File dir, String filename) { + return filename.endsWith(".plugin"); + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/xml/EcuDefinitionDocumentLoader.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/xml/EcuDefinitionDocumentLoader.java new file mode 100644 index 0000000000..4435940c9c --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/xml/EcuDefinitionDocumentLoader.java @@ -0,0 +1,70 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition.xml; + +import java.io.FileInputStream; +import java.io.IOException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +import com.romraider.logger.ecu.definition.EcuDefinition; + +/** + * Parse a given XML definition file into a DOM. + */ +public class EcuDefinitionDocumentLoader { + + private EcuDefinitionDocumentLoader() { + } + + /** + * Parse a given XML definition file into a DOM. + * @param ecuDef - an ECU Definition containing a File to parse. + * @return a DOM. + */ + public static final Document getDocument(EcuDefinition ecuDef) { + final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = null; + try { + builder = dbf.newDocumentBuilder(); + } + catch (ParserConfigurationException e) { + e.printStackTrace(); + } + + Document document = null; + try { + document = builder.parse( + new FileInputStream(ecuDef.getEcuDefFile())); + } + catch (SAXException e) { + e.printStackTrace(); + } + catch (IOException e) { + e.printStackTrace(); + } + return document; + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/xml/EcuDefinitionHandler.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/xml/EcuDefinitionHandler.java new file mode 100644 index 0000000000..9997036923 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/xml/EcuDefinitionHandler.java @@ -0,0 +1,256 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition.xml; + +import com.romraider.logger.ecu.definition.EcuDefinition; +import com.romraider.logger.ecu.definition.EcuDefinitionImpl; +import static com.romraider.util.ParamChecker.isNullOrEmpty; + +import org.apache.log4j.Logger; +import org.xml.sax.Attributes; +import org.xml.sax.helpers.DefaultHandler; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +/* + + + CAL ID:A4TC300K + 200 + A4TC300K + 03 + AT + 3614446205 + + +*/ +public final class EcuDefinitionHandler extends DefaultHandler { + private static final Logger LOGGER = Logger.getLogger(EcuDefinitionHandler.class); + private static final String TAG_ROM = "rom"; + private static final String TAG_ROMID = "romid"; + private static final String TAG_CALID = "internalidstring"; + private static final String TAG_ECUID = "ecuid"; + private static final String TAG_CASEID = "caseid"; + private static final String TAG_ADDRESS = "internalidaddress"; + private static final String TAG_YEAR = "year"; + private static final String TAG_MARKET = "market"; + private static final String TAG_MAKE = "make"; + private static final String TAG_MODEL = "model"; + private static final String TAG_SUBMODEL = "submodel"; + private static final String TAG_TRANS = "transmission"; + private static final String TAG_MEMMODEL = "memmodel"; + private static final String TAG_FLASH = "flashmethod"; + private static final String TAG_SIZE = "filesize"; + private static final String TAG_OBSOLETE = "obsolete"; + private static final String ATTR_BASE = "base"; + private Map ecuDefinitionMap = new HashMap(); + private String calId; + private String ecuId; + private String caseId; + private String address; + private String year; + private String market; + private String make; + private String model; + private String submodel; + private String transmission; + private String memmodel; + private String flashmethod; + private String filesize; + private String obsolete; + private String inherit; + private String carString; + private StringBuilder charBuffer; + private File ecuDefsFile; + + public EcuDefinitionHandler(File ecuDefsFile) { + this.ecuDefsFile = ecuDefsFile; + } + + public void startDocument() { + ecuDefinitionMap = new HashMap(); + } + + public void startElement(String uri, String localName, String qName, Attributes attributes) { + if (TAG_ROM.equals(qName)) { + inherit = attributes.getValue(ATTR_BASE); + } + else if (TAG_ROMID.equals(qName)) { + calId = ""; + ecuId = ""; + caseId = ""; + address = ""; + year = ""; + market = ""; + make = ""; + model = ""; + submodel = ""; + transmission = ""; + memmodel = ""; + flashmethod = ""; + filesize = ""; + obsolete = "0"; + carString = ""; + } + charBuffer = new StringBuilder(); + } + + public void characters(char[] ch, int start, int length) { + charBuffer.append(ch, start, length); + } + + public void endElement(String uri, String localName, String qName) { + if (TAG_ROM.equals(qName)) { + inherit = null; + } + else if (TAG_ROMID.equals(qName)) { + if (!isNullOrEmpty(ecuId) && + !isNullOrEmpty(calId) && + !isNullOrEmpty(year) && + !isNullOrEmpty(market) && + !isNullOrEmpty(make) && + !isNullOrEmpty(model) && + !isNullOrEmpty(submodel) && + !isNullOrEmpty(transmission) + ) { + carString = String.format("%s %s %s %s %s %s", + year, market, make, model, submodel, transmission); + ecuDefinitionMap.put(ecuId, + new EcuDefinitionImpl( + ecuId, calId, carString, inherit, ecuDefsFile)); + } + if (!isNullOrEmpty(ecuId) && + !isNullOrEmpty(calId) && + !isNullOrEmpty(address) && + !isNullOrEmpty(year) && + !isNullOrEmpty(market) && + !isNullOrEmpty(make) && + !isNullOrEmpty(model) && + !isNullOrEmpty(submodel) && + !isNullOrEmpty(transmission) && + !isNullOrEmpty(memmodel) && + !isNullOrEmpty(flashmethod) && + !isNullOrEmpty(obsolete) + ) { + LOGGER.debug(romDetail()); + } + } + else if (TAG_CALID.equals(qName)) { + calId = charBuffer.toString(); + } + else if (TAG_ECUID.equals(qName)) { + ecuId = charBuffer.toString(); + } + else if (TAG_CASEID.equals(qName)) { + caseId = charBuffer.toString(); + } + else if (TAG_ADDRESS.equals(qName)) { + address = charBuffer.toString(); + } + else if (TAG_YEAR.equals(qName)) { + year = charBuffer.toString(); + if (!year.isEmpty()) { + try { + if (Integer.parseInt(year) < 90) { + year = "20" + year; + } + } + catch (NumberFormatException e) { + if ((year.contains("/") || year.contains("-")) && + year.length() < 6 ) + year = "20" + year; + } + } + else { + year = "20xx"; + } + } + else if (TAG_MARKET.equals(qName)) { + market = charBuffer.toString(); + } + else if (TAG_MAKE.equals(qName)) { + make = charBuffer.toString(); + } + else if (TAG_MODEL.equals(qName)) { + model = charBuffer.toString(); + } + else if (TAG_SUBMODEL.equals(qName)) { + submodel = charBuffer.toString(); + } + else if (TAG_TRANS.equals(qName)) { + transmission = charBuffer.toString(); + } + else if (TAG_MEMMODEL.equals(qName)) { + memmodel = charBuffer.toString(); + } + else if (TAG_FLASH.equals(qName)) { + flashmethod = charBuffer.toString(); + } + else if (TAG_SIZE.equals(qName)) { + filesize = charBuffer.toString(); + } + else if (TAG_OBSOLETE.equals(qName)) { + obsolete = charBuffer.toString(); + } + } + + public Map getEcuDefinitionMap() { + return ecuDefinitionMap; + } + + public String romDetail() { + return String.format( + "calid='%s'," + + "address='%s'," + + "string='%s'," + + "caseid='%s'," + + "year='%s'," + + "market='%s'," + + "make='%s'," + + "model='%s'," + + "submodel='%s'," + + "transmission='%s'," + + "memmodel='%s'," + + "flash='%s'," + + "filesize='%s'," + + "obsolete='%s'," + + "inherit='%s'" + + "file='%s'", + calId, + address, + calId, + caseId, + year, + market, + make, + model, + submodel, + transmission, + memmodel, + flashmethod, + filesize , + obsolete, + inherit, + ecuDefsFile.getName() + ); + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/xml/EcuDefinitionInheritanceList.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/xml/EcuDefinitionInheritanceList.java new file mode 100644 index 0000000000..b761292319 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/xml/EcuDefinitionInheritanceList.java @@ -0,0 +1,119 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition.xml; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +import com.romraider.logger.ecu.definition.EcuDefinition; + +/** + * Get a List of ROM inheritance Nodes from the given DOM and ECU definition. + */ +public class EcuDefinitionInheritanceList { + + private EcuDefinitionInheritanceList() { + } + + /** + * Get a List of ROM inheritance Nodes + * @param document - the DOM document containing the ECU definition and + * its parents. + * @param ecuDef - ECU definition to start with. + * @return a List of Node starting with the given ECU listing back to the + * base definition. + */ + public static final List getInheritanceList( + Document document, + EcuDefinition ecuDef) { + + final XPathFactory xpf = XPathFactory.newInstance(); + final XPath xpath = xpf.newXPath(); + final List inheritance = new ArrayList(); + inheritance.add(ecuDef.getCalId()); + inheritance.add(ecuDef.getInherits()); + String base = ecuDef.getInherits(); + while (!base.contains("BASE")) { + base = getBaseRomId(xpath, document, base); + inheritance.add(base); + } + final List inheritanceNodes = new ArrayList(); + for (String inheritId : inheritance) { + Node baseNode = getBaseRomIdNode(xpath, document, inheritId); + inheritanceNodes.add(baseNode); + } + return inheritanceNodes; + } + + /** + * Get the CAL ID string of the base inheritance definition. + * @param xpath - the XPath evaluation environment. + * @param document - W3C DOM Document of an ECU definition XML file. + * @param calId - the known CAL ID to find the inheritance for. + * @return the CAL ID from the attribute 'base' in the 'rom' element . + */ + private static final String getBaseRomId( + XPath xpath, Document document, String calId) { + + String romBase = null; + try { + final Node calidNode = (Node) xpath.evaluate( + "/roms/rom/romid[xmlid='" + calId + "']", + document, XPathConstants.NODE); + romBase = calidNode.getParentNode().getAttributes().item(0). + getNodeValue(); + } + catch (XPathExpressionException e) { + e.printStackTrace(); + } + return romBase; + } + + /** + * Get the 'rom' Node which contains the CAL ID string. + * @param xpath - the XPath evaluation environment. + * @param document - W3C DOM Document of an ECU definition XM file. + * @param calId - the known CAL ID to find the inheritance for. + * @return the Node of the 'rom' parent element to the CAL ID. + */ + private static final Node getBaseRomIdNode( + XPath xpath, Document document, String calId) { + + Node romIdNode = null; + try { + romIdNode = (Node) xpath.evaluate( + "/roms/rom/romid[xmlid='" + calId + "']", + document, XPathConstants.NODE); + romIdNode = romIdNode.getParentNode(); + } + catch (XPathExpressionException e) { + e.printStackTrace(); + } + return romIdNode; + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/definition/xml/EcuTableDefinitionHandler.java b/java_console/romraider/src/com/romraider/logger/ecu/definition/xml/EcuTableDefinitionHandler.java new file mode 100644 index 0000000000..534d653098 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/definition/xml/EcuTableDefinitionHandler.java @@ -0,0 +1,113 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.definition.xml; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +/** + * Parse an ECU Definition XML file and return the attributes of the + * table name supplied. + */ +public final class EcuTableDefinitionHandler { + + private EcuTableDefinitionHandler() { + } + + /** + * Parse a given ECU Definition XML file and return the attributes of the + * table name supplied. + * @param document - W3C DOM Document of an ECU definition XML file. + * @param inheritanceNodeList - a List of Nodes of the 'rom' parents + * to the CAL ID supplied. + * @param tableName - the name of the table to find. + * @return a Map of table and scaling name/value attributes. + */ + public static final Map getTableDefinition( + Document document, + List inheritanceNodeList, + String tableName) { + + final Map table = + new HashMap(); + for (Node inheritIdNode : inheritanceNodeList) { + get2dTableMap(inheritIdNode, tableName, table); + } + return table; + } + + /** + * For the given Node and Table Name populate a Map with the table's + * attributes and the scaling attributes. + * @param xpath - the XPath evaluation environment. + * @param node - the 'rom' Node to root the search within. + * @param tableName - the Table Name to locate. + * @param tableMap2d - a Map to populate with attribute names and values. + */ + private static final void get2dTableMap( + Node node, String tableName, + Map tableMap2d) { + + final XPathFactory xpf = XPathFactory.newInstance(); + final XPath xpath = xpf.newXPath(); + try { + final Node tableNode = (Node) xpath.evaluate( + "table[@name='" + tableName + "']", + node, XPathConstants.NODE); + addAttributesAsMap(tableNode, tableMap2d); + + final Node scalingNode = (Node) xpath.evaluate( + "scaling", + tableNode, XPathConstants.NODE); + addAttributesAsMap(scalingNode, tableMap2d); + } + catch (NullPointerException e) { + } + catch (XPathExpressionException e) { + e.printStackTrace(); + } + } + + /** + * Convert XML attributes to a Map. + * @param node - the Node containing the attributes. + * @param tableMap - the Map to populate with attribute names and values. + */ + private static final void addAttributesAsMap( + Node node, Map tableMap) { + + final NamedNodeMap attrs = node.getAttributes(); + for (int i = 0; i < attrs.getLength(); i++) { + tableMap.put( + attrs.item(i).getNodeName(), + attrs.item(i).getNodeValue()); + } + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/exception/ConfigurationException.java b/java_console/romraider/src/com/romraider/logger/ecu/exception/ConfigurationException.java new file mode 100644 index 0000000000..3c7fa21f5d --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/exception/ConfigurationException.java @@ -0,0 +1,41 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.exception; + +public final class ConfigurationException extends RuntimeException { + + private static final long serialVersionUID = 2021993520731842524L; + + public ConfigurationException() { + } + + public ConfigurationException(String string) { + super(string); + } + + public ConfigurationException(String string, Throwable throwable) { + super(string, throwable); + } + + public ConfigurationException(Throwable throwable) { + super(throwable); + } + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/exception/FileLoggerException.java b/java_console/romraider/src/com/romraider/logger/ecu/exception/FileLoggerException.java new file mode 100644 index 0000000000..b0142612aa --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/exception/FileLoggerException.java @@ -0,0 +1,41 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.exception; + +public final class FileLoggerException extends RuntimeException { + + private static final long serialVersionUID = -7851192938290131460L; + + public FileLoggerException() { + } + + public FileLoggerException(String string) { + super(string); + } + + public FileLoggerException(String string, Throwable throwable) { + super(string, throwable); + } + + public FileLoggerException(Throwable throwable) { + super(throwable); + } + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/exception/InvalidResponseException.java b/java_console/romraider/src/com/romraider/logger/ecu/exception/InvalidResponseException.java new file mode 100644 index 0000000000..cb031ef7a4 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/exception/InvalidResponseException.java @@ -0,0 +1,41 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.exception; + +public final class InvalidResponseException extends RuntimeException { + + private static final long serialVersionUID = 296093377055913575L; + + public InvalidResponseException() { + } + + public InvalidResponseException(String string) { + super(string); + } + + public InvalidResponseException(String string, Throwable throwable) { + super(string, throwable); + } + + public InvalidResponseException(Throwable throwable) { + super(throwable); + } + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/exception/NotConnectedException.java b/java_console/romraider/src/com/romraider/logger/ecu/exception/NotConnectedException.java new file mode 100644 index 0000000000..1fa0d57bea --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/exception/NotConnectedException.java @@ -0,0 +1,39 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.exception; + +public final class NotConnectedException extends RuntimeException { + private static final long serialVersionUID = -7287379536144468034L; + + public NotConnectedException() { + } + + public NotConnectedException(String string) { + super(string); + } + + public NotConnectedException(String string, Throwable throwable) { + super(string, throwable); + } + + public NotConnectedException(Throwable throwable) { + super(throwable); + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/exception/PortNotFoundException.java b/java_console/romraider/src/com/romraider/logger/ecu/exception/PortNotFoundException.java new file mode 100644 index 0000000000..54f1ff2934 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/exception/PortNotFoundException.java @@ -0,0 +1,40 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.exception; + +public final class PortNotFoundException extends RuntimeException { + + private static final long serialVersionUID = -523838685805525387L; + + public PortNotFoundException() { + } + + public PortNotFoundException(String string) { + super(string); + } + + public PortNotFoundException(String string, Throwable throwable) { + super(string, throwable); + } + + public PortNotFoundException(Throwable throwable) { + super(throwable); + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/exception/SerialCommunicationException.java b/java_console/romraider/src/com/romraider/logger/ecu/exception/SerialCommunicationException.java new file mode 100644 index 0000000000..16954a6f4c --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/exception/SerialCommunicationException.java @@ -0,0 +1,41 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.exception; + +public final class SerialCommunicationException extends RuntimeException { + + private static final long serialVersionUID = -3468947970939582263L; + + public SerialCommunicationException() { + } + + public SerialCommunicationException(String msg) { + super(msg); + } + + public SerialCommunicationException(String msg, Throwable cause) { + super(msg, cause); + } + + public SerialCommunicationException(Throwable cause) { + super(cause); + } + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/exception/UnsupportedPortTypeException.java b/java_console/romraider/src/com/romraider/logger/ecu/exception/UnsupportedPortTypeException.java new file mode 100644 index 0000000000..353c839acd --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/exception/UnsupportedPortTypeException.java @@ -0,0 +1,40 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.exception; + +public final class UnsupportedPortTypeException extends RuntimeException { + + private static final long serialVersionUID = 5398746954800909391L; + + public UnsupportedPortTypeException() { + } + + public UnsupportedPortTypeException(String string) { + super(string); + } + + public UnsupportedPortTypeException(String string, Throwable throwable) { + super(string, throwable); + } + + public UnsupportedPortTypeException(Throwable throwable) { + super(throwable); + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/exception/UnsupportedProtocolException.java b/java_console/romraider/src/com/romraider/logger/ecu/exception/UnsupportedProtocolException.java new file mode 100644 index 0000000000..4d0a07e0cf --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/exception/UnsupportedProtocolException.java @@ -0,0 +1,41 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.exception; + +public final class UnsupportedProtocolException extends RuntimeException { + + private static final long serialVersionUID = -5005888534387937344L; + + public UnsupportedProtocolException() { + } + + public UnsupportedProtocolException(String string) { + super(string); + } + + public UnsupportedProtocolException(String string, Throwable throwable) { + super(string, throwable); + } + + public UnsupportedProtocolException(Throwable throwable) { + super(throwable); + } + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfile.java b/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfile.java new file mode 100644 index 0000000000..09028e6c6a --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfile.java @@ -0,0 +1,40 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2014 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.profile; + +import com.romraider.logger.ecu.definition.EcuDataConvertor; +import com.romraider.logger.ecu.definition.LoggerData; + +public interface UserProfile { + + boolean contains(LoggerData loggerData); + + boolean isSelectedOnLiveDataTab(LoggerData loggerData); + + boolean isSelectedOnGraphTab(LoggerData loggerData); + + boolean isSelectedOnDashTab(LoggerData loggerData); + + EcuDataConvertor getSelectedConvertor(LoggerData loggerData); + + byte[] getBytes(); + + String getProtocol(); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileFileFilter.java b/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileFileFilter.java new file mode 100644 index 0000000000..6f8ac25f94 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileFileFilter.java @@ -0,0 +1,38 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.profile; + +import com.romraider.swing.GenericFileFilter; +import javax.swing.filechooser.FileFilter; +import java.io.File; + +public final class UserProfileFileFilter extends FileFilter { + + private final FileFilter filter = new GenericFileFilter("ECU Logger User Profiles", "xml"); + + public boolean accept(File file) { + return filter.accept(file); + } + + public String getDescription() { + return filter.getDescription(); + } + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileImpl.java new file mode 100644 index 0000000000..0e4ca5e514 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileImpl.java @@ -0,0 +1,151 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2014 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.profile; + +import com.romraider.Settings; +import com.romraider.logger.ecu.definition.EcuDataConvertor; +import com.romraider.logger.ecu.definition.EcuParameter; +import com.romraider.logger.ecu.definition.EcuSwitch; +import com.romraider.logger.ecu.definition.ExternalData; +import com.romraider.logger.ecu.definition.LoggerData; +import com.romraider.logger.ecu.exception.ConfigurationException; +import com.romraider.util.SettingsManager; + +import static com.romraider.util.ParamChecker.checkNotNull; +import static com.romraider.util.ParamChecker.isNullOrEmpty; +import java.util.Map; + +public final class UserProfileImpl implements UserProfile { + private static final String NEW_LINE = System.getProperty("line.separator"); + private final Map params; + private final Map switches; + private final Map external; + private final String protocol; + + public UserProfileImpl( + Map params, + Map switches, + Map external, + String protocol) { + + checkNotNull(params, "params"); + checkNotNull(switches, "switches"); + checkNotNull(external, "external"); + this.params = params; + this.switches = switches; + this.external = external; + this.protocol = protocol; + } + + public boolean contains(LoggerData loggerData) { + checkNotNull(loggerData, "loggerData"); + return getMap(loggerData).keySet().contains(loggerData.getId()); + } + + public boolean isSelectedOnLiveDataTab(LoggerData loggerData) { + checkNotNull(loggerData, "loggerData"); + return contains(loggerData) && getUserProfileItem(loggerData).isLiveDataSelected(); + } + + public boolean isSelectedOnGraphTab(LoggerData loggerData) { + checkNotNull(loggerData, "loggerData"); + return contains(loggerData) && getUserProfileItem(loggerData).isGraphSelected(); + } + + public boolean isSelectedOnDashTab(LoggerData loggerData) { + checkNotNull(loggerData, "loggerData"); + return contains(loggerData) && getUserProfileItem(loggerData).isDashSelected(); + } + + public EcuDataConvertor getSelectedConvertor(LoggerData loggerData) { + checkNotNull(loggerData, "loggerData"); + if (contains(loggerData)) { + String defaultUnits = getUserProfileItem(loggerData).getUnits(); + if (defaultUnits != null && loggerData.getConvertors().length > 1) { + for (EcuDataConvertor convertor : loggerData.getConvertors()) { + if (defaultUnits.equals(convertor.getUnits())) return convertor; + } + throw new ConfigurationException("Unknown default units, '" + defaultUnits + "', specified for [" + loggerData.getId() + "] " + loggerData.getName()); + } + } + return loggerData.getSelectedConvertor(); + } + + public byte[] getBytes() { + return buildXml().getBytes(); + } + + @Override + public String getProtocol() { + return protocol; + } + + private String buildXml() { + final Settings settings = SettingsManager.getSettings(); + StringBuilder builder = new StringBuilder(); + builder.append("").append(NEW_LINE); + builder.append("").append(NEW_LINE).append(NEW_LINE); + builder.append("").append(NEW_LINE); + if (!params.isEmpty()) { + builder.append(" ").append(NEW_LINE); + appendLoggerDataElements(builder, "parameter", params, true); + builder.append(" ").append(NEW_LINE); + } + if (!switches.isEmpty()) { + builder.append(" ").append(NEW_LINE); + appendLoggerDataElements(builder, "switch", switches, false); + builder.append(" ").append(NEW_LINE); + } + if (!external.isEmpty()) { + builder.append(" ").append(NEW_LINE); + // Comment out the next line to disable Externals from being saved + // as there seems to be a bug when a profile is reloaded, the + // Logger tries to connect twice causing one or both to fail. + appendLoggerDataElements(builder, "external", external, true); + builder.append(" ").append(NEW_LINE); + } + builder.append("").append(NEW_LINE); + return builder.toString(); + } + + private void appendLoggerDataElements(StringBuilder builder, String dataType, Map dataMap, boolean showUnits) { + for (String id : dataMap.keySet()) { + UserProfileItem item = dataMap.get(id); + builder.append(" <").append(dataType).append(" id=\"").append(id).append("\""); + if (item.isLiveDataSelected()) builder.append(" livedata=\"selected\""); + if (item.isGraphSelected()) builder.append(" graph=\"selected\""); + if (item.isDashSelected()) builder.append(" dash=\"selected\""); + if (showUnits && !isNullOrEmpty(item.getUnits())) + builder.append(" units=\"").append(item.getUnits()).append("\""); + builder.append("/>").append(NEW_LINE); + } + } + + private UserProfileItem getUserProfileItem(LoggerData loggerData) { + return getMap(loggerData).get(loggerData.getId()); + } + + private Map getMap(LoggerData loggerData) { + if (loggerData instanceof EcuParameter) return params; + else if (loggerData instanceof EcuSwitch) return switches; + else if (loggerData instanceof ExternalData) return external; + else throw new UnsupportedOperationException("Unknown LoggerData type: " + loggerData.getClass()); + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileItem.java b/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileItem.java new file mode 100644 index 0000000000..1813c5f978 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileItem.java @@ -0,0 +1,32 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.profile; + +public interface UserProfileItem { + + boolean isDashSelected(); + + boolean isGraphSelected(); + + boolean isLiveDataSelected(); + + String getUnits(); + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileItemImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileItemImpl.java new file mode 100644 index 0000000000..e27507f6e8 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileItemImpl.java @@ -0,0 +1,51 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.profile; + +public final class UserProfileItemImpl implements UserProfileItem { + private final String units; + private final boolean liveDataSelected; + private final boolean graphSelected; + private final boolean dashSelected; + + public UserProfileItemImpl(String units, boolean liveDataSelected, boolean graphSelected, boolean dashSelected) { + this.units = units; + this.liveDataSelected = liveDataSelected; + this.graphSelected = graphSelected; + this.dashSelected = dashSelected; + } + + public boolean isDashSelected() { + return dashSelected; + } + + public boolean isGraphSelected() { + return graphSelected; + } + + public boolean isLiveDataSelected() { + return liveDataSelected; + } + + public String getUnits() { + return units; + } + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileLoader.java b/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileLoader.java new file mode 100644 index 0000000000..33b5a9a6c8 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileLoader.java @@ -0,0 +1,26 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.profile; + +public interface UserProfileLoader { + String BACKUP_PROFILE = "/.RomRaider/profile_backup.xml"; + + UserProfile loadProfile(String filePath); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileLoaderImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileLoaderImpl.java new file mode 100644 index 0000000000..6884432b9b --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/profile/UserProfileLoaderImpl.java @@ -0,0 +1,56 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.profile; + +import com.romraider.logger.ecu.profile.xml.UserProfileHandler; +import static com.romraider.util.ParamChecker.checkNotNullOrEmpty; +import static com.romraider.util.SaxParserFactory.getSaxParser; +import org.apache.log4j.Logger; +import org.xml.sax.SAXParseException; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; + +public final class UserProfileLoaderImpl implements UserProfileLoader { + private static final Logger LOGGER = Logger.getLogger(UserProfileLoaderImpl.class); + + public UserProfile loadProfile(String userProfileFilePath) { + checkNotNullOrEmpty(userProfileFilePath, "userProfileFilePath"); + LOGGER.info("Loading profile: " + userProfileFilePath); + try { + InputStream inputStream = new BufferedInputStream(new FileInputStream(new File(userProfileFilePath))); + try { + UserProfileHandler handler = new UserProfileHandler(); + getSaxParser().parse(inputStream, handler); + return handler.getUserProfile(); + } finally { + inputStream.close(); + } + } catch (SAXParseException spe) { + // catch general parsing exception - enough people don't unzip the defs that a better error message is in order + LOGGER.error("Error loading user profile file: " + userProfileFilePath + ". Please make sure the definition file is correct. If it is in a ZIP archive, unzip the file and try again."); + return null; + } catch (Exception e) { + LOGGER.error("Error loading user profile file: " + userProfileFilePath, e); + return null; + } + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/profile/xml/UserProfileHandler.java b/java_console/romraider/src/com/romraider/logger/ecu/profile/xml/UserProfileHandler.java new file mode 100644 index 0000000000..633e830344 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/profile/xml/UserProfileHandler.java @@ -0,0 +1,79 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2014 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.profile.xml; + +import com.romraider.logger.ecu.profile.UserProfile; +import com.romraider.logger.ecu.profile.UserProfileImpl; +import com.romraider.logger.ecu.profile.UserProfileItem; +import com.romraider.logger.ecu.profile.UserProfileItemImpl; +import org.xml.sax.Attributes; +import org.xml.sax.helpers.DefaultHandler; +import java.util.HashMap; +import java.util.Map; + +public final class UserProfileHandler extends DefaultHandler { + private static final String SELECTED = "selected"; + private static final String TAG_PROFILE = "profile"; + private static final String TAG_PARAMETER = "parameter"; + private static final String TAG_SWITCH = "switch"; + private static final String TAG_EXTERNAL = "external"; + private static final String ATTR_PROTOCOL = "protocol"; + private static final String ATTR_ID = "id"; + private static final String ATTR_UNITS = "units"; + private static final String ATTR_LIVE_DATA = "livedata"; + private static final String ATTR_GRAPH = "graph"; + private static final String ATTR_DASH = "dash"; + private Map params; + private Map switches; + private Map external; + private String protocol; + + public void startDocument() { + params = new HashMap(); + switches = new HashMap(); + external = new HashMap(); + } + + public void startElement(String uri, String localName, String qName, Attributes attributes) { + if (TAG_PROFILE.equals(qName)) { + protocol = attributes.getValue(ATTR_PROTOCOL); + } else if (TAG_PARAMETER.equals(qName)) { + params.put(attributes.getValue(ATTR_ID), getUserProfileItem(attributes)); + } else if (TAG_SWITCH.equals(qName)) { + switches.put(attributes.getValue(ATTR_ID), getUserProfileItem(attributes)); + } else if (TAG_EXTERNAL.equals(qName)) { + external.put(attributes.getValue(ATTR_ID), getUserProfileItem(attributes)); + } + } + + public UserProfile getUserProfile() { + return new UserProfileImpl(params, switches, external, protocol); + } + + private UserProfileItem getUserProfileItem(Attributes attributes) { + return new UserProfileItemImpl( + attributes.getValue(ATTR_UNITS), + SELECTED.equalsIgnoreCase(attributes.getValue(ATTR_LIVE_DATA)), + SELECTED.equalsIgnoreCase(attributes.getValue(ATTR_GRAPH)), + SELECTED.equalsIgnoreCase(attributes.getValue(ATTR_DASH)) + ); + } + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/DataRegistrationBroker.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/DataRegistrationBroker.java new file mode 100644 index 0000000000..ba791b1625 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/DataRegistrationBroker.java @@ -0,0 +1,32 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui; + +import com.romraider.logger.ecu.definition.LoggerData; + +public interface DataRegistrationBroker extends StatusChangeListener { + + void registerLoggerDataForLogging(LoggerData loggerData); + + void deregisterLoggerDataFromLogging(LoggerData loggerData); + + void clear(); + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/DataRegistrationBrokerImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/DataRegistrationBrokerImpl.java new file mode 100644 index 0000000000..d5844449db --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/DataRegistrationBrokerImpl.java @@ -0,0 +1,93 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui; + +import com.romraider.logger.ecu.comms.controller.LoggerController; +import com.romraider.logger.ecu.definition.LoggerData; +import com.romraider.logger.ecu.ui.handler.DataUpdateHandlerManager; +import static com.romraider.util.ParamChecker.checkNotNull; +import static java.util.Collections.synchronizedList; +import java.util.ArrayList; +import java.util.List; + +public final class DataRegistrationBrokerImpl implements DataRegistrationBroker { + private final List registeredLoggerData = synchronizedList(new ArrayList()); + private final LoggerController controller; + private final DataUpdateHandlerManager handlerManager; + private final String id; + + public DataRegistrationBrokerImpl(LoggerController controller, DataUpdateHandlerManager handlerManager) { + checkNotNull(controller, handlerManager); + this.controller = controller; + this.handlerManager = handlerManager; + id = System.currentTimeMillis() + "_" + hashCode(); + } + + public synchronized void registerLoggerDataForLogging(final LoggerData loggerData) { + if (!registeredLoggerData.contains(loggerData)) { + // register param with handlers + handlerManager.registerData(loggerData); + + // add logger + controller.addLogger(id, loggerData); + + // add to registered parameters list + registeredLoggerData.add(loggerData); + } + } + + public synchronized void deregisterLoggerDataFromLogging(LoggerData loggerData) { + if (registeredLoggerData.contains(loggerData)) { + // deregister from dependant objects + deregisterLoggerDataFromDependants(loggerData); + + // remove from registered list + registeredLoggerData.remove(loggerData); + } + } + + public synchronized void clear() { + for (LoggerData loggerData : registeredLoggerData) { + deregisterLoggerDataFromDependants(loggerData); + } + registeredLoggerData.clear(); + } + + public synchronized void connecting() { + } + + public synchronized void readingData() { + } + + public synchronized void loggingData() { + } + + public synchronized void stopped() { + } + + private void deregisterLoggerDataFromDependants(LoggerData loggerData) { + // remove logger + controller.removeLogger(id, loggerData); + + // deregister param from handlers + handlerManager.deregisterData(loggerData); + } + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/EcuDataComparator.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/EcuDataComparator.java new file mode 100644 index 0000000000..60c1fd43a0 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/EcuDataComparator.java @@ -0,0 +1,31 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui; + +import com.romraider.logger.ecu.definition.LoggerData; +import java.util.Comparator; + +public final class EcuDataComparator implements Comparator { + + public int compare(LoggerData loggerData1, LoggerData loggerData2) { + return loggerData1.getName().compareTo(loggerData2.getName()); + } + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/MessageListener.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/MessageListener.java new file mode 100644 index 0000000000..1d612e26ec --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/MessageListener.java @@ -0,0 +1,35 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui; + +public interface MessageListener { + + void reportStats(String message); + + void reportMessage(String message); + + void reportMessageInTitleBar(String message); + + void reportError(String error); + + void reportError(Exception e); + + void reportError(String error, Exception e); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/SerialPortComboBox.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/SerialPortComboBox.java new file mode 100644 index 0000000000..cfa40eed63 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/SerialPortComboBox.java @@ -0,0 +1,103 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui; + +import static com.romraider.util.ParamChecker.checkNotNull; + +import java.util.Set; +import java.util.TreeSet; + +import javax.swing.JComboBox; + +import com.romraider.io.serial.port.SerialPortRefreshListener; +import com.romraider.util.SettingsManager; + +public final class SerialPortComboBox extends JComboBox implements SerialPortRefreshListener { + private static final long serialVersionUID = 5693976713268676676L; + + public SerialPortComboBox() { + } + + @Override + public synchronized void refreshPortList(Set ports, String defaultSelectedPort) { + checkNotNull(ports); + boolean changeDetected = ports.isEmpty() || ports.size() != getItemCount(); + if (!changeDetected) { + for (int i = 0; i < getItemCount(); i++) { + String port = (String) getItemAt(i); + if (!ports.contains(port)) { + changeDetected = true; + break; + } + } + if (!changeDetected) { + Set comboPorts = new TreeSet(); + for (int i = 0; i < getItemCount(); i++) { + comboPorts.add((String) getItemAt(i)); + } + for (String port : ports) { + if (!comboPorts.contains(port)) { + changeDetected = true; + break; + } + } + } + } + if (changeDetected) { + String selectedPort = (String) getSelectedItem(); + if (selectedPort == null) { + selectedPort = defaultSelectedPort; + } + removeAllItems(); + if (!ports.isEmpty()) { + for (String port : ports) { + addItem(port); + } + if (selectedPort != null && ports.contains(selectedPort)) { + setSelectedItem(selectedPort); + SettingsManager.getSettings().setLoggerPort(selectedPort); + } else { + setSelectedIndex(0); + SettingsManager.getSettings().setLoggerPort((String) getItemAt(0)); + } + } + } + } + + @Override + public void setSelectedItem(Object object) { + if (contains(object)) { + super.setSelectedItem(object); + } else { + if (getItemCount() >= 1) { + setSelectedIndex(0); + } + } + } + + private boolean contains(Object object) { + for (int i = 0; i < getItemCount(); i++) { + if (getItemAt(i) != null && getItemAt(i).equals(object)) { + return true; + } + } + return false; + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/StatusChangeListener.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/StatusChangeListener.java new file mode 100644 index 0000000000..00bce5eff7 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/StatusChangeListener.java @@ -0,0 +1,31 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui; + +public interface StatusChangeListener { + + void connecting(); + + void readingData(); + + void loggingData(); + + void stopped(); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/StatusIndicator.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/StatusIndicator.java new file mode 100644 index 0000000000..5c14803577 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/StatusIndicator.java @@ -0,0 +1,73 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui; + +import static java.awt.BorderLayout.WEST; +import static java.awt.Font.BOLD; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import java.awt.BorderLayout; + +public final class StatusIndicator extends JPanel implements StatusChangeListener { + private static final long serialVersionUID = -3244690866698807677L; + private final JLabel statusLabel = new JLabel(); + private static final String TEXT_CONNECTING = "Connecting "; + private static final String TEXT_READING = "Reading data "; + private static final String TEXT_LOGGING = "Logging to file "; + private static final String TEXT_STOPPED = "Stopped "; + private static final ImageIcon ICON_CONNECTING = new ImageIcon(StatusIndicator.class.getClass().getResource("/graphics/logger_blue.png")); + private static final ImageIcon ICON_READING = new ImageIcon(StatusIndicator.class.getClass().getResource("/graphics/logger_green.png")); + private static final ImageIcon ICON_LOGGING = new ImageIcon(StatusIndicator.class.getClass().getResource("/graphics/logger_recording.png")); + private static final ImageIcon ICON_STOPPED = new ImageIcon(StatusIndicator.class.getClass().getResource("/graphics/logger_stop.png")); + + public StatusIndicator() { + setLayout(new BorderLayout()); + statusLabel.setFont(getFont().deriveFont(BOLD)); + add(statusLabel, WEST); + stopped(); + } + + public void connecting() { + updateStatusLabel(TEXT_CONNECTING, ICON_CONNECTING); + } + + public void readingData() { + updateStatusLabel(TEXT_READING, ICON_READING); + } + + public void loggingData() { + updateStatusLabel(TEXT_LOGGING, ICON_LOGGING); + } + + public void stopped() { + updateStatusLabel(TEXT_STOPPED, ICON_STOPPED); + } + + private void updateStatusLabel(final String text, final ImageIcon icon) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + statusLabel.setText(text); + statusLabel.setIcon(icon); + } + }); + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/DataUpdateHandler.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/DataUpdateHandler.java new file mode 100644 index 0000000000..d9c966db6f --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/DataUpdateHandler.java @@ -0,0 +1,37 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.handler; + +import com.romraider.logger.ecu.comms.query.Response; +import com.romraider.logger.ecu.definition.LoggerData; + +public interface DataUpdateHandler { + + void registerData(LoggerData loggerData); + + void handleDataUpdate(Response response); + + void deregisterData(LoggerData loggerData); + + void cleanUp(); + + void reset(); + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/DataUpdateHandlerManager.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/DataUpdateHandlerManager.java new file mode 100644 index 0000000000..49fa50e8b7 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/DataUpdateHandlerManager.java @@ -0,0 +1,36 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.handler; + +import com.romraider.logger.ecu.definition.LoggerData; + +public interface DataUpdateHandlerManager { + + void addHandler(DataUpdateHandler handler); + + void registerData(LoggerData loggerData); + + void deregisterData(LoggerData loggerData); + + void cleanUp(); + + void reset(); + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/DataUpdateHandlerManagerImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/DataUpdateHandlerManagerImpl.java new file mode 100644 index 0000000000..7f19994bcf --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/DataUpdateHandlerManagerImpl.java @@ -0,0 +1,57 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.handler; + +import com.romraider.logger.ecu.definition.LoggerData; +import java.util.ArrayList; +import java.util.List; + +public final class DataUpdateHandlerManagerImpl implements DataUpdateHandlerManager { + private final List handlers = new ArrayList(); + + public synchronized void addHandler(DataUpdateHandler handler) { + handlers.add(handler); + } + + public synchronized void registerData(LoggerData loggerData) { + for (DataUpdateHandler handler : handlers) { + handler.registerData(loggerData); + } + } + + public synchronized void deregisterData(LoggerData loggerData) { + for (DataUpdateHandler handler : handlers) { + handler.deregisterData(loggerData); + } + } + + public synchronized void cleanUp() { + for (DataUpdateHandler handler : handlers) { + handler.cleanUp(); + } + } + + public synchronized void reset() { + for (DataUpdateHandler handler : handlers) { + handler.reset(); + } + } + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileLogger.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileLogger.java new file mode 100644 index 0000000000..b3012c2402 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileLogger.java @@ -0,0 +1,32 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.handler.file; + +public interface FileLogger { + void start(); + + void stop(); + + void writeHeaders(String headers); + + void writeLine(String line, long timestamp); + + boolean isStarted(); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileLoggerControllerSwitchHandler.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileLoggerControllerSwitchHandler.java new file mode 100644 index 0000000000..051af92be0 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileLoggerControllerSwitchHandler.java @@ -0,0 +1,25 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.handler.file; + +public interface FileLoggerControllerSwitchHandler { + + void handleSwitch(double switchValue); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileLoggerControllerSwitchMonitor.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileLoggerControllerSwitchMonitor.java new file mode 100644 index 0000000000..248428d414 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileLoggerControllerSwitchMonitor.java @@ -0,0 +1,29 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.handler.file; + +import com.romraider.logger.ecu.definition.EcuSwitch; + +public interface FileLoggerControllerSwitchMonitor { + + void monitorFileLoggerSwitch(double switchValue); + + EcuSwitch getEcuSwitch(); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileLoggerControllerSwitchMonitorImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileLoggerControllerSwitchMonitorImpl.java new file mode 100644 index 0000000000..fb62bb5753 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileLoggerControllerSwitchMonitorImpl.java @@ -0,0 +1,42 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.handler.file; + +import com.romraider.logger.ecu.definition.EcuSwitch; +import static com.romraider.util.ParamChecker.checkNotNull; + +public final class FileLoggerControllerSwitchMonitorImpl implements FileLoggerControllerSwitchMonitor { + private final EcuSwitch fileLoggingSwitch; + private final FileLoggerControllerSwitchHandler handler; + + public FileLoggerControllerSwitchMonitorImpl(EcuSwitch fileLoggingSwitch, FileLoggerControllerSwitchHandler handler) { + checkNotNull(fileLoggingSwitch, handler); + this.fileLoggingSwitch = fileLoggingSwitch; + this.handler = handler; + } + + public void monitorFileLoggerSwitch(double switchValue) { + handler.handleSwitch(switchValue); + } + + public EcuSwitch getEcuSwitch() { + return fileLoggingSwitch; + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileLoggerImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileLoggerImpl.java new file mode 100644 index 0000000000..74018aba3e --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileLoggerImpl.java @@ -0,0 +1,146 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.handler.file; + +import static com.romraider.util.ParamChecker.checkNotNull; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.text.SimpleDateFormat; +import java.util.Date; + +import com.romraider.Settings; +import com.romraider.logger.ecu.exception.FileLoggerException; +import com.romraider.logger.ecu.ui.MessageListener; +import com.romraider.util.FormatFilename; +import com.romraider.util.SettingsManager; + +public final class FileLoggerImpl implements FileLogger { + private static final String NEW_LINE = System.getProperty("line.separator"); + private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss"); + private final SimpleDateFormat timestampFormat = new SimpleDateFormat("HH:mm:ss.SSS"); + private final MessageListener messageListener; + private boolean started; + private OutputStream os; + private long startTimestamp; + private boolean zero; + + public FileLoggerImpl(MessageListener messageListener) { + checkNotNull(messageListener); + this.messageListener = messageListener; + } + + @Override + public void start() { + if (!started) { + stop(); + try { + String filePath = buildFilePath(); + os = new BufferedOutputStream(new FileOutputStream(filePath)); + messageListener.reportMessageInTitleBar( + "Started logging to file: " + + FormatFilename.getShortName(filePath)); + zero = true; + } catch (Exception e) { + stop(); + throw new FileLoggerException(e); + } + started = true; + } + } + + @Override + public void stop() { + if (os != null) { + try { + os.close(); + messageListener.reportMessageInTitleBar("Stopped logging to file."); + } catch (Exception e) { + throw new FileLoggerException(e); + } + } + started = false; + } + + @Override + public boolean isStarted() { + return started; + } + + @Override + public void writeHeaders(String headers) { + String timeHeader = "Time"; + if (!SettingsManager.getSettings().isFileLoggingAbsoluteTimestamp()) { + timeHeader = timeHeader + " (msec)"; + } + writeText(timeHeader + headers); + } + + @Override + public void writeLine(String line, long timestamp) { + writeText(prependTimestamp(line, timestamp)); + } + + private void writeText(String text) { + try { + os.write(text.getBytes()); + if (!text.endsWith(NEW_LINE)) { + os.write(NEW_LINE.getBytes()); + } + } catch (Exception e) { + stop(); + throw new FileLoggerException(e); + } + } + + private String prependTimestamp(String line, long timestamp) { + String formattedTimestamp; + if (SettingsManager.getSettings().isFileLoggingAbsoluteTimestamp()) { + formattedTimestamp = timestampFormat.format(new Date(timestamp)); + } else { + if (zero) { + formattedTimestamp = "0"; + startTimestamp = System.currentTimeMillis(); + zero = false; + } else { + formattedTimestamp = String.valueOf(System.currentTimeMillis() - startTimestamp); + } + } + return new StringBuilder(formattedTimestamp).append(line).toString(); + } + + private String buildFilePath() { + String logDir = SettingsManager.getSettings().getLoggerOutputDirPath(); + if (!logDir.endsWith(File.separator)) { + logDir += File.separator; + } + logDir += "romraiderlog_"; + Settings settings = SettingsManager.getSettings(); + if (settings.getLogfileNameText() != null + && !settings.getLogfileNameText().isEmpty()) { + logDir += settings.getLogfileNameText() + "_"; + } + logDir += dateFormat.format(new Date()) + ".csv"; + return logDir; + } + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileUpdateHandler.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileUpdateHandler.java new file mode 100644 index 0000000000..7206f44272 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileUpdateHandler.java @@ -0,0 +1,32 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.handler.file; + +import com.romraider.logger.ecu.ui.StatusChangeListener; +import com.romraider.logger.ecu.ui.handler.DataUpdateHandler; + +public interface FileUpdateHandler extends DataUpdateHandler { + + void addListener(StatusChangeListener listener); + + void start(); + + void stop(); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileUpdateHandlerImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileUpdateHandlerImpl.java new file mode 100644 index 0000000000..7795075bf3 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/file/FileUpdateHandlerImpl.java @@ -0,0 +1,212 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.handler.file; + +import static com.romraider.util.ParamChecker.checkNotNull; +import static java.util.Collections.synchronizedList; +import static java.util.Collections.synchronizedMap; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import com.romraider.logger.ecu.comms.query.Response; +import com.romraider.logger.ecu.definition.ConvertorUpdateListener; +import com.romraider.logger.ecu.definition.LoggerData; +import com.romraider.logger.ecu.ui.MessageListener; +import com.romraider.logger.ecu.ui.StatusChangeListener; + +public final class FileUpdateHandlerImpl implements FileUpdateHandler, ConvertorUpdateListener { + private final Map loggerDatas = synchronizedMap(new LinkedHashMap()); + private final List listeners = synchronizedList(new ArrayList()); + private final FileLogger fileLogger; + private Line currentLine = new Line(loggerDatas.keySet()); + + public FileUpdateHandlerImpl(MessageListener messageListener) { + fileLogger = new FileLoggerImpl(messageListener); + } + + @Override + public synchronized void addListener(StatusChangeListener listener) { + checkNotNull(listener, "listener"); + listeners.add(listener); + } + + @Override + public synchronized void registerData(LoggerData loggerData) { + if (loggerDatas.keySet().contains(loggerData)) { + loggerDatas.put(loggerData, loggerDatas.get(loggerData) + 1); + } else { + loggerDatas.put(loggerData, 1); + resetLine(); + writeHeaders(); + } + } + + @Override + public synchronized void handleDataUpdate(Response response) { + if (fileLogger.isStarted()) { + for (LoggerData loggerData : response.getData()) { + currentLine.updateParamValue(loggerData, loggerData.getSelectedConvertor().format(response.getDataValue(loggerData))); + } + if (currentLine.isFull()) { + fileLogger.writeLine(currentLine.values(), response.getTimestamp()); + resetLine(); + } + } + } + + @Override + public synchronized void deregisterData(LoggerData loggerData) { + if (loggerDatas.keySet().contains(loggerData) && loggerDatas.get(loggerData) > 1) { + loggerDatas.put(loggerData, loggerDatas.get(loggerData) - 1); + } else { + loggerDatas.remove(loggerData); + resetLine(); + writeHeaders(); + } + } + + @Override + public synchronized void cleanUp() { + if (fileLogger.isStarted()) { + fileLogger.stop(); + } + } + + @Override + public synchronized void reset() { + } + + @Override + public synchronized void notifyConvertorUpdate(LoggerData updatedLoggerData) { + resetLine(); + writeHeaders(); + } + + @Override + public synchronized void start() { + if (!fileLogger.isStarted()) { + fileLogger.start(); + notifyListeners(true); + writeHeaders(); + } + } + + @Override + public synchronized void stop() { + if (fileLogger.isStarted()) { + fileLogger.stop(); + notifyListeners(false); + } + } + + private void resetLine() { + currentLine = new Line(loggerDatas.keySet()); + } + + private void writeHeaders() { + if (fileLogger.isStarted()) { + fileLogger.writeHeaders(currentLine.headers()); + } + } + + private void notifyListeners(boolean loggingToFile) { + for (StatusChangeListener listener : listeners) { + if (loggingToFile) { + listener.loggingData(); + } else { + listener.readingData(); + } + } + } + + private final class Line { + private final Locale userLocale = Locale.getDefault(); + private static final char COMMA = ','; + private static final char SEMICOLON = ';'; + private final Set locales = new HashSet( + Arrays.asList(new String[] { + "be_BY","bg_BG","ca_ES","cs_CZ","da_DK","de_AT","de_CH","de_DE","de_LU", + "el_CY","el_GR","es_AR","es_BO","es_CL","es_CO","es_EC","es_ES","es_PE", + "es_PY","es_UY","es_VE","et_EE","fi_FI","fr_BE","fr_CA","fr_CH","fr_FR", + "fr_LU","hr_HR","hu_HU","in_ID","is_IS","it_CH","it_IT","lt_LT","lv_LV", + "mk_MK","nl_BE","nl_NL","no_NO","no_NO_NY","pl_PL","pt_BR","pt_PT", + "ro_RO","ru_RU","sk_SK","sl_SI","sq_AL","sr_BA","sr_CS","sr_ME","sr_RS", + "sv_SE","tr_TR","uk_UA","vi_VN" + } + )); + private final Map loggerDataValues; + private final char delimiter; + { + if (locales.contains(userLocale.toString())) { + delimiter = SEMICOLON; + } + else { + delimiter = COMMA; + } + } + + public Line(Set loggerDatas) { + this.loggerDataValues = new LinkedHashMap(); + for (LoggerData loggerData : loggerDatas) { + loggerDataValues.put(loggerData, null); + } + } + + public synchronized void updateParamValue(LoggerData loggerData, String value) { + if (loggerDataValues.containsKey(loggerData)) { + loggerDataValues.put(loggerData, value); + } + } + + public synchronized boolean isFull() { + for (LoggerData loggerData : loggerDataValues.keySet()) { + if (loggerDataValues.get(loggerData) == null) { + return false; + } + } + return true; + } + + public synchronized String headers() { + StringBuilder buffer = new StringBuilder(); + for (LoggerData loggerData : loggerDataValues.keySet()) { + buffer.append(delimiter).append(loggerData.getName()).append(" (") + .append(loggerData.getSelectedConvertor().getUnits()).append(')'); + } + return buffer.toString(); + } + + public synchronized String values() { + StringBuilder buffer = new StringBuilder(); + for (LoggerData loggerData : loggerDataValues.keySet()) { + String value = loggerDataValues.get(loggerData); + buffer.append(delimiter).append(value); + } + return buffer.toString(); + } + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/graph/SpringUtilities.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/graph/SpringUtilities.java new file mode 100644 index 0000000000..26942935a5 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/graph/SpringUtilities.java @@ -0,0 +1,222 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.handler.graph; + +import org.apache.log4j.Logger; +import javax.swing.Spring; +import javax.swing.SpringLayout; +import java.awt.Component; +import java.awt.Container; + +/** + * A 1.4 file that provides utility methods for + * creating form- or grid-style layouts with SpringLayout. + * These utilities are used by several programs, such as + * SpringBox and SpringCompactGrid. + */ +public class SpringUtilities { + private static final Logger LOGGER = Logger.getLogger(SpringUtilities.class); + + private SpringUtilities() { + throw new UnsupportedOperationException(); + } + + /** + * A debugging utility that prints to stdout the component's + * minimum, preferred, and maximum sizes. + */ + public static void printSizes(Component c) { + LOGGER.debug("minimumSize = " + c.getMinimumSize()); + LOGGER.debug("preferredSize = " + c.getPreferredSize()); + LOGGER.debug("maximumSize = " + c.getMaximumSize()); + } + + /** + * Aligns the first rows * cols + * components of parent in + * a grid. Each component is as big as the maximum + * preferred width and height of the components. + * The parent is made just big enough to fit them all. + * + * @param rows number of rows + * @param cols number of columns + * @param initialX x location to start the grid at + * @param initialY y location to start the grid at + * @param xPad x padding between cells + * @param yPad y padding between cells + */ + public static void makeGrid(Container parent, + int rows, int cols, + int initialX, int initialY, + int xPad, int yPad) { + SpringLayout layout; + try { + layout = (SpringLayout) parent.getLayout(); + } catch (ClassCastException exc) { + LOGGER.error("The first argument to makeGrid must use SpringLayout."); + return; + } + + Spring xPadSpring = Spring.constant(xPad); + Spring yPadSpring = Spring.constant(yPad); + Spring initialXSpring = Spring.constant(initialX); + Spring initialYSpring = Spring.constant(initialY); + int max = rows * cols; + + //Calculate Springs that are the max of the width/height so that all + //cells have the same size. + Spring maxWidthSpring = layout.getConstraints(parent.getComponent(0)). + getWidth(); + Spring maxHeightSpring = layout.getConstraints(parent.getComponent(0)). + getWidth(); + for (int i = 1; i < max; i++) { + SpringLayout.Constraints cons = layout.getConstraints( + parent.getComponent(i)); + + maxWidthSpring = Spring.max(maxWidthSpring, cons.getWidth()); + maxHeightSpring = Spring.max(maxHeightSpring, cons.getHeight()); + } + + //Apply the new width/height Spring. This forces all the + //components to have the same size. + for (int i = 0; i < max; i++) { + SpringLayout.Constraints cons = layout.getConstraints( + parent.getComponent(i)); + + cons.setWidth(maxWidthSpring); + cons.setHeight(maxHeightSpring); + } + + //Then adjust the x/y constraints of all the cells so that they + //are aligned in a grid. + SpringLayout.Constraints lastCons = null; + SpringLayout.Constraints lastRowCons = null; + for (int i = 0; i < max; i++) { + SpringLayout.Constraints cons = layout.getConstraints( + parent.getComponent(i)); + if (i % cols == 0) { //start of new row + lastRowCons = lastCons; + cons.setX(initialXSpring); + } else { //x position depends on previous component + cons.setX(Spring.sum(lastCons.getConstraint(SpringLayout.EAST), + xPadSpring)); + } + + if (i / cols == 0) { //first row + cons.setY(initialYSpring); + } else { //y position depends on previous row + cons.setY(Spring.sum(lastRowCons.getConstraint(SpringLayout.SOUTH), + yPadSpring)); + } + lastCons = cons; + } + + //Set the parent's size. + SpringLayout.Constraints pCons = layout.getConstraints(parent); + pCons.setConstraint(SpringLayout.SOUTH, + Spring.sum( + Spring.constant(yPad), + lastCons.getConstraint(SpringLayout.SOUTH))); + pCons.setConstraint(SpringLayout.EAST, + Spring.sum( + Spring.constant(xPad), + lastCons.getConstraint(SpringLayout.EAST))); + } + + /* Used by makeCompactGrid. */ + + private static SpringLayout.Constraints getConstraintsForCell( + int row, int col, + Container parent, + int cols) { + SpringLayout layout = (SpringLayout) parent.getLayout(); + Component c = parent.getComponent(row * cols + col); + return layout.getConstraints(c); + } + + /** + * Aligns the first rows * cols + * components of parent in + * a grid. Each component in a column is as wide as the maximum + * preferred width of the components in that column; + * height is similarly determined for each row. + * The parent is made just big enough to fit them all. + * + * @param rows number of rows + * @param cols number of columns + * @param initialX x location to start the grid at + * @param initialY y location to start the grid at + * @param xPad x padding between cells + * @param yPad y padding between cells + */ + public static void makeCompactGrid(Container parent, + int rows, int cols, + int initialX, int initialY, + int xPad, int yPad) { + SpringLayout layout; + try { + layout = (SpringLayout) parent.getLayout(); + } catch (ClassCastException exc) { + LOGGER.error("The first argument to makeCompactGrid must use SpringLayout."); + return; + } + + //Align all cells in each column and make them the same width. + Spring x = Spring.constant(initialX); + for (int c = 0; c < cols; c++) { + Spring width = Spring.constant(0); + for (int r = 0; r < rows; r++) { + width = Spring.max(width, + getConstraintsForCell(r, c, parent, cols). + getWidth()); + } + for (int r = 0; r < rows; r++) { + SpringLayout.Constraints constraints = + getConstraintsForCell(r, c, parent, cols); + constraints.setX(x); + constraints.setWidth(width); + } + x = Spring.sum(x, Spring.sum(width, Spring.constant(xPad))); + } + + //Align all cells in each row and make them the same height. + Spring y = Spring.constant(initialY); + for (int r = 0; r < rows; r++) { + Spring height = Spring.constant(0); + for (int c = 0; c < cols; c++) { + height = Spring.max(height, + getConstraintsForCell(r, c, parent, cols). + getHeight()); + } + for (int c = 0; c < cols; c++) { + SpringLayout.Constraints constraints = + getConstraintsForCell(r, c, parent, cols); + constraints.setY(y); + constraints.setHeight(height); + } + y = Spring.sum(y, Spring.sum(height, Spring.constant(yPad))); + } + + //Set the parent's size. + SpringLayout.Constraints pCons = layout.getConstraints(parent); + pCons.setConstraint(SpringLayout.SOUTH, y); + pCons.setConstraint(SpringLayout.EAST, x); + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/injector/InjectorUpdateHandler.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/injector/InjectorUpdateHandler.java new file mode 100644 index 0000000000..e7381b82f5 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/injector/InjectorUpdateHandler.java @@ -0,0 +1,187 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.handler.injector; + +import com.romraider.logger.ecu.comms.query.Response; +import com.romraider.logger.ecu.definition.LoggerData; +import com.romraider.logger.ecu.ui.handler.DataUpdateHandler; +import com.romraider.logger.ecu.ui.tab.injector.InjectorTab; +import static java.lang.Math.abs; +import static java.lang.System.currentTimeMillis; +import org.apache.log4j.Logger; +import javax.swing.SwingUtilities; +import java.util.Set; + +public final class InjectorUpdateHandler implements DataUpdateHandler { + private static final Logger LOGGER = Logger.getLogger(InjectorUpdateHandler.class); + private static final String PULSE_WIDTH_16 = "E28"; + private static final String PULSE_WIDTH_32 = "E60"; + private static final String ENGINE_LOAD_16 = "E2"; + private static final String ENGINE_LOAD_32 = "E32"; + private InjectorTab injectorTab; + private double lastMafv; + private long lastUpdate; + + public synchronized void registerData(LoggerData loggerData) { + } + + public synchronized void handleDataUpdate(Response response) { + if (injectorTab.isRecordData() + && (containsData(response, PULSE_WIDTH_16, ENGINE_LOAD_16) + || containsData(response, PULSE_WIDTH_32, ENGINE_LOAD_32))) { + boolean valid = true; + + // cl/ol check + if ((containsData(response, "E3") || containsData(response, "E33"))) { + double clOl = -1; + if (containsData(response, "E3")) { + clOl = (int) findValue(response, "E3"); + LOGGER.trace("INJ:[CL/OL:E3]: " + clOl); + } + if (containsData(response, "E33")) { + clOl = (int) findValue(response, "E33"); + LOGGER.trace("INJ:[CL/OL:E33]: " + clOl); + } + valid = injectorTab.isValidClOl(clOl); + LOGGER.trace("INJ:[CL/OL]: " + valid); + } + + // afr check + if (valid && containsData(response, "P58")) { + double afr = findValue(response, "P58"); + LOGGER.trace("INJ:[AFR:P58]: " + afr); + valid = injectorTab.isValidAfr(afr); + LOGGER.trace("INJ:[AFR]: " + valid); + } + + // rpm check + if (valid && containsData(response, "P8")) { + double rpm = findValue(response, "P8"); + LOGGER.trace("INJ:[RPM:P8]: " + rpm); + valid = injectorTab.isValidRpm(rpm); + LOGGER.trace("INJ:[RPM]: " + valid); + } + + // maf check + if (valid && containsData(response, "P12")) { + double maf = findValue(response, "P12"); + LOGGER.trace("INJ:[MAF:P12]: " + maf); + valid = injectorTab.isValidMaf(maf); + LOGGER.trace("INJ:[MAF]: " + valid); + } + + // intake air temp check + if (valid && containsData(response, "P11")) { + double temp = findValue(response, "P11"); + LOGGER.trace("INJ:[IAT:P11]: " + temp); + valid = injectorTab.isValidIntakeAirTemp(temp); + LOGGER.trace("INJ:[IAT]: " + valid); + } + + // coolant temp check + if (valid && containsData(response, "P2")) { + double temp = findValue(response, "P2"); + LOGGER.trace("INJ:[CT:P2]: " + temp); + valid = injectorTab.isValidCoolantTemp(temp); + LOGGER.trace("INJ:[CT]: " + valid); + } + + // dMAFv/dt check + if (valid && containsData(response, "P18")) { + double mafv = findValue(response, "P18"); + long now = currentTimeMillis(); + double mafvChange = abs((mafv - lastMafv) / (now - lastUpdate) * 1000); + LOGGER.trace("INJ:[dMAFv/dt]: " + mafvChange); + valid = injectorTab.isValidMafvChange(mafvChange); + LOGGER.trace("INJ:[dMAFv/dt]: " + valid); + lastMafv = mafv; + lastUpdate = now; + } + + // tip-in throttle check + if (valid && (containsData(response, "E23") || containsData(response, "E54"))) { + double tipIn = -1; + if (containsData(response, "E23")) { + tipIn = findValue(response, "E23"); + LOGGER.trace("INJ:[TIP:E23]: " + tipIn); + } + if (containsData(response, "E54")) { + tipIn = findValue(response, "E54"); + LOGGER.trace("INJ:[TIP:E54]: " + tipIn); + } + valid = injectorTab.isValidTipInThrottle(tipIn); + LOGGER.trace("INJ:[TIP]: " + valid); + } + + if (valid) { + final double pulseWidth = containsData(response, PULSE_WIDTH_16) ? findValue(response, PULSE_WIDTH_16) : findValue(response, PULSE_WIDTH_32); + double load = containsData(response, ENGINE_LOAD_16) ? findValue(response, ENGINE_LOAD_16) : findValue(response, ENGINE_LOAD_32); + double stoichAfr = injectorTab.getFuelStoichAfr(); + double density = injectorTab.getFuelDensity(); + final double fuelcc = load / 2 / stoichAfr * 1000 / density; + LOGGER.trace("Injector Data: " + pulseWidth + "ms, " + fuelcc + "cc"); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + injectorTab.addData(pulseWidth, fuelcc); + } + }); + } + } + } + + private boolean containsData(Response response, String... ids) { + Set datas = response.getData(); + for (String id : ids) { + boolean found = false; + for (LoggerData data : datas) { + if (data.getId().equals(id)) { + found = true; + break; + } + } + if (!found) { + return false; + } + } + return true; + } + + private double findValue(Response response, String id) { + for (final LoggerData loggerData : response.getData()) { + if (id.equals(loggerData.getId())) { + return response.getDataValue(loggerData); + } + } + throw new IllegalStateException("Expected data item " + id + " not in response."); + } + + public synchronized void deregisterData(LoggerData loggerData) { + } + + public synchronized void cleanUp() { + } + + public synchronized void reset() { + } + + public void setInjectorTab(InjectorTab injectorTab) { + this.injectorTab = injectorTab; + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/livedata/LiveDataRow.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/livedata/LiveDataRow.java new file mode 100644 index 0000000000..39133df72a --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/livedata/LiveDataRow.java @@ -0,0 +1,79 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.handler.livedata; + +import com.romraider.logger.ecu.definition.LoggerData; +import static com.romraider.util.ParamChecker.checkNotNull; + +public final class LiveDataRow { + private static final double ZERO = 0.0; + private final LoggerData loggerData; + private double minValue; + private double maxValue; + private double currentValue; + private boolean updated = false; + + public LiveDataRow(LoggerData loggerData) { + checkNotNull(loggerData, "loggerData"); + this.loggerData = loggerData; + } + + public LoggerData getLoggerData() { + return loggerData; + } + + public String getName() { + return loggerData.getName(); + } + + public String getMinValue() { + return loggerData.getSelectedConvertor().format(minValue); + } + + public String getMaxValue() { + return loggerData.getSelectedConvertor().format(maxValue); + } + + public String getCurrentValue() { + return loggerData.getSelectedConvertor().format(currentValue); + } + + public String getUnits() { + return loggerData.getSelectedConvertor().getUnits(); + } + + public void updateValue(double value) { + currentValue = value; + if (currentValue < minValue || !updated) { + minValue = currentValue; + } + if (currentValue > maxValue || !updated) { + maxValue = currentValue; + } + updated = true; + } + + public void reset() { + minValue = ZERO; + maxValue = ZERO; + currentValue = ZERO; + updated = false; + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/livedata/LiveDataTableModel.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/livedata/LiveDataTableModel.java new file mode 100644 index 0000000000..8a2c6baa17 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/livedata/LiveDataTableModel.java @@ -0,0 +1,108 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.handler.livedata; + +import com.romraider.logger.ecu.definition.LoggerData; +import static java.util.Collections.synchronizedList; +import static java.util.Collections.synchronizedMap; +import javax.swing.table.AbstractTableModel; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public final class LiveDataTableModel extends AbstractTableModel { + private static final long serialVersionUID = 3712433453224086342L; + private final String[] columnNames = {"Logger Data", "Min Value", "Current Value", "Max Value", "Units"}; + private final List registeredLoggerData = synchronizedList(new LinkedList()); + private final Map dataRowMap = synchronizedMap(new LinkedHashMap()); + + public synchronized int getRowCount() { + return dataRowMap.size(); + } + + public int getColumnCount() { + return columnNames.length; + } + + public String getColumnName(int col) { + return columnNames[col]; + } + + public boolean isCellEditable(int row, int col) { + return false; + } + + public synchronized Object getValueAt(int row, int col) { + LiveDataRow dataRow = dataRowMap.get(registeredLoggerData.get(row)); + switch (col) { + case 0: + return dataRow.getName(); + case 1: + return dataRow.getMinValue(); + case 2: + return dataRow.getCurrentValue(); + case 3: + return dataRow.getMaxValue(); + case 4: + return dataRow.getUnits(); + default: + return "Error!"; + } + } + + public synchronized void addParam(LoggerData loggerData) { + if (!registeredLoggerData.contains(loggerData)) { + dataRowMap.put(loggerData, new LiveDataRow(loggerData)); + registeredLoggerData.add(loggerData); + fireTableDataChanged(); + } + } + + public synchronized void removeParam(LoggerData loggerData) { + registeredLoggerData.remove(loggerData); + dataRowMap.remove(loggerData); + fireTableDataChanged(); + } + + public synchronized void updateParam(LoggerData loggerData, double value) { + LiveDataRow dataRow = dataRowMap.get(loggerData); + if (dataRow != null) { + dataRow.updateValue(value); + int index = registeredLoggerData.indexOf(loggerData); + fireTableRowsUpdated(index, index); + } + } + + public synchronized void reset() { + for (LiveDataRow liveDataRow : dataRowMap.values()) { + liveDataRow.reset(); + } + fireTableDataChanged(); + } + + public synchronized void resetRow(LoggerData loggerData) { + LiveDataRow liveDataRow = dataRowMap.get(loggerData); + if (liveDataRow != null) { + liveDataRow.reset(); + fireTableDataChanged(); + } + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/livedata/LiveDataUpdateHandler.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/livedata/LiveDataUpdateHandler.java new file mode 100644 index 0000000000..b054b51b77 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/livedata/LiveDataUpdateHandler.java @@ -0,0 +1,65 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.handler.livedata; + +import com.romraider.logger.ecu.comms.query.Response; +import com.romraider.logger.ecu.definition.ConvertorUpdateListener; +import com.romraider.logger.ecu.definition.LoggerData; +import com.romraider.logger.ecu.ui.handler.DataUpdateHandler; +import javax.swing.SwingUtilities; + +public final class LiveDataUpdateHandler implements DataUpdateHandler, ConvertorUpdateListener { + private final LiveDataTableModel dataTableModel; + + public LiveDataUpdateHandler(LiveDataTableModel dataTableModel) { + this.dataTableModel = dataTableModel; + } + + public synchronized void registerData(LoggerData loggerData) { + // add to datatable + dataTableModel.addParam(loggerData); + } + + public synchronized void handleDataUpdate(final Response response) { + for (final LoggerData loggerData : response.getData()) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + dataTableModel.updateParam(loggerData, response.getDataValue(loggerData)); + } + }); + } + } + + public synchronized void deregisterData(LoggerData loggerData) { + // remove from datatable + dataTableModel.removeParam(loggerData); + } + + public synchronized void cleanUp() { + } + + public synchronized void reset() { + dataTableModel.reset(); + } + + public synchronized void notifyConvertorUpdate(LoggerData updatedLoggerData) { + dataTableModel.resetRow(updatedLoggerData); + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/table/TableUpdateHandler.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/table/TableUpdateHandler.java new file mode 100644 index 0000000000..f6a1498e2d --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/handler/table/TableUpdateHandler.java @@ -0,0 +1,122 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.handler.table; + +import static com.romraider.util.ParamChecker.isNullOrEmpty; +import static java.util.Collections.synchronizedMap; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; + +import com.romraider.logger.ecu.comms.query.Response; +import com.romraider.logger.ecu.definition.LoggerData; +import com.romraider.logger.ecu.ui.handler.DataUpdateHandler; +import com.romraider.maps.Table; +import com.romraider.maps.Table2D; +import com.romraider.maps.Table3D; + +public final class TableUpdateHandler implements DataUpdateHandler { + private static final TableUpdateHandler INSTANCE = new TableUpdateHandler(); + private final Map> tableMap = synchronizedMap(new HashMap>()); + + private TableUpdateHandler() { + tableMap.clear(); + } + + @Override + public void registerData(LoggerData loggerData) { + } + + @Override + public void handleDataUpdate(Response response) { + for (LoggerData loggerData : response.getData()) { + List tables = tableMap.get(loggerData.getId()); + if (tables != null && !tables.isEmpty()) { + String formattedValue = loggerData.getSelectedConvertor().format(response.getDataValue(loggerData)); + for(ListIterator
item = tables.listIterator(); item.hasNext();) { + item.next().highlightLiveData(formattedValue); + } + } + } + } + + @Override + public void deregisterData(LoggerData loggerData) { + } + + @Override + public void cleanUp() { + } + + @Override + public void reset() { + } + + public void registerTable(Table table) { + String logParam = table.getLogParam(); + if (!isNullOrEmpty(logParam)) { + if (!tableMap.containsKey(logParam)) { + tableMap.put(logParam, new ArrayList
()); + } + tableMap.get(logParam).add(table); + } + registerAxes(table); + } + + public void deregisterTable(Table table) { + String logParam = table.getLogParam(); + if (tableMap.containsKey(logParam)) { + List
tables = tableMap.get(logParam); + tables.remove(table); + if (tables.isEmpty()) { + tableMap.remove(logParam); + } + } + deregisterAxes(table); + } + + public static TableUpdateHandler getInstance() { + return INSTANCE; + } + + private void registerAxes(Table table) { + if (table instanceof Table2D) { + registerTable(((Table2D) table).getAxis()); + } + if (table instanceof Table3D) { + registerTable(((Table3D) table).getXAxis()); + registerTable(((Table3D) table).getYAxis()); + } + } + + private void deregisterAxes(Table table) { + if (table instanceof Table2D) { + deregisterTable(((Table2D) table).getAxis()); + } + if (table instanceof Table3D) { + deregisterTable(((Table3D) table).getXAxis()); + deregisterTable(((Table3D) table).getYAxis()); + } + } + +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/paramlist/ParameterListTable.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/paramlist/ParameterListTable.java new file mode 100644 index 0000000000..fa80320829 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/paramlist/ParameterListTable.java @@ -0,0 +1,97 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.paramlist; + +import com.romraider.logger.ecu.definition.EcuData; +import com.romraider.logger.ecu.definition.ExternalData; + +import static com.romraider.util.ParamChecker.isNullOrEmpty; +import javax.swing.JTable; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableRowSorter; + +import java.awt.event.MouseEvent; +import java.util.List; + +public final class ParameterListTable extends JTable { + private static final long serialVersionUID = -8489190548281346227L; + private UnitsComboBoxEditor comboBoxEditor = new UnitsComboBoxEditor(); + private UnitsComboBoxRenderer comboBoxRenderer = new UnitsComboBoxRenderer(); + private final ParameterListTableModel tableModel; + + public ParameterListTable(ParameterListTableModel tableModel) { + super(tableModel); + this.tableModel = tableModel; + this.getTableHeader().setReorderingAllowed(false); + for (int column = 0; column < tableModel.getColumnCount(); column++) { + if (tableModel.getColumnName(2).equalsIgnoreCase("units")) { + setColumnSortable(column, false); + } + else { + setColumnSortable(column, true); + } + } + } + + public TableCellRenderer getCellRenderer(int row, int col) { + return displayComboBox(row, col) ? comboBoxRenderer : super.getCellRenderer(row, col); + } + + public TableCellEditor getCellEditor(int row, int col) { + return displayComboBox(row, col) ? comboBoxEditor : super.getCellEditor(row, col); + } + + public String getToolTipText(MouseEvent mouseEvent) { + List parameterRows = tableModel.getParameterRows(); + if (!isNullOrEmpty(parameterRows)) { + ParameterRow parameterRow = parameterRows.get(rowAtPoint(mouseEvent.getPoint())); + if (parameterRow != null) { + String description = parameterRow.getLoggerData().getDescription(); + if (!isNullOrEmpty(description)) { + return description; + } + } + } + return super.getToolTipText(mouseEvent); + } + + private boolean displayComboBox(int row, int col) { + Object value = getValueAt(row, col); + if (EcuData.class.isAssignableFrom(value.getClass())) { + EcuData ecuData = (EcuData) value; + if (ecuData.getConvertors().length > 1) + return true; + } + if (ExternalData.class.isAssignableFrom(value.getClass())) { + ExternalData externalData = (ExternalData) value; + if (externalData.getConvertors().length > 1) + return true; + } + return false; + } + + private void setColumnSortable(int column, boolean state) { + TableRowSorter sorter = + new TableRowSorter(tableModel); + sorter.setSortable(column, state); + setRowSorter(sorter); + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/paramlist/ParameterListTableModel.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/paramlist/ParameterListTableModel.java new file mode 100644 index 0000000000..f62ae4e06b --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/paramlist/ParameterListTableModel.java @@ -0,0 +1,127 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.paramlist; + +import com.romraider.logger.ecu.definition.LoggerData; +import com.romraider.logger.ecu.ui.DataRegistrationBroker; +import static java.util.Collections.synchronizedList; +import static java.util.Collections.synchronizedMap; +import javax.swing.table.AbstractTableModel; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public final class ParameterListTableModel extends AbstractTableModel { + private static final long serialVersionUID = -2556400867696538881L; + private final String[] columnNames; + private final List registeredLoggerData = synchronizedList(new LinkedList()); + private final Map paramRowMap = synchronizedMap(new LinkedHashMap()); + private final DataRegistrationBroker broker; + + public ParameterListTableModel(DataRegistrationBroker broker, String dataType) { + this.broker = broker; + columnNames = new String[]{"Selected?", dataType, "Units"}; + } + + public synchronized int getRowCount() { + return paramRowMap.size(); + } + + public int getColumnCount() { + return columnNames.length; + } + + public String getColumnName(int col) { + return columnNames[col]; + } + + public boolean isCellEditable(int row, int col) { + return col == 0 || col == 2; + } + + public synchronized Object getValueAt(int row, int col) { + ParameterRow paramRow = paramRowMap.get(registeredLoggerData.get(row)); + switch (col) { + case 0: + return paramRow.isSelected(); + case 1: + return paramRow.getLoggerData().getName(); + case 2: + LoggerData loggerData = paramRow.getLoggerData(); + return loggerData.getConvertors().length > 1 ? loggerData : loggerData.getSelectedConvertor().getUnits(); + default: + return "Error!"; + } + } + + public synchronized void setValueAt(Object value, int row, int col) { + ParameterRow paramRow = paramRowMap.get(registeredLoggerData.get(row)); + if (col == 0 && paramRow != null) { + Boolean selected = (Boolean) value; + setSelected(paramRow, selected); + fireTableRowsUpdated(row, row); + } + } + + public Class getColumnClass(int col) { + return getValueAt(0, col).getClass(); + } + + public synchronized void addParam(LoggerData loggerData, boolean selected) { + if (!registeredLoggerData.contains(loggerData)) { + ParameterRow paramRow = new ParameterRow(loggerData); + paramRowMap.put(loggerData, paramRow); + registeredLoggerData.add(loggerData); + setSelected(paramRow, selected); + fireTableDataChanged(); + } + } + + public synchronized void selectParam(LoggerData loggerData, boolean selected) { + if (registeredLoggerData.contains(loggerData)) { + setSelected(paramRowMap.get(loggerData), selected); + fireTableDataChanged(); + } + } + + public synchronized void clear() { + broker.clear(); + paramRowMap.clear(); + registeredLoggerData.clear(); + try { + fireTableDataChanged(); + } + catch (Exception e) { + // Swallow complaints from TableRowSorter when the table is empty + } + } + + public List getParameterRows() { + return new ArrayList(paramRowMap.values()); + } + + private void setSelected(ParameterRow paramRow, boolean selected) { + paramRow.setSelected(selected); + if (selected) broker.registerLoggerDataForLogging(paramRow.getLoggerData()); + else broker.deregisterLoggerDataFromLogging(paramRow.getLoggerData()); + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/paramlist/ParameterRow.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/paramlist/ParameterRow.java new file mode 100644 index 0000000000..de17affd3e --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/paramlist/ParameterRow.java @@ -0,0 +1,46 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.paramlist; + +import com.romraider.logger.ecu.definition.LoggerData; +import static com.romraider.util.ParamChecker.checkNotNull; + +public final class ParameterRow { + private final LoggerData loggerData; + private boolean selected = false; + + public ParameterRow(LoggerData loggerData) { + checkNotNull(loggerData, "loggerData"); + this.loggerData = loggerData; + } + + public LoggerData getLoggerData() { + return loggerData; + } + + public boolean isSelected() { + return selected; + } + + public void setSelected(boolean selected) { + this.selected = selected; + loggerData.setSelected(selected); + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/paramlist/UnitsComboBoxEditor.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/paramlist/UnitsComboBoxEditor.java new file mode 100644 index 0000000000..18d9f25d6c --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/paramlist/UnitsComboBoxEditor.java @@ -0,0 +1,67 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.paramlist; + +import com.romraider.logger.ecu.definition.EcuDataConvertor; +import com.romraider.logger.ecu.definition.LoggerData; + +import javax.swing.AbstractCellEditor; +import javax.swing.JComboBox; +import javax.swing.JTable; +import javax.swing.table.TableCellEditor; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public final class UnitsComboBoxEditor extends AbstractCellEditor implements TableCellEditor, ActionListener { + private static final long serialVersionUID = -3472910399604360821L; + private static final String EDIT_COMMAND = "EDIT"; + private LoggerData currentEcuData; + + public Object getCellEditorValue() { + return currentEcuData.getSelectedConvertor(); + } + + public Component getTableCellEditorComponent(JTable table, Object ecuData, boolean isSelected, int row, int column) { + currentEcuData = (LoggerData) ecuData; + EcuDataConvertor[] convertors = currentEcuData.getConvertors(); + JComboBox comboBox = new JComboBox(); + for (EcuDataConvertor convertor : convertors) { + comboBox.addItem(convertor); + } + comboBox.setSelectedItem(currentEcuData.getSelectedConvertor()); + comboBox.setEditable(false); + comboBox.setEnabled(true); + comboBox.setActionCommand(EDIT_COMMAND); + comboBox.addActionListener(this); + return comboBox; + } + + public void actionPerformed(ActionEvent actionEvent) { + if (EDIT_COMMAND.equals(actionEvent.getActionCommand())) { + Object source = actionEvent.getSource(); + if (source != null && JComboBox.class.isAssignableFrom(source.getClass())) { + JComboBox comboBox = (JComboBox) source; + currentEcuData.selectConvertor((EcuDataConvertor) comboBox.getSelectedItem()); + fireEditingStopped(); + } + } + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/paramlist/UnitsComboBoxRenderer.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/paramlist/UnitsComboBoxRenderer.java new file mode 100644 index 0000000000..b77f778588 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/paramlist/UnitsComboBoxRenderer.java @@ -0,0 +1,44 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.paramlist; + +import com.romraider.logger.ecu.definition.EcuDataConvertor; +import com.romraider.logger.ecu.definition.LoggerData; + +import javax.swing.JComboBox; +import javax.swing.JTable; +import javax.swing.table.TableCellRenderer; +import java.awt.Component; + +public final class UnitsComboBoxRenderer extends JComboBox implements TableCellRenderer { + + private static final long serialVersionUID = -6288079743431509778L; + + public Component getTableCellRendererComponent(JTable table, Object ecuData, boolean isSelected, boolean hasFocus, int row, int column) { + LoggerData currentEcuData = (LoggerData) ecuData; + EcuDataConvertor[] convertors = currentEcuData.getConvertors(); + JComboBox comboBox = new JComboBox(); + for (EcuDataConvertor convertor : convertors) { + comboBox.addItem(convertor); + } + comboBox.setSelectedItem(currentEcuData.getSelectedConvertor()); + return comboBox; + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/playback/PlaybackManager.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/playback/PlaybackManager.java new file mode 100644 index 0000000000..f1f84ed17e --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/playback/PlaybackManager.java @@ -0,0 +1,38 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.playback; + +import java.io.File; + +public interface PlaybackManager { + void load(File file); + + void play(); + + void play(int speed); + + void step(int increment); + + void pause(); + + void stop(); + + void reset(); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/playback/PlaybackManagerImpl.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/playback/PlaybackManagerImpl.java new file mode 100644 index 0000000000..8f5a2b2e06 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/playback/PlaybackManagerImpl.java @@ -0,0 +1,83 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.playback; + +import com.romraider.logger.ecu.comms.query.Response; +import com.romraider.logger.ecu.comms.query.ResponseImpl; +import com.romraider.logger.ecu.definition.LoggerData; +import com.romraider.logger.ecu.ui.handler.DataUpdateHandler; +import static com.romraider.util.ThreadUtil.sleep; +import java.io.File; +import java.util.List; + +//TODO: Finish me. +public final class PlaybackManagerImpl implements PlaybackManager { + private final List loggerDatas; + private final DataUpdateHandler[] dataUpdateHandlers; + + public PlaybackManagerImpl(List loggerDatas, DataUpdateHandler... dataUpdateHandlers) { + this.loggerDatas = loggerDatas; + this.dataUpdateHandlers = dataUpdateHandlers; + } + + public void load(File file) { + // TODO: Finish me! + for (DataUpdateHandler handler : dataUpdateHandlers) { + handler.registerData(loggerDatas.get(10)); + handler.registerData(loggerDatas.get(20)); + handler.registerData(loggerDatas.get(30)); + } + } + + public void play() { + double d = 0.0; + while (true) { + for (DataUpdateHandler handler : dataUpdateHandlers) { + Response response = new ResponseImpl(); + response.setDataValue(loggerDatas.get(10), d); + response.setDataValue(loggerDatas.get(20), d); + response.setDataValue(loggerDatas.get(30), d); + handler.handleDataUpdate(response); + d += 100.0; + } + sleep(100L); + } + } + + public void play(int speed) { + throw new UnsupportedOperationException(); + } + + public void step(int increment) { + throw new UnsupportedOperationException(); + } + + public void pause() { + throw new UnsupportedOperationException(); + } + + public void stop() { + throw new UnsupportedOperationException(); + } + + public void reset() { + throw new UnsupportedOperationException(); + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/layout/BetterFlowLayout.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/layout/BetterFlowLayout.java new file mode 100644 index 0000000000..12ac7ab87b --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/layout/BetterFlowLayout.java @@ -0,0 +1,79 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.swing.layout; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Insets; + +public final class BetterFlowLayout extends FlowLayout { + private static final long serialVersionUID = -6784712723817241270L; + + public BetterFlowLayout() { + super(); + } + + public BetterFlowLayout(int align) { + super(align); + } + + public BetterFlowLayout(int align, int hgap, int vgap) { + super(align, hgap, vgap); + } + + @Override + public Dimension preferredLayoutSize(Container target) { + return betterPreferredSize(target); + } + + @Override + public Dimension minimumLayoutSize(Container target) { + return betterPreferredSize(target); + } + + public Dimension betterPreferredSize(Container target) { + synchronized (target.getTreeLock()) { + Insets insets = target.getInsets(); + int maxwidth = target.getWidth() - (insets.left + insets.right + getHgap() * 2); + int nmembers = target.getComponentCount(); + int x = 0, y = insets.top + getVgap(); + int rowh = 0; + for (int i = 0; i < nmembers; i++) { + Component m = target.getComponent(i); + if (m.isVisible()) { + Dimension d = m.getPreferredSize(); + m.setSize(d.width, d.height); + if ((x == 0) || ((x + d.width) <= maxwidth)) { + if (x > 0) x += getHgap(); + x += d.width; + rowh = Math.max(rowh, d.height); + } else { + x = d.width; + y += getVgap() + rowh; + rowh = d.height; + } + } + } + return new Dimension(maxwidth, y + rowh + getVgap()); + } + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/menubar/util/FileHelper.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/menubar/util/FileHelper.java new file mode 100644 index 0000000000..8ae0f2c552 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/menubar/util/FileHelper.java @@ -0,0 +1,91 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.swing.menubar.util; + +import com.romraider.logger.ecu.profile.UserProfile; +import com.romraider.swing.GenericFileFilter; +import static com.romraider.util.ParamChecker.isNullOrEmpty; +import static javax.swing.JFileChooser.DIRECTORIES_ONLY; +import javax.swing.JFileChooser; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +public final class FileHelper { + private static final String USER_HOME_DIR = System.getProperty("user.home"); + + private FileHelper() { + throw new UnsupportedOperationException(); + } + + public static File getFile(String filePath) { + return isNullOrEmpty(filePath) ? new File(USER_HOME_DIR) : new File(filePath); + } + + public static JFileChooser getProfileFileChooser(File lastProfileFile) { + return getFileChooser(lastProfileFile, "ECU Logger User Profiles", "xml"); + } + + public static JFileChooser getDefinitionFileChooser(File lastDefFile) { + return getFileChooser(lastDefFile, "ECU Logger Definitions", "xml"); + } + + public static String saveProfileToFile(UserProfile profile, File destinationFile) throws IOException { + String profileFilePath = destinationFile.getAbsolutePath(); + if (!profileFilePath.endsWith(".xml")) { + profileFilePath += ".xml"; + destinationFile = new File(profileFilePath); + } + FileOutputStream fos = new FileOutputStream(destinationFile); + try { + fos.write(profile.getBytes()); + } finally { + fos.close(); + } + return profileFilePath; + } + + public static JFileChooser getLoggerOutputDirFileChooser(File lastLoggerOutputDir) { + JFileChooser fc; + if (lastLoggerOutputDir.exists() && lastLoggerOutputDir.isDirectory()) { + fc = new JFileChooser(lastLoggerOutputDir.getAbsolutePath()); + } else { + fc = new JFileChooser(); + } + fc.setFileSelectionMode(DIRECTORIES_ONLY); + return fc; + } + + private static JFileChooser getFileChooser(File file, String description, String... extensions) { + JFileChooser fc = getFileChooser(file); + fc.setFileFilter(new GenericFileFilter(description, extensions)); + return fc; + } + + private static JFileChooser getFileChooser(File file) { + if (file.exists() && file.isFile() && file.getParentFile() != null) { + String dir = file.getParentFile().getAbsolutePath(); + JFileChooser fc = new JFileChooser(dir); + fc.setSelectedFile(file); + return fc; + } + return new JFileChooser(); + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/AirFuelLearningTableModel.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/AirFuelLearningTableModel.java new file mode 100644 index 0000000000..c3ac2f59ff --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/AirFuelLearningTableModel.java @@ -0,0 +1,79 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.swing.tools.tablemodels; + +import java.util.List; + +import javax.swing.table.DefaultTableModel; + +/** + * Air/Fuel Learning Table Model for Learning Table Values display. Note + * that column 0 is used as a row header. + */ +public final class AirFuelLearningTableModel extends DefaultTableModel { + private static final long serialVersionUID = 8643509881981835534L; + private List> afLearning; + private String[] columnNames = new String[]{" ","A","B","C","D"}; + + @Override + public final int getColumnCount() { + return columnNames.length; + } + + @Override + public final String getColumnName(int column) { + return columnNames[column]; + } + + @Override + public final Object getValueAt(int row, int column) { + if (afLearning != null) { + return afLearning.get(row).get(column); + } + else { + return null; + } + } + + @Override + public final int getRowCount() { + return (afLearning != null) ? afLearning.size() : 0; + } + + @Override + public final Class getColumnClass(int column) { + return getValueAt(0, column).getClass(); + } + + @Override + public final boolean isCellEditable(int row, int column) { + return false; + } + + public final void setColumnHeadings(String[] afRanges) { + if (afRanges.length > 0) { + this.columnNames = afRanges; + } + } + + public final void setAfLearningInfo(List> afLearning) { + this.afLearning = afLearning; + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/FineLearningKnockCorrectionTableModel.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/FineLearningKnockCorrectionTableModel.java new file mode 100644 index 0000000000..ed32799f3c --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/FineLearningKnockCorrectionTableModel.java @@ -0,0 +1,92 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.swing.tools.tablemodels; + +import java.util.List; + +import javax.swing.table.DefaultTableModel; + +import com.romraider.logger.ecu.comms.query.EcuQuery; + +/** + * Fine Learning Knock Correction Table Model for Learning Table Values display. + * Note that column 0 is used as a row header + */ +public final class FineLearningKnockCorrectionTableModel extends DefaultTableModel { + private static final long serialVersionUID = -7369506358159217982L; + private List> flkc; + private String[] columnNames = new String[]{" ","A","B","C","D","E"}; + private String[] rowNames = new String[]{"1","2","3","4","5","6","7","8"}; + + @Override + public final int getColumnCount() { + return columnNames.length; + } + + @Override + public final String getColumnName(int column) { + return columnNames[column]; + } + + /** + * Column 0 is used as a row header + */ + @Override + public final Object getValueAt(int row, int column) { + Object result = null; + if (flkc != null) { + result = (column == 0) + ? result = rowNames[row] + : flkc.get(row).get(column).getResponse(); + } + return result; + } + + @Override + public final int getRowCount() { + return (flkc != null) ? flkc.size() : 0; + } + + @Override + public final Class getColumnClass(int column) { + return getValueAt(0, column).getClass(); + } + + @Override + public final boolean isCellEditable(int row, int column) { + return false; + } + + public final void setColumnHeadings(String[] loadRanges) { + if (loadRanges.length > 0) { + this.columnNames = loadRanges; + } + } + + public final void setRomHeadings(String[] rpmRanges) { + if (rpmRanges.length > 0) { + this.rowNames = rpmRanges; + } + } + + public final void setFlkcData(List> flkc) { + this.flkc = flkc; + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/ReadCodesTableModel.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/ReadCodesTableModel.java new file mode 100644 index 0000000000..14018a1445 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/ReadCodesTableModel.java @@ -0,0 +1,83 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.swing.tools.tablemodels; + +import java.util.ArrayList; + +import javax.swing.table.DefaultTableModel; + +import com.romraider.logger.ecu.comms.query.EcuQuery; + +public final class ReadCodesTableModel extends DefaultTableModel { + private static final long serialVersionUID = -4229633011594395331L; + private ArrayList dtcSet; + + public final int getColumnCount() { + return 3; + } + + public final String getColumnName(int column) { + switch (column) { + case 0: return "Diagnostic Code Name"; + case 1: return "Temporary"; + case 2: return "Memorized"; + default: return ""; + } + } + + public final Object getValueAt(int row, int column) { + if (dtcSet != null) { + final double result = dtcSet.get(row).getResponse(); + switch (column) { + case 0: + return " " + dtcSet.get(row).getLoggerData().getName(); + case 1: + return (result == 1 || result == 3) + ? new Boolean(true) + : new Boolean(false); + case 2: + return (result == 2 || result == 3) + ? new Boolean(true) + : new Boolean(false); + default: + return null; + } + } + else { + return null; + } + } + + public final int getRowCount() { + return (dtcSet != null) ? dtcSet.size() : 0; + } + + public final Class getColumnClass(int column) { + return getValueAt(0, column).getClass(); + } + + public final boolean isCellEditable(int row, int column) { + return false; + } + + public final void setDtcList(ArrayList dtcSet) { + this.dtcSet = dtcSet; + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/VehicleInformationTableModel.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/VehicleInformationTableModel.java new file mode 100644 index 0000000000..328607831d --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/VehicleInformationTableModel.java @@ -0,0 +1,68 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.swing.tools.tablemodels; + +import java.util.Map; + +import javax.swing.table.DefaultTableModel; + +public final class VehicleInformationTableModel extends DefaultTableModel { + private static final long serialVersionUID = 9214968582253321667L; + private Map vehicleInfo; + + @Override + public final int getColumnCount() { + return vehicleInfo.keySet().size(); + } + + @Override + public final String getColumnName(int column) { + return (String) vehicleInfo.keySet().toArray()[column]; + } + + @Override + public final Object getValueAt(int row, int column) { + if (vehicleInfo != null) { + return vehicleInfo.get(getColumnName(column)); + } + else { + return null; + } + } + + @Override + public final int getRowCount() { + return (vehicleInfo != null) ? 1 : 0; + } + + @Override + public final Class getColumnClass(int column) { + return getValueAt(0, column).getClass(); + } + + @Override + public final boolean isCellEditable(int row, int column) { + return false; + } + + public final void setVehicleInfo(Map vehicleInfo) { + this.vehicleInfo = vehicleInfo; + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/renderers/CentreRenderer.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/renderers/CentreRenderer.java new file mode 100644 index 0000000000..56dcf58f6e --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/renderers/CentreRenderer.java @@ -0,0 +1,50 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.swing.tools.tablemodels.renderers; + +import java.awt.Component; + +import javax.swing.JLabel; +import javax.swing.JTable; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableCellRenderer; + +public class CentreRenderer implements TableCellRenderer { + private DefaultTableCellRenderer renderer; + + /** + * This class is used to centre align text for the given table. + * @param table - table to have centre aligned values + */ + public CentreRenderer(JTable table) { + renderer = (DefaultTableCellRenderer) + table.getTableHeader().getDefaultRenderer(); + renderer.setHorizontalAlignment(JLabel.CENTER); + } + + @Override + public Component getTableCellRendererComponent( + JTable table, Object value, boolean isSelected, + boolean hasFocus, int row, int col) { + + return renderer.getTableCellRendererComponent( + table, value, isSelected, hasFocus, row, col); + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/renderers/LtvCellRenderer.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/renderers/LtvCellRenderer.java new file mode 100644 index 0000000000..038d8dadb2 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/tools/tablemodels/renderers/LtvCellRenderer.java @@ -0,0 +1,89 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2014 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.swing.tools.tablemodels.renderers; + +import java.awt.Color; +import java.awt.Component; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.text.ParseException; + +import javax.swing.JLabel; +import javax.swing.JTable; +import javax.swing.table.DefaultTableCellRenderer; + +/** + * This class is used to set the Default Table Cell Renderer for String and + * Double object types. The cell will be centred and text coloured according + * to its value above and below zero. + */ + +public class LtvCellRenderer extends DefaultTableCellRenderer { + private static final long serialVersionUID = -171010635742251272L; + private static final DecimalFormat numberFormat = + new DecimalFormat("0.00;-0.00"); + + @Override + public Component getTableCellRendererComponent( + JTable table, Object value, boolean isSelected, boolean hasFocus, + int row, int column) { + + final Component c = super.getTableCellRendererComponent( + table, value, isSelected, hasFocus, row, column); + + if (c instanceof JLabel) { + final JLabel cell = (JLabel) c; + cell.setHorizontalAlignment(JLabel.CENTER); + cell.setForeground(Color.BLACK); + + if (column > 0) { + if (value instanceof Double) { + cell.setText(numberFormat.format(value)); + setColour(cell, ((Double) value).doubleValue()); + + } + if (value instanceof String) { + final String data = (String) value; + if (data.contains("%")) { + final String numText = data.split(" ")[0]; + double num; + try { + num = NumberFormat.getNumberInstance() + .parse(numText).doubleValue(); + setColour(cell, num); + } catch (ParseException e) { + throw new IllegalArgumentException(e); + } + } + } + } + } + return c; + } + + private final void setColour(JLabel cell, double value) { + if (value < 0) { + cell.setForeground(Color.RED); + } + else if (value > 0) { + cell.setForeground(Color.BLUE); + } + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/vertical/VerticalLabelUI.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/vertical/VerticalLabelUI.java new file mode 100644 index 0000000000..3438aa915b --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/vertical/VerticalLabelUI.java @@ -0,0 +1,99 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.swing.vertical; + +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.plaf.basic.BasicLabelUI; +import java.awt.Dimension; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; + +public final class VerticalLabelUI extends BasicLabelUI { + private static Rectangle PAINT_ICON_R = new Rectangle(); + private static Rectangle PAINT_TEXT_R = new Rectangle(); + private static Rectangle PAINT_VIEW_R = new Rectangle(); + private static Insets PAINT_VIEW_INSETS = new Insets(0, 0, 0, 0); + protected boolean clockwise; + + static { + labelUI = new VerticalLabelUI(false); + } + + public VerticalLabelUI(boolean clockwise) { + super(); + this.clockwise = clockwise; + } + + public Dimension getPreferredSize(JComponent c) { + Dimension dim = super.getPreferredSize(c); + return new Dimension(dim.height, dim.width); + } + + public void paint(Graphics graphics, JComponent component) { + JLabel label = (JLabel) component; + String text = label.getText(); + Icon icon = (label.isEnabled()) ? label.getIcon() : label.getDisabledIcon(); + + if ((icon == null) && (text == null)) return; + + FontMetrics fm = graphics.getFontMetrics(); + PAINT_VIEW_INSETS = component.getInsets(PAINT_VIEW_INSETS); + PAINT_VIEW_R.x = PAINT_VIEW_INSETS.left; + PAINT_VIEW_R.y = PAINT_VIEW_INSETS.top; + + // Use inverted height & width + PAINT_VIEW_R.height = component.getWidth() - (PAINT_VIEW_INSETS.left + PAINT_VIEW_INSETS.right); + PAINT_VIEW_R.width = component.getHeight() - (PAINT_VIEW_INSETS.top + PAINT_VIEW_INSETS.bottom); + + PAINT_ICON_R.x = PAINT_ICON_R.y = PAINT_ICON_R.width = PAINT_ICON_R.height = 0; + PAINT_TEXT_R.x = PAINT_TEXT_R.y = PAINT_TEXT_R.width = PAINT_TEXT_R.height = 0; + + String clippedText = layoutCL(label, fm, text, icon, PAINT_VIEW_R, PAINT_ICON_R, PAINT_TEXT_R); + int textWidth = fm.stringWidth(clippedText); + + Graphics2D g2 = (Graphics2D) graphics; + AffineTransform tr = g2.getTransform(); + if (clockwise) { + g2.rotate(Math.PI / 2); + g2.translate(component.getHeight() / 2 - textWidth / 2, -component.getWidth()); + } else { + g2.rotate(-Math.PI / 2); + g2.translate(-component.getHeight() / 2 - textWidth / 2, 0); + } + + if (icon != null) icon.paintIcon(component, graphics, PAINT_ICON_R.x, PAINT_ICON_R.y); + + if (text != null) { + int textX = PAINT_TEXT_R.x; + int textY = PAINT_TEXT_R.y + fm.getAscent(); + + if (label.isEnabled()) paintEnabledText(label, graphics, clippedText, textX, textY); + else paintDisabledText(label, graphics, clippedText, textX, textY); + } + + g2.setTransform(tr); + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/vertical/VerticalTextIcon.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/vertical/VerticalTextIcon.java new file mode 100644 index 0000000000..ccd693b039 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/swing/vertical/VerticalTextIcon.java @@ -0,0 +1,316 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.swing.vertical; + +import javax.swing.Icon; +import java.awt.Component; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +/** + * VTextIcon is an Icon implementation which draws a short string vertically. + * It's useful for JTabbedPanes with LEFT or RIGHT tabs but can be used in any + * component which supports Icons, such as JLabel or JButton + * You can provide a hint to indicate whether to rotate the string + * to the left or right, or not at all, and it checks to make sure + * that the rotation is legal for the given string + * (for example, Chinese/Japanese/Korean scripts have special rules when + * drawn vertically and should never be rotated) + */ +public final class VerticalTextIcon implements Icon, PropertyChangeListener { + String fLabel; + String[] fCharStrings; // for efficiency, break the fLabel into one-char strings to be passed to drawString + int[] fCharWidths; // Roman characters should be centered when not rotated (Japanese fonts are monospaced) + int[] fPosition; // Japanese half-height characters need to be shifted when drawn vertically + int fWidth, fHeight, fCharHeight, fDescent; // Cached for speed + int fRotation; + Component fComponent; + + static final int POSITION_NORMAL = 0; + static final int POSITION_TOP_RIGHT = 1; + static final int POSITION_FAR_TOP_RIGHT = 2; + + public static final int ROTATE_DEFAULT = 0x00; + public static final int ROTATE_NONE = 0x01; + public static final int ROTATE_LEFT = 0x02; + public static final int ROTATE_RIGHT = 0x04; + + /** + * Creates a VTextIcon for the specified component + * with the specified label. + * It sets the orientation to the default for the string + * + * @see #verifyRotation + */ + public VerticalTextIcon(Component component, String label) { + this(component, label, ROTATE_DEFAULT); + } + + /** + * Creates a VTextIcon for the specified component + * with the specified label. + * It sets the orientation to the provided value if it's legal for the string + * + * @see #verifyRotation + */ + public VerticalTextIcon(Component component, String label, int rotateHint) { + fComponent = component; + fLabel = label; + fRotation = verifyRotation(label, rotateHint); + calcDimensions(); + fComponent.addPropertyChangeListener(this); + } + + /** + * sets the label to the given string, updating the orientation as needed + * and invalidating the layout if the size changes + * + * @see #verifyRotation + */ + public void setLabel(String label) { + fLabel = label; + fRotation = verifyRotation(label, fRotation); // Make sure the current rotation is still legal + recalcDimensions(); + } + + /** + * Checks for changes to the font on the fComponent + * so that it can invalidate the layout if the size changes + */ + public void propertyChange(PropertyChangeEvent e) { + String prop = e.getPropertyName(); + if ("font".equals(prop)) { + recalcDimensions(); + } + } + + /** + * Calculates the dimensions. If they've changed, + * invalidates the component + */ + void recalcDimensions() { + int wOld = getIconWidth(); + int hOld = getIconHeight(); + calcDimensions(); + if (wOld != getIconWidth() || hOld != getIconHeight()) + fComponent.invalidate(); + } + + void calcDimensions() { + FontMetrics fm = fComponent.getFontMetrics(fComponent.getFont()); + fCharHeight = fm.getAscent() + fm.getDescent(); + fDescent = fm.getDescent(); + if (fRotation == ROTATE_NONE) { + int len = fLabel.length(); + char data[] = new char[len]; + fLabel.getChars(0, len, data, 0); + // if not rotated, width is that of the widest char in the string + fWidth = 0; + // we need an array of one-char strings for drawString + fCharStrings = new String[len]; + fCharWidths = new int[len]; + fPosition = new int[len]; + char ch; + for (int i = 0; i < len; i++) { + ch = data[i]; + fCharWidths[i] = fm.charWidth(ch); + if (fCharWidths[i] > fWidth) + fWidth = fCharWidths[i]; + fCharStrings[i] = new String(data, i, 1); + // small kana and punctuation + if (sDrawsInTopRight.indexOf(ch) >= 0) // if ch is in sDrawsInTopRight + fPosition[i] = POSITION_TOP_RIGHT; + else if (sDrawsInFarTopRight.indexOf(ch) >= 0) + fPosition[i] = POSITION_FAR_TOP_RIGHT; + else + fPosition[i] = POSITION_NORMAL; + } + // and height is the font height * the char count, + one extra leading at the bottom + fHeight = fCharHeight * len + fDescent; + } else { + // if rotated, width is the height of the string + fWidth = fCharHeight; + // and height is the width, plus some buffer space + fHeight = fm.stringWidth(fLabel) + 2 * kBufferSpace; + } + } + + /** + * Draw the icon at the specified location. Icon implementations + * may use the Component argument to get properties useful for + * painting, e.g. the foreground or background color. + */ + public void paintIcon(Component c, Graphics g, int x, int y) { + // We don't insist that it be on the same Component + g.setColor(c.getForeground()); + g.setFont(c.getFont()); + if (fRotation == ROTATE_NONE) { + int yPos = y + fCharHeight; + for (int i = 0; i < fCharStrings.length; i++) { + // Special rules for Japanese - "half-height" characters (like ya, yu, yo in combinations) + // should draw in the top-right quadrant when drawn vertically + // - they draw in the bottom-left normally + int tweak; + switch (fPosition[i]) { + case POSITION_NORMAL: + // Roman fonts should be centered. Japanese fonts are always monospaced. + g.drawString(fCharStrings[i], x + ((fWidth - fCharWidths[i]) / 2), yPos); + break; + case POSITION_TOP_RIGHT: + tweak = fCharHeight / 3; // Should be 2, but they aren't actually half-height + g.drawString(fCharStrings[i], x + (tweak / 2), yPos - tweak); + break; + case POSITION_FAR_TOP_RIGHT: + tweak = fCharHeight - fCharHeight / 3; + g.drawString(fCharStrings[i], x + (tweak / 2), yPos - tweak); + break; + } + yPos += fCharHeight; + } + } else if (fRotation == ROTATE_LEFT) { + g.translate(x + fWidth, y + fHeight); + ((Graphics2D) g).rotate(-NINETY_DEGREES); + g.drawString(fLabel, kBufferSpace, -fDescent); + ((Graphics2D) g).rotate(NINETY_DEGREES); + g.translate(-(x + fWidth), -(y + fHeight)); + } else if (fRotation == ROTATE_RIGHT) { + g.translate(x, y); + ((Graphics2D) g).rotate(NINETY_DEGREES); + g.drawString(fLabel, kBufferSpace, -fDescent); + ((Graphics2D) g).rotate(-NINETY_DEGREES); + g.translate(-x, -y); + } + } + + /** + * Returns the icon's width. + * + * @return an int specifying the fixed width of the icon. + */ + public int getIconWidth() { + return fWidth; + } + + /** + * Returns the icon's height. + * + * @return an int specifying the fixed height of the icon. + */ + public int getIconHeight() { + return fHeight; + } + + /** + * verifyRotation + *

+ * returns the best rotation for the string (ROTATE_NONE, ROTATE_LEFT, ROTATE_RIGHT) + *

+ * This is public static so you can use it to test a string without creating a VTextIcon + *

+ * from http://www.unicode.org/unicode/reports/tr9/tr9-3.html + * When setting text using the Arabic script in vertical lines, + * it is more common to employ a horizontal baseline that + * is rotated by 90� counterclockwise so that the characters + * are ordered from top to bottom. Latin text and numbers + * may be rotated 90� clockwise so that the characters + * are also ordered from top to bottom. + *

+ * Rotation rules + * - Roman can rotate left, right, or none - default right (counterclockwise) + * - CJK can't rotate + * - Arabic must rotate - default left (clockwise) + *

+ * from the online edition of _The Unicode Standard, Version 3.0_, file ch10.pdf page 4 + * Ideographs are found in three blocks of the Unicode Standard... + * U+4E00-U+9FFF, U+3400-U+4DFF, U+F900-U+FAFF + *

+ * Hiragana is U+3040-U+309F, katakana is U+30A0-U+30FF + *

+ * from http://www.unicode.org/unicode/faq/writingdirections.html + * East Asian scripts are frequently written in vertical lines + * which run from top-to-bottom and are arrange columns either + * from left-to-right (Mongolian) or right-to-left (other scripts). + * Most characters use the same shape and orientation when displayed + * horizontally or vertically, but many punctuation characters + * will change their shape when displayed vertically. + *

+ * Letters and words from other scripts are generally rotated through + * ninety degree angles so that they, too, will read from top to bottom. + * That is, letters from left-to-right scripts will be rotated clockwise + * and letters from right-to-left scripts counterclockwise, both + * through ninety degree angles. + *

+ * Unlike the bidirectional case, the choice of vertical layout + * is usually treated as a formatting style; therefore, + * the Unicode Standard does not define default rendering behavior + * for vertical text nor provide directionality controls designed to override such behavior + */ + public static int verifyRotation(String label, int rotateHint) { + boolean hasCJK = false; + boolean hasMustRotate = false; // Arabic, etc + + int len = label.length(); + char data[] = new char[len]; + char ch; + label.getChars(0, len, data, 0); + for (int i = 0; i < len; i++) { + ch = data[i]; + if ((ch >= '\u4E00' && ch <= '\u9FFF') || + (ch >= '\u3400' && ch <= '\u4DFF') || + (ch >= '\uF900' && ch <= '\uFAFF') || + (ch >= '\u3040' && ch <= '\u309F') || + (ch >= '\u30A0' && ch <= '\u30FF')) + hasCJK = true; + if ((ch >= '\u0590' && ch <= '\u05FF') || // Hebrew + (ch >= '\u0600' && ch <= '\u06FF') || // Arabic + (ch >= '\u0700' && ch <= '\u074F')) // Syriac + hasMustRotate = true; + } + // If you mix Arabic with Chinese, you're on your own + if (hasCJK) + return DEFAULT_CJK; + + int legal = hasMustRotate ? LEGAL_MUST_ROTATE : LEGAL_ROMAN; + if ((rotateHint & legal) > 0) + return rotateHint; + + // The hint wasn't legal, or it was zero + return hasMustRotate ? DEFAULT_MUST_ROTATE : DEFAULT_ROMAN; + } + + // The small kana characters and Japanese punctuation that draw in the top right quadrant: + // small a, i, u, e, o, tsu, ya, yu, yo, wa (katakana only) ka ke + static final String sDrawsInTopRight = + "\u3041\u3043\u3045\u3047\u3049\u3063\u3083\u3085\u3087\u308E" + // hiragana + "\u30A1\u30A3\u30A5\u30A7\u30A9\u30C3\u30E3\u30E5\u30E7\u30EE\u30F5\u30F6"; // katakana + static final String sDrawsInFarTopRight = "\u3001\u3002"; // comma, full stop + + static final int DEFAULT_CJK = ROTATE_NONE; + static final int LEGAL_ROMAN = ROTATE_NONE | ROTATE_LEFT | ROTATE_RIGHT; + static final int DEFAULT_ROMAN = ROTATE_RIGHT; + static final int LEGAL_MUST_ROTATE = ROTATE_LEFT | ROTATE_RIGHT; + static final int DEFAULT_MUST_ROTATE = ROTATE_LEFT; + + static final double NINETY_DEGREES = Math.toRadians(90.0); + static final int kBufferSpace = 5; +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/tab/Tab.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/tab/Tab.java new file mode 100644 index 0000000000..577650ed1c --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/tab/Tab.java @@ -0,0 +1,46 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.tab; + +import com.romraider.logger.ecu.definition.EcuParameter; +import com.romraider.logger.ecu.definition.EcuSwitch; +import com.romraider.logger.ecu.definition.ExternalData; +import javax.swing.JPanel; +import java.util.List; + +/** + * Interface for Logger tabs that have the following: + *

+ * Control panel where logging information and constraints are set + * ChartPanel where graph of data is displayed + */ +public interface Tab { + JPanel getPanel(); + + boolean isRecordData(); + + void addData(double xData, double yData); + + void setEcuParams(List params); + + void setEcuSwitches(List switches); + + void setExternalDatas(List external); +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/tab/TableFinder.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/tab/TableFinder.java new file mode 100644 index 0000000000..96467f4549 --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/tab/TableFinder.java @@ -0,0 +1,33 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.tab; + +import com.romraider.maps.Rom; +import com.romraider.maps.Table; +import java.util.List; + +public final class TableFinder { + public static Table findTableStartsWith(Rom rom, String name) { + List

tables = rom.findTables("^" + name + ".*$"); + if (tables.isEmpty()) throw new IllegalStateException("No table found for name: \"" + name + "\""); + if (tables.size() > 1) throw new IllegalStateException("Multiple tables found for name: \"" + name + "\""); + return tables.get(0); + } +} diff --git a/java_console/romraider/src/com/romraider/logger/ecu/ui/tab/injector/InjectorTab.java b/java_console/romraider/src/com/romraider/logger/ecu/ui/tab/injector/InjectorTab.java new file mode 100644 index 0000000000..4d6c898a1e --- /dev/null +++ b/java_console/romraider/src/com/romraider/logger/ecu/ui/tab/injector/InjectorTab.java @@ -0,0 +1,46 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.logger.ecu.ui.tab.injector; + +import com.romraider.logger.ecu.ui.tab.Tab; + +public interface InjectorTab extends Tab { + + double getFuelStoichAfr(); + + double getFuelDensity(); + + boolean isValidClOl(double value); + + boolean isValidAfr(double value); + + boolean isValidRpm(double value); + + boolean isValidMaf(double value); + + boolean isValidMafvChange(double value); + + boolean isValidCoolantTemp(double value); + + boolean isValidIntakeAirTemp(double value); + + boolean isValidTipInThrottle(double value); + +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/maps/DataCell.java b/java_console/romraider/src/com/romraider/maps/DataCell.java new file mode 100644 index 0000000000..42ebb2d011 --- /dev/null +++ b/java_console/romraider/src/com/romraider/maps/DataCell.java @@ -0,0 +1,535 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.maps; + +import static com.romraider.util.ColorScaler.getScaledColor; +import static com.romraider.util.ParamChecker.isNullOrEmpty; +import static javax.swing.BorderFactory.createLineBorder; + +import java.awt.Color; +import java.awt.Font; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.io.Serializable; +import java.text.DecimalFormat; + +import javax.swing.JLabel; +import javax.swing.border.Border; + +import org.apache.log4j.Logger; + +import com.romraider.Settings; +import com.romraider.editor.ecu.ECUEditorManager; +import com.romraider.util.JEPUtil; +import com.romraider.util.SettingsManager; + +public class DataCell extends JLabel implements MouseListener, Serializable { + private static final long serialVersionUID = -2904293227148940937L; + private static final Logger LOGGER = Logger.getLogger(DataCell.class); + private final DecimalFormat PERCENT_FORMAT = new DecimalFormat("#,##0.0%"); + private final Font defaultFont = new Font("Arial", Font.BOLD, 12); + int unSelectMask1 = MouseEvent.BUTTON1_DOWN_MASK + MouseEvent.CTRL_DOWN_MASK + MouseEvent.ALT_DOWN_MASK; + int unSelectMask2 = MouseEvent.BUTTON3_DOWN_MASK + MouseEvent.CTRL_DOWN_MASK + MouseEvent.ALT_DOWN_MASK; + + private final Table table; + + private Boolean selected = false; + private Boolean highlighted = false; + private Boolean traced = false; + + private int x = 0; + private int y = 0; + + private double binValue = 0.0; + private double originalValue = 0.0; + private double compareToValue = 0.0; + private String liveValue = Settings.BLANK; + + private final Color defaultBorderColor = new Color(0, 0, 0); + private final Color increaseBorderColor = getSettings().getIncreaseBorder(); + private final Color decreaseBorderColor = getSettings().getDecreaseBorder(); + + private String staticText = null; + + public DataCell(Table table) { + this.table = table; + this.setHorizontalAlignment(CENTER); + this.setVerticalAlignment(CENTER); + this.setFont(defaultFont); + this.setOpaque(true); + this.setVisible(true); + this.addMouseListener(this); + } + + public DataCell(Table table, String staticText) { + this(table); + this.staticText = staticText; + table.setStaticDataTable(true); + } + + public DataCell(Table table, double originalValue, int x, int y) { + this(table); + this.originalValue = originalValue; + this.binValue = originalValue; + this.x = x; + this.y = y; + this.setPreferredSize(getSettings().getCellSize()); + } + + public double getBinValue() { + return binValue; + } + + public double getRealValue() { + return JEPUtil.evaluate(table.getCurrentScale().getExpression(), binValue); + } + + public void setRealValue(String input) { + // create parser + try { + double result = 0.0; + if (!"x".equalsIgnoreCase(input)) { + result = JEPUtil.evaluate(table.getCurrentScale().getByteExpression(), Double.parseDouble(input)); + if (table.getStorageType() != Settings.STORAGE_TYPE_FLOAT) { + result = (int) Math.round(result); + } + + if(binValue != result) { + this.setBinValue(result); + } + } + } catch (NumberFormatException e) { + // Do nothing. input is null or not a valid number. + } + } + + public double getCompareValue() { + return binValue - compareToValue; + } + + public double getRealCompareValue() { + return JEPUtil.evaluate(table.getCurrentScale().getExpression(), binValue) - JEPUtil.evaluate(table.getCurrentScale().getExpression(), compareToValue); + } + + public double getRealCompareChangeValue() { + double realBinValue = JEPUtil.evaluate(table.getCurrentScale().getExpression(), binValue); + double realCompareValue = JEPUtil.evaluate(table.getCurrentScale().getExpression(), compareToValue); + + if(realCompareValue != 0.0) { + // Compare change formula ((V2 - V1) / |V1|). + return ((realBinValue - realCompareValue) / Math.abs(realCompareValue)); + } else { + // Use this to avoid divide by 0 or infinite increase. + return realBinValue - realCompareValue; + } + } + + public Color getCompareColor() { + if(table instanceof Table1D) { + Table1D checkTable = (Table1D)table; + if(checkTable.isAxis() && !getSettings().isColorAxis()) { + return getSettings().getAxisColor(); + } + } + + double compareScale; + if (0.0 == getCompareValue()) { + return Settings.UNCHANGED_VALUE_COLOR; + }else if(table.getMinCompare() == table.getMaxCompare()) { + return getSettings().getMaxColor(); + } else { + compareScale = (getCompareValue() - table.getMinCompare()) / (table.getMaxCompare() - table.getMinCompare()); + } + return getScaledColor(compareScale); + } + + public Color getBinColor() { + if(table instanceof Table1D) { + Table1D checkTable = (Table1D)table; + if(checkTable.isAxis() && !getSettings().isColorAxis()) { + return getSettings().getAxisColor(); + } + } + + if (table.getMaxAllowedBin() < getBinValue()) { + return getSettings().getWarningColor(); + } else if (table.getMinAllowedBin() > getBinValue()) { + return getSettings().getWarningColor(); + } else { + // limits not set, scale based on table values + double colorScale; + if (table.getMaxBin() - table.getMinBin() == 0.0) { + // if all values are the same, color will be middle value + colorScale = .5; + } else { + colorScale = (getBinValue() - table.getMinBin()) / (table.getMaxBin() - table.getMinBin()); + } + + return getScaledColor(colorScale); + } + } + + public void drawCell() { + if(table == null) { + // Table will be null in the static case. + return; + } + + this.invalidate(); + setFont(getSettings().getTableFont()); + setText(getCellText()); + setToolTipText(getCellToolTip()); + setBackground(getCellBackgroundColor()); + setForeground(getCellTextColor()); + setBorder(getCellBorder()); + this.validate(); + table.validate(); + table.repaint(); + } + + private Color getCellBackgroundColor() { + Settings settings = getSettings(); + Color backgroundColor; + + if(highlighted) { + backgroundColor = settings.getHighlightColor(); + } else if(selected) { + backgroundColor = settings.getSelectColor(); + } else if(null == table.getCompareTable()) { + backgroundColor = getBinColor(); + }else { + backgroundColor = getCompareColor(); + } + + return backgroundColor; + } + + private Color getCellTextColor() { + Color textColor; + + if(traced) { + if(!getLiveValue().isEmpty()) { + if(table instanceof Table1D) { + textColor = Settings.scaleTextColor; + } else { + textColor = Settings.liveDataTraceTextColor; + } + } else { + textColor = Settings.scaleTextColor; + } + } else if (highlighted) { + textColor = Settings.highlightTextColor; + } else if (selected) { + textColor = Settings.selectTextColor; + } else { + textColor = Settings.scaleTextColor; + } + + return textColor; + } + + private Border getCellBorder() { + Border border; + if(traced) { + border = createLineBorder(getSettings().getliveValueColor(), 2); + } else { + double checkValue; + + if(null == table.getCompareTable()) { + checkValue= originalValue; + } else { + checkValue = compareToValue; + } + + if (checkValue < binValue) { + border = createLineBorder(increaseBorderColor, 2); + } else if (checkValue > binValue) { + border = createLineBorder(decreaseBorderColor, 2); + } else { + border = createLineBorder(defaultBorderColor, 1); + } + } + + return border; + } + + private String getCellText() { + if(table.isStaticDataTable()) { + return getStaticText(); + } + + DecimalFormat formatter = new DecimalFormat(table.getCurrentScale().getFormat()); + String displayString = ""; + + if (null == table.getCompareTable()) { + displayString = formatter.format(getRealValue()); + } else if (table.getCompareDisplay() == Settings.COMPARE_DISPLAY_ABSOLUTE) { + displayString = formatter.format(getRealCompareValue()); + } else if (table.getCompareDisplay() == Settings.COMPARE_DISPLAY_PERCENT) { + if (getCompareValue() == 0.0) { + displayString = PERCENT_FORMAT.format(0.0); + } else { + displayString = PERCENT_FORMAT.format(getRealCompareChangeValue()); + } + } + + if(traced) { + if(!(table instanceof Table1D)) { + displayString = getLiveValueString(displayString); + } + } + return displayString; + } + + private String getCellToolTip() { + if(table.isStaticDataTable()) { + return getStaticText(); + } + + return Double.toString(getRealValue()); + } + + private String getLiveValue() { + return this.liveValue; + } + + private String getLiveValueString(String currentValue) { + return currentValue + (isNullOrEmpty(getLiveValue()) ? Settings.BLANK : (':' + getLiveValue())); + } + + public void setBinValue(double newBinValue) { + if(binValue == newBinValue) { + return; + } + + double checkedValue = newBinValue; + + // make sure it's in range + if(checkedValue < table.getMinAllowedBin()) { + checkedValue = table.getMinAllowedBin(); + } + + if(checkedValue > table.getMaxAllowedBin()) { + checkedValue = table.getMaxAllowedBin(); + } + + if(binValue == checkedValue) { + return; + } + + // set bin. + binValue = checkedValue; + drawCell(); + } + + @Override + public String toString() { + return getCellText(); + } + + public Boolean isSelected() { + return selected; + } + + public void setSelected(Boolean selected) { + if(!table.isStaticDataTable() && this.selected != selected) { + this.selected = selected; + drawCell(); + } + } + + public void setHighlighted(Boolean highlighted) { + if(!table.isStaticDataTable() && this.highlighted != highlighted) { + this.highlighted = highlighted; + drawCell(); + } + } + + public boolean isHighlighted() { + return highlighted; + } + + @Override + public void mouseEntered(MouseEvent e) { + if(unSelectMask1 == (e.getModifiersEx() & unSelectMask1)) { + clearCell(); + } else if(unSelectMask2 == (e.getModifiersEx() & unSelectMask2)) { + clearCell(); + } else { + table.highlight(x, y); + } + } + + @Override + public void mousePressed(MouseEvent e) { + if (!e.isControlDown()) { + table.clearSelection(); + } + + if (e.isControlDown() && e.isAltDown()) { + clearCell(); + } else { + table.startHighlight(x, y); + } + requestFocus(); + ECUEditorManager.getECUEditor().getTableToolBar().updateTableToolBar(table); + } + + @Override + public void mouseReleased(MouseEvent e) { + table.stopHighlight(); + } + + @Override + public void mouseClicked(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + } + + private void clearCell() { + if(isHighlighted()) { + setHighlighted(false); + } + if(isSelected()) { + setSelected(false); + } + } + + public void increment(double increment) { + double oldValue = getRealValue(); + + if (table.getCurrentScale().getCoarseIncrement() < 0.0) { + increment = 0.0 - increment; + } + + double incResult = JEPUtil.evaluate(table.getCurrentScale().getByteExpression(), (oldValue + increment)); + if (table.getStorageType() == Settings.STORAGE_TYPE_FLOAT) { + if(binValue != incResult) { + this.setBinValue(incResult); + } + } else { + int roundResult = (int) Math.round(incResult); + if(binValue != roundResult) { + this.setBinValue(roundResult); + } + } + + // make sure table is incremented if change isn't great enough + int maxValue = (int) Math.pow(8, table.getStorageType()); + + if (table.getStorageType() != Settings.STORAGE_TYPE_FLOAT && + oldValue == getRealValue() && + binValue > 0.0 && + binValue < maxValue) { + LOGGER.debug(maxValue + " " + binValue); + increment(increment * 2); + } + } + + public void undo() { + this.setBinValue(originalValue); + } + + public void setRevertPoint() { + this.setOriginalValue(binValue); + this.drawCell(); + } + + public void setOriginalValue(double originalValue) { + this.originalValue = originalValue; + } + + public void setCompareValue(DataCell compareCell) { + if(Settings.DATA_TYPE_BIN == table.getCompareValueType()) + { + if(this.compareToValue == compareCell.binValue) { + return; + } + + this.compareToValue = compareCell.binValue; + } else { + if(this.compareToValue == compareCell.originalValue) { + return; + } + + this.compareToValue = compareCell.originalValue; + } + } + + public void multiply(double factor) { + setBinValue(binValue * factor); + } + + public void setLiveDataTrace(boolean trace) { + if(traced != trace) { + traced = trace; + drawCell(); + } + } + + public void setLiveDataTraceValue(String liveValue) { + if(this.liveValue != liveValue) { + this.liveValue = liveValue; + drawCell(); + } + } + + private Settings getSettings() { + return SettingsManager.getSettings(); + } + + @Override + public boolean equals(Object other) { + if(other == null) { + return false; + } + + if(!(other instanceof DataCell)) { + return false; + } + + DataCell otherCell = (DataCell) other; + + if(this.table.isStaticDataTable() != otherCell.table.isStaticDataTable()) { + return false; + } + + return binValue == otherCell.binValue; + } + + public String getStaticText() { + String displayString = null; + try { + DecimalFormat formatter = new DecimalFormat(table.getCurrentScale().getFormat()); + + double staticDouble = Double.parseDouble(staticText); + displayString = formatter.format(JEPUtil.evaluate(table.getCurrentScale().getExpression(), staticDouble)); + } catch (Exception ex) { + displayString = this.staticText; + } + return displayString; + } + + public void setY(int y) { + this.y = y; + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/maps/Rom.java b/java_console/romraider/src/com/romraider/maps/Rom.java new file mode 100644 index 0000000000..5092850db3 --- /dev/null +++ b/java_console/romraider/src/com/romraider/maps/Rom.java @@ -0,0 +1,419 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2014 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.maps; + +import static com.romraider.maps.RomChecksum.calculateRomChecksum; +import static com.romraider.util.HexUtil.asBytes; +import static com.romraider.util.HexUtil.asHex; +import static javax.swing.JOptionPane.DEFAULT_OPTION; +import static javax.swing.JOptionPane.QUESTION_MESSAGE; +import static javax.swing.JOptionPane.showOptionDialog; + +import java.beans.PropertyVetoException; +import java.io.File; +import java.io.Serializable; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Vector; + +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; +import javax.swing.tree.DefaultMutableTreeNode; + +import org.apache.log4j.Logger; + +import com.romraider.Settings; +import com.romraider.logger.ecu.ui.handler.table.TableUpdateHandler; +import com.romraider.swing.CategoryTreeNode; +import com.romraider.swing.JProgressPane; +import com.romraider.swing.TableFrame; +import com.romraider.swing.TableTreeNode; +import com.romraider.util.SettingsManager; +import com.romraider.xml.InvalidTableNameException; +import com.romraider.xml.TableNotFoundException; + +public class Rom extends DefaultMutableTreeNode implements Serializable { + private static final long serialVersionUID = 7865405179738828128L; + private static final Logger LOGGER = Logger.getLogger(Rom.class); + private RomID romID = new RomID(); + private String fileName = ""; + private File fullFileName = new File("."); + private final Vector tableNodes = new Vector(); + private byte[] binData; + private boolean isAbstract = false; + + public Rom() { + tableNodes.clear(); + } + + public void refreshDisplayedTables() { + // Remove all nodes from the ROM tree node. + super.removeAllChildren(); + + Settings settings = SettingsManager.getSettings(); + + // Add nodes to ROM tree. + for (TableTreeNode tableTreeNode : tableNodes) { + TableFrame tableFrame = tableTreeNode.getFrame(); + Table table = tableFrame.getTable(); + + if (settings.isDisplayHighTables() || settings.getUserLevel() >= table.getUserLevel()) { + boolean categoryExists = false; + + for (int j = 0; j < getChildCount(); j++) { + if (getChildAt(j).toString().equals(table.getCategory())) { + // add to appropriate category + getChildAt(j).add(tableTreeNode); + categoryExists = true; + break; + } + } + + if (!categoryExists) { // if category does not already exist, create it + CategoryTreeNode categoryNode = new CategoryTreeNode(table.getCategory()); + categoryNode.add(tableTreeNode); + this.add(categoryNode); + } + } + } + } + + public void addTable(Table table) { + boolean found = false; + String frameTitle = this.getFileName()+" - "+table.getName(); + + for (int i = 0; i < tableNodes.size(); i++) { + if (tableNodes.get(i).getTable().equals(table)) { + tableNodes.remove(i); + tableNodes.add(i, new TableTreeNode(new TableFrame(frameTitle, table))); + found = true; + break; + } + } + if (!found) { + tableNodes.add(new TableTreeNode(new TableFrame(frameTitle, table))); + } + } + + public void addTableByName(Table table) { + boolean found = false; + String frameTitle = this.getFileName()+" - "+table.getName(); + + for (int i = 0; i < tableNodes.size(); i++) { + if (tableNodes.get(i).getTable().getName().equalsIgnoreCase(table.getName())) { + tableNodes.remove(i); + tableNodes.add(i, new TableTreeNode(new TableFrame(frameTitle, table))); + found = true; + break; + } + } + if (!found) { + tableNodes.add(new TableTreeNode(new TableFrame(frameTitle, table))); + } + } + + public void removeTable(Table table) { + for(int i = 0; i < tableNodes.size(); i++) { + if(tableNodes.get(i).getTable().equals(table)) { + tableNodes.remove(i); + return; + } + } + } + + public void removeTableByName(Table table) { + for(int i = 0; i < tableNodes.size(); i++) { + if(tableNodes.get(i).getTable().getName().equalsIgnoreCase(table.getName())) { + tableNodes.remove(i); + return; + } + } + } + + public Table getTableByName(String tableName) throws TableNotFoundException, InvalidTableNameException { + if(null == tableName || tableName.isEmpty()) { + throw new InvalidTableNameException(); + } + + for (TableTreeNode tableNode : tableNodes) { + if (tableNode.getTable().getName().equalsIgnoreCase(tableName)) { + return tableNode.getTable(); + } + } + throw new TableNotFoundException(); + } + + public List
findTables(String regex) { + List
result = new ArrayList
(); + for (TableTreeNode tableNode : tableNodes) { + String name = tableNode.getTable().getName(); + if (name.matches(regex)) result.add(tableNode.getTable()); + } + return result; + } + + public void populateTables(byte[] binData, JProgressPane progress) { + this.binData = binData; + for (int i = 0; i < tableNodes.size(); i++) { + + // update progress + int currProgress = (int) (i / (double) tableNodes.size() * 40); + progress.update("Populating tables...", 50 + currProgress); + + Table table = tableNodes.get(i).getTable(); + try { + // if storageaddress has not been set (or is set to 0) omit table + if (table.getStorageAddress() != 0) { + try { + table.populateTable(binData, this.getRomID().getRamOffset()); + TableUpdateHandler.getInstance().registerTable(table); + + if (null != table.getName() && table.getName().equalsIgnoreCase("Checksum Fix")){ + setEditStamp(binData, table.getStorageAddress()); + } + } catch (ArrayIndexOutOfBoundsException ex) { + + LOGGER.error(table.getName() + + " type " + table.getType() + " start " + + table.getStorageAddress() + " " + binData.length + " filesize", ex); + + // table storage address extends beyond end of file + JOptionPane.showMessageDialog(SwingUtilities.windowForComponent(table), "Storage address for table \"" + table.getName() + + "\" is out of bounds.\nPlease check ECU definition file.", "ECU Definition Error", JOptionPane.ERROR_MESSAGE); + tableNodes.removeElementAt(i); + i--; + } catch (IndexOutOfBoundsException iex) { + LOGGER.error(table.getName() + + " type " + table.getType() + " start " + + table.getStorageAddress() + " " + binData.length + " filesize", iex); + + // table storage address extends beyond end of file + JOptionPane.showMessageDialog(SwingUtilities.windowForComponent(table), "Storage address for table \"" + table.getName() + + "\" is out of bounds.\nPlease check ECU definition file.", "ECU Definition Error", JOptionPane.ERROR_MESSAGE); + tableNodes.removeElementAt(i); + i--; + } + + } else { + tableNodes.removeElementAt(i); + // decrement i because length of vector has changed + i--; + } + + } catch (NullPointerException ex) { + LOGGER.error("Error Populating Table", ex); + JOptionPane.showMessageDialog(SwingUtilities.windowForComponent(table), "There was an error loading table " + table.getName(), "ECU Definition Error", JOptionPane.ERROR_MESSAGE); + tableNodes.removeElementAt(i); + i--; + } + } + } + + private void setEditStamp(byte[] binData, int address) { + byte[] stampData = new byte[4]; + System.arraycopy(binData, address+204, stampData, 0, stampData.length); + String stamp = asHex(stampData); + if (stamp.equalsIgnoreCase("FFFFFFFF")) { + romID.setEditStamp(""); + } + else { + StringBuilder niceStamp = new StringBuilder(stamp); + niceStamp.replace(6, 9, String.valueOf(0xFF & stampData[3])); + niceStamp.insert(6, " v"); + niceStamp.insert(4, "-"); + niceStamp.insert(2, "-"); + niceStamp.insert(0, "20"); + romID.setEditStamp(niceStamp.toString()); + } + } + + public void setRomID(RomID romID) { + this.romID = romID; + } + + public RomID getRomID() { + return romID; + } + + public String getRomIDString() { + return romID.getXmlid(); + } + + @Override + public String toString() { + String output = ""; + output = output + "\n---- Rom ----" + romID.toString(); + for (int i = 0; i < tableNodes.size(); i++) { + output = output + tableNodes.get(i).getTable(); + } + output = output + "\n---- End Rom ----"; + + return output; + } + + public String getFileName() { + return fileName; + } + + public Vector
getTables() { + Vector
tables = new Vector
(); + for(TableTreeNode tableNode : tableNodes) { + tables.add(tableNode.getTable()); + } + return tables; + } + + public Vector getTableNodes() { + return this.tableNodes; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public byte[] saveFile() { + final List checksumTables = new ArrayList(); + for (TableTreeNode tableNode : tableNodes) { + tableNode.getTable().saveFile(binData); + if (tableNode.getTable().getName().contains("Checksum Fix")) { + checksumTables.add(tableNode); + } + } + + if (checksumTables.size() == 1) { + final TableTreeNode checksum = checksumTables.get(0); + byte count = binData[checksum.getTable().getStorageAddress() + 207]; + if (count == -1) { + count = 1; + } + else { + count++; + } + String currentDate = new SimpleDateFormat("yyMMdd").format(new Date()); + String stamp = String.format("%s%02x", currentDate, count); + byte[] romStamp = asBytes(stamp); + System.arraycopy( + romStamp, + 0, + binData, + checksum.getTable().getStorageAddress() + 204, + 4); + setEditStamp(binData, checksum.getTable().getStorageAddress()); + } + + for (TableTreeNode checksum : checksumTables) { + if (!checksum.getTable().isLocked()) { + calculateRomChecksum( + binData, + checksum.getTable().getStorageAddress(), + checksum.getTable().getDataSize() + ); + } + else if (checksum.getTable().isLocked() && + !checksum.getTable().isButtonSelected()) { + + Object[] options = {"Yes", "No"}; + final String message = String.format( + "One or more ROM image Checksums is invalid. " + + "Calculate new Checksums?%n" + + "(NOTE: this will only fix the Checksums it will NOT repair a corrupt ROM image)"); + int answer = showOptionDialog( + SwingUtilities.windowForComponent(checksum.getTable()), + message, + "Checksum Fix", + DEFAULT_OPTION, + QUESTION_MESSAGE, + null, + options, + options[0]); + if (answer == 0) { + calculateRomChecksum( + binData, + checksum.getTable().getStorageAddress(), + checksum.getTable().getDataSize() + ); + } + } + } + return binData; + } + + public void clearData() { + super.removeAllChildren(); + + // Hide and dispose all frames. + for(TableTreeNode tableTreeNode : tableNodes) { + TableFrame frame = tableTreeNode.getFrame(); + frame.setVisible(false); + try { + frame.setClosed(true); + } catch (PropertyVetoException e) { + ; // Do nothing. + } + frame.dispose(); + } + + tableNodes.clear(); + binData = null; + } + + public int getRealFileSize() { + return binData.length; + } + + public File getFullFileName() { + return fullFileName; + } + + public void setFullFileName(File fullFileName) { + this.fullFileName = fullFileName; + this.setFileName(fullFileName.getName()); + for (TableTreeNode tableNode : tableNodes) { + String frameTitle = this.getFileName() + " - " + tableNode.getTable().getName(); + tableNode.getFrame().setTitle(frameTitle); + } + } + + public boolean isAbstract() { + return isAbstract; + } + + public void setAbstract(boolean isAbstract) { + this.isAbstract = isAbstract; + } + + public void refreshTableCompareMenus() { + for(TableTreeNode tableNode : getTableNodes()) { + tableNode.getFrame().refreshSimilarOpenTables(); + } + } + + @Override + public DefaultMutableTreeNode getChildAt(int i) { + return (DefaultMutableTreeNode) super.getChildAt(i); + } + + @Override + public DefaultMutableTreeNode getLastChild() { + return (DefaultMutableTreeNode) super.getLastChild(); + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/maps/RomChecksum.java b/java_console/romraider/src/com/romraider/maps/RomChecksum.java new file mode 100644 index 0000000000..adf90c796f --- /dev/null +++ b/java_console/romraider/src/com/romraider/maps/RomChecksum.java @@ -0,0 +1,81 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.maps; + +import static com.romraider.xml.RomAttributeParser.parseByteValue; +import static com.romraider.xml.RomAttributeParser.parseIntegerValue; + +import com.romraider.Settings; + +public class RomChecksum { + + public static void calculateRomChecksum(byte[] input, int storageAddress, int dataSize) { + for (int i = storageAddress; i < storageAddress + dataSize; i+=12) { + byte[] newSum = calculateChecksum(input, + (int)parseByteValue(input, 0, i , 4, true), + (int)parseByteValue(input, 0, i+4, 4, true)); + System.arraycopy(newSum, 0, input, i + 8, 4); + } + } + + public static int validateRomChecksum(byte[] input, int storageAddress, int dataSize) { + int result = 0; + int[] results = new int[dataSize / 12]; + int j = 0; + for (int i = storageAddress; i < storageAddress + dataSize; i+=12) { + int startAddr = (int)parseByteValue(input, 0, i , 4, true); + int endAddr = (int)parseByteValue(input, 0, i+4, 4, true); + int diff = (int)parseByteValue(input, 0, i+8, 4, true); + if (j == 0 && + startAddr == 0 && + endAddr == 0 && + diff == Settings.CHECK_TOTAL) { + return result = -1; // -1, all checksums disabled if the first one is disabled + } + else { + results[j] = validateChecksum(input, startAddr, endAddr, diff); + } + j++; + } + for (j = 0; j < (dataSize / 12); j++) { + if (results[j] != 0) { + return j + 1; // position of invalid checksum + } + } + return result; // 0, all checksums are valid + } + + private static int validateChecksum(byte[] input, int startAddr, int endAddr, int diff) { + int byteSum = 0; + for (int i=startAddr; i scales = new Vector(); + protected Scale curScale; + + protected int storageAddress; + protected int storageType; + protected boolean signed; + protected int endian; + protected boolean flip; + protected DataCell[] data = new DataCell[0]; + protected boolean beforeRam = false; + protected int ramOffset = 0; + protected BorderLayout borderLayout = new BorderLayout(); + protected GridLayout centerLayout = new GridLayout(1, 1, 0, 0); + protected JPanel centerPanel = new JPanel(centerLayout); + protected JLabel tableLabel; + protected int verticalOverhead = 103; + protected int horizontalOverhead = 2; + protected int cellHeight = (int) getSettings().getCellSize().getHeight(); + protected int cellWidth = (int) getSettings().getCellSize().getWidth(); + protected int minHeight = 100; + protected int minWidthNoOverlay = 465; + protected int minWidthOverlay = 700; + protected int highlightX; + protected int highlightY; + protected boolean highlight = false; + protected int userLevel = 0; + protected boolean locked = false; + + protected String logParam = Settings.BLANK; + protected boolean overlayLog = false; + + protected CopyTableWorker copyTableWorker; + protected CopySelectionWorker copySelectionWorker; + + protected double minAllowedBin = 0.0; + protected double maxAllowedBin = 0.0; + + protected double maxBin; + protected double minBin; + + protected double maxCompare = 0.0; + protected double minCompare = 0.0; + + protected int compareDisplay = Settings.COMPARE_DISPLAY_ABSOLUTE; + protected int compareValueType = Settings.DATA_TYPE_BIN; + + protected boolean staticDataTable = false; + protected String liveAxisValue = Settings.BLANK; + protected int liveDataIndex = 0; + + private Table compareTable = null; + + public Table() { + scales.clear(); + scales.add(new Scale()); + + this.setLayout(borderLayout); + this.add(centerPanel, BorderLayout.CENTER); + centerPanel.setVisible(true); + + // key binding actions + Action rightAction = new AbstractAction() { + private static final long serialVersionUID = 1042884198300385041L; + + @Override + public void actionPerformed(ActionEvent e) { + cursorRight(); + } + }; + Action leftAction = new AbstractAction() { + private static final long serialVersionUID = -4970441255677214171L; + + @Override + public void actionPerformed(ActionEvent e) { + cursorLeft(); + } + }; + Action downAction = new AbstractAction() { + private static final long serialVersionUID = -7898502951121825984L; + + @Override + public void actionPerformed(ActionEvent e) { + cursorDown(); + } + }; + Action upAction = new AbstractAction() { + private static final long serialVersionUID = 6937621541727666631L; + + @Override + public void actionPerformed(ActionEvent e) { + cursorUp(); + } + }; + Action incCoarseAction = new AbstractAction() { + private static final long serialVersionUID = -8308522736529183148L; + + @Override + public void actionPerformed(ActionEvent e) { + getToolbar().incrementCoarse(); + } + }; + Action decCoarseAction = new AbstractAction() { + private static final long serialVersionUID = -7407628920997400915L; + + @Override + public void actionPerformed(ActionEvent e) { + getToolbar().decrementCoarse(); + } + }; + Action incFineAction = new AbstractAction() { + private static final long serialVersionUID = 7261463425941761433L; + + @Override + public void actionPerformed(ActionEvent e) { + getToolbar().incrementFine(); + } + }; + Action decFineAction = new AbstractAction() { + private static final long serialVersionUID = 8929400237520608035L; + + @Override + public void actionPerformed(ActionEvent e) { + getToolbar().decrementFine(); + } + }; + Action num0Action = new AbstractAction() { + private static final long serialVersionUID = -6310984176739090034L; + + @Override + public void actionPerformed(ActionEvent e) { + getToolbar().focusSetValue('0'); + } + }; + Action num1Action = new AbstractAction() { + private static final long serialVersionUID = -6187220355403883499L; + + @Override + public void actionPerformed(ActionEvent e) { + getToolbar().focusSetValue('1'); + } + }; + Action num2Action = new AbstractAction() { + private static final long serialVersionUID = -8745505977907325720L; + + @Override + public void actionPerformed(ActionEvent e) { + getToolbar().focusSetValue('2'); + } + }; + Action num3Action = new AbstractAction() { + private static final long serialVersionUID = 4694872385823448942L; + + @Override + public void actionPerformed(ActionEvent e) { + getToolbar().focusSetValue('3'); + } + }; + Action num4Action = new AbstractAction() { + private static final long serialVersionUID = 4005741329254221678L; + + @Override + public void actionPerformed(ActionEvent e) { + getToolbar().focusSetValue('4'); + } + }; + Action num5Action = new AbstractAction() { + private static final long serialVersionUID = -5846094949106279884L; + + @Override + public void actionPerformed(ActionEvent e) { + getToolbar().focusSetValue('5'); + } + }; + Action num6Action = new AbstractAction() { + private static final long serialVersionUID = -5338656374925334150L; + + @Override + public void actionPerformed(ActionEvent e) { + getToolbar().focusSetValue('6'); + } + }; + Action num7Action = new AbstractAction() { + private static final long serialVersionUID = 1959983381590509303L; + + @Override + public void actionPerformed(ActionEvent e) { + getToolbar().focusSetValue('7'); + } + }; + Action num8Action = new AbstractAction() { + private static final long serialVersionUID = 7442763278699460648L; + + @Override + public void actionPerformed(ActionEvent e) { + getToolbar().focusSetValue('8'); + } + }; + Action num9Action = new AbstractAction() { + private static final long serialVersionUID = 7475171864584215094L; + + @Override + public void actionPerformed(ActionEvent e) { + getToolbar().focusSetValue('9'); + } + }; + Action numPointAction = new AbstractAction() { + private static final long serialVersionUID = -4729135055857591830L; + + @Override + public void actionPerformed(ActionEvent e) { + getToolbar().focusSetValue('.'); + } + }; + Action copyAction = new AbstractAction() { + private static final long serialVersionUID = -6978981449261938672L; + + @Override + public void actionPerformed(ActionEvent e) { + copySelection(); + } + }; + Action pasteAction = new AbstractAction() { + private static final long serialVersionUID = 2026817603236490899L; + + @Override + public void actionPerformed(ActionEvent e) { + paste(); + } + }; + Action interpolate = new AbstractAction() { + private static final long serialVersionUID = -2350912575392447149L; + + @Override + public void actionPerformed(ActionEvent e) { + interpolate(); + } + }; + Action verticalInterpolate = new AbstractAction() { + private static final long serialVersionUID = -2350912575392447149L; + + @Override + public void actionPerformed(ActionEvent e) { + verticalInterpolate(); + } + }; + Action horizontalInterpolate = new AbstractAction() { + private static final long serialVersionUID = -6346750245035640773L; + + @Override + public void actionPerformed(ActionEvent e) { + horizontalInterpolate(); + } + }; + Action multiplyAction = new AbstractAction() { + private static final long serialVersionUID = -2350912575392447149L; + + @Override + public void actionPerformed(ActionEvent e) { + getToolbar().multiply(); + } + }; + Action numNegAction = new AbstractAction() { + private static final long serialVersionUID = -6346750245035640773L; + + @Override + public void actionPerformed(ActionEvent e) { + getToolbar().focusSetValue('-'); + } + }; + + // set input mapping + InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW); + + KeyStroke right = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0); + KeyStroke left = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0); + KeyStroke up = KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0); + KeyStroke down = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0); + KeyStroke decrement = KeyStroke.getKeyStroke('-'); + KeyStroke increment = KeyStroke.getKeyStroke('+'); + KeyStroke decrement2 = KeyStroke.getKeyStroke("control DOWN"); + KeyStroke increment2 = KeyStroke.getKeyStroke("control UP"); + KeyStroke decrement3 = KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, KeyEvent.CTRL_DOWN_MASK); + KeyStroke increment3 = KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, KeyEvent.CTRL_DOWN_MASK); + KeyStroke decrement4 = KeyStroke.getKeyStroke("control shift DOWN"); + KeyStroke increment4 = KeyStroke.getKeyStroke("control shift UP"); + KeyStroke num0 = KeyStroke.getKeyStroke('0'); + KeyStroke num1 = KeyStroke.getKeyStroke('1'); + KeyStroke num2 = KeyStroke.getKeyStroke('2'); + KeyStroke num3 = KeyStroke.getKeyStroke('3'); + KeyStroke num4 = KeyStroke.getKeyStroke('4'); + KeyStroke num5 = KeyStroke.getKeyStroke('5'); + KeyStroke num6 = KeyStroke.getKeyStroke('6'); + KeyStroke num7 = KeyStroke.getKeyStroke('7'); + KeyStroke num8 = KeyStroke.getKeyStroke('8'); + KeyStroke num9 = KeyStroke.getKeyStroke('9'); + KeyStroke mulKey = KeyStroke.getKeyStroke('*'); + KeyStroke mulKeys = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.CTRL_DOWN_MASK); + KeyStroke numPoint = KeyStroke.getKeyStroke('.'); + KeyStroke copy = KeyStroke.getKeyStroke("control C"); + KeyStroke paste = KeyStroke.getKeyStroke("control V"); + KeyStroke interp = KeyStroke.getKeyStroke("shift I"); + KeyStroke vinterp = KeyStroke.getKeyStroke("shift V"); + KeyStroke hinterp = KeyStroke.getKeyStroke("shift H"); + KeyStroke numNeg = KeyStroke.getKeyStroke('-'); + + im.put(right, "right"); + im.put(left, "left"); + im.put(up, "up"); + im.put(down, "down"); + im.put(increment, "incCoarseAction"); + im.put(decrement, "decCoarseAction"); + im.put(increment2, "incCoarseAction"); + im.put(decrement2, "decCoarseAction"); + im.put(increment3, "incFineAction"); + im.put(decrement3, "decFineAction"); + im.put(increment4, "incFineAction"); + im.put(decrement4, "decFineAction"); + im.put(num0, "num0Action"); + im.put(num1, "num1Action"); + im.put(num2, "num2Action"); + im.put(num3, "num3Action"); + im.put(num4, "num4Action"); + im.put(num5, "num5Action"); + im.put(num6, "num6Action"); + im.put(num7, "num7Action"); + im.put(num8, "num8Action"); + im.put(num9, "num9Action"); + im.put(numPoint, "numPointAction"); + im.put(copy, "copyAction"); + im.put(paste, "pasteAction"); + im.put(interp, "interpolate"); + im.put(vinterp, "verticalInterpolate"); + im.put(hinterp, "horizontalInterpolate"); + im.put(mulKey, "mulAction"); + im.put(mulKeys, "mulAction"); + im.put(numNeg, "numNeg"); + + getActionMap().put(im.get(right), rightAction); + getActionMap().put(im.get(left), leftAction); + getActionMap().put(im.get(up), upAction); + getActionMap().put(im.get(down), downAction); + getActionMap().put(im.get(increment), incCoarseAction); + getActionMap().put(im.get(decrement), decCoarseAction); + getActionMap().put(im.get(increment2), incCoarseAction); + getActionMap().put(im.get(decrement2), decCoarseAction); + getActionMap().put(im.get(increment3), incFineAction); + getActionMap().put(im.get(decrement3), decFineAction); + getActionMap().put(im.get(increment4), incFineAction); + getActionMap().put(im.get(decrement4), decFineAction); + getActionMap().put(im.get(num0), num0Action); + getActionMap().put(im.get(num1), num1Action); + getActionMap().put(im.get(num2), num2Action); + getActionMap().put(im.get(num3), num3Action); + getActionMap().put(im.get(num4), num4Action); + getActionMap().put(im.get(num5), num5Action); + getActionMap().put(im.get(num6), num6Action); + getActionMap().put(im.get(num7), num7Action); + getActionMap().put(im.get(num8), num8Action); + getActionMap().put(im.get(num9), num9Action); + getActionMap().put(im.get(numPoint), numPointAction); + getActionMap().put(im.get(mulKey), multiplyAction); + getActionMap().put(im.get(mulKeys), multiplyAction); + getActionMap().put(im.get(copy), copyAction); + getActionMap().put(im.get(paste), pasteAction); + getActionMap().put(im.get(interp), interpolate); + getActionMap().put(im.get(vinterp), verticalInterpolate); + getActionMap().put(im.get(hinterp), horizontalInterpolate); + getActionMap().put(im.get(numNeg), numNegAction); + + this.setInputMap(WHEN_FOCUSED, im); + } + + public DataCell[] getData() { + return data; + } + + public void setData(DataCell[] data) { + this.data = data; + } + + public void populateTable(byte[] input, int romRamOffset) throws ArrayIndexOutOfBoundsException, IndexOutOfBoundsException { + // temporarily remove lock + boolean tempLock = locked; + locked = false; + + if (!beforeRam) { + this.ramOffset = romRamOffset; + } + + for (int i = 0; i < data.length; i++) { + if (data[i] == null) { + double dataValue = 0.0; + + // populate data cells + if (storageType == Settings.STORAGE_TYPE_FLOAT) { //float storage type + byte[] byteValue = new byte[4]; + byteValue[0] = input[getStorageAddress() + i * 4 - ramOffset]; + byteValue[1] = input[getStorageAddress() + i * 4 - ramOffset + 1]; + byteValue[2] = input[getStorageAddress() + i * 4 - ramOffset + 2]; + byteValue[3] = input[getStorageAddress() + i * 4 - ramOffset + 3]; + dataValue = RomAttributeParser.byteToFloat(byteValue, endian); + + } else { // integer storage type + dataValue = RomAttributeParser.parseByteValue(input, + endian, + getStorageAddress() + i * storageType - ramOffset, + storageType, + signed); + } + + data[i] = new DataCell(this, dataValue, 0, i); + data[i].setPreferredSize(new Dimension(cellWidth, cellHeight)); + centerPanel.add(data[i]); + + // show locked cell + if (tempLock) { + data[i].setForeground(Color.GRAY); + } + } + } + + // reset locked status + locked = tempLock; + calcCellRanges(); + } + + public int getType() { + return type; + } + + public DataCell getDataCell(int location) { + return data[location]; + } + + public void setType(int type) { + this.type = type; + } + + @Override + public String getName() { + if(null == name || name.isEmpty()) { + StringBuilder sb = new StringBuilder(); + sb.append(Settings.DEFAULT_TABLE_NAME); + + if(0 != this.getStorageAddress()) { + sb.append(" ("+this.getStorageAddress() + ")"); + } + + if(null != this.getLogParam() && !this.getLogParam().isEmpty()) { + sb.append(" - " + this.getLogParam()); + } + + return sb.toString(); + } + return name; + } + + @Override + public void setName(String name) { + if(null != name && !name.isEmpty()) { + this.name = name; + } + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Scale getCurrentScale() { + return this.curScale; + } + + public Scale getScale(String scaleName) throws NameNotFoundException { + for (Scale scale : scales) { + if (scale.getName().equalsIgnoreCase(scaleName)) { + return scale; + } + } + return new Scale(); + } + + public Vector getScales() { + return scales; + } + + public void addScale(Scale scale) { + // look for scale, replace or add new + for (int i = 0; i < scales.size(); i++) { + if (scales.get(i).getName().equalsIgnoreCase(scale.getName())) { + scales.remove(i); + break; + } + } + scales.add(scale); + + if(null == curScale) { + this.curScale = scale; + } + + if(SettingsManager.getSettings().getDefaultScale().equalsIgnoreCase(scale.getName())) { + this.curScale = scale; + } + + validateScaling(); + } + + public int getStorageAddress() { + return storageAddress; + } + + public void setStorageAddress(int storageAddress) { + this.storageAddress = storageAddress; + } + + public int getStorageType() { + return storageType; + } + + public void setStorageType(int storageType) { + this.storageType = storageType; + calcValueRange(); + } + + public boolean isSignedData() { + return signed; + } + + public void setSignedData(boolean signed) { + this.signed = signed; + } + + public int getEndian() { + return endian; + } + + public void setEndian(int endian) { + this.endian = endian; + } + + public void setDataSize(int size) { + data = new DataCell[size]; + } + + public int getDataSize() { + return data.length; + } + + public boolean getFlip() { + return flip; + } + + public void setFlip(boolean flip) { + this.flip = flip; + } + + public void setLogParam(String logParam) { + this.logParam = logParam; + } + + public String getLogParam() { + return logParam; + } + + @Override + public String toString() { + /*String output = "\n ---- Table " + name + " ----" + + scale + + "\n Category: " + category + + "\n Type: " + type + + "\n Description: " + description + + "\n Storage Address: " + Integer.toHexString(storageAddress) + + "\n Storage Type: " + storageType + + "\n Endian: " + endian + + "\n Flip: " + flip + + "\n ---- End Table " + name + " ----"; + for (int i = 0; i < data.length; i++) { + if (data[i] != null) { + output = output + "\nData: " + data[i]; + } + } + + return output;*/ + return getName(); + } + + @Override + public boolean equals(Object other) { + try { + if(null == other) { + return false; + } + + if(other == this) { + return true; + } + + if(!(other instanceof Table)) { + return false; + } + + Table otherTable = (Table)other; + + if( (null == this.getName() && null == otherTable.getName()) + || (this.getName().isEmpty() && otherTable.getName().isEmpty()) ) { + ;// Skip name compare if name is null or empty. + } else { + if(!this.getName().equalsIgnoreCase(otherTable.getName())) { + return false; + } + } + + if(this.data.length != otherTable.data.length) + { + return false; + } + + if(this.data.equals(otherTable.data)) + { + return true; + } + + // Compare Bin Values + for(int i=0 ; i < this.data.length ; i++) { + if(! this.data[i].equals(otherTable.data[i])) { + return false; + } + } + + return true; + } catch(Exception ex) { + // TODO: Log Exception. + return false; + } + } + + public double getMaxAllowedBin() { + return maxAllowedBin; + } + + public double getMinAllowedBin() { + return minAllowedBin; + } + + public double getMaxAllowedReal() { + return JEPUtil.evaluate(getCurrentScale().getExpression(), getMaxAllowedBin()); + } + + public double getMinAllowedReal() { + return JEPUtil.evaluate(getCurrentScale().getExpression(), getMinAllowedBin()); + } + + private void calcValueRange() { + if (getStorageType() != Settings.STORAGE_TYPE_FLOAT) { + if (isSignedData()) { + switch (getStorageType()) { + case 1: + minAllowedBin = Byte.MIN_VALUE; + maxAllowedBin = Byte.MAX_VALUE; + break; + case 2: + minAllowedBin = Short.MIN_VALUE; + maxAllowedBin = Short.MAX_VALUE; + break; + case 4: + minAllowedBin = Integer.MIN_VALUE; + maxAllowedBin = Integer.MAX_VALUE; + break; + } + } + else { + maxAllowedBin = (Math.pow(256, getStorageType()) - 1); + minAllowedBin = 0.0; + } + } else { + maxAllowedBin = Float.MAX_VALUE; + + if(isSignedData()) { + minAllowedBin = 0.0; + } else { + minAllowedBin = -Float.MAX_VALUE; + } + } + } + + public void calcCellRanges() { + double binMax = data[0].getBinValue(); + double binMin = data[0].getBinValue(); + + double compareMax = data[0].getCompareValue(); + double compareMin = data[0].getCompareValue(); + + for(DataCell cell : data) { + // Calc bin + if(binMax < cell.getBinValue()) { + binMax = cell.getBinValue(); + } + if(binMin > cell.getBinValue()) { + binMin = cell.getBinValue(); + } + + // Calc compare + double compareValue = cell.getCompareValue(); + if(compareMax < compareValue) { + compareMax = compareValue; + } + if(compareMin > compareValue) { + compareMin = compareValue; + } + } + setMaxBin(binMax); + setMinBin(binMin); + setMaxCompare(compareMax); + setMinCompare(compareMin); + } + + public double getMaxBin() { + return this.maxBin; + } + + public double getMinBin() { + return this.minBin; + } + + public double getMaxReal() { + return JEPUtil.evaluate(getCurrentScale().getExpression(), getMaxBin()); + } + + public double getMinReal() { + return JEPUtil.evaluate(getCurrentScale().getExpression(), getMinBin()); + } + + public void setMaxBin(double maxBin) { + this.maxBin = maxBin; + } + + public void setMinBin(double minBin) { + this.minBin = minBin; + } + + public double getMaxCompare() { + return this.maxCompare; + } + + public void setMaxCompare(double maxCompare) { + this.maxCompare = maxCompare; + } + + public double getMinCompare() { + return this.minCompare; + } + + public void setMinCompare(double minCompare) { + this.minCompare = minCompare; + } + + public void drawTable() { + for(DataCell cell : data) { + if(null != cell) { + cell.drawCell(); + } + } + } + + public Dimension getFrameSize() { + int height = verticalOverhead + cellHeight; + int width = horizontalOverhead + data.length * cellWidth; + if (height < minHeight) { + height = minHeight; + } + int minWidth = isLiveDataSupported() ? minWidthOverlay : minWidthNoOverlay; + if (width < minWidth) { + width = minWidth; + } + return new Dimension(width, height); + } + + public void increment(double increment) { + if (!locked && !(userLevel > getSettings().getUserLevel())) { + for (DataCell cell : data) { + if (cell.isSelected()) { + cell.increment(increment); + } + } + } else if (userLevel > getSettings().getUserLevel()) { + JOptionPane.showMessageDialog(this, "This table can only be modified by users with a userlevel of \n" + + userLevel + " or greater. Click View->User Level to change your userlevel.", + "Table cannot be modified", + JOptionPane.INFORMATION_MESSAGE); + } + } + + public void multiply(double factor) { + if (!locked && !(userLevel > getSettings().getUserLevel())) { + for (DataCell cell : data) { + if (cell.isSelected()) { + cell.multiply(factor); + } + } + } else if (userLevel > getSettings().getUserLevel()) { + JOptionPane.showMessageDialog(this, "This table can only be modified by users with a userlevel of \n" + + userLevel + " or greater. Click View->User Level to change your userlevel.", + "Table cannot be modified", + JOptionPane.INFORMATION_MESSAGE); + } + } + + public void setRealValue(String realValue) { + if (!locked && userLevel <= getSettings().getUserLevel()) { + for(DataCell cell : data) { + if (cell.isSelected()) { + cell.setRealValue(realValue); + } + } + } else if (userLevel > getSettings().getUserLevel()) { + JOptionPane.showMessageDialog(this, "This table can only be modified by users with a userlevel of \n" + + userLevel + " or greater. Click View->User Level to change your userlevel.", + "Table cannot be modified", + JOptionPane.INFORMATION_MESSAGE); + } + } + + public void clearSelection() { + clearSelectedData(); + } + + public void clearSelectedData() { + for (DataCell cell : data) { + if(cell.isSelected()) { + cell.setSelected(false); + } + } + } + + public void startHighlight(int x, int y) { + this.highlightY = y; + this.highlightX = x; + highlight = true; + highlight(x, y); + } + + public void highlight(int x, int y) { + if (highlight) { + for (int i = 0; i < data.length; i++) { + if ((i >= highlightY && i <= y) || (i <= highlightY && i >= y)) { + data[i].setHighlighted(true); + } else { + data[i].setHighlighted(false); + } + } + } + } + + public void stopHighlight() { + highlight = false; + // loop through, selected and un-highlight + for (DataCell cell : data) { + if (cell.isHighlighted()) { + cell.setHighlighted(false); + if(!cell.isSelected()) { + cell.setSelected(true); + } + } + } + } + + public abstract void cursorUp(); + + public abstract void cursorDown(); + + public abstract void cursorLeft(); + + public abstract void cursorRight(); + + public void setRevertPoint() { + for (DataCell cell : data) { + cell.setRevertPoint(); + } + } + + public void undoAll() { + clearLiveDataTrace(); + for (DataCell cell : data) { + cell.undo(); + } + } + + public void undoSelected() { + clearLiveDataTrace(); + for (DataCell cell : data) { + // reset current value to original value + if (cell.isSelected()) { + cell.undo(); + } + } + } + + public byte[] saveFile(byte[] binData) { + if (userLevel <= getSettings().getUserLevel() && (userLevel < 5 || getSettings().isSaveDebugTables()) ) { + for (int i = 0; i < data.length; i++) { + // determine output byte values + byte[] output; + if (storageType != Settings.STORAGE_TYPE_FLOAT) { + // convert byte values + output = RomAttributeParser.parseIntegerValue((int) data[i].getBinValue(), endian, storageType); + for (int z = 0; z < storageType; z++) { // insert into file + binData[i * storageType + z + getStorageAddress() - ramOffset] = output[z]; + } + + } else { // float + // convert byte values + output = RomAttributeParser.floatToByte((float) data[i].getBinValue(), endian); + for (int z = 0; z < 4; z++) { // insert in to file + binData[i * 4 + z + getStorageAddress() - ramOffset] = output[z]; + } + } + } + } + return binData; + } + + public boolean isBeforeRam() { + return beforeRam; + } + + public void setBeforeRam(boolean beforeRam) { + this.beforeRam = beforeRam; + } + + @Override + public void addKeyListener(KeyListener listener) { + super.addKeyListener(listener); + for (DataCell cell : data) { + for (int z = 0; z < storageType; z++) { + cell.addKeyListener(listener); + } + } + } + + public void selectCellAt(int y) { + if(y >= 0 && y < data.length) { + clearSelection(); + data[y].setSelected(true); + highlightY = y; + ECUEditorManager.getECUEditor().getTableToolBar().updateTableToolBar(this); + } + } + + public void copySelection() { + Window ancestorWindow = SwingUtilities.getWindowAncestor(this); + + if(null != ancestorWindow) { + ancestorWindow.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + } + + ECUEditorManager.getECUEditor().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + copySelectionWorker = new CopySelectionWorker(this); + copySelectionWorker.execute(); + } + + public StringBuffer getTableAsString() { + StringBuffer output = new StringBuffer(Settings.BLANK); + for (int i = 0; i < data.length; i++) { + output.append(data[i].getRealValue()); + if (i < data.length - 1) { + output.append(Settings.TAB); + } + } + return output; + } + + public void copyTable() { + Window ancestorWindow = SwingUtilities.getWindowAncestor(this); + if(null != ancestorWindow) { + ancestorWindow.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + } + ECUEditorManager.getECUEditor().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + copyTableWorker = new CopyTableWorker(this); + copyTableWorker.execute(); + } + + public String getCellAsString(int index) { + return data[index].getText(); + } + + public void pasteValues(String[] input) { + //set real values + for (int i = 0; i < input.length; i++) { + try { + Double.parseDouble(input[i]); + data[i].setRealValue(input[i]); + } catch (NumberFormatException ex) { /* not a number, do nothing */ } + } + } + + public void paste() { + // TODO: This sounds like desearialize. + + StringTokenizer st = new StringTokenizer(Settings.BLANK); + try { + String input = (String) Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null).getTransferData(DataFlavor.stringFlavor); + st = new StringTokenizer(input); + } catch (UnsupportedFlavorException ex) { /* wrong paste type -- do nothing */ + } catch (IOException ex) { + } + + String pasteType = st.nextToken(); + + if ("[Table1D]".equalsIgnoreCase(pasteType)) { // copied entire table + int i = 0; + while (st.hasMoreTokens()) { + String currentToken = st.nextToken(); + try { + if (!data[i].getText().equalsIgnoreCase(currentToken)) { + data[i].setRealValue(currentToken); + } + } catch (ArrayIndexOutOfBoundsException ex) { /* table larger than target, ignore*/ } + i++; + } + } else if ("[Selection1D]".equalsIgnoreCase(pasteType)) { // copied selection + if (data[highlightY].isSelected()) { + int i = 0; + while (st.hasMoreTokens()) { + try { + data[highlightY + i].setRealValue(st.nextToken()); + } catch (ArrayIndexOutOfBoundsException ex) { /* paste larger than target, ignore */ } + i++; + } + } + } + } + + public void verticalInterpolate() { + horizontalInterpolate(); + } + + public void horizontalInterpolate() { + int[] coords = { getDataSize(), 0}; + DataCell[] tableData = getData(); + + int y; + for (y = 0; y < getDataSize(); y++) { + if (tableData[y].isSelected()) { + if (y < coords[0]) + coords[0] = y; + if (y > coords[1]) + coords[1] = y; + } + } + if (coords[1] - coords[0] > 1) { + double diff = (tableData[coords[0]].getRealValue() - tableData[coords[1]].getRealValue()) / (coords[1] - coords[0]); + if (Math.abs(diff) > 0) { + for (y = coords[0] + 1; y < coords[1]; y++) + data[y].setRealValue(String.valueOf(tableData[y - 1].getRealValue() - diff)); + } + } + } + + public void interpolate() { + horizontalInterpolate(); + } + + public void validateScaling() { + if (type != Settings.TABLE_SWITCH) { + + // make sure a scale is present + if (scales.isEmpty()) { + scales.add(new Scale()); + } + + for(Scale scale : scales) { + double startValue = 5; + double toReal = JEPUtil.evaluate(scale.getExpression(), startValue); // convert real world value of "5" + double endValue = JEPUtil.evaluate(scale.getByteExpression(), toReal); + + // if real to byte doesn't equal 5, report conflict + if (Math.abs(endValue - startValue) > .001) { + + JPanel panel = new JPanel(); + panel.setLayout(new GridLayout(4, 1)); + panel.add(new JLabel("The real value and byte value conversion expressions for table " + getName() + " are invalid.")); + panel.add(new JLabel("To real value: " + scale.getExpression())); + panel.add(new JLabel("To byte: " + scale.getByteExpression())); + + JCheckBox check = new JCheckBox("Always display this message", true); + check.setHorizontalAlignment(JCheckBox.RIGHT); + panel.add(check); + + check.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + getSettings().setCalcConflictWarning(((JCheckBox) e.getSource()).isSelected()); + } + } + ); + + JOptionPane.showMessageDialog(SwingUtilities.windowForComponent(this), panel, + "Warning", JOptionPane.ERROR_MESSAGE); + } + } + } + } + + public void populateCompareValues(Table otherTable) { + if(null == otherTable) { + return; + } + + DataCell[] compareData = otherTable.getData(); + if(data.length != compareData.length) { + return; + } + + clearLiveDataTrace(); + + int i = 0; + for(DataCell cell : data) { + cell.setCompareValue(compareData[i]); + i++; + } + + calcCellRanges(); + drawTable(); + } + + public void setCompareDisplay(int compareDisplay) { + this.compareDisplay = compareDisplay; + drawTable(); + } + + public int getCompareDisplay() { + return this.compareDisplay; + } + + public void setCompareValueType(int compareValueType) { + this.compareValueType = compareValueType; + drawTable(); + } + + public int getCompareValueType() { + return this.compareValueType; + } + + public int getUserLevel() { + return userLevel; + } + + public void setUserLevel(int userLevel) { + this.userLevel = userLevel; + if (userLevel > 5) { + userLevel = 5; + } else if (userLevel < 1) { + userLevel = 1; + } + } + + public void setScaleByName(String scaleName) throws NameNotFoundException { + for(Scale scale : scales) { + if(scale.getName().equalsIgnoreCase(scaleName)) { + Scale currentScale = getCurrentScale(); + if(currentScale == null || !currentScale.equals(scale)) { + this.setCurrentScale(scale); + } + return; + } + } + + throw new NameNotFoundException(); + } + + public void setCurrentScale(Scale curScale) { + this.curScale = curScale; + updateTableLabel(); + drawTable(); + } + + public Settings getSettings() + { + return SettingsManager.getSettings(); + } + + public TableToolBar getToolbar() + { + return ECUEditorManager.getECUEditor().getTableToolBar(); + } + + public boolean isLocked() { + return locked; + } + + public void setLocked(boolean locked) { + this.locked = locked; + } + + public void setOverlayLog(boolean overlayLog) { + this.overlayLog = overlayLog; + if (overlayLog) { + clearLiveDataTrace(); + } + } + + public boolean getOverlayLog() + { + return this.overlayLog; + } + + public double getLiveAxisValue() { + try { + return Double.parseDouble(liveAxisValue); + } catch (NumberFormatException e) { + return 0.0; + } + } + + public abstract boolean isLiveDataSupported(); + + public abstract boolean isButtonSelected(); + + public void highlightLiveData(String liveVal) { + if (getOverlayLog()) { + double liveValue = 0.0; + try{ + liveValue = Double.parseDouble(liveVal); + } catch(NumberFormatException nex) { + return; + } + + int startIdx = data.length; + for (int i = 0; i < data.length; i++) { + double currentValue = data[i].getRealValue(); + if (liveValue == currentValue) { + startIdx = i; + break; + } else if (liveValue < currentValue){ + startIdx = i-1; + break; + } + } + + setLiveDataIndex(startIdx); + DataCell cell = data[getLiveDataIndex()]; + cell.setLiveDataTrace(true); + cell.setLiveDataTraceValue(liveVal); + getToolbar().setLiveDataValue(liveVal); + } + } + + public void updateLiveDataHighlight() { + if (getOverlayLog()) { + data[getLiveDataIndex()].setLiveDataTrace(true); + } + } + + public int getLiveDataIndex() { + return liveDataIndex; + } + + public void setLiveDataIndex(int index) { + if (index < 0) { + index = 0; + } + if (index >= data.length) { + index = data.length - 1; + } + this.liveDataIndex = index; + } + + public void clearLiveDataTrace() { + for (DataCell cell : data) { + cell.setLiveDataTrace(false); + } + } + + public String getLogParamString() { + return getName()+ ":" + getLogParam(); + } + + public Table getCompareTable() { + return compareTable; + } + + public void setCompareTable(Table compareTable) { + this.compareTable = compareTable; + } + + public void updateTableLabel() { + if(null == name || name.isEmpty()) { + ;// Do not update label. + } else if(null == getCurrentScale () || "0x" == getCurrentScale().getUnit()) { + // static or no scale exists. + tableLabel.setText(getName()); + } else { + tableLabel.setText(getName() + " (" + getCurrentScale().getUnit() + ")"); + } + } + + public void colorCells() { + calcCellRanges(); + drawTable(); + } + + public void refreshCompare() { + populateCompareValues(getCompareTable()); + } + + public boolean isStaticDataTable() { + return staticDataTable; + } + + public void setStaticDataTable(boolean staticDataTable) { + this.staticDataTable = staticDataTable; + } +} + +class CopySelectionWorker extends SwingWorker { + Table table; + + public CopySelectionWorker(Table table) { + this.table = table; + } + + @Override + protected Void doInBackground() throws Exception { + // find bounds of selection + // coords[0] = x min, y min, x max, y max + String newline = System.getProperty("line.separator"); + String output = "[Selection1D]" + newline; + boolean copy = false; + int[] coords = new int[2]; + coords[0] = table.getDataSize(); + + for (int i = 0; i < table.getDataSize(); i++) { + if (table.getData()[i].isSelected()) { + if (i < coords[0]) { + coords[0] = i; + copy = true; + } + if (i > coords[1]) { + coords[1] = i; + copy = true; + } + } + } + //make a string of the selection + for (int i = coords[0]; i <= coords[1]; i++) { + if (table.getData()[i].isSelected()) { + output = output + table.getData()[i].getText(); + } else { + output = output + "x"; // x represents non-selected cell + } + if (i < coords[1]) { + output = output + "\t"; + } + } + //copy to clipboard + if (copy) { + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(output), null); + } + return null; + } + + @Override + public void done() { + Window ancestorWindow = SwingUtilities.getWindowAncestor(table); + if(null != ancestorWindow) { + ancestorWindow.setCursor(null); + } + table.setCursor(null); + ECUEditorManager.getECUEditor().setCursor(null); + } +} + +class CopyTableWorker extends SwingWorker { + Table table; + + public CopyTableWorker(Table table) { + this.table = table; + } + + @Override + protected Void doInBackground() throws Exception { + String tableHeader = table.getSettings().getTableHeader(); + StringBuffer output = new StringBuffer(tableHeader); + output.append(table.getTableAsString()); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(String.valueOf(output)), null); + return null; + } + + @Override + public void done() { + Window ancestorWindow = SwingUtilities.getWindowAncestor(table); + if(null != ancestorWindow) { + ancestorWindow.setCursor(null); + } + table.setCursor(null); + ECUEditorManager.getECUEditor().setCursor(null); + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/maps/Table1D.java b/java_console/romraider/src/com/romraider/maps/Table1D.java new file mode 100644 index 0000000000..21a11ebb52 --- /dev/null +++ b/java_console/romraider/src/com/romraider/maps/Table1D.java @@ -0,0 +1,306 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.maps; + +import java.awt.BorderLayout; + +import javax.swing.JLabel; + +import com.romraider.Settings; + +public class Table1D extends Table { + private static final long serialVersionUID = -8747180767803835631L; + private Table axisParent = null; + + public Table1D() { + super(); + } + + public void setAxisParent(Table axisParent) { + this.axisParent = axisParent; + } + + public Table getAxisParent() { + return axisParent; + } + + public void addStaticDataCell(DataCell input) { + for(int i = 0; i < data.length; i++) { + if(data[i] == null) { + data[i] = input; + data[i].setY(i); + break; + } + } + } + + @Override + public void populateTable(byte[] input, int romRamOffset) throws ArrayIndexOutOfBoundsException, IndexOutOfBoundsException { + centerLayout.setRows(1); + centerLayout.setColumns(this.getDataSize()); + + super.populateTable(input, romRamOffset); + + // add to table + for (int i = 0; i < this.getDataSize(); i++) { + centerPanel.add(this.getDataCell(i)); + } + + if(null == name || name.isEmpty()) { + ;// Do not add label. + } else if(null == getCurrentScale () || "0x" == getCurrentScale().getUnit()) { + // static or no scale exists. + tableLabel = new JLabel(getName(), JLabel.CENTER); + add(tableLabel, BorderLayout.NORTH); + } else { + tableLabel = new JLabel(getName() + " (" + getCurrentScale().getUnit() + ")", JLabel.CENTER); + add(tableLabel, BorderLayout.NORTH); + } + } + + @Override + public String toString() { + return super.toString() + " (1D)"; + } + + @Override + public void cursorUp() { + if (type == Settings.TABLE_Y_AXIS) { + if (highlightY > 0 && data[highlightY].isSelected()) { + selectCellAt(highlightY - 1); + } + } else if (type == Settings.TABLE_X_AXIS) { + // Y axis is on top.. nothing happens + } else if (type == Settings.TABLE_1D) { + // no where to move up to + } + } + + @Override + public void cursorDown() { + if (type == Settings.TABLE_Y_AXIS) { + if (getAxisParent().getType() == Settings.TABLE_3D) { + if (highlightY < getDataSize() - 1 && data[highlightY].isSelected()) { + selectCellAt(highlightY + 1); + } + } else if (getAxisParent().getType() == Settings.TABLE_2D) { + if (data[highlightY].isSelected()) { + getAxisParent().selectCellAt(highlightY); + } + } + } else if (type == Settings.TABLE_X_AXIS && data[highlightY].isSelected()) { + ((Table3D) getAxisParent()).selectCellAt(highlightY, this); + } else if (type == Settings.TABLE_1D) { + // no where to move down to + } + } + + @Override + public void cursorLeft() { + if (type == Settings.TABLE_Y_AXIS) { + // X axis is on left.. nothing happens + if (getAxisParent().getType() == Settings.TABLE_2D) { + if (data[highlightY].isSelected()) { + selectCellAt(highlightY - 1); + } + } + } else if (type == Settings.TABLE_X_AXIS && data[highlightY].isSelected()) { + if (highlightY > 0) { + selectCellAt(highlightY - 1); + } + } else if (type == Settings.TABLE_1D && data[highlightY].isSelected()) { + if (highlightY > 0) { + selectCellAt(highlightY - 1); + } + } + } + + @Override + public void cursorRight() { + if (type == Settings.TABLE_Y_AXIS && data[highlightY].isSelected()) { + if (getAxisParent().getType() == Settings.TABLE_3D) { + ((Table3D) getAxisParent()).selectCellAt(highlightY, this); + } else if (getAxisParent().getType() == Settings.TABLE_2D) { + selectCellAt(highlightY + 1); + } + } else if (type == Settings.TABLE_X_AXIS && data[highlightY].isSelected()) { + if (highlightY < getDataSize() - 1) { + selectCellAt(highlightY + 1); + } + } else if (type == Settings.TABLE_1D && data[highlightY].isSelected()) { + if (highlightY < getDataSize() - 1) { + selectCellAt(highlightY + 1); + } + } + } + + @Override + public void clearSelection() { + // Call to the axis parent. The axis parent should then call to clear this data. + getAxisParent().clearSelection(); + } + + @Override + public void startHighlight(int x, int y) { + Table axisParent = getAxisParent(); + axisParent.clearSelectedData(); + + if(axisParent instanceof Table3D) { + Table3D table3D = (Table3D) axisParent; + if(getType() == Settings.TABLE_X_AXIS) { + table3D.getYAxis().clearSelectedData(); + } else if (getType() == Settings.TABLE_Y_AXIS) { + table3D.getXAxis().clearSelectedData(); + } + } else if (axisParent instanceof Table2D) { + ((Table2D) axisParent).getAxis().clearSelectedData(); + } + + + super.startHighlight(x, y); + } + + @Override + public String getCellAsString(int index) { + return data[index].getText(); + } + + @Override + public void highlightLiveData(String liveVal) { + if (getOverlayLog()) { + double liveValue = 0.0; + try{ + liveValue = Double.parseDouble(liveVal); + } catch(NumberFormatException nex) { + return; + } + + int startIdx = data.length; + for (int i = 0; i < data.length; i++) { + double currentValue = 0.0; + if(isStaticDataTable() && null != data[i].getStaticText()) { + try { + currentValue = Double.parseDouble(data[i].getStaticText()); + } catch(NumberFormatException nex) { + return; + } + } else { + currentValue = data[i].getRealValue(); + } + + if (liveValue == currentValue) { + startIdx = i; + break; + } else if (liveValue < currentValue){ + startIdx = i-1; + break; + } + } + + setLiveDataIndex(startIdx); + DataCell cell = data[getLiveDataIndex()]; + cell.setLiveDataTrace(true); + cell.setLiveDataTraceValue(liveVal); + getToolbar().setLiveDataValue(liveVal); + } + getAxisParent().updateLiveDataHighlight(); + } + + @Override + public boolean isLiveDataSupported() { + return false; + } + + @Override + public boolean isButtonSelected() { + return true; + } + + public boolean isAxis() { + return getType() == Settings.TABLE_X_AXIS || + getType() == Settings.TABLE_Y_AXIS || isStaticDataTable(); + } + + @Override + public boolean equals(Object other) { + try { + if(null == other) { + return false; + } + + if(other == this) { + return true; + } + + if(!(other instanceof Table1D)) { + return false; + } + + Table1D otherTable = (Table1D)other; + + if(this.isAxis() != otherTable.isAxis()) { + return false; + } + + if(this.data.length != otherTable.data.length) + { + return false; + } + + if(this.data.equals(otherTable.data)) + { + return true; + } + + // Compare Bin Values + for(int i=0 ; i < this.data.length ; i++) { + if(! this.data[i].equals(otherTable.data[i])) { + return false; + } + } + + return true; + } catch(Exception ex) { + // TODO: Log Exception. + return false; + } + } + + @Override + public void updateTableLabel() { + this.getAxisParent().updateTableLabel(); + } + + @Override + public StringBuffer getTableAsString() { + if(isStaticDataTable()) { + StringBuffer output = new StringBuffer(Settings.BLANK); + for (int i = 0; i < data.length; i++) { + output.append(data[i].getStaticText()); + if (i < data.length - 1) { + output.append(Settings.TAB); + } + } + return output; + } else { + return super.getTableAsString(); + } + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/maps/Table2D.java b/java_console/romraider/src/com/romraider/maps/Table2D.java new file mode 100644 index 0000000000..7b91b89fa9 --- /dev/null +++ b/java_console/romraider/src/com/romraider/maps/Table2D.java @@ -0,0 +1,509 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.maps; + +import static com.romraider.util.ParamChecker.isNullOrEmpty; + +import java.awt.BorderLayout; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.event.KeyListener; +import java.io.IOException; +import java.util.StringTokenizer; + +import javax.naming.NameNotFoundException; +import javax.swing.JLabel; +import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; + +import com.romraider.Settings; +import com.romraider.editor.ecu.ECUEditorManager; +import com.romraider.util.SettingsManager; + +public class Table2D extends Table { + private static final long serialVersionUID = -7684570967109324784L; + private Table1D axis = new Table1D(); + private JLabel axisLabel; + + private CopyTable2DWorker copyTable2DWorker; + private CopySelection2DWorker copySelection2DWorker; + + public Table2D() { + super(); + verticalOverhead += 18; + } + + public Table1D getAxis() { + return axis; + } + + public void setAxis(Table1D axis) { + this.axis = axis; + axis.setAxisParent(this); + } + + @Override + public String toString() { + return super.toString() + " (2D)";// + axis; + } + + @Override + public void populateCompareValues(Table otherTable) { + if(null == otherTable || !(otherTable instanceof Table2D)) { + return; + } + + Table2D compareTable2D = (Table2D) otherTable; + if(data.length != compareTable2D.data.length || + axis.data.length != compareTable2D.axis.data.length) { + return; + } + + super.populateCompareValues(otherTable); + axis.populateCompareValues(compareTable2D.getAxis()); + } + + @Override + public void refreshCompare() { + populateCompareValues(getCompareTable()); + axis.refreshCompare(); + } + + @Override + public StringBuffer getTableAsString() { + StringBuffer output = new StringBuffer(Settings.BLANK); + output.append(axis.getTableAsString()); + output.append(Settings.NEW_LINE); + output.append(super.getTableAsString()); + return output; + } + + @Override + public Dimension getFrameSize() { + int height = verticalOverhead + cellHeight * 2; + int width = horizontalOverhead + data.length * cellWidth; + if (height < minHeight) { + height = minHeight; + } + int minWidth = isLiveDataSupported() ? minWidthOverlay : minWidthNoOverlay; + if (width < minWidth) { + width = minWidth; + } + return new Dimension(width, height); + } + + @Override + public void populateTable(byte[] input, int romRamOffset) throws ArrayIndexOutOfBoundsException, IndexOutOfBoundsException { + centerLayout.setRows(2); + centerLayout.setColumns(this.getDataSize()); + + try { + axis.populateTable(input, romRamOffset); + super.populateTable(input, romRamOffset); + } catch (ArrayIndexOutOfBoundsException ex) { + throw new ArrayIndexOutOfBoundsException(); + } + + // add to table + for (int i = 0; i < this.getDataSize(); i++) { + centerPanel.add(axis.getDataCell(i)); + } + if (flip) { + for (int i = this.getDataSize() - 1; i >= 0; i--) { + centerPanel.add(this.getDataCell(i)); + } + } else { + for (int i = 0; i < this.getDataSize(); i++) { + centerPanel.add(this.getDataCell(i)); + } + } + + if(null == axis.getName() || axis.getName().isEmpty() || "" == axis.getName()) { + ;// Do not add label. + } else if(null == axis.getCurrentScale() || "0x" == axis.getCurrentScale().getUnit()) { + // static or no scale exists. + axisLabel = new JLabel(axis.getName(), JLabel.CENTER); + add(axisLabel, BorderLayout.NORTH); + } else { + axisLabel = new JLabel(axis.getName() + " (" + axis.getCurrentScale().getUnit() + ")", JLabel.CENTER); + add(axisLabel, BorderLayout.NORTH); + } + + tableLabel = new JLabel(getCurrentScale().getUnit(), JLabel.CENTER); + add(tableLabel, BorderLayout.SOUTH); + repaint(); + } + + @Override + public void updateTableLabel() { + if(null == axis.getName() || axis.getName().length() < 1 || "" == axis.getName()) { + ;// Do not update label. + } else if(null == axis.getCurrentScale() || "0x" == axis.getCurrentScale().getUnit()) { + // static or no scale exists. + axisLabel.setText(axis.getName()); + } else { + axisLabel.setText(axis.getName() + " (" + axis.getCurrentScale().getUnit() + ")"); + } + + tableLabel.setText(getCurrentScale().getUnit()); + } + + @Override + public void clearSelection() { + axis.clearSelectedData(); + clearSelectedData(); + } + + @Override + public void setRevertPoint() { + super.setRevertPoint(); + axis.setRevertPoint(); + } + + @Override + public void undoAll() { + super.undoAll(); + axis.undoAll(); + } + + @Override + public byte[] saveFile(byte[] binData) { + binData = super.saveFile(binData); + binData = axis.saveFile(binData); + return binData; + } + + @Override + public void addKeyListener(KeyListener listener) { + super.addKeyListener(listener); + axis.addKeyListener(listener); + } + + @Override + public void cursorUp() { + if (data[highlightY].isSelected()) { + axis.selectCellAt(highlightY); + } + } + + @Override + public void drawTable() { + super.drawTable(); + axis.drawTable(); + } + + @Override + public void cursorDown() { + axis.cursorDown(); + } + + @Override + public void cursorLeft() { + if (highlightY > 0 && data[highlightY].isSelected()) { + selectCellAt(highlightY - 1); + } else { + axis.cursorLeft(); + } + } + + @Override + public void cursorRight() { + if (highlightY < data.length - 1 && data[highlightY].isSelected()) { + selectCellAt(highlightY + 1); + } else { + axis.cursorRight(); + } + } + + @Override + public void startHighlight(int x, int y) { + axis.clearSelectedData(); + super.startHighlight(x, y); + } + + @Override + public void copySelection() { + Window ancestorWindow = SwingUtilities.getWindowAncestor(this); + if(null != ancestorWindow) { + ancestorWindow.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + } + ECUEditorManager.getECUEditor().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + super.copySelection(); + copySelection2DWorker = new CopySelection2DWorker(this); + copySelection2DWorker.execute(); + } + + @Override + public void copyTable() { + Window ancestorWindow = SwingUtilities.getWindowAncestor(this); + if(null != ancestorWindow) { + ancestorWindow.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + } + ECUEditorManager.getECUEditor().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + copyTable2DWorker = new CopyTable2DWorker(this); + copyTable2DWorker.execute(); + } + + @Override + public void paste() { + StringTokenizer st = new StringTokenizer(""); + String input = ""; + try { + input = (String) Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null).getTransferData(DataFlavor.stringFlavor); + st = new StringTokenizer(input); + } catch (UnsupportedFlavorException ex) { /* wrong paste type -- do nothing */ + } catch (IOException ex) { + } + + String pasteType = st.nextToken(); + + if (pasteType.equalsIgnoreCase("[Table2D]")) { // Paste table + String axisValues = "[Table1D]" + Settings.NEW_LINE + st.nextToken(Settings.NEW_LINE); + String dataValues = "[Table1D]" + Settings.NEW_LINE + st.nextToken(Settings.NEW_LINE); + + // put axis in clipboard and paste + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(axisValues), null); + axis.paste(); + // put datavalues in clipboard and paste + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(dataValues), null); + super.paste(); + // reset clipboard + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(input), null); + + } else if (pasteType.equalsIgnoreCase("[Selection1D]")) { // paste selection + if (data[highlightY].isSelected()) { + super.paste(); + } else { + axis.paste(); + } + } + } + + @Override + public void interpolate() { + super.interpolate(); + this.getAxis().interpolate(); + } + + @Override + public void verticalInterpolate() { + super.verticalInterpolate(); + this.getAxis().verticalInterpolate(); + } + + @Override + public void horizontalInterpolate() { + super.horizontalInterpolate(); + this.getAxis().horizontalInterpolate(); + } + + @Override + public boolean isLiveDataSupported() { + return !isNullOrEmpty(axis.getLogParam()); + } + + @Override + public boolean isButtonSelected() { + return true; + } + + @Override + public void clearLiveDataTrace() { + super.clearLiveDataTrace(); + axis.clearLiveDataTrace(); + } + + @Override + public void updateLiveDataHighlight() { + if (getOverlayLog()) { + data[axis.getLiveDataIndex()].setLiveDataTrace(true); + } + } + + @Override + public String getLogParamString() { + StringBuilder sb = new StringBuilder(); + sb.append(axis.getLogParamString()+ ", "); + sb.append(getName()+ ":" + getLogParam()); + return sb.toString(); + } + + @Override + public void setOverlayLog(boolean overlayLog) { + super.setOverlayLog(overlayLog); + axis.setOverlayLog(overlayLog); + if (overlayLog) { + axis.clearLiveDataTrace(); + } + } + + @Override + public void setCompareDisplay(int compareDisplay) { + super.setCompareDisplay(compareDisplay); + axis.setCompareDisplay(compareDisplay); + } + + @Override + public void setCompareValueType(int compareValueType) { + super.setCompareValueType(compareValueType); + axis.setCompareValueType(compareValueType); + } + + @Override + public void setCurrentScale(Scale curScale) { + if(SettingsManager.getSettings().isScaleHeadersAndData() && !axis.isStaticDataTable()) { + try { + this.axis.setScaleByName(curScale.getName()); + } catch (NameNotFoundException e) { + try { + this.axis.setScaleByName(SettingsManager.getSettings().getDefaultScale()); + } catch (NameNotFoundException e1) { + } + } + } + this.curScale = curScale; + updateTableLabel(); + drawTable(); + } + + @Override + public boolean equals(Object other) { + try { + if(null == other) { + return false; + } + + if(other == this) { + return true; + } + + if(!(other instanceof Table2D)) { + return false; + } + + Table2D otherTable = (Table2D)other; + + if( (null == this.getName() && null == otherTable.getName()) + || (this.getName().isEmpty() && otherTable.getName().isEmpty()) ) { + ;// Skip name compare if name is null or empty. + } else if (!this.getName().equalsIgnoreCase(otherTable.getName())) { + return false; + } + + if(!this.axis.equals(otherTable.axis)) { + return false; + } + + if(this.data.length != otherTable.data.length) + { + return false; + } + + if(this.data.equals(otherTable.data)) + { + return true; + } + + // Compare Bin Values + for(int i = 0 ; i < this.data.length ; i++) { + if(! this.data[i].equals(otherTable.data[i])) { + return false; + } + } + + return true; + } catch(Exception ex) { + // TODO: Log Exception. + return false; + } + } + + @Override + public void repaint() { + super.repaint(); + if(null != axis) { + axis.repaint(); + } + } +} + +class CopySelection2DWorker extends SwingWorker { + Table2D table; + Table extendedTable; + + public CopySelection2DWorker(Table2D table) + { + this.table = table; + } + + @Override + protected Void doInBackground() throws Exception { + table.getAxis().copySelection(); + return null; + } + + @Override + public void done() { + Window ancestorWindow = SwingUtilities.getWindowAncestor(table); + if(null != ancestorWindow) { + ancestorWindow.setCursor(null); + } + table.setCursor(null); + ECUEditorManager.getECUEditor().setCursor(null); + } +} + +class CopyTable2DWorker extends SwingWorker { + Table2D table; + + public CopyTable2DWorker(Table2D table) + { + this.table = table; + } + + @Override + protected Void doInBackground() throws Exception { + String tableHeader = table.getSettings().getTable2DHeader(); + StringBuffer output = new StringBuffer(tableHeader); + output.append(table.getTableAsString()); + + //copy to clipboard + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(output.toString()), null); + return null; + + } + + @Override + public void done() { + Window ancestorWindow = SwingUtilities.getWindowAncestor(table); + if(null != ancestorWindow) { + ancestorWindow.setCursor(null); + } + table.setCursor(null); + ECUEditorManager.getECUEditor().setCursor(null); + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/maps/Table3D.java b/java_console/romraider/src/com/romraider/maps/Table3D.java new file mode 100644 index 0000000000..25e02550b8 --- /dev/null +++ b/java_console/romraider/src/com/romraider/maps/Table3D.java @@ -0,0 +1,1142 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.maps; + +import static com.romraider.util.ParamChecker.isNullOrEmpty; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.event.KeyListener; +import java.io.IOException; +import java.util.StringTokenizer; + +import javax.naming.NameNotFoundException; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; + +import com.romraider.Settings; +import com.romraider.editor.ecu.ECUEditorManager; +import com.romraider.logger.ecu.ui.swing.vertical.VerticalLabelUI; +import com.romraider.util.SettingsManager; +import com.romraider.xml.RomAttributeParser; + +public class Table3D extends Table { + + private static final long serialVersionUID = 3103448753263606599L; + private Table1D xAxis = new Table1D(); + private Table1D yAxis = new Table1D(); + private JLabel xAxisLabel; + private JLabel yAxisLabel; + + @SuppressWarnings("hiding") + DataCell[][] data = new DataCell[1][1]; + private boolean swapXY = false; + private boolean flipX = false; + private boolean flipY = false; + + CopyTable3DWorker copyTable3DWorker; + CopySelection3DWorker copySelection3DWorker; + + public Table3D() { + super(); + verticalOverhead += 39; + horizontalOverhead += 10; + } + + public Table1D getXAxis() { + return xAxis; + } + + public void setXAxis(Table1D xAxis) { + this.xAxis = xAxis; + xAxis.setAxisParent(this); + } + + public Table1D getYAxis() { + return yAxis; + } + + public void setYAxis(Table1D yAxis) { + this.yAxis = yAxis; + yAxis.setAxisParent(this); + } + + public boolean getSwapXY() { + return swapXY; + } + + public void setSwapXY(boolean swapXY) { + this.swapXY = swapXY; + } + + public boolean getFlipX() { + return flipX; + } + + public void setFlipX(boolean flipX) { + this.flipX = flipX; + } + + public boolean getFlipY() { + return flipY; + } + + public void setFlipY(boolean flipY) { + this.flipY = flipY; + } + + public void setSizeX(int size) { + data = new DataCell[size][data[0].length]; + centerLayout.setColumns(size + 1); + } + + public int getSizeX() { + return data.length; + } + + public void setSizeY(int size) { + data = new DataCell[data.length][size]; + centerLayout.setRows(size + 1); + } + + public int getSizeY() { + return data[0].length; + } + + @Override + public void drawTable() { + for(DataCell[] column : data) { + for(DataCell cell : column) { + if(null != cell) { + cell.drawCell(); + } + } + } + xAxis.drawTable(); + yAxis.drawTable(); + } + + @Override + public void populateTable(byte[] input, int romRamOffset) throws NullPointerException, ArrayIndexOutOfBoundsException, IndexOutOfBoundsException { + // fill first empty cell + centerPanel.add(new JLabel()); + if (!beforeRam) { + this.ramOffset = romRamOffset; + } + + // temporarily remove lock + boolean tempLock = locked; + locked = false; + + // populate axiis + try { + xAxis.populateTable(input, romRamOffset); + yAxis.populateTable(input, romRamOffset); + } catch (ArrayIndexOutOfBoundsException ex) { + throw new ArrayIndexOutOfBoundsException(); + } + + for (int x = 0; x < xAxis.getDataSize(); x++) { + centerPanel.add(xAxis.getDataCell(x)); + } + + int offset = 0; + + int iMax = swapXY ? xAxis.getDataSize() : yAxis.getDataSize(); + int jMax = swapXY ? yAxis.getDataSize() : xAxis.getDataSize(); + for (int i = 0; i < iMax; i++) { + for (int j = 0; j < jMax; j++) { + + int x = flipY ? jMax - j - 1 : j; + int y = flipX ? iMax - i - 1 : i; + if (swapXY) { + int z = x; + x = y; + y = z; + } + + + double cellBinValue; + + // populate data cells + if (storageType == Settings.STORAGE_TYPE_FLOAT) { //float storage type + byte[] byteValue = new byte[4]; + byteValue[0] = input[getStorageAddress() + offset * 4 - ramOffset]; + byteValue[1] = input[getStorageAddress() + offset * 4 - ramOffset + 1]; + byteValue[2] = input[getStorageAddress() + offset * 4 - ramOffset + 2]; + byteValue[3] = input[getStorageAddress() + offset * 4 - ramOffset + 3]; + cellBinValue = RomAttributeParser.byteToFloat(byteValue, endian); + + } else { // integer storage type + cellBinValue = RomAttributeParser.parseByteValue(input, + endian, + getStorageAddress() + offset * storageType - ramOffset, + storageType, + signed); + } + + // show locked cell + if (tempLock) { + data[x][y].setForeground(Color.GRAY); + } + + data[x][y] = new DataCell(this, cellBinValue, x, y); + offset++; + } + } + + for (int y = 0; y < yAxis.getDataSize(); y++) { + centerPanel.add(yAxis.getDataCell(y)); + for (int x = 0; x < xAxis.getDataSize(); x++) { + centerPanel.add(data[x][y]); + } + } + + // reset locked status + locked = tempLock; + + GridLayout topLayout = new GridLayout(2, 1); + JPanel topPanel = new JPanel(topLayout); + this.add(topPanel, BorderLayout.NORTH); + topPanel.add(new JLabel(getName(), JLabel.CENTER), BorderLayout.NORTH); + + if(null == xAxis.getName() || xAxis.getName().length() < 1 || "" == xAxis.getName()) { + ;// Do not add label. + } else if(null == xAxis.getCurrentScale() || "0x" == xAxis.getCurrentScale().getUnit()) { + // static or no scale exists. + xAxisLabel = new JLabel(xAxis.getName(), JLabel.CENTER); + topPanel.add(xAxisLabel, BorderLayout.NORTH); + } else { + xAxisLabel = new JLabel(xAxis.getName() + " (" + xAxis.getCurrentScale().getUnit() + ")", JLabel.CENTER); + topPanel.add(xAxisLabel, BorderLayout.NORTH); + } + + yAxisLabel = null; + if(null == yAxis.getName() || yAxis.getName().length() < 1 || "" == yAxis.getName()) { + ;// Do not add label. + } else if(null == yAxis.getCurrentScale() || "0x" == yAxis.getCurrentScale().getUnit()) { + // static or no scale exists. + yAxisLabel = new JLabel(yAxis.getName()); + } else { + yAxisLabel = new JLabel(yAxis.getName() + " (" + yAxis.getCurrentScale().getUnit() + ")"); + } + + yAxisLabel.setUI(new VerticalLabelUI(false)); + add(yAxisLabel, BorderLayout.WEST); + + tableLabel = new JLabel(getCurrentScale().getUnit(), JLabel.CENTER); + add(tableLabel, BorderLayout.SOUTH); + + calcCellRanges(); + } + + @Override + public void updateTableLabel() { + if(null == xAxis.getName() || xAxis.getName().length() < 1 || "" == xAxis.getName()) { + ;// Do not update label. + } else if(null == xAxis.getCurrentScale() || "0x" == xAxis.getCurrentScale().getUnit()) { + // static or no scale exists. + xAxisLabel.setText(xAxis.getName()); + } else { + xAxisLabel.setText(xAxis.getName() + " (" + xAxis.getCurrentScale().getUnit() + ")"); + } + + if(null == yAxis.getName() || yAxis.getName().length() < 1 || "" == yAxis.getName()) { + ;// Do not update label. + } else if(null == yAxis.getCurrentScale() || "0x" == yAxis.getCurrentScale().getUnit()) { + // static or no scale exists. + yAxisLabel.setText(yAxis.getName()); + } else { + yAxisLabel.setText(yAxis.getName() + " (" + yAxis.getCurrentScale().getUnit() + ")"); + } + + tableLabel.setText(getCurrentScale().getUnit()); + } + + @Override + public void calcCellRanges() { + double binMax = data[0][0].getBinValue(); + double binMin = data[0][0].getBinValue(); + + double compareMax = data[0][0].getCompareValue(); + double compareMin = data[0][0].getCompareValue(); + + for(DataCell[] column : data) { + for(DataCell cell : column) { + // Calc bin + if(binMax < cell.getBinValue()) { + binMax = cell.getBinValue(); + } + if(binMin > cell.getBinValue()) { + binMin = cell.getBinValue(); + } + + // Calc compare + double compareValue = cell.getCompareValue(); + if(compareMax < compareValue) { + compareMax = compareValue; + } + if(compareMin > compareValue) { + compareMin = compareValue; + } + } + } + setMaxBin(binMax); + setMinBin(binMin); + setMaxCompare(compareMax); + setMinCompare(compareMin); + } + + @Override + public StringBuffer getTableAsString() { + StringBuffer output = new StringBuffer(Settings.BLANK); + + output.append(xAxis.getTableAsString()); + output.append(Settings.NEW_LINE); + + for (int y = 0; y < getSizeY(); y++) { + output.append(yAxis.data[y].getRealValue()); + output.append(Settings.TAB); + + for (int x = 0; x < getSizeX(); x++) { + output.append(data[x][y].getRealValue()); + if (x < getSizeX() - 1) { + output.append(Settings.TAB); + } + } + + if (y < getSizeY() - 1) { + output.append(Settings.NEW_LINE); + } + } + + return output; + } + + @Override + public void populateCompareValues(Table otherTable) { + if(null == otherTable || !(otherTable instanceof Table3D)) { + return; + } + + Table3D compareTable3D = (Table3D) otherTable; + if(data.length != compareTable3D.data.length || + data[0].length != compareTable3D.data[0].length || + xAxis.getDataSize() != compareTable3D.xAxis.getDataSize() || + yAxis.getDataSize() != compareTable3D.yAxis.getDataSize()) { + return; + } + + clearLiveDataTrace(); + + int x=0; + for (DataCell[] column : data) { + int y = 0; + for(DataCell cell : column) { + cell.setCompareValue(compareTable3D.data[x][y]); + y++; + } + x++; + } + + xAxis.populateCompareValues(compareTable3D.getXAxis()); + yAxis.populateCompareValues(compareTable3D.getYAxis()); + + calcCellRanges(); + drawTable(); + } + + @Override + public void refreshCompare() { + populateCompareValues(getCompareTable()); + xAxis.refreshCompare(); + yAxis.refreshCompare(); + } + + @Override + public Dimension getFrameSize() { + int height = verticalOverhead + cellHeight * data[0].length; + int width = horizontalOverhead + data.length * cellWidth; + if (height < minHeight) { + height = minHeight; + } + int minWidth = isLiveDataSupported() ? minWidthOverlay : minWidthNoOverlay; + if (width < minWidth) { + width = minWidth; + } + return new Dimension(width, height); + } + + @Override + public String toString() { + return super.toString() + " (3D)";/* + + "\n Flip X: " + flipX + + "\n Size X: " + data.length + + "\n Flip Y: " + flipY + + "\n Size Y: " + data[0].length + + "\n Swap X/Y: " + swapXY + + xAxis + + yAxis;*/ + } + + @Override + public void increment(double increment) { + if (!locked) { + for (int x = 0; x < this.getSizeX(); x++) { + for (int y = 0; y < this.getSizeY(); y++) { + if (data[x][y].isSelected()) { + data[x][y].increment(increment); + } + } + } + } + } + + @Override + public void multiply(double factor) { + if (!locked) { + for (int x = 0; x < this.getSizeX(); x++) { + for (int y = 0; y < this.getSizeY(); y++) { + if (data[x][y].isSelected()) { + data[x][y].multiply(factor); + } + } + } + } + } + + @Override + public void clearSelection() { + xAxis.clearSelectedData(); + yAxis.clearSelectedData(); + clearSelectedData(); + } + + @Override + public void clearSelectedData() { + for (int x = 0; x < this.getSizeX(); x++) { + for (int y = 0; y < this.getSizeY(); y++) { + data[x][y].setSelected(false); + } + } + } + + @Override + public void highlight(int xCoord, int yCoord) { + if (highlight) { + for (int x = 0; x < this.getSizeX(); x++) { + for (int y = 0; y < this.getSizeY(); y++) { + if (((y >= highlightY && y <= yCoord) || + (y <= highlightY && y >= yCoord)) && + ((x >= highlightX && x <= xCoord) || + (x <= highlightX && x >= xCoord))) { + data[x][y].setHighlighted(true); + } else { + data[x][y].setHighlighted(false); + } + } + } + } + } + + @Override + public void stopHighlight() { + highlight = false; + // loop through, selected and un-highlight + for (int x = 0; x < this.getSizeX(); x++) { + for (int y = 0; y < this.getSizeY(); y++) { + if (data[x][y].isHighlighted()) { + data[x][y].setSelected(true); + data[x][y].setHighlighted(false); + } + } + } + } + + @Override + public void setRevertPoint() { + for (int x = 0; x < this.getSizeX(); x++) { + for (int y = 0; y < this.getSizeY(); y++) { + data[x][y].setRevertPoint(); + } + } + yAxis.setRevertPoint(); + xAxis.setRevertPoint(); + } + + @Override + public void undoAll() { + clearLiveDataTrace(); + for (int x = 0; x < this.getSizeX(); x++) { + for (int y = 0; y < this.getSizeY(); y++) { + data[x][y].undo(); + } + } + yAxis.undoAll(); + xAxis.undoAll(); + } + + @Override + public void undoSelected() { + clearLiveDataTrace(); + for (int x = 0; x < this.getSizeX(); x++) { + for (int y = 0; y < this.getSizeY(); y++) { + if (data[x][y].isSelected()) { + data[x][y].undo(); + } + } + } + } + + + @Override + public byte[] saveFile(byte[] binData) { + if ( userLevel <= getSettings().getUserLevel() && (userLevel < 5 || getSettings().isSaveDebugTables()) ) { + binData = xAxis.saveFile(binData); + binData = yAxis.saveFile(binData); + int offset = 0; + + int iMax = swapXY ? xAxis.getDataSize() : yAxis.getDataSize(); + int jMax = swapXY ? yAxis.getDataSize() : xAxis.getDataSize(); + for (int i = 0; i < iMax; i++) { + for (int j = 0; j < jMax; j++) { + + int x = flipY ? jMax - j - 1 : j; + int y = flipX ? iMax - i - 1 : i; + if (swapXY) { + int z = x; + x = y; + y = z; + } + + // determine output byte values + byte[] output; + if (storageType != Settings.STORAGE_TYPE_FLOAT) { + output = RomAttributeParser.parseIntegerValue((int) data[x][y].getBinValue(), endian, storageType); + for (int z = 0; z < storageType; z++) { + binData[offset * storageType + z + getStorageAddress() - ramOffset] = output[z]; + } + } else { // float + output = RomAttributeParser.floatToByte((float) data[x][y].getBinValue(), endian); + for (int z = 0; z < 4; z++) { + binData[offset * 4 + z + getStorageAddress() - ramOffset] = output[z]; + } + } + + + offset++; + } + } + } + return binData; + } + + @Override + public void setRealValue(String realValue) { + if (!locked && !(userLevel > getSettings().getUserLevel()) ) { + for(DataCell[] column : data) { + for(DataCell cell : column) { + if(cell.isSelected()) { + cell.setRealValue(realValue); + } + } + } + } else if (userLevel > getSettings().getUserLevel()) { + JOptionPane.showMessageDialog(this, "This table can only be modified by users with a userlevel of \n" + + userLevel + " or greater. Click View->User Level to change your userlevel.", + "Table cannot be modified", + JOptionPane.INFORMATION_MESSAGE); + } + xAxis.setRealValue(realValue); + yAxis.setRealValue(realValue); + } + + @Override + public void addKeyListener(KeyListener listener) { + xAxis.addKeyListener(listener); + yAxis.addKeyListener(listener); + for (int x = 0; x < this.getSizeX(); x++) { + for (int y = 0; y < this.getSizeY(); y++) { + data[x][y].addKeyListener(listener); + } + } + } + + public void selectCellAt(int y, Table1D axisType) { + if (axisType.getType() == Settings.TABLE_Y_AXIS) { + selectCellAt(0, y); + } else { // y axis + selectCellAt(y, 0); + } + ECUEditorManager.getECUEditor().getTableToolBar().updateTableToolBar(this); + } + + public void deSelectCellAt(int x, int y) { + clearSelection(); + data[x][y].setSelected(false); + highlightX = x; + highlightY = y; + } + + public void selectCellAt(int x, int y) { + clearSelection(); + data[x][y].setSelected(true); + highlightX = x; + highlightY = y; + } + + public void selectCellAtWithoutClear(int x, int y) { + data[x][y].setSelected(true); + highlightX = x; + highlightY = y; + } + + @Override + public void cursorUp() { + if (highlightY > 0 && data[highlightX][highlightY].isSelected()) { + selectCellAt(highlightX, highlightY - 1); + } else if (data[highlightX][highlightY].isSelected()) { + xAxis.selectCellAt(highlightX); + } else { + xAxis.cursorUp(); + yAxis.cursorUp(); + } + } + + @Override + public void cursorDown() { + if (highlightY < getSizeY() - 1 && data[highlightX][highlightY].isSelected()) { + selectCellAt(highlightX, highlightY + 1); + } else { + xAxis.cursorDown(); + yAxis.cursorDown(); + } + } + + @Override + public void cursorLeft() { + if (highlightX > 0 && data[highlightX][highlightY].isSelected()) { + selectCellAt(highlightX - 1, highlightY); + } else if (data[highlightX][highlightY].isSelected()) { + yAxis.selectCellAt(highlightY); + } else { + xAxis.cursorLeft(); + yAxis.cursorLeft(); + } + } + + @Override + public void cursorRight() { + if (highlightX < getSizeX() - 1 && data[highlightX][highlightY].isSelected()) { + selectCellAt(highlightX + 1, highlightY); + } else { + xAxis.cursorRight(); + yAxis.cursorRight(); + } + } + + @Override + public void startHighlight(int x, int y) { + xAxis.clearSelectedData(); + yAxis.clearSelectedData(); + super.startHighlight(x, y); + } + + @Override + public void copySelection() { + Window ancestorWindow = SwingUtilities.getWindowAncestor(this); + if(null != ancestorWindow) { + ancestorWindow.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + } + ECUEditorManager.getECUEditor().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + copySelection3DWorker = new CopySelection3DWorker(this); + copySelection3DWorker.execute(); + + } + + @Override + public void copyTable() { + Window ancestorWindow = SwingUtilities.getWindowAncestor(this); + if(null != ancestorWindow) { + ancestorWindow.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + } + ECUEditorManager.getECUEditor().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + copyTable3DWorker = new CopyTable3DWorker(this); + copyTable3DWorker.execute(); + } + + @Override + public void paste() { + StringTokenizer st = new StringTokenizer(""); + String input = ""; + try { + input = (String) Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null).getTransferData(DataFlavor.stringFlavor); + st = new StringTokenizer(input); + } catch (UnsupportedFlavorException ex) { /* wrong paste type -- do nothing */ + } catch (IOException ex) { + } + + String pasteType = st.nextToken(); + + if ("[Table3D]".equalsIgnoreCase(pasteType)) { // Paste table + String newline = System.getProperty("line.separator"); + String xAxisValues = "[Table1D]" + newline + st.nextToken(newline); + + // build y axis and data values + StringBuffer yAxisValues = new StringBuffer("[Table1D]" + newline + st.nextToken("\t")); + StringBuffer dataValues = new StringBuffer("[Table3D]" + newline + st.nextToken("\t") + st.nextToken(newline)); + while (st.hasMoreTokens()) { + yAxisValues.append("\t").append(st.nextToken("\t")); + dataValues.append(newline).append(st.nextToken("\t")).append(st.nextToken(newline)); + } + + // put x axis in clipboard and paste + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(xAxisValues), null); + xAxis.paste(); + // put y axis in clipboard and paste + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(String.valueOf(yAxisValues)), null); + yAxis.paste(); + // put datavalues in clipboard and paste + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(String.valueOf(dataValues)), null); + pasteValues(); + // reset clipboard + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(input), null); + + } else if ("[Selection3D]".equalsIgnoreCase(pasteType)) { // paste selection + pasteValues(); + } else if ("[Selection1D]".equalsIgnoreCase(pasteType)) { // paste selection + xAxis.paste(); + yAxis.paste(); + } + } + + public void pasteValues() { + StringTokenizer st = new StringTokenizer(""); + String newline = System.getProperty("line.separator"); + try { + String input = (String) Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null).getTransferData(DataFlavor.stringFlavor); + st = new StringTokenizer(input); + } catch (UnsupportedFlavorException ex) { /* wrong paste type -- do nothing */ + } catch (IOException ex) { + } + + String pasteType = st.nextToken(); + + // figure paste start cell + int startX = 0; + int startY = 0; + // if pasting a table, startX and Y at 0, else highlight is start + if ("[Selection3D]".equalsIgnoreCase(pasteType)) { + startX = highlightX; + startY = highlightY; + } + + // set values + for (int y = startY; y < getSizeY(); y++) { + if (st.hasMoreTokens()) { + StringTokenizer currentLine = new StringTokenizer(st.nextToken(newline)); + for (int x = startX; x < getSizeX(); x++) { + if (currentLine.hasMoreTokens()) { + String currentToken = currentLine.nextToken(); + + try { + if (!data[x][y].getText().equalsIgnoreCase(currentToken)) { + data[x][y].setRealValue(currentToken); + } + } catch (ArrayIndexOutOfBoundsException ex) { /* copied table is larger than current table*/ } + } + } + } + } + } + + @Override + public void verticalInterpolate() { + int[] coords = { getSizeX(), getSizeY(), 0, 0}; + DataCell[][] tableData = get3dData(); + + int x, y; + for (x = 0; x < getSizeX(); x++) { + for (y = 0; y < getSizeY(); y++) { + if (tableData[x][y].isSelected()) { + if (x < coords[0]) + coords[0] = x; + if (x > coords[2]) + coords[2] = x; + if (y < coords[1]) + coords[1] = y; + if (y > coords[3]) + coords[3] = y; + } + } + } + if (coords[3] - coords[1] > 1) { + double diff; + for (y = coords[0]; y <= coords[2]; y++) { + diff = (tableData[y][coords[1]].getRealValue() - tableData[y][coords[3]].getRealValue()) / (coords[3] - coords[1]); + if (Math.abs(diff) > 0) { + for (x = coords[1] + 1; x < coords[3]; x++) + tableData[y][x].setRealValue(String.valueOf(tableData[y][x - 1].getRealValue() - diff)); + } + } + } + // Interpolate y axis in case the y axis in selected. + this.getYAxis().verticalInterpolate(); + } + + @Override + public void horizontalInterpolate() { + int[] coords = { getSizeX(), getSizeY(), 0, 0 }; + DataCell[][] tableData = get3dData(); + + int x, y; + for (x = 0; x < getSizeX(); x++) { + for (y = 0; y < getSizeY(); y++) { + if (tableData[x][y].isSelected()) { + if (x < coords[0]) + coords[0] = x; + if (x > coords[2]) + coords[2] = x; + if (y < coords[1]) + coords[1] = y; + if (y > coords[3]) + coords[3] = y; + } + } + } + if (coords[2] - coords[0] > 1) { + double diff; + for (x = coords[1]; x <= coords[3]; x++) { + diff = (tableData[coords[0]][x].getRealValue() - tableData[coords[2]][x].getRealValue()) / (coords[2] - coords[0]); + if (Math.abs(diff) > 0) { + for (y = coords[0] + 1; y < coords[2]; y++) + tableData[y][x].setRealValue(String.valueOf(tableData[y - 1][x].getRealValue() - diff)); + } + } + } + // Interpolate x axis in case the x axis in selected. + this.getXAxis().horizontalInterpolate(); + } + + @Override + public void interpolate() { + verticalInterpolate(); + horizontalInterpolate(); + } + + @Override + public boolean isLiveDataSupported() { + return !isNullOrEmpty(xAxis.getLogParam()) && !isNullOrEmpty(yAxis.getLogParam()); + } + + @Override + public boolean isButtonSelected() { + return true; + } + + @Override + public void highlightLiveData(String liveValue) { + if (getOverlayLog()) { + int x = xAxis.getLiveDataIndex(); + int y = yAxis.getLiveDataIndex(); + DataCell cell = data[x][y]; + cell.setLiveDataTrace(true); + cell.setLiveDataTraceValue(liveValue); + getToolbar().setLiveDataValue(liveValue); + } + } + + @Override + public void updateLiveDataHighlight() { + if (getOverlayLog()) { + int x = xAxis.getLiveDataIndex(); + int y = yAxis.getLiveDataIndex(); + data[x][y].setLiveDataTrace(true); + } + } + + @Override + public void clearLiveDataTrace() { + xAxis.clearLiveDataTrace(); + yAxis.clearLiveDataTrace(); + for (int x = 0; x < getSizeX(); x++) { + for (int y = 0; y < getSizeY(); y++) { + data[x][y].setLiveDataTrace(false); + } + } + } + + public DataCell[][] get3dData() { + return data; + } + + @Override + public void setCompareDisplay(int compareDisplay) { + super.setCompareDisplay(compareDisplay); + xAxis.setCompareDisplay(compareDisplay); + yAxis.setCompareDisplay(compareDisplay); + } + + @Override + public void setCompareValueType(int compareValueType) { + super.setCompareValueType(compareValueType); + xAxis.setCompareValueType(compareValueType); + yAxis.setCompareValueType(compareValueType); + } + + @Override + public void setCurrentScale(Scale curScale) { + if(SettingsManager.getSettings().isScaleHeadersAndData()) { + if(!xAxis.isStaticDataTable()) { + try { + this.xAxis.setScaleByName(curScale.getName()); + } catch (NameNotFoundException e) { + try { + this.xAxis.setScaleByName(SettingsManager.getSettings().getDefaultScale()); + } catch (NameNotFoundException e1) { + } + } + } + if(!yAxis.isStaticDataTable()) { + try { + this.yAxis.setScaleByName(curScale.getName()); + } catch (NameNotFoundException e) { + try { + this.yAxis.setScaleByName(SettingsManager.getSettings().getDefaultScale()); + } catch (NameNotFoundException e1) { + } + } + } + } + this.curScale = curScale; + updateTableLabel(); + drawTable(); + } + + @Override + public String getLogParamString() { + StringBuilder sb = new StringBuilder(); + sb.append(xAxis.getLogParamString()+", "); + sb.append(yAxis.getLogParamString()+", "); + sb.append(getName()+ ":" + getLogParam()); + return sb.toString(); + } + + @Override + public void setOverlayLog(boolean overlayLog) { + super.setOverlayLog(overlayLog); + xAxis.setOverlayLog(overlayLog); + yAxis.setOverlayLog(overlayLog); + if (overlayLog) { + xAxis.clearLiveDataTrace(); + yAxis.clearLiveDataTrace(); + } + } + + @Override + public boolean equals(Object other) { + try { + if(null == other) { + return false; + } + + if(other == this) { + return true; + } + + if(!(other instanceof Table3D)) { + return false; + } + + Table3D otherTable = (Table3D)other; + + if( (null == this.getName() && null == otherTable.getName()) + || (this.getName().isEmpty() && otherTable.getName().isEmpty()) ) { + ;// Skip name compare if name is null or empty. + } else if(!this.getName().equalsIgnoreCase(otherTable.getName())) { + return false; + } + + if(! this.xAxis.equals(otherTable.xAxis)) { + return false; + } + + if(! this.yAxis.equals(otherTable.yAxis)) { + return false; + } + + if(this.data.length != otherTable.data.length || this.data[0].length != otherTable.data[0].length) + { + return false; + } + + if(this.data.equals(otherTable.data)) + { + return true; + } + + // Compare Bin Values + for(int i = 0 ; i < this.data.length ; i++) { + for(int j = 0; j < this.data[i].length ; j++) { + if(! this.data[i][j].equals(otherTable.data[i][j]) ) { + return false; + } + } + } + + return true; + } catch(Exception ex) { + // TODO: Log Exception. + return false; + } + } + + @Override + public void repaint() { + super.repaint(); + + if(null != xAxis) { + xAxis.repaint(); + } + + if(null != yAxis) { + yAxis.repaint(); + } + } +} + +class CopySelection3DWorker extends SwingWorker { + Table3D table; + + public CopySelection3DWorker(Table3D table) + { + this.table = table; + } + + @Override + protected Void doInBackground() throws Exception { + // find bounds of selection + // coords[0] = x min, y min, x max, y max + boolean copy = false; + int[] coords = new int[4]; + coords[0] = table.getSizeX(); + coords[1] = table.getSizeY(); + + for (int x = 0; x < table.getSizeX(); x++) { + for (int y = 0; y < table.getSizeY(); y++) { + if (table.get3dData()[x][y].isSelected()) { + if (x < coords[0]) { + coords[0] = x; + copy = true; + } + if (x > coords[2]) { + coords[2] = x; + copy = true; + } + if (y < coords[1]) { + coords[1] = y; + copy = true; + } + if (y > coords[3]) { + coords[3] = y; + copy = true; + } + } + } + } + // make string of selection + if (copy) { + String newline = System.getProperty("line.separator"); + StringBuffer output = new StringBuffer("[Selection3D]" + newline); + for (int y = coords[1]; y <= coords[3]; y++) { + for (int x = coords[0]; x <= coords[2]; x++) { + if (table.get3dData()[x][y].isSelected()) { + output.append(table.get3dData()[x][y].getText()); + } else { + output.append("x"); // x represents non-selected cell + } + if (x < coords[2]) { + output.append("\t"); + } + } + if (y < coords[3]) { + output.append(newline); + } + //copy to clipboard + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(String.valueOf(output)), null); + } + } else { + table.getXAxis().copySelection(); + table.getYAxis().copySelection(); + } + return null; + } + + @Override + public void done() { + Window ancestorWindow = SwingUtilities.getWindowAncestor(table); + if(null != ancestorWindow) { + ancestorWindow.setCursor(null); + } + table.setCursor(null); + ECUEditorManager.getECUEditor().setCursor(null); + } +} + +class CopyTable3DWorker extends SwingWorker { + Table3D table; + + public CopyTable3DWorker(Table3D table) + { + this.table = table; + } + + @Override + protected Void doInBackground() throws Exception { + String tableHeader = table.getSettings().getTable3DHeader(); + StringBuffer output = new StringBuffer(tableHeader); + output.append(table.getTableAsString()); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(String.valueOf(output)), null); + return null; + } + + @Override + public void done() { + Window ancestorWindow = SwingUtilities.getWindowAncestor(table); + if(null != ancestorWindow){ + ancestorWindow.setCursor(null); + } + table.setCursor(null); + ECUEditorManager.getECUEditor().setCursor(null); + } +} diff --git a/java_console/romraider/src/com/romraider/maps/TableSwitch.java b/java_console/romraider/src/com/romraider/maps/TableSwitch.java new file mode 100644 index 0000000000..52b9e0c366 --- /dev/null +++ b/java_console/romraider/src/com/romraider/maps/TableSwitch.java @@ -0,0 +1,388 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2014 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.maps; + +import static com.romraider.maps.RomChecksum.validateRomChecksum; +import static com.romraider.util.ByteUtil.indexOfBytes; +import static com.romraider.util.HexUtil.asBytes; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.INFORMATION_MESSAGE; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.Insets; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.swing.AbstractButton; +import javax.swing.ButtonGroup; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextArea; + +import com.romraider.Settings; + +public class TableSwitch extends Table { + + private static final long serialVersionUID = -4887718305447362308L; + private final ButtonGroup buttonGroup = new ButtonGroup(); + private final Map switchStates = new HashMap(); + private int dataSize = 0; + + public TableSwitch() { + super(); + storageType = 1; + type = Settings.TABLE_SWITCH; + locked = true; + removeAll(); + setLayout(new BorderLayout()); + } + + @Override + public void setDataSize(int size) { + if (dataSize == 0) dataSize = size; + } + + @Override + public int getDataSize() { + return dataSize; + } + + @Override + public void populateTable(byte[] input, int romRamOffset) throws ArrayIndexOutOfBoundsException, IndexOutOfBoundsException { + JPanel radioPanel = new JPanel(new GridLayout(0, 1)); + radioPanel.add(new JLabel(" " + getName())); + for (String stateName : switchStates.keySet()) { + JRadioButton button = new JRadioButton(stateName); + buttonGroup.add(button); + radioPanel.add(button); + } + add(radioPanel, BorderLayout.CENTER); + + // Validate the ROM image checksums. + // if the result is >0: position of failed checksum + // if the result is 0: all the checksums matched + // if the result is -1: all the checksums have been previously disabled + if (super.getName().contains("Checksum Fix")) { + int result = validateRomChecksum(input, getStorageAddress(), dataSize); + String message = String.format( + "Checksum No. %d is invalid in table: %s%n" + + "The ROM image may be corrupt or it has been %n" + + "hex edited manually.%n" + + "The checksum can be corrected when the ROM is saved.", + result, super.getName()); + if (result > 0) { + showMessageDialog(this, + message, + "ERROR - Checksums Failed", + WARNING_MESSAGE); + setButtonsUnselected(buttonGroup); + } + else if (result == -1){ + message = "All Checksums are disabled."; + showMessageDialog(this, + message, + "Warning - Checksum Status", + INFORMATION_MESSAGE); + getButtonByText(buttonGroup, "on").setSelected(true); + } + else { + getButtonByText(buttonGroup, "off").setSelected(true); + locked = false; + } + return; + } + + // Validate XML switch definition data against the ROM data to select + // the appropriate switch setting or throw an error if there is a + // mismatch and disable this table's editing ability. + if (!beforeRam) { + this.ramOffset = romRamOffset; + } + Map sourceStatus = new HashMap(); + for (String stateName : switchStates.keySet()) { + byte[] sourceData = new byte[dataSize]; + System.arraycopy( + input, + storageAddress - ramOffset, + sourceData, + 0, + dataSize); + int compareResult = indexOfBytes(sourceData, getValues(stateName)); + if (compareResult == -1) { + getButtonByText(buttonGroup, stateName).setSelected(false); + } + else { + getButtonByText(buttonGroup, stateName).setSelected(true); + } + sourceStatus.put(stateName, compareResult); + } + + for (String source : sourceStatus.keySet()) { + if (sourceStatus.get(source) != -1) { + locked = false; + break; + } + } + + if (locked) { + String mismatch = String.format("Table: %s%nTable editing has been disabled.%nDefinition file or ROM image may be corrupt.", super.getName()); + showMessageDialog(this, + mismatch, + "ERROR - Data Mismatch", + ERROR_MESSAGE); + setButtonsUnselected(buttonGroup); + } + } + + @Override + public void setName(String name) { + super.setName(name); + } + + @Override + public int getType() { + return Settings.TABLE_SWITCH; + } + + @Override + public void setDescription(String description) { + super.setDescription(description); + JTextArea descriptionArea = new JTextArea(description); + descriptionArea.setOpaque(false); + descriptionArea.setEditable(false); + descriptionArea.setWrapStyleWord(true); + descriptionArea.setLineWrap(true); + descriptionArea.setMargin(new Insets(0,5,5,5)); + + add(descriptionArea, BorderLayout.SOUTH); + } + + @Override + public byte[] saveFile(byte[] input) { + if (!super.getName().contains("Checksum Fix")) { + if (!locked) { + JRadioButton selectedButton = getSelectedButton(buttonGroup); + System.arraycopy( + switchStates.get(selectedButton.getText()), + 0, + input, + getStorageAddress() - ramOffset, + dataSize); + } + } + return input; + } + + public void setValues(String name, String input) { + switchStates.put(name, asBytes(input)); + } + + public byte[] getValues(String key) { + return switchStates.get(key); + } + + @Override + public Dimension getFrameSize() { + int height = verticalOverhead + 75; + int width = horizontalOverhead; + if (height < minHeight) { + height = minHeight; + } + int minWidth = isLiveDataSupported() ? minWidthOverlay : minWidthNoOverlay; + if (width < minWidth) { + width = minWidth; + } + return new Dimension(width, height); + } + + public ButtonGroup getButtonGroup() { + return this.buttonGroup; + } + + public Map getSwitchStates() { + return this.switchStates; + } + + @Override + public void cursorUp() { + } + + @Override + public void cursorDown() { + } + + @Override + public void cursorLeft() { + } + + @Override + public void cursorRight() { + } + + @Override + public boolean isLiveDataSupported() { + return false; + } + + @Override + public boolean isButtonSelected() { + if (buttonGroup.getSelection() == null) { + return false; + } + else { + return true; + } + } + + @Override + public boolean equals(Object other) { + // TODO: Validate DTC equals. + try { + if(null == other) { + return false; + } + + if(other == this) { + return true; + } + + if(!(other instanceof TableSwitch)) { + return false; + } + + TableSwitch otherTable = (TableSwitch)other; + + if( (null == this.getName() && null == otherTable.getName()) + || (this.getName().isEmpty() && otherTable.getName().isEmpty()) ) { + ;// Skip name compare if name is null or empty. + } else if(!this.getName().equalsIgnoreCase(otherTable.getName())) { + return false; + } + + if(this.getDataSize() != otherTable.getDataSize()) { + return false; + } + + if(this.getSwitchStates() == otherTable.getSwitchStates()) { + return true; + } + + // Compare Map Keys + Set keys = new HashSet(this.getSwitchStates().keySet()); + Set otherKeys = new HashSet(otherTable.getSwitchStates().keySet()); + + if(keys.size() != otherKeys.size()) { + return false; + } + + if(!keys.containsAll(otherKeys)) { + return false; + } + + // Compare Map Values. + Set values = new HashSet(this.getSwitchStates().values()); + Set otherValues = new HashSet(otherTable.getSwitchStates().values()); + if(values.equals(otherValues)) { + return true; + } + + // Compare DTC. Is there a better way to compare the DTC? + for(String key : keys) { + JRadioButton button = getButtonByText(this.getButtonGroup(), key); + JRadioButton otherButton = getButtonByText(otherTable.getButtonGroup(), key); + + if(button.isSelected() != otherButton.isSelected()) { + return false; + } + } + + return true; + } catch(Exception ex) { + // TODO: Log Exception. + return false; + } + } + + @Override + public void populateCompareValues(Table compareTable) { + return; // Do nothing. + } + + @Override + public void calcCellRanges() { + return; // Do nothing. + } + + @Override + public void drawTable() + { + return; // Do nothing. + } + + @Override + public void updateTableLabel() { + return; // Do nothing. + } + + @Override + public void setCurrentScale(Scale curScale) { + return; // Do nothing. + } + + + // returns the selected radio button in the specified group + private static JRadioButton getSelectedButton(ButtonGroup group) { + for (Enumeration e = group.getElements(); e.hasMoreElements(); ) { + JRadioButton b = (JRadioButton)e.nextElement(); + if (b.getModel() == group.getSelection()) { + return b; + } + } + return null; + } + + // Unselects & disables all radio buttons in the specified group + private static void setButtonsUnselected(ButtonGroup group) { + for (Enumeration e = group.getElements(); e.hasMoreElements(); ) { + JRadioButton b = (JRadioButton)e.nextElement(); + b.setSelected(false); + b.setEnabled(false); + } + } + + // returns the radio button based on its display text + private static JRadioButton getButtonByText(ButtonGroup group, String text) { + for (Enumeration e = group.getElements(); e.hasMoreElements(); ) { + JRadioButton b = (JRadioButton)e.nextElement(); + if (b.getText().equalsIgnoreCase(text)) { + return b; + } + } + return null; + } +} diff --git a/java_console/romraider/src/com/romraider/net/BrowserControl.java b/java_console/romraider/src/com/romraider/net/BrowserControl.java new file mode 100644 index 0000000000..371769b24d --- /dev/null +++ b/java_console/romraider/src/com/romraider/net/BrowserControl.java @@ -0,0 +1,89 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.net; + +import org.apache.log4j.Logger; +import java.io.IOException; +import java.lang.reflect.Method; +import java.net.URI; + +public class BrowserControl { + private static final Logger LOGGER = Logger.getLogger(BrowserControl.class); + + private BrowserControl() { + throw new UnsupportedOperationException(); + } + + public static void displayURL(String url) { + try { + Class display = Class.forName("java.awt.Desktop"); + Object getDesktopMethod = display.getDeclaredMethod("getDesktop").invoke(null); + Method browseMethod = display.getDeclaredMethod("browse", java.net.URI.class); + browseMethod.invoke(getDesktopMethod, new URI(url)); + } catch (Exception e) { + LOGGER.debug("Failed to display URL via java.awt.Desktop. Calling by OS depended method.", e); + displayURLtraditional(url); + } + } + + private static void displayURLtraditional(String url) { + boolean windows = isWindowsPlatform(); + String cmd = null; + try { + if (windows) { + // cmd = 'rundll32 url.dll,FileProtocolHandler http://...' + cmd = WIN_PATH + " " + WIN_FLAG + " " + url; + Runtime.getRuntime().exec(cmd); + } else { + cmd = UNIX_PATH + " " + UNIX_FLAG + "(" + url + ")"; + Process p = Runtime.getRuntime().exec(cmd); + try { + int exitCode = p.waitFor(); + if (exitCode != 0) { + cmd = UNIX_PATH + " " + url; + Runtime.getRuntime().exec(cmd); + } + } + catch (InterruptedException x) { + LOGGER.error("Error bringing up browser, command=" + cmd, x); + } + } + } + catch (IOException x) { + // couldn't exec browser + LOGGER.error("Could not invoke browser, command=" + cmd, x); + } + } + + public static boolean isWindowsPlatform() { + String os = System.getProperty("os.name"); + if (os != null && os.startsWith(WIN_ID)) { + return true; + } else { + return false; + } + } + + private static final String WIN_ID = "Windows"; + private static final String WIN_PATH = "rundll32"; + private static final String WIN_FLAG = "url.dll,FileProtocolHandler"; + private static final String UNIX_PATH = "netscape"; + private static final String UNIX_FLAG = "-remote openURL"; +} diff --git a/java_console/romraider/src/com/romraider/net/URL.java b/java_console/romraider/src/com/romraider/net/URL.java new file mode 100644 index 0000000000..58ea1a0985 --- /dev/null +++ b/java_console/romraider/src/com/romraider/net/URL.java @@ -0,0 +1,68 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.net; + +import javax.swing.JLabel; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +public class URL extends JLabel implements MouseListener { + + private static final long serialVersionUID = 8972482185527165793L; + String url = ""; + + public URL(String url) { + super(url); + this.url = url; + this.setFont(new Font("Arial", Font.PLAIN, 12)); + this.addMouseListener(this); + } + + public void paint(Graphics g) { + super.paint(g); + Font f = getFont(); + FontMetrics fm = getFontMetrics(f); + int x1 = 0; + int y1 = fm.getHeight() + 3; + int x2 = fm.stringWidth(getText()); + if (getText().length() > 0) { + g.drawLine(x1, y1, x2, y1); + } + } + + public void mouseClicked(MouseEvent e) { + BrowserControl.displayURL(url); + } + + public void mousePressed(MouseEvent e) { + } + + public void mouseReleased(MouseEvent e) { + } + + public void mouseEntered(MouseEvent e) { + } + + public void mouseExited(MouseEvent e) { + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/ramtune/test/command/executor/CommandExecutor.java b/java_console/romraider/src/com/romraider/ramtune/test/command/executor/CommandExecutor.java new file mode 100644 index 0000000000..142c20f8b4 --- /dev/null +++ b/java_console/romraider/src/com/romraider/ramtune/test/command/executor/CommandExecutor.java @@ -0,0 +1,27 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.ramtune.test.command.executor; + +public interface CommandExecutor { + + byte[] executeCommand(byte[] command); + + void close(); +} diff --git a/java_console/romraider/src/com/romraider/ramtune/test/io/RamTuneTestAppConnectionProperties.java b/java_console/romraider/src/com/romraider/ramtune/test/io/RamTuneTestAppConnectionProperties.java new file mode 100644 index 0000000000..4c707ce376 --- /dev/null +++ b/java_console/romraider/src/com/romraider/ramtune/test/io/RamTuneTestAppConnectionProperties.java @@ -0,0 +1,75 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.ramtune.test.io; + +import com.romraider.io.connection.ConnectionProperties; + +public final class RamTuneTestAppConnectionProperties implements ConnectionProperties { + private final ConnectionProperties defaultConnectionProperties; + private final int sendTimeout; + + public RamTuneTestAppConnectionProperties(ConnectionProperties defaultConnectionProperties, int sendTimeout) { + this.defaultConnectionProperties = defaultConnectionProperties; + this.sendTimeout = sendTimeout; + } + + public int getBaudRate() { + return defaultConnectionProperties.getBaudRate(); + } + + public void setBaudRate(int b) { + + } + + public int getDataBits() { + return defaultConnectionProperties.getDataBits(); + } + + public int getStopBits() { + return defaultConnectionProperties.getStopBits(); + } + + public int getParity() { + return defaultConnectionProperties.getParity(); + } + + public int getConnectTimeout() { + return defaultConnectionProperties.getConnectTimeout(); + } + + public int getSendTimeout() { + return sendTimeout > 0 ? sendTimeout : defaultConnectionProperties.getSendTimeout(); + } + + public String toString() { + final String properties = String.format( + "%s[baudRate=%d, dataBits=%d, stopBits=%d, parity=%d, " + + "connectTimeout=%d, sendTimeout=%d]", + getClass().getSimpleName(), + getBaudRate(), + getDataBits(), + getStopBits(), + getParity(), + getConnectTimeout(), + getSendTimeout() + ); + return properties; + } +} diff --git a/java_console/romraider/src/com/romraider/swing/AbstractFrame.java b/java_console/romraider/src/com/romraider/swing/AbstractFrame.java new file mode 100644 index 0000000000..d52e79a49f --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/AbstractFrame.java @@ -0,0 +1,65 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import javax.swing.JFrame; +import java.awt.HeadlessException; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + + +public abstract class AbstractFrame extends JFrame implements WindowListener, PropertyChangeListener { + public AbstractFrame() throws HeadlessException { + super(); + } + + public AbstractFrame(String arg0) throws HeadlessException { + super(arg0); + } + + private static final long serialVersionUID = 7948304087075622157L; + + public void windowActivated(WindowEvent arg0) { + } + + public void windowClosed(WindowEvent e) { + } + + public void windowClosing(WindowEvent e) { + } + + public void windowDeactivated(WindowEvent e) { + } + + public void windowDeiconified(WindowEvent e) { + } + + public void windowIconified(WindowEvent e) { + } + + public void windowOpened(WindowEvent e) { + } + + public void propertyChange(PropertyChangeEvent arg0) { + } + +} diff --git a/java_console/romraider/src/com/romraider/swing/CategoryTreeNode.java b/java_console/romraider/src/com/romraider/swing/CategoryTreeNode.java new file mode 100644 index 0000000000..e5092b9be0 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/CategoryTreeNode.java @@ -0,0 +1,31 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import javax.swing.tree.DefaultMutableTreeNode; + +public class CategoryTreeNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = -752423096680196879L; + + public CategoryTreeNode(String name) { + super(name); + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/swing/CompareImagesForm.java b/java_console/romraider/src/com/romraider/swing/CompareImagesForm.java new file mode 100644 index 0000000000..6d374895a6 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/CompareImagesForm.java @@ -0,0 +1,392 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Vector; + +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ListCellRenderer; +import javax.swing.ListSelectionModel; +import javax.swing.border.EmptyBorder; +import javax.swing.border.EtchedBorder; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import com.romraider.Settings; +import com.romraider.editor.ecu.ECUEditorManager; +import com.romraider.maps.Rom; +import com.romraider.maps.Table; + +public class CompareImagesForm extends JFrame implements ActionListener { + + private static final long serialVersionUID = -8937472127815934398L; + private final Vector roms; + private final JPanel contentPane; + private final JComboBox comboBoxImageLeft; + private final JComboBox comboBoxImageRight; + private final JButton btnCompare; + private final JList listChanges; + private final DefaultListModel listModelChanges = new DefaultListModel(); + private final ChangeListCellRenderer changeRenderer = new ChangeListCellRenderer(); + private final JScrollPane scrollPaneResults; + private final JLabel lblImageResultString; + + public CompareImagesForm(Vector roms, Image parentImage) { + this.setIconImage(parentImage); + setResizable(false); + this.roms = roms; + + setTitle("Compare Images"); + + setBounds(100, 100, 600, 450); + this.contentPane = new JPanel(); + this.contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); + setContentPane(this.contentPane); + + JLabel lblSelectImages = new JLabel("Selected Images"); + lblSelectImages.setBounds(10, 11, 79, 14); + + contentPane.setLayout(null); + + JPanel panelImageSelector = new JPanel(); + panelImageSelector.setBounds(10, 36, 574, 94); + panelImageSelector.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null)); + panelImageSelector.setLayout(null); + + this.comboBoxImageLeft = new JComboBox(); + this.comboBoxImageLeft.setBounds(10, 7, 554, 20); + this.comboBoxImageLeft.setToolTipText("Select an image to compare."); + this.comboBoxImageLeft.setRenderer( new ComboBoxRenderer() ); + panelImageSelector.add(this.comboBoxImageLeft); + + this.comboBoxImageRight = new JComboBox(); + this.comboBoxImageRight.setBounds(10, 32, 554, 20); + this.comboBoxImageRight.setToolTipText("Select an image to compare."); + this.comboBoxImageRight.setRenderer( new ComboBoxRenderer() ); + panelImageSelector.add(this.comboBoxImageRight); + + this.btnCompare = new JButton("Compare"); + this.btnCompare.addActionListener(this); + this.btnCompare.setBounds(10, 64, 89, 23); + panelImageSelector.add(this.btnCompare); + this.contentPane.add(panelImageSelector); + this.contentPane.add(lblSelectImages); + + JLabel lblResults = new JLabel("Results:"); + lblResults.setBounds(10, 141, 46, 14); + contentPane.add(lblResults); + + lblImageResultString = new JLabel("Compare images..."); + lblImageResultString.setBounds(66, 141, 518, 14); + contentPane.add(lblImageResultString); + scrollPaneResults = new JScrollPane(); + scrollPaneResults.setBounds(10, 166, 574, 245); + contentPane.add(scrollPaneResults); + + this.listChanges = new JList(this.listModelChanges); + scrollPaneResults.setViewportView(this.listChanges); + listChanges.setCellRenderer(changeRenderer); + listChanges.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + listChanges.addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent arg0) { + if (!arg0.getValueIsAdjusting()) { + ListItem selectedTableName = (ListItem) listChanges.getSelectedValue(); + String tableName = selectedTableName.getValue(); + Rom leftRom = (Rom) comboBoxImageLeft.getSelectedItem(); + Rom rightRom = (Rom) comboBoxImageRight.getSelectedItem(); + + // Display Tables + TableTreeNode leftNode = findAndShowTable(leftRom, tableName); + TableTreeNode rightNode = findAndShowTable(rightRom, tableName); + + // Set Comparison + if(leftNode != null && rightNode != null) { + leftNode.getFrame().compareByTable(rightNode.getTable()); + // Update menu bar + for(int i = 0; i< leftNode.getFrame().getTableMenuBar().getSimilarOpenTables().getItemCount(); i++) { + JMenuItem leftItem = leftNode.getFrame().getTableMenuBar().getSimilarOpenTables().getItem(i); + String romFileName = leftItem.getText(); + if(rightRom.getFileName().equalsIgnoreCase(romFileName)) { + leftItem.setSelected(true); + break; + } + } + } + } + } + }); + populateComboBoxes(); + } + + private TableTreeNode findAndShowTable(Rom rom, String tableName) { + for(TableTreeNode node : rom.getTableNodes()) { + if(node != null && node.getTable().getName().equals(tableName)){ + ECUEditorManager.getECUEditor().displayTable(node.getFrame()); + return node; + } + } + return null; + } + + public void populateComboBoxes() + { + for(int i=0; i 1) { + comboBoxImageRight.setSelectedIndex(1); + } + } + + public void compareTables(Rom left, Rom right) + { + listModelChanges.clear(); + + int equal = 0; + int different = 0; + int missing = 0; + + for(TableTreeNode leftNode : left.getTableNodes()) + { + Boolean found = false; + Table leftTable = leftNode.getTable(); + for(TableTreeNode rightNode : right.getTableNodes()) + { + Table rightTable = rightNode.getTable(); + + if(leftTable.getName().equalsIgnoreCase(rightTable.getName())) + { + if(leftTable.equals(rightTable)) { + equal++; + listModelChanges.addElement(new ListItem(1, leftTable.getName())); + } + else { + different++; + listModelChanges.addElement(new ListItem(2, leftTable.getName())); + } + found = true; + break; + } + } + + if(!found) { + missing++; + listModelChanges.addElement(new ListItem(3, leftTable.getName())); + } + } + + // Check if rightTables has tables that do not exist in left table. + for(TableTreeNode rightFrame : right.getTableNodes()) { + Boolean found = false; + for(TableTreeNode leftFrame : left.getTableNodes()) { + if(leftFrame.getTable().getName().equalsIgnoreCase(rightFrame.getTable().getName())) + { + found = true; + break; + } + } + + if(!found) { + missing++; + listModelChanges.addElement(new ListItem(3, rightFrame.getTable().getName())); + } + } + + // Fill out the result string. + if(equal > 0 && different == 0 && missing == 0) { + lblImageResultString.setText("Images are equal."); + lblImageResultString.setForeground(Settings.TABLE_EQUAL_COLOR); + } else if(different > 0) { + lblImageResultString.setText("Images are NOT equal. Equal Tables: "+equal+", Changed Tables: "+different+", Missing Tables: "+missing); + lblImageResultString.setForeground(Settings.TABLE_DIFFERENT_COLOR); + } else { + lblImageResultString.setText("Images are NOT equal. Equal Tables: "+equal+", Changed Tables: "+different+", Missing Tables: "+missing); + lblImageResultString.setForeground(Settings.TABLE_MISSING_COLOR); + } + + // Check if the list has items. + if(listModelChanges.size() < 1) + { + listModelChanges.addElement(new ListItem(0, "No tables are equal, different, or missing.")); + lblImageResultString.setText("Unable to compare images."); + lblImageResultString.setForeground(Color.RED); + return; + } + + // Add list items for 0 counts. + if(equal == 0) + { + listModelChanges.addElement(new ListItem(1, "No Equal Tables.")); + } + + if(different == 0) + { + listModelChanges.addElement(new ListItem(2, "No Changed Tables.")); + } + + if(missing == 0) + { + listModelChanges.addElement(new ListItem(3, "No Missing Tables.")); + } + + } + + @Override + public void actionPerformed(ActionEvent e) { + this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + if (e.getSource() == this.btnCompare) { + if(this.comboBoxImageLeft.getItemCount() > 0 && this.comboBoxImageRight.getItemCount() > 0) + { + Rom leftRom = (Rom)this.comboBoxImageLeft.getSelectedItem(); + Rom rightRom = (Rom)this.comboBoxImageRight.getSelectedItem(); + if(leftRom != null && rightRom != null) + { + compareTables(leftRom, rightRom); + } + } + } + this.setCursor(Cursor.getDefaultCursor()); + } + + class ComboBoxRenderer extends JLabel implements ListCellRenderer + { + + private static final long serialVersionUID = 831689602429105854L; + + public ComboBoxRenderer() { + setOpaque(true); + setHorizontalAlignment(LEFT); + setVerticalAlignment(CENTER); + } + + @Override + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) { + + if(isSelected) { + setBackground(list.getSelectionBackground()); + setForeground(list.getSelectionForeground()); + } else { + setBackground(list.getBackground()); + setForeground(list.getForeground()); + } + + if(value != null) + { + // Set the text to the rom file name. + Rom rom = (Rom)value; + setText(rom.getFileName()); + setFont(list.getFont()); + } + return this; + } + } + + class ChangeListCellRenderer extends JLabel implements ListCellRenderer { + + private static final long serialVersionUID = -3645192077787635239L; + + public ChangeListCellRenderer() + { + setOpaque(true); + setHorizontalAlignment(LEFT); + setVerticalAlignment(CENTER); + } + + @Override + public Component getListCellRendererComponent(JList paramList, Object value, + int index, boolean isSelected, boolean cellHasFocus) { + + // Set the background color. + if(isSelected) { + setBackground(paramList.getSelectionBackground()); + } else { + setBackground(paramList.getBackground()); + } + + // Set the foreground color based on the item type. + ListItem item = (ListItem)value; + switch(item.getType()) { + case 1: + // equal - default green + setForeground(Settings.TABLE_EQUAL_COLOR); + break; + case 2: + // different - default red + setForeground(Settings.TABLE_DIFFERENT_COLOR); + break; + case 3: + // missing - default yellow + setForeground(Settings.TABLE_MISSING_COLOR); + break; + default: + setForeground(paramList.getForeground()); + break; + } + setText(item.getValue()); + return this; + } + } + + class ListItem { + + private int type; + private String value; + + public ListItem(int type, String value) { + this.type = type; + this.value = value; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + } +} diff --git a/java_console/romraider/src/com/romraider/swing/CustomToolbarLayout.java b/java_console/romraider/src/com/romraider/swing/CustomToolbarLayout.java new file mode 100644 index 0000000000..a925d0f2a4 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/CustomToolbarLayout.java @@ -0,0 +1,110 @@ +package com.romraider.swing; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Insets; + +public class CustomToolbarLayout extends FlowLayout { + + private static final long serialVersionUID = 1L; + + public CustomToolbarLayout() { + super(); + } + + public CustomToolbarLayout(int align) { + super(align); + } + + public CustomToolbarLayout(int align, int hgap, int vgap) { + super(align, hgap, vgap); + } + + @Override + public Dimension minimumLayoutSize(Container target) { + // Size of largest component, so we can resize it in + // either direction with something like a split-pane. + return computeMinSize(target); + } + + @Override + public Dimension preferredLayoutSize(Container target) { + return computeSize(target); + } + + private Dimension computeSize(Container target) { + synchronized (target.getTreeLock()) { + int hgap = getHgap(); + int vgap = getVgap(); + int width = target.getWidth(); + + // Let this behave like a regular FlowLayout (single row) + // if the container hasn't been assigned any size yet + if (0 == width) { + width = Integer.MAX_VALUE; + } + + Insets insets = target.getInsets(); + if (null == insets){ + insets = new Insets(0, 0, 0, 0); + } + int reqdWidth = 0; + + int maxwidth = width - (insets.left + insets.right + hgap * 2); + int n = target.getComponentCount(); + int x = 0; + int y = insets.top + vgap; // FlowLayout starts by adding vgap, so do that here too. + int rowHeight = 0; + + for (int i = 0; i < n; i++) { + Component c = target.getComponent(i); + if (c.isVisible()) { + Dimension d = c.getPreferredSize(); + if ((x == 0) || ((x + d.width) <= maxwidth)) { + // fits in current row. + if (x > 0) { + x += hgap; + } + x += d.width; + rowHeight = Math.max(rowHeight, d.height); + } + else { + // Start of new row + x = d.width; + y += vgap + rowHeight; + rowHeight = d.height; + } + reqdWidth = Math.max(reqdWidth, x); + } + } + y += rowHeight; + y += insets.bottom; + return new Dimension(reqdWidth+insets.left+insets.right, y); + } + } + + private Dimension computeMinSize(Container target) { + synchronized (target.getTreeLock()) { + int minx = Integer.MAX_VALUE; + int miny = Integer.MIN_VALUE; + boolean found_one = false; + int n = target.getComponentCount(); + + for (int i = 0; i < n; i++) { + Component c = target.getComponent(i); + if (c.isVisible()) { + found_one = true; + Dimension d = c.getPreferredSize(); + minx = Math.min(minx, d.width); + miny = Math.min(miny, d.height); + } + } + if (found_one) { + return new Dimension(minx, miny); + } + return new Dimension(0, 0); + } + } +} diff --git a/java_console/romraider/src/com/romraider/swing/DebugPanel.java b/java_console/romraider/src/com/romraider/swing/DebugPanel.java new file mode 100644 index 0000000000..fdbc3ad915 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/DebugPanel.java @@ -0,0 +1,54 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import static com.romraider.Version.PRODUCT_NAME; +import com.romraider.net.URL; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import java.awt.BorderLayout; +import java.awt.GridLayout; + +public class DebugPanel extends JPanel { + + private static final long serialVersionUID = -7159385694793030962L; + + public DebugPanel(Exception ex, String url) { + setLayout(new BorderLayout()); + + JPanel top = new JPanel(new GridLayout(7, 1)); + top.add(new JLabel(PRODUCT_NAME + " has encountered an exception. Please review the details below.")); + top.add(new JLabel("If you are unable to fix this problem please visit the following website")); + top.add(new JLabel("and provide these details and the steps that lead to this error.")); + top.add(new JLabel()); + top.add(new URL(url)); + top.add(new JLabel()); + top.add(new JLabel("Details:")); + add(top, BorderLayout.NORTH); + + JTextArea output = new JTextArea(ex.getMessage()); + add(output, BorderLayout.CENTER); + output.setAutoscrolls(true); + output.setRows(10); + output.setColumns(40); + ex.printStackTrace(); + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/swing/DefinitionManager.java b/java_console/romraider/src/com/romraider/swing/DefinitionManager.java new file mode 100644 index 0000000000..18e9415463 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/DefinitionManager.java @@ -0,0 +1,263 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.util.Vector; + +import javax.swing.JFileChooser; +import javax.swing.ListSelectionModel; + +import com.romraider.editor.ecu.ECUEditorManager; +import com.romraider.util.SettingsManager; + +public class DefinitionManager extends javax.swing.JFrame implements ActionListener { + + private static final long serialVersionUID = -3920843496218196737L; + public static int MOVE_UP = 0; + public static int MOVE_DOWN = 1; + + Vector fileNames; + + public DefinitionManager() { + this.setIconImage(ECUEditorManager.getECUEditor().getIconImage()); + initComponents(); + initSettings(); + + definitionList.setFont(new Font("Tahoma", Font.PLAIN, 11)); + definitionList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + btnCancel.addActionListener(this); + btnSave.addActionListener(this); + btnAddDefinition.addActionListener(this); + btnRemoveDefinition.addActionListener(this); + btnMoveUp.addActionListener(this); + btnMoveDown.addActionListener(this); + btnApply.addActionListener(this); + btnUndo.addActionListener(this); + + } + + private void initSettings() { + // add definitions to list + Vector definitionFiles = SettingsManager.getSettings().getEcuDefinitionFiles(); + fileNames = new Vector(); + + for (int i = 0; i < definitionFiles.size(); i++) { + fileNames.add(definitionFiles.get(i).getAbsolutePath()); + } + + updateListModel(); + } + + // //GEN-BEGIN:initComponents + private void initComponents() { + jScrollPane1 = new javax.swing.JScrollPane(); + definitionList = new javax.swing.JList(); + defLabel = new javax.swing.JLabel(); + btnMoveUp = new javax.swing.JButton(); + btnMoveDown = new javax.swing.JButton(); + btnAddDefinition = new javax.swing.JButton(); + btnRemoveDefinition = new javax.swing.JButton(); + btnSave = new javax.swing.JButton(); + btnCancel = new javax.swing.JButton(); + btnApply = new javax.swing.JButton(); + btnUndo = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle("Definition File Manager"); + jScrollPane1.setViewportView(definitionList); + + defLabel.setText("ECU Definition File Priority"); + + btnMoveUp.setText("Move Up"); + + btnMoveDown.setText("Move Down"); + + btnAddDefinition.setText("Add..."); + + btnRemoveDefinition.setText("Remove"); + + btnSave.setText("Save"); + + btnCancel.setText("Cancel"); + + btnApply.setText("Apply"); + + btnUndo.setText("Undo"); + +// org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane()); +// getContentPane().setLayout(layout); +// layout.setHorizontalGroup( +// layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) +// .add(layout.createSequentialGroup() +// .addContainerGap() +// .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) +// .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 448, Short.MAX_VALUE) +// .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup() +// .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING) +// .add(layout.createSequentialGroup() +// .add(btnSave) +// .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) +// .add(btnApply) +// .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) +// .add(btnUndo) +// .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) +// .add(btnCancel)) +// .add(layout.createSequentialGroup() +// .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) +// .add(defLabel) +// .add(layout.createSequentialGroup() +// .add(btnMoveDown) +// .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) +// .add(btnMoveUp))) +// .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 80, Short.MAX_VALUE) +// .add(btnAddDefinition))) +// .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) +// .add(btnRemoveDefinition))) +// .addContainerGap()) +// ); +// +// layout.linkSize(new java.awt.Component[]{btnAddDefinition, btnMoveDown, btnMoveUp, btnRemoveDefinition}, org.jdesktop.layout.GroupLayout.HORIZONTAL); +// +// layout.setVerticalGroup( +// layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) +// .add(layout.createSequentialGroup() +// .addContainerGap() +// .add(defLabel) +// .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) +// .add(jScrollPane1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) +// .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) +// .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) +// .add(btnMoveUp) +// .add(btnMoveDown) +// .add(btnRemoveDefinition, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 23, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) +// .add(btnAddDefinition)) +// .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) +// .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) +// .add(btnSave) +// .add(btnApply) +// .add(btnUndo) +// .add(btnCancel)) +// .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) +// ); + pack(); + }// //GEN-END:initComponents + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == btnCancel) { + dispose(); + + } else if (e.getSource() == btnSave) { + saveSettings(); + dispose(); + + } else if (e.getSource() == btnApply) { + saveSettings(); + + } else if (e.getSource() == btnMoveUp) { + moveSelection(MOVE_UP); + + } else if (e.getSource() == btnMoveDown) { + moveSelection(MOVE_DOWN); + + } else if (e.getSource() == btnAddDefinition) { + addFile(); + + } else if (e.getSource() == btnRemoveDefinition) { + removeSelection(); + + } else if (e.getSource() == btnUndo) { + initSettings(); + + } + + } + + public void saveSettings() { + Vector output = new Vector(); + + // create file vector + for (int i = 0; i < fileNames.size(); i++) { + output.add(new File(fileNames.get(i))); + } + + // save + SettingsManager.getSettings().setEcuDefinitionFiles(output); + } + + public void addFile() { + JFileChooser fc = new JFileChooser("./"); + fc.setFileFilter(new XMLFilter()); + + if (fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + fileNames.add(fc.getSelectedFile().getAbsolutePath()); + updateListModel(); + } + } + + public void moveSelection(int direction) { + int selectedIndex = definitionList.getSelectedIndex(); + String fileName = fileNames.get(selectedIndex); + + if (direction == MOVE_UP && selectedIndex > 0) { + fileNames.remove(selectedIndex); + fileNames.add(--selectedIndex, fileName); + + } else if (direction == MOVE_DOWN && selectedIndex < definitionList.getModel().getSize()) { + fileNames.remove(selectedIndex); + fileNames.add(++selectedIndex, fileName); + + } + updateListModel(); + definitionList.setSelectedIndex(selectedIndex); + } + + public void removeSelection() { + int index = definitionList.getSelectedIndex(); + if (index < 0) return; + fileNames.remove(index); + updateListModel(); + + } + + public void updateListModel() { + definitionList.setListData(fileNames); + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton btnAddDefinition; + private javax.swing.JButton btnApply; + private javax.swing.JButton btnCancel; + private javax.swing.JButton btnMoveDown; + private javax.swing.JButton btnMoveUp; + private javax.swing.JButton btnRemoveDefinition; + private javax.swing.JButton btnSave; + private javax.swing.JButton btnUndo; + private javax.swing.JLabel defLabel; + private javax.swing.JList definitionList; + private javax.swing.JScrollPane jScrollPane1; + // End of variables declaration//GEN-END:variables + +} diff --git a/java_console/romraider/src/com/romraider/swing/ECUEditorMenuBar.java b/java_console/romraider/src/com/romraider/swing/ECUEditorMenuBar.java new file mode 100644 index 0000000000..cd564f7eb3 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/ECUEditorMenuBar.java @@ -0,0 +1,623 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2014 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import static com.romraider.Version.ABOUT_ICON; +import static com.romraider.Version.BUILDNUMBER; +import static com.romraider.Version.ECU_DEFS_URL; +import static com.romraider.Version.PRODUCT_NAME; +import static com.romraider.Version.SUPPORT_URL; +import static com.romraider.Version.VERSION; +import static javax.swing.JOptionPane.CANCEL_OPTION; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.INFORMATION_MESSAGE; +import static javax.swing.JOptionPane.showConfirmDialog; +import static javax.swing.JOptionPane.showMessageDialog; +import static javax.swing.WindowConstants.DISPOSE_ON_CLOSE; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; + +import javax.swing.ButtonGroup; +import javax.swing.JFileChooser; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.JSeparator; + +import com.romraider.Settings; +import com.romraider.editor.ecu.ECUEditor; +import com.romraider.editor.ecu.ECUEditorManager; +import com.romraider.maps.Rom; +import com.romraider.maps.Table; +import com.romraider.net.BrowserControl; +import com.romraider.util.SettingsManager; + +public class ECUEditorMenuBar extends JMenuBar implements ActionListener { + + private static final long serialVersionUID = -4777040428837855236L; + private final JMenu fileMenu = new JMenu("File"); + private final JMenuItem openImage = new JMenuItem("Open Image..."); + private final JMenuItem openImages = new JMenuItem("Open Image(s)..."); + private final JMenuItem saveImage = new JMenuItem("Save Image As..."); + private final JMenuItem saveAsRepository = new JMenuItem("Save Image As Repository..."); + private final JMenuItem refreshImage = new JMenuItem("Refresh Image"); + private final JMenuItem closeImage = new JMenuItem("Close Image"); + private final JMenuItem closeAll = new JMenuItem("Close All Images"); + private final JMenuItem exit = new JMenuItem("Exit"); + + private final JMenu definitionMenu = new JMenu("ECU Definitions"); + private final JMenuItem defManager = new JMenuItem("ECU Definition Manager..."); + private final JMenuItem editDefinition = new JMenuItem("Edit ECU Definitions..."); + private final JMenuItem updateDefinition = new JMenuItem("Get ECU Definitions..."); + + private final JMenu editMenu = new JMenu("Edit"); + private final JMenuItem settings = new JMenuItem(PRODUCT_NAME + " Settings..."); + private final JMenuItem compareImages = new JMenuItem("Compare Images..."); + private final JMenu convertRom = new JMenu("Convert Image"); + private final JMenuItem convertIncrease = new JMenuItem("160KB --> 192KB..."); + private final JMenuItem convertDecrease = new JMenuItem("192KB --> 160KB..."); + private final ButtonGroup convertGroup = new ButtonGroup(); + + private final JMenu viewMenu = new JMenu("View"); + private final JMenuItem romProperties = new JMenuItem("ECU Image Properties"); + private final ButtonGroup levelGroup = new ButtonGroup(); + private final JMenu levelMenu = new JMenu("User Level"); + private final JRadioButtonMenuItem level1 = new JRadioButtonMenuItem("1 Beginner"); + private final JRadioButtonMenuItem level2 = new JRadioButtonMenuItem("2 Intermediate"); + private final JRadioButtonMenuItem level3 = new JRadioButtonMenuItem("3 Advanced"); + private final JRadioButtonMenuItem level4 = new JRadioButtonMenuItem("4 Highest"); + private final JRadioButtonMenuItem level5 = new JRadioButtonMenuItem("5 Debug Mode"); + +// private final JMenu loggerMenu = new JMenu("Logger"); +// private final JMenuItem openLogger = new JMenuItem("Launch Logger..."); + + private final JMenu ramTuneMenu = new JMenu("SSM"); + private final JMenuItem launchRamTuneTestApp = new JMenuItem("Launch Test App..."); + + private final JMenu helpMenu = new JMenu("Help"); + private final JMenuItem about = new JMenuItem("About " + PRODUCT_NAME); + + public ECUEditorMenuBar() { + // file menu items + add(fileMenu); + fileMenu.setMnemonic('F'); + + fileMenu.add(openImage); + openImage.addActionListener(this); + openImage.setMnemonic('O'); + + //fileMenu.add(openImages); + //openImages.addActionListener(this); + //openImages.setMnemonic('I'); + + fileMenu.add(saveImage); + saveImage.addActionListener(this); + saveImage.setMnemonic('S'); + + fileMenu.add(saveAsRepository); + saveAsRepository.setMnemonic('D'); + saveAsRepository.addActionListener(this); + + fileMenu.add(refreshImage); + refreshImage.addActionListener(this); + refreshImage.setMnemonic('R'); + + fileMenu.add(new JSeparator()); + + fileMenu.add(closeImage); + closeImage.addActionListener(this); + closeImage.setMnemonic('C'); + + //fileMenu.add(closeAll); + //closeAll.addActionListener(this); + //closeAll.setMnemonic('A'); + + fileMenu.add(new JSeparator()); + + fileMenu.add(exit); + exit.addActionListener(this); + exit.setMnemonic('X'); + + // edit menu items + add(editMenu); + editMenu.setMnemonic('E'); + + editMenu.add(settings); + settings.addActionListener(this); + settings.setMnemonic('S'); + + editMenu.add(compareImages); + compareImages.addActionListener(this); + compareImages.setMnemonic('C'); + + editMenu.add(convertRom); + convertRom.setMnemonic('O'); + + convertRom.add(convertIncrease); + convertIncrease.addActionListener(this); + convertIncrease.setMnemonic('I'); + + convertRom.add(convertDecrease); + convertDecrease.addActionListener(this); + convertDecrease.setMnemonic('D'); + + convertGroup.add(convertIncrease); + convertGroup.add(convertDecrease); + + // ecu def menu items + add(definitionMenu); + definitionMenu.setMnemonic('D'); + + definitionMenu.add(defManager); + defManager.addActionListener(this); + defManager.setMnemonic('D'); + + definitionMenu.add(updateDefinition); + updateDefinition.addActionListener(this); + updateDefinition.setMnemonic('U'); + + //definitionMenu.add(editDefinition); + //editDefinition.setMnemonic('E'); + //editDefinition.addActionListener(this); + + // view menu items + add(viewMenu); + viewMenu.setMnemonic('V'); + + viewMenu.add(romProperties); + romProperties.addActionListener(this); + romProperties.setMnemonic('P'); + + viewMenu.add(levelMenu); + levelMenu.setMnemonic('U'); + + levelMenu.add(level1); + level1.addActionListener(this); + level1.setMnemonic('1'); + + levelMenu.add(level2); + level2.addActionListener(this); + level2.setMnemonic('2'); + + levelMenu.add(level3); + level3.addActionListener(this); + level3.setMnemonic('3'); + + levelMenu.add(level4); + level4.addActionListener(this); + level4.setMnemonic('4'); + + levelMenu.add(level5); + level5.addActionListener(this); + level5.setMnemonic('5'); + + levelGroup.add(level1); + levelGroup.add(level2); + levelGroup.add(level3); + levelGroup.add(level4); + levelGroup.add(level5); + + // select correct userlevel button + if (getSettings().getUserLevel() == 1) { + level1.setSelected(true); + } else if (getSettings().getUserLevel() == 2) { + level2.setSelected(true); + } else if (getSettings().getUserLevel() == 3) { + level3.setSelected(true); + } else if (getSettings().getUserLevel() == 4) { + level4.setSelected(true); + } else if (getSettings().getUserLevel() == 5) { + level5.setSelected(true); + } + + // logger menu items +// add(loggerMenu); +// loggerMenu.setMnemonic('L'); +// +// loggerMenu.add(openLogger); +// openLogger.addActionListener(this); +// openLogger.setMnemonic('O'); + + // ramtune menu items + add(ramTuneMenu); + ramTuneMenu.setMnemonic('R'); + + ramTuneMenu.add(launchRamTuneTestApp); + launchRamTuneTestApp.addActionListener(this); + launchRamTuneTestApp.setMnemonic('L'); + + // help menu items + add(helpMenu); + helpMenu.setMnemonic('H'); + + helpMenu.add(about); + about.addActionListener(this); + about.setMnemonic('A'); + + updateMenu(); + } + + public void updateMenu() { + String file = getLastSelectedRomFileName(); + if ("".equals(file)) { + saveImage.setEnabled(false); + saveAsRepository.setEnabled(false); + closeImage.setEnabled(false); + //closeAll.setEnabled(false); + romProperties.setEnabled(false); + saveImage.setText("Save As..."); + saveAsRepository.setText("Save As Repository..."); + compareImages.setEnabled(false); + convertRom.setEnabled(false); + } else { + saveImage.setEnabled(true); + saveAsRepository.setEnabled(true); + closeImage.setEnabled(true); + //closeAll.setEnabled(true); + romProperties.setEnabled(true); + saveImage.setText("Save " + file + " As..."); + saveAsRepository.setText("Save "+ file +" As Repository..."); + compareImages.setEnabled(true); + convertRom.setEnabled(true); + } + refreshImage.setText("Refresh " + file); + closeImage.setText("Close " + file); + romProperties.setText(file + "Properties"); + + int lastSelectedRomSize = 0; + Rom lastSelectedRom = ECUEditorManager.getECUEditor().getLastSelectedRom(); + if(null != lastSelectedRom) { + lastSelectedRomSize = lastSelectedRom.getRealFileSize(); + } + + if(Settings.SIXTEENBIT_SMALL_SIZE == lastSelectedRomSize) { + convertIncrease.setEnabled(true); + convertDecrease.setEnabled(false); + } else if (Settings.SIXTEENBIT_LARGE_SIZE == lastSelectedRomSize) { + convertIncrease.setEnabled(false); + convertDecrease.setEnabled(true); + } else { + convertIncrease.setEnabled(false); + convertDecrease.setEnabled(false); + } + + openImages.setEnabled(false); + editDefinition.setEnabled(false); + revalidate(); + } + + @Override + public void actionPerformed(ActionEvent e) { + ECUEditor parent = ECUEditorManager.getECUEditor(); + if (e.getSource() == openImage) { + try { + openImageDialog(); + } catch (Exception ex) { + showMessageDialog(parent, + new DebugPanel(ex, getSettings().getSupportURL()), "Exception", ERROR_MESSAGE); + } + + } else if (e.getSource() == openImages) { + try { + openImagesDialog(); + } catch (Exception ex) { + showMessageDialog(parent, + new DebugPanel(ex, getSettings().getSupportURL()), "Exception", ERROR_MESSAGE); + } + + } else if (e.getSource() == saveImage) { + try { + this.saveImage(); + } catch (Exception ex) { + showMessageDialog(parent, + new DebugPanel(ex, getSettings().getSupportURL()), "Exception", ERROR_MESSAGE); + } + } else if (e.getSource() == saveAsRepository) { + try { + this.saveAsRepository(); + } catch(Exception ex) { + showMessageDialog(parent, + new DebugPanel(ex, getSettings().getSupportURL()), "Exception", ERROR_MESSAGE); + } + } else if (e.getSource() == closeImage) { + parent.closeImage(); + + } else if (e.getSource() == closeAll) { + parent.closeAllImages(); + + } else if (e.getSource() == exit) { + parent.handleExit(); + System.exit(0); + + } else if (e.getSource() == romProperties) { + showMessageDialog(parent, new RomPropertyPanel(parent.getLastSelectedRom()), + parent.getLastSelectedRom().getRomIDString() + " Properties", INFORMATION_MESSAGE); + + } else if (e.getSource() == refreshImage) { + try { + refreshImage(); + } catch (Exception ex) { + showMessageDialog(parent, new DebugPanel(ex, + getSettings().getSupportURL()), "Exception", ERROR_MESSAGE); + } + + } else if (e.getSource() == settings) { + SettingsForm form = new SettingsForm(); + form.setLocationRelativeTo(parent); + form.setVisible(true); + + } else if (e.getSource() == compareImages){ + CompareImagesForm form = new CompareImagesForm(parent.getImages(), parent.getIconImage()); + form.setLocationRelativeTo(parent); + form.setVisible(true); + + } else if (e.getSource() == convertIncrease) { + try { + increaseRomSize(); + refreshImage(); + } catch (Exception ex) { + showMessageDialog(parent, + new DebugPanel(ex, getSettings().getSupportURL()), "Exception", ERROR_MESSAGE); + } + + } else if (e.getSource() == convertDecrease) { + try { + decreaseRomSize(); + refreshImage(); + } catch (Exception ex) { + showMessageDialog(parent, + new DebugPanel(ex, getSettings().getSupportURL()), "Exception", ERROR_MESSAGE); + } + + } else if (e.getSource() == defManager) { + DefinitionManager form = new DefinitionManager(); + form.setLocationRelativeTo(parent); + form.setVisible(true); + + } else if (e.getSource() == level1) { + parent.setUserLevel(1); + + } else if (e.getSource() == level2) { + parent.setUserLevel(2); + + } else if (e.getSource() == level3) { + parent.setUserLevel(3); + + } else if (e.getSource() == level4) { + parent.setUserLevel(4); + + } else if (e.getSource() == level5) { + parent.setUserLevel(5); + +// } else if (e.getSource() == openLogger) { +// parent.launchLogger(); + } else if (e.getSource() == updateDefinition) { + BrowserControl.displayURL(ECU_DEFS_URL); + +// } else if (e.getSource() == launchRamTuneTestApp) { +// RamTuneTestApp.startTestApp(DISPOSE_ON_CLOSE); + + } else if (e.getSource() == about) { + //TODO: change this to use com.romraider.swing.menubar.action.AboutAction + String message = PRODUCT_NAME + " - ECU Editor\n" + + "Version: " + VERSION + "\n" + + "Build #: " + BUILDNUMBER + "\n" + + SUPPORT_URL; + String title = "About " + PRODUCT_NAME; + showMessageDialog(parent, message, title, INFORMATION_MESSAGE, ABOUT_ICON); + } + } + + public void refreshImage() throws Exception { + ECUEditor parent = ECUEditorManager.getECUEditor(); + if (parent.getLastSelectedRom() != null) { + File file = parent.getLastSelectedRom().getFullFileName(); + parent.closeImage(); + parent.openImage(file); + } + } + + public void openImageDialog() throws Exception { + ECUEditor parent = ECUEditorManager.getECUEditor(); + JFileChooser fc = new JFileChooser(SettingsManager.getSettings().getLastImageDir()); + fc.setFileFilter(new ECUImageFilter()); + fc.setDialogTitle("Open Image"); + + if (fc.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION) { + parent.openImage(fc.getSelectedFile()); + SettingsManager.getSettings().setLastImageDir(fc.getCurrentDirectory()); + } + } + + public void openImagesDialog() throws Exception { + ECUEditor parent = ECUEditorManager.getECUEditor(); + JFileChooser fc = new JFileChooser(getSettings().getLastImageDir()); + fc.setFileFilter(new ECUImageFilter()); + fc.setMultiSelectionEnabled(true); + fc.setDialogTitle("Open Image(s)"); + + if(fc.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION) { + parent.openImages(fc.getSelectedFiles()); + SettingsManager.getSettings().setLastImageDir(fc.getCurrentDirectory()); + } + } + + public void saveImage() throws Exception { + Rom lastSelectedRom = ECUEditorManager.getECUEditor().getLastSelectedRom(); + if (lastSelectedRom != null) { + File selectedFile = getImageOutputFile(); + if(null != selectedFile){ + byte[] output = lastSelectedRom.saveFile(); + this.writeImage(output, selectedFile); + } + } + } + + private File getImageOutputFile() throws Exception { + ECUEditor parent = ECUEditorManager.getECUEditor(); + JFileChooser fc = new JFileChooser(SettingsManager.getSettings().getLastImageDir()); + fc.setFileFilter(new ECUImageFilter()); + if (fc.showSaveDialog(parent) == JFileChooser.APPROVE_OPTION) { + File selectedFile = fc.getSelectedFile(); + if (selectedFile.exists()) { + int option = showConfirmDialog(parent, selectedFile.getName() + " already exists! Overwrite?"); + + // option: 0 = Cancel, 1 = No + if (option == CANCEL_OPTION || option == 1) { + return null; + } + } + return selectedFile; + } + return null; + } + + private void writeImage(byte[] output, File selectedFile) throws Exception { + ECUEditor parent = ECUEditorManager.getECUEditor(); + FileOutputStream fos = new FileOutputStream(selectedFile); + try { + fos.write(output); + } finally { + fos.close(); + } + parent.getLastSelectedRom().setFullFileName(selectedFile.getAbsoluteFile()); + parent.setLastSelectedRom(parent.getLastSelectedRom()); + SettingsManager.getSettings().setLastImageDir(selectedFile.getParentFile()); + } + + private File getRepositoryOutputDir() { + JFileChooser fc = new JFileChooser(); + fc.setCurrentDirectory(getSettings().getLastRepositoryDir()); + fc.setDialogTitle("Select Repository Directory"); + fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + + // disable the "All files" option + fc.setAcceptAllFileFilterUsed(false); + + if (fc.showSaveDialog(ECUEditorManager.getECUEditor()) == JFileChooser.APPROVE_OPTION) { + File selectedDir = fc.getSelectedFile(); + if (selectedDir.exists()) { + int option = showConfirmDialog(ECUEditorManager.getECUEditor(), selectedDir.getName() + " already exists! Overwrite?"); + + // option: 0 = Cancel, 1 = No + if (option == CANCEL_OPTION || option == 1) { + return null; + } + } + return selectedDir; + } + return null; + } + + private void saveAsRepository() throws Exception { + File selectedDir = getRepositoryOutputDir(); + String separator = System.getProperty("file.separator"); + + if(null != selectedDir) { + for(TableTreeNode treeNode : ECUEditorManager.getECUEditor().getLastSelectedRom().getTableNodes()) + { + Table table = treeNode.getTable(); + String category = table.getCategory(); + String tableName = table.getName(); + String tableDirString = selectedDir.getAbsolutePath() + separator + category; + File tableDir = new File(tableDirString.replace('/', '-')); + tableDir.mkdirs(); + String tableFileString = tableDir.getAbsolutePath() + separator + tableName+".txt"; + File tableFile = new File(tableFileString.replace('/', '-')); + + if(tableFile.exists()) + { + tableFile.delete(); + } + + tableFile.createNewFile(); + StringBuffer tableData = table.getTableAsString(); + BufferedWriter out = new BufferedWriter(new FileWriter(tableFile)); + try { + out.write(tableData.toString()); + } finally { + try { + out.close(); + } catch(Exception ex) { + ;// Do Nothing. + } + } + } + getSettings().setLastRepositoryDir(selectedDir); + } + } + + private void increaseRomSize() throws Exception{ + Rom lastSelectedRom = ECUEditorManager.getECUEditor().getLastSelectedRom(); + if (lastSelectedRom != null) { + File selectedFile = getImageOutputFile(); + if(null != selectedFile){ + if(lastSelectedRom.getRealFileSize() != Settings.SIXTEENBIT_SMALL_SIZE) + { + showMessageDialog(ECUEditorManager.getECUEditor(), "Error converting image. Image size is invalid."); + } else { + byte[] output = lastSelectedRom.saveFile(); + byte[] incOutput = new byte[Settings.SIXTEENBIT_LARGE_SIZE]; + System.arraycopy(output, 0, incOutput, 0, Settings.SIXTEENBIT_START_ADDRESS); + System.arraycopy(output, Settings.SIXTEENBIT_START_ADDRESS, incOutput, Settings.SIXTEENBIT_END_ADDRESS, Settings.SIXTEENBIT_SEGMENT_SIZE); + for(int i = Settings.SIXTEENBIT_START_ADDRESS; i < Settings.SIXTEENBIT_END_ADDRESS; i++) { + // Fill space. + incOutput[i] = Settings.SIXTEENBIT_SEGMENT_VALUE; + } + this.writeImage(incOutput, selectedFile); + } + } + } + } + + private void decreaseRomSize() throws Exception { + Rom lastSelectedRom = ECUEditorManager.getECUEditor().getLastSelectedRom(); + if (lastSelectedRom != null) { + File selectedFile = getImageOutputFile(); + if(null != selectedFile){ + if(lastSelectedRom.getRealFileSize() != Settings.SIXTEENBIT_LARGE_SIZE) + { + showMessageDialog(ECUEditorManager.getECUEditor(), "Error converting image. Image size is invalid."); + } else { + byte[] output =lastSelectedRom.saveFile(); + byte[] decOutput = new byte[Settings.SIXTEENBIT_SMALL_SIZE]; + System.arraycopy(output, 0, decOutput, 0, Settings.SIXTEENBIT_START_ADDRESS); + System.arraycopy(output, Settings.SIXTEENBIT_END_ADDRESS, decOutput, Settings.SIXTEENBIT_START_ADDRESS, Settings.SIXTEENBIT_SEGMENT_SIZE); + this.writeImage(decOutput, selectedFile); + } + } + } + } + + private String getLastSelectedRomFileName() { + Rom lastSelectedRom = ECUEditorManager.getECUEditor().getLastSelectedRom(); + return lastSelectedRom == null ? "" : lastSelectedRom.getFileName() + " "; + } + + private Settings getSettings() { + return SettingsManager.getSettings(); + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/swing/ECUEditorToolBar.java b/java_console/romraider/src/com/romraider/swing/ECUEditorToolBar.java new file mode 100644 index 0000000000..f95303a31c --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/ECUEditorToolBar.java @@ -0,0 +1,157 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import static javax.swing.BorderFactory.createLineBorder; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JOptionPane; +import javax.swing.JToolBar; + +import com.romraider.Settings; +import com.romraider.editor.ecu.ECUEditor; +import com.romraider.editor.ecu.ECUEditorManager; +import com.romraider.util.SettingsManager; + +public class ECUEditorToolBar extends JToolBar implements ActionListener { + + private static final long serialVersionUID = 7778170684606193919L; + private final JButton openImage = new JButton(); + private final JButton saveImage = new JButton(); + private final JButton refreshImage = new JButton(); + private final JButton closeImage = new JButton(); + + public ECUEditorToolBar(String name) { + super(name); + this.setFloatable(true); + this.setRollover(true); + FlowLayout toolBarLayout = new FlowLayout(FlowLayout.LEFT, 0, 0); + this.setLayout(toolBarLayout); + //this.setBorder(BorderFactory.createTitledBorder("Editor Tools")); + + this.updateIcons(); + + this.add(openImage); + this.add(saveImage); + this.add(closeImage); + this.add(refreshImage); + + openImage.setMaximumSize(new Dimension(58, 50)); + openImage.setBorder(createLineBorder(new Color(150, 150, 150), 0)); + saveImage.setMaximumSize(new Dimension(50, 50)); + saveImage.setBorder(createLineBorder(new Color(150, 150, 150), 0)); + closeImage.setMaximumSize(new Dimension(50, 50)); + closeImage.setBorder(createLineBorder(new Color(150, 150, 150), 0)); + refreshImage.setMaximumSize(new Dimension(50, 50)); + refreshImage.setBorder(createLineBorder(new Color(150, 150, 150), 0)); + + this.updateButtons(); + + openImage.addActionListener(this); + saveImage.addActionListener(this); + closeImage.addActionListener(this); + refreshImage.addActionListener(this); + } + + public void updateIcons() { + int iconScale = getSettings().getEditorIconScale(); + openImage.setIcon(rescaleImageIcon(new ImageIcon(getClass().getResource("/graphics/icon-open.png")), iconScale)); + saveImage.setIcon(rescaleImageIcon(new ImageIcon(getClass().getResource("/graphics/icon-save.png")), iconScale)); + refreshImage.setIcon(rescaleImageIcon(new ImageIcon(getClass().getResource("/graphics/icon-refresh.png")), iconScale)); + closeImage.setIcon(rescaleImageIcon(new ImageIcon( getClass().getResource("/graphics/icon-close.png")), iconScale)); + repaint(); + } + + private ImageIcon rescaleImageIcon(ImageIcon imageIcon, int percentOfOriginal) { + int newHeight = (int) (imageIcon.getImage().getHeight(this) * (percentOfOriginal * .01)); + int newWidth = (int) (imageIcon.getImage().getWidth(this) * (percentOfOriginal * .01)); + + if(newHeight > 0 && newWidth > 0) + { + imageIcon.setImage(imageIcon.getImage().getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH)); + } + return imageIcon; + } + + public void updateButtons() { + String file = getEditor().getLastSelectedRomFileName(); + + openImage.setToolTipText("Open Image"); + saveImage.setToolTipText("Save " + file + " As New Image..."); + refreshImage.setToolTipText("Refresh " + file + " from saved copy"); + closeImage.setToolTipText("Close " + file); + + if ("".equals(file)) { + saveImage.setEnabled(false); + refreshImage.setEnabled(false); + closeImage.setEnabled(false); + } else { + saveImage.setEnabled(true); + refreshImage.setEnabled(true); + closeImage.setEnabled(true); + } + revalidate(); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == openImage) { + try { + ((ECUEditorMenuBar) getEditor().getJMenuBar()).openImageDialog(); + } catch (Exception ex) { + JOptionPane.showMessageDialog(getEditor(), new DebugPanel(ex, + getSettings().getSupportURL()), "Exception", JOptionPane.ERROR_MESSAGE); + } + } else if (e.getSource() == saveImage) { + try { + ((ECUEditorMenuBar) getEditor().getJMenuBar()).saveImage(); + getEditor().refreshUI(); + } catch (Exception ex) { + JOptionPane.showMessageDialog(getEditor(), new DebugPanel(ex, + getSettings().getSupportURL()), "Exception", JOptionPane.ERROR_MESSAGE); + } + } else if (e.getSource() == closeImage) { + getEditor().closeImage(); + } else if (e.getSource() == refreshImage) { + try { + ((ECUEditorMenuBar) getEditor().getJMenuBar()).refreshImage(); + } catch (Exception ex) { + JOptionPane.showMessageDialog(getEditor(), new DebugPanel(ex, + getSettings().getSupportURL()), "Exception", JOptionPane.ERROR_MESSAGE); + } + } + } + + private Settings getSettings() { + return SettingsManager.getSettings(); + } + + private ECUEditor getEditor() { + return ECUEditorManager.getECUEditor(); + } +} diff --git a/java_console/romraider/src/com/romraider/swing/ECUImageFilter.java b/java_console/romraider/src/com/romraider/swing/ECUImageFilter.java new file mode 100644 index 0000000000..118a02c516 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/ECUImageFilter.java @@ -0,0 +1,36 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import javax.swing.filechooser.FileFilter; +import java.io.File; + +public class ECUImageFilter extends FileFilter { + + private final FileFilter filter = new GenericFileFilter("ECU Image Files", "bin", "hex"); + + public boolean accept(File file) { + return filter.accept(file); + } + + public String getDescription() { + return filter.getDescription(); + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/swing/GenericFileFilter.java b/java_console/romraider/src/com/romraider/swing/GenericFileFilter.java new file mode 100644 index 0000000000..f19ff92128 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/GenericFileFilter.java @@ -0,0 +1,103 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import javax.swing.filechooser.FileFilter; +import java.io.File; +import java.util.Enumeration; +import java.util.Hashtable; + +public final class GenericFileFilter extends FileFilter { + + private final Hashtable filters = new Hashtable(); + private String description = null; + private String fullDescription = null; + private boolean useExtensionsInDescription = true; + + public GenericFileFilter(String description, String... extensions) { + setDescription(description); + for (String extension : extensions) { + addExtension(extension); + } + } + + public boolean accept(File f) { + if (f != null) { + if (f.isDirectory()) { + return true; + } + String extension = getExtension(f); + if (extension != null && filters.get(getExtension(f)) != null) { + return true; + } + } + return false; + } + + public String getExtension(File f) { + if (f != null) { + String filename = f.getName(); + int i = filename.lastIndexOf('.'); + if (i > 0 && i < filename.length() - 1) { + return filename.substring(i + 1).toLowerCase(); + } + } + return null; + } + + public void addExtension(String extension) { + filters.put(extension.toLowerCase(), this); + fullDescription = null; + } + + public String getDescription() { + if (fullDescription == null) { + if (description == null || isExtensionListInDescription()) { + fullDescription = description == null ? "(" : description + " ("; + // build the description from the extension list + Enumeration extensions = filters.keys(); + if (extensions != null) { + fullDescription += "." + extensions.nextElement(); + while (extensions.hasMoreElements()) { + fullDescription += ", ." + extensions.nextElement(); + } + } + fullDescription += ")"; + } else { + fullDescription = description; + } + } + return fullDescription; + } + + public void setDescription(String description) { + this.description = description; + fullDescription = null; + } + + public void setExtensionListInDescription(boolean b) { + useExtensionsInDescription = b; + fullDescription = null; + } + + public boolean isExtensionListInDescription() { + return useExtensionsInDescription; + } +} diff --git a/java_console/romraider/src/com/romraider/swing/JProgressPane.java b/java_console/romraider/src/com/romraider/swing/JProgressPane.java new file mode 100644 index 0000000000..106062a59e --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/JProgressPane.java @@ -0,0 +1,80 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Font; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JProgressBar; + +public class JProgressPane extends JPanel implements PropertyChangeListener{ + + private static final long serialVersionUID = -6827936662738014543L; + JLabel label = new JLabel(); + JProgressBar progressBar = new JProgressBar(JProgressBar.HORIZONTAL, 0, 100); + String status = "ready"; + int percent = 0; + + public JProgressPane() { + + this.setPreferredSize(new Dimension(500, 18)); + this.setLayout(new BorderLayout(1, 2)); + label.setHorizontalAlignment(JLabel.CENTER); + label.setText(" Ready..."); + label.setFont(new Font("Tahoma", Font.PLAIN, 11)); + label.setHorizontalAlignment(JLabel.LEFT); + progressBar.setMinimumSize(new Dimension(200, 50)); + progressBar.setValue(0); + + this.add(progressBar, BorderLayout.WEST); + this.add(label, BorderLayout.CENTER); + + } + + public void update(String status, int percent) { + label.setText(" " + status); + progressBar.setValue(percent); + } + + public void setStatus(String status) { + this.status = status; + } + + public JProgressBar getProgressBar() { + return this.progressBar; + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if("progress" == evt.getPropertyName()) { + int progress = (Integer) evt.getNewValue(); + progressBar.setValue(progress); + label.setText(" " + status); + } + else{ + ;// do nothing + } + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/swing/JTableChooser.java b/java_console/romraider/src/com/romraider/swing/JTableChooser.java new file mode 100644 index 0000000000..09f59f3117 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/JTableChooser.java @@ -0,0 +1,122 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import java.awt.Dimension; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.Vector; + +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTree; +import javax.swing.ScrollPaneConstants; +import javax.swing.SwingUtilities; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreePath; + +import com.romraider.editor.ecu.ECUEditorManager; +import com.romraider.maps.Rom; +import com.romraider.maps.Table; + +public class JTableChooser extends JOptionPane implements MouseListener { + public JTableChooser() { + } + + private static final long serialVersionUID = 5611729002131147882L; + JPanel displayPanel = new JPanel(); + DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("Open Images"); + JTree displayTree = new JTree(rootNode); + JScrollPane displayScrollPane; + + public Table showChooser(Table targetTable) { + Vector roms = ECUEditorManager.getECUEditor().getImages(); + int nameLength = 0; + + for (int i = 0; i < roms.size(); i++) { + Rom rom = roms.get(i); + DefaultMutableTreeNode romNode = new DefaultMutableTreeNode(rom.getFileName()); + rootNode.add(romNode); + + for (TableTreeNode tableTreeNode : rom.getTableNodes()) { + Table table = tableTreeNode.getTable(); + // use the length of the table name to set the width of the displayTree + // so the entire name can be read without being cut off on the right + if (table.getName().length() > nameLength) { + nameLength = table.getName().length(); + } + TableChooserTreeNode tableNode = new TableChooserTreeNode(table.getName(), table); + + // categories + boolean categoryExists = false; + for (int k = 0; k < romNode.getChildCount(); k++) { + if (romNode.getChildAt(k).toString().equalsIgnoreCase(table.getCategory())) { + ((DefaultMutableTreeNode) romNode.getChildAt(k)).add(tableNode); + categoryExists = true; + break; + } + } + + if (!categoryExists) { + DefaultMutableTreeNode categoryNode = new DefaultMutableTreeNode(table.getCategory()); + romNode.add(categoryNode); + categoryNode.add(tableNode); + } + } + } + + displayTree.setPreferredSize(new Dimension(nameLength*7, 400)); + displayTree.setMinimumSize(new Dimension(nameLength*7, 400)); + + displayTree.expandPath(new TreePath(rootNode.getPath())); + displayTree.setRootVisible(false); + + displayTree.addMouseListener(this); + displayScrollPane = new JScrollPane(displayTree); + displayScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + displayPanel.add(displayScrollPane); + + Object[] values = {"Compare", "Cancel"}; + + if ((showOptionDialog(SwingUtilities.windowForComponent(targetTable), displayPanel, "Select a Map", JOptionPane.DEFAULT_OPTION, + JOptionPane.PLAIN_MESSAGE, null, values, values[0]) == 0 + && (displayTree.getLastSelectedPathComponent() instanceof TableChooserTreeNode))) { + return ((TableChooserTreeNode) displayTree.getLastSelectedPathComponent()).getTable(); + } else { + return null; + } + } + + @Override + public void mouseReleased(MouseEvent e) { + displayTree.setPreferredSize(new Dimension(displayTree.getWidth(), (displayTree.getRowCount()*displayTree.getRowHeight()))); + displayTree.revalidate(); + } + @Override + public void mouseClicked(MouseEvent e){} + @Override + public void mouseEntered(MouseEvent e){} + @Override + public void mouseExited(MouseEvent e){} + @Override + public void mousePressed(MouseEvent e){} + +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/swing/LookAndFeelManager.java b/java_console/romraider/src/com/romraider/swing/LookAndFeelManager.java new file mode 100644 index 0000000000..0f91ca7f90 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/LookAndFeelManager.java @@ -0,0 +1,69 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import static com.romraider.Version.PRODUCT_NAME; +import static com.romraider.util.Platform.LINUX; +import static com.romraider.util.Platform.MAC_OS_X; +import static com.romraider.util.Platform.isPlatform; +import static javax.swing.UIManager.getCrossPlatformLookAndFeelClassName; +import static javax.swing.UIManager.getSystemLookAndFeelClassName; +import static javax.swing.UIManager.setLookAndFeel; + +import javax.swing.JDialog; +import javax.swing.JFrame; + +import org.apache.log4j.Logger; + +public final class LookAndFeelManager { + private static final Logger LOGGER = Logger.getLogger(LookAndFeelManager.class); + + private LookAndFeelManager() { + throw new UnsupportedOperationException(); + } + + public static void initLookAndFeel() { + try { + if (isPlatform(MAC_OS_X)) { + System.setProperty("apple.awt.rendering", "true"); + System.setProperty("apple.awt.brushMetalLook", "true"); + System.setProperty("apple.laf.useScreenMenuBar", "true"); + System.setProperty("apple.awt.window.position.forceSafeCreation", "true"); + System.setProperty("com.apple.mrj.application.apple.menu.about.name", PRODUCT_NAME); + } + + setLookAndFeel(getLookAndFeel()); + + // make sure we have nice window decorations. + JFrame.setDefaultLookAndFeelDecorated(true); + JDialog.setDefaultLookAndFeelDecorated(true); + + } catch (Exception ex) { + LOGGER.error("Error loading system look and feel.", ex); + } + } + + private static String getLookAndFeel() { +// if (true) return "com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"; + // Linux has issues with the gtk look and feel themes. + if (isPlatform(LINUX)) return getCrossPlatformLookAndFeelClassName(); + return getSystemLookAndFeelClassName(); + } +} diff --git a/java_console/romraider/src/com/romraider/swing/MDIDesktopPane.java b/java_console/romraider/src/com/romraider/swing/MDIDesktopPane.java new file mode 100644 index 0000000000..6e0516ffcc --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/MDIDesktopPane.java @@ -0,0 +1,295 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Insets; +import java.awt.Point; +import java.beans.PropertyVetoException; + +import javax.swing.DefaultDesktopManager; +import javax.swing.JComponent; +import javax.swing.JDesktopPane; +import javax.swing.JInternalFrame; +import javax.swing.JScrollPane; +import javax.swing.JViewport; + +import com.romraider.Settings; +import com.romraider.editor.ecu.ECUEditor; +import com.romraider.editor.ecu.ECUEditorManager; +import com.romraider.util.SettingsManager; + +/** + * An extension of WDesktopPane that supports often used MDI functionality. This + * class also handles setting scroll bars for when windows move too far to the left or + * bottom, providing the MDIDesktopPane is in a ScrollPane. + */ +public class MDIDesktopPane extends JDesktopPane { + + private static final long serialVersionUID = -1839360490978587035L; + private final MDIDesktopManager manager; + + public MDIDesktopPane() { + manager = new MDIDesktopManager(this); + setDesktopManager(manager); + setDragMode(JDesktopPane.OUTLINE_DRAG_MODE); + } + + @Override + public void setBounds(int x, int y, int w, int h) { + super.setBounds(x, y, w, h); + checkDesktopSize(); + } + + public Component add(JInternalFrame frame) { + Point p; + int w; + int h; + + // get frame location. + if(SettingsManager.getSettings().isAlwaysOpenTableAtZero()) { + p = new Point(0, 0); + } else { + if (getAllFrames().length > 0) { + JInternalFrame selectedFrame = getSelectedFrame(); + if(null == selectedFrame || !selectedFrame.isVisible()) { + // if none selected get the location at index 0. + p = getAllFrames()[0].getLocation(); + } else { + // get the selected frame location and open off of that location. + p = selectedFrame.getLocation(); + } + + p.x = p.x + Settings.FRAME_OFFSET; + p.y = p.y + Settings.FRAME_OFFSET; + } else { + p = new Point(0, 0); + } + } + + Component retval = super.add(frame); + frame.setLocation(p.x, p.y); + + checkDesktopSize(); + + if (frame.isResizable()) { + w = getWidth() - (getWidth() / 3); + h = getHeight() - (getHeight() / 3); + if (w < frame.getMinimumSize().getWidth()) { + w = (int) frame.getMinimumSize().getWidth(); + } + if (h < frame.getMinimumSize().getHeight()) { + h = (int) frame.getMinimumSize().getHeight(); + } + frame.setSize(w, h); + } + + moveToFront(frame); + frame.setVisible(true); + + if(frame instanceof TableFrame) { + getEditor().getTableToolBar().updateTableToolBar(); + ((TableFrame) frame).RegisterTable(); + } + + try { + frame.setSelected(true); + } catch (PropertyVetoException e) { + frame.toBack(); + } + + return retval; + } + + @Override + public void remove(Component c) { + super.remove(c); + getEditor().getTableToolBar().updateTableToolBar(); + checkDesktopSize(); + } + + public ECUEditor getEditor() { + return ECUEditorManager.getECUEditor(); + } + + /** + * Cascade all internal frames + */ + public void cascadeFrames() { + int x = 0; + int y = 0; + JInternalFrame allFrames[] = getAllFrames(); + + manager.setNormalSize(); + int frameHeight = (getBounds().height - 5) - allFrames.length * Settings.FRAME_OFFSET; + int frameWidth = (getBounds().width - 5) - allFrames.length * Settings.FRAME_OFFSET; + for (int i = allFrames.length - 1; i >= 0; i--) { + allFrames[i].setSize(frameWidth, frameHeight); + allFrames[i].setLocation(x, y); + x = x + Settings.FRAME_OFFSET; + y = y + Settings.FRAME_OFFSET; + } + } + + /** + * Tile all internal frames + */ + public void tileFrames() { + java.awt.Component allFrames[] = getAllFrames(); + manager.setNormalSize(); + int frameHeight = getBounds().height / allFrames.length; + int y = 0; + for (int i = 0; i < allFrames.length; i++) { + allFrames[i].setSize(getBounds().width, frameHeight); + allFrames[i].setLocation(0, y); + y = y + frameHeight; + } + } + + /** + * Sets all component size properties ( maximum, minimum, preferred) + * to the given dimension. + */ + public void setAllSize(Dimension d) { + setMinimumSize(d); + setMaximumSize(d); + setPreferredSize(d); + } + + /** + * Sets all component size properties ( maximum, minimum, preferred) + * to the given width and height. + */ + public void setAllSize(int width, int height) { + setAllSize(new Dimension(width, height)); + } + + private void checkDesktopSize() { + if (getParent() != null && isVisible()) { + manager.resizeDesktop(); + } + } +} + +/** + * Private class used to replace the standard DesktopManager for JDesktopPane. + * Used to provide scrollbar functionality. + */ +class MDIDesktopManager extends DefaultDesktopManager { + /** + * + */ + private static final long serialVersionUID = -7668105643849176819L; + private final MDIDesktopPane desktop; + + public MDIDesktopManager(MDIDesktopPane desktop) { + this.desktop = desktop; + } + + @Override + public void endResizingFrame(JComponent f) { + super.endResizingFrame(f); + resizeDesktop(); + } + + @Override + public void endDraggingFrame(JComponent f) { + super.endDraggingFrame(f); + resizeDesktop(); + } + + public void setNormalSize() { + JScrollPane scrollPane = getScrollPane(); + int x = 0; + int y = 0; + Insets scrollInsets = getScrollPaneInsets(); + + if (scrollPane != null) { + Dimension d = scrollPane.getVisibleRect().getSize(); + if (scrollPane.getBorder() != null) { + d.setSize(d.getWidth() - scrollInsets.left - scrollInsets.right, + d.getHeight() - scrollInsets.top - scrollInsets.bottom); + } + + d.setSize(d.getWidth() - 20, d.getHeight() - 20); + desktop.setAllSize(x, y); + scrollPane.revalidate(); + } + } + + public MDIDesktopPane getDesktop() + { + return this.desktop; + } + + private Insets getScrollPaneInsets() { + JScrollPane scrollPane = getScrollPane(); + if (scrollPane == null) { + return new Insets(0, 0, 0, 0); + } else { + return getScrollPane().getBorder().getBorderInsets(scrollPane); + } + } + + private JScrollPane getScrollPane() { + if (desktop.getParent() instanceof JViewport) { + JViewport viewPort = (JViewport) desktop.getParent(); + if (viewPort.getParent() instanceof JScrollPane) { + return (JScrollPane) viewPort.getParent(); + } + } + return null; + } + + protected void resizeDesktop() { + int x = 0; + int y = 0; + JScrollPane scrollPane = getScrollPane(); + Insets scrollInsets = getScrollPaneInsets(); + + if (scrollPane != null) { + JInternalFrame allFrames[] = desktop.getAllFrames(); + for (int i = 0; i < allFrames.length; i++) { + if (allFrames[i].getX() + allFrames[i].getWidth() > x) { + x = allFrames[i].getX() + allFrames[i].getWidth(); + } + if (allFrames[i].getY() + allFrames[i].getHeight() > y) { + y = allFrames[i].getY() + allFrames[i].getHeight(); + } + } + Dimension d = scrollPane.getVisibleRect().getSize(); + if (scrollPane.getBorder() != null) { + d.setSize(d.getWidth() - scrollInsets.left - scrollInsets.right, + d.getHeight() - scrollInsets.top - scrollInsets.bottom); + } + + if (x <= d.getWidth()) { + x = ((int) d.getWidth()) - 20; + } + if (y <= d.getHeight()) { + y = ((int) d.getHeight()) - 20; + } + desktop.setAllSize(x, y); + scrollPane.invalidate(); + scrollPane.validate(); + } + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/swing/ParameterIdsTableModel.java b/java_console/romraider/src/com/romraider/swing/ParameterIdsTableModel.java new file mode 100644 index 0000000000..c2d145ee36 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/ParameterIdsTableModel.java @@ -0,0 +1,79 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2014 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import java.util.Map; + +import javax.swing.table.DefaultTableModel; + +public final class ParameterIdsTableModel extends DefaultTableModel { + private static final long serialVersionUID = -9125596053556735977L; + private static final String[] colNames = { + "Parameter Name", "Logger ID"}; + private Map parameterList; + + @Override + public final int getColumnCount() { + return colNames.length; + } + + @Override + public final String getColumnName(int column) { + return colNames[column]; + } + + @Override + public final Object getValueAt(int row, int column) { + if (null != parameterList) { + final String key = (String) parameterList.keySet().toArray()[row]; + final String value = parameterList.get(key); + switch (column) { + case 0: + return key; + case 1: + return value; + default: + return null; + } + } + else { + return null; + } + } + + @Override + public final int getRowCount() { + return (null != parameterList) ? parameterList.size() : 0; + } + + @Override + public final Class getColumnClass(int column) { + return getValueAt(0, column).getClass(); + } + + @Override + public final boolean isCellEditable(int row, int column) { + return false; + } + + public final void setParameterList(Map parameterList) { + this.parameterList = parameterList; + } +} diff --git a/java_console/romraider/src/com/romraider/swing/RomCellRenderer.java b/java_console/romraider/src/com/romraider/swing/RomCellRenderer.java new file mode 100644 index 0000000000..95412f7716 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/RomCellRenderer.java @@ -0,0 +1,151 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import static javax.swing.BorderFactory.createLineBorder; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.GridLayout; + +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.TreeCellRenderer; + +import com.romraider.Settings; +import com.romraider.maps.Rom; +import com.romraider.maps.Table; + +public class RomCellRenderer implements TreeCellRenderer { + + JLabel fileName; + JLabel carInfo; + DefaultTreeCellRenderer defaultRenderer = new DefaultTreeCellRenderer(); + + public RomCellRenderer() { + fileName = new JLabel(" "); + fileName.setFont(new Font("Tahoma", Font.BOLD, 11)); + fileName.setHorizontalAlignment(JLabel.CENTER); + + carInfo = new JLabel(" "); + carInfo.setFont(new Font("Tahoma", Font.PLAIN, 10)); + carInfo.setHorizontalAlignment(JLabel.CENTER); + + } + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, + boolean selected, boolean expanded, boolean leaf, int row, + boolean hasFocus) { + + Component returnValue = null; + + if (value != null && value instanceof Rom) { + Rom rom = ((Rom) value); + + if (expanded) { + fileName.setText("- " + rom.getFileName()); + } else { + fileName.setText("+ " + rom.getFileName()); + } + + + carInfo.setText(rom.getRomIDString() + ", " + + rom.getRomID().getCaseId() + "; " + + rom.getRomID().getYear() + " " + + rom.getRomID().getMake() + " " + + rom.getRomID().getModel() + " " + + rom.getRomID().getSubModel() + ", " + + rom.getRomID().getTransmission() + ); + + JPanel renderer = new JPanel(new GridLayout(2, 1)); + renderer.add(fileName); + renderer.add(carInfo); + + if (selected) { + renderer.setBackground(new Color(220, 220, 255)); + renderer.setBorder(createLineBorder(new Color(0, 0, 225))); + + } else { + renderer.setBorder(createLineBorder(new Color(220, 0, 0))); + renderer.setBackground(new Color(255, 210, 210)); + } + + renderer.setPreferredSize(new Dimension(tree.getParent().getWidth(), 30)); + renderer.setMaximumSize(new Dimension(tree.getParent().getWidth(), 30)); + renderer.setEnabled(tree.isEnabled()); + returnValue = renderer; + } else if (value != null && value instanceof TableTreeNode) { + + Table table = ((TableFrame)((DefaultMutableTreeNode) value).getUserObject()).getTable(); + JPanel renderer = new JPanel(new GridLayout(1, 1)); + renderer.setBorder(createLineBorder(Color.WHITE)); + JLabel tableName = new JLabel(""); + renderer.setBackground(Color.WHITE); + + // display icon + if (table.getType() == Settings.TABLE_1D) { + tableName = new JLabel(table.getName() + " ", new ImageIcon(getClass().getResource("/graphics/1d.gif")), JLabel.LEFT); + } else if (table.getType() == Settings.TABLE_2D) { + tableName = new JLabel(table.getName() + " ", new ImageIcon(getClass().getResource("/graphics/2d.gif")), JLabel.LEFT); + } else if (table.getType() == Settings.TABLE_3D) { + tableName = new JLabel(table.getName() + " ", new ImageIcon(getClass().getResource("/graphics/3d.gif")), JLabel.LEFT); + } else if (table.getType() == Settings.TABLE_SWITCH) { + tableName = new JLabel(table.getName() + " ", new ImageIcon(getClass().getResource("/graphics/switch.gif")), JLabel.LEFT); + } + + // set color + renderer.add(tableName); + tableName.setFont(new Font("Tahoma", Font.PLAIN, 11)); + + if (selected) { + renderer.setBackground(new Color(220, 220, 255)); + renderer.setBorder(createLineBorder(new Color(0, 0, 225))); + } + + if (table.getUserLevel() == 5) { + tableName.setForeground(new Color(255, 150, 150)); + tableName.setFont(new Font("Tahoma", Font.ITALIC, 11)); + + } else if (table.getUserLevel() > table.getSettings().getUserLevel()) { + //tableName.setForeground(new Color(185, 185, 185)); + tableName.setFont(new Font("Tahoma", Font.ITALIC, 11)); + + } + + returnValue = renderer; + } + + if (returnValue == null) { + returnValue = defaultRenderer.getTreeCellRendererComponent(tree, + value, selected, expanded, leaf, row, hasFocus); + } + + return returnValue; + + } +} diff --git a/java_console/romraider/src/com/romraider/swing/RomPropertyPanel.java b/java_console/romraider/src/com/romraider/swing/RomPropertyPanel.java new file mode 100644 index 0000000000..898ba53821 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/RomPropertyPanel.java @@ -0,0 +1,305 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import com.romraider.maps.Rom; + +public class RomPropertyPanel extends javax.swing.JPanel { + + private static final long serialVersionUID = 5583360728106071942L; + Rom rom = new Rom(); + + public RomPropertyPanel(Rom rom) { + initComponents(); + + // populate fields + fileName.setText(rom.getFileName()); + xmlID.setText(rom.getRomID().getXmlid()); + ecuVersion.setText(rom.getRomID().getCaseId()); + fileSize.setText((rom.getRealFileSize() / 1024) + "kb"); + internalID.setText(rom.getRomID().getInternalIdString()); + storageAddress.setText("0x" + Integer.toHexString(rom.getRomID().getInternalIdAddress())); + + make.setText(rom.getRomID().getMake()); + market.setText(rom.getRomID().getMarket()); + year.setText(rom.getRomID().getYear() + ""); + model.setText(rom.getRomID().getModel()); + submodel.setText(rom.getRomID().getSubModel()); + transmission.setText(rom.getRomID().getTransmission()); + editStamp.setText(rom.getRomID().getEditStamp()); + + tableList.setListData(rom.getTables()); + } + + // //GEN-BEGIN:initComponents + private void initComponents() { + lblFilename = new javax.swing.JLabel(); + fileName = new javax.swing.JLabel(); + lblECURevision = new javax.swing.JLabel(); + xmlID = new javax.swing.JLabel(); + lblFilesize = new javax.swing.JLabel(); + fileSize = new javax.swing.JLabel(); + lblEcuVersion = new javax.swing.JLabel(); + ecuVersion = new javax.swing.JLabel(); + lblInternalId = new javax.swing.JLabel(); + internalID = new javax.swing.JLabel(); + lblStorageAddress = new javax.swing.JLabel(); + storageAddress = new javax.swing.JLabel(); + lblMake = new javax.swing.JLabel(); + lblMarket = new javax.swing.JLabel(); + lblTransmission = new javax.swing.JLabel(); + lblModel = new javax.swing.JLabel(); + lblSubmodel = new javax.swing.JLabel(); + lblYear = new javax.swing.JLabel(); + make = new javax.swing.JLabel(); + market = new javax.swing.JLabel(); + year = new javax.swing.JLabel(); + model = new javax.swing.JLabel(); + submodel = new javax.swing.JLabel(); + transmission = new javax.swing.JLabel(); + jScrollPane1 = new javax.swing.JScrollPane(); + tableList = new javax.swing.JList(); + lblTables = new javax.swing.JLabel(); + lblEditStamp = new javax.swing.JLabel(); + editStamp = new javax.swing.JLabel(); + + lblEditStamp.setText("Edit Stamp:"); + + editStamp.setText("stamp"); + + lblFilename.setText("Filename:"); + + fileName.setText("Filename"); + + lblECURevision.setText("ECU Revision:"); + + xmlID.setText("XMLID"); + + lblFilesize.setText("Filesize:"); + + fileSize.setText("999kb"); + + lblEcuVersion.setText("ECU Version:"); + + ecuVersion.setText("ECUVER"); + + lblInternalId.setText("Internal ID:"); + + internalID.setText("INTERNAL"); + + lblStorageAddress.setText("ID Storage Address:"); + + storageAddress.setText("0x00"); + + lblMake.setText("Make:"); + + lblMarket.setText("Market:"); + + lblTransmission.setText("Transmission:"); + + lblModel.setText("Model:"); + + lblSubmodel.setText("Submodel:"); + + lblYear.setText("Year:"); + + make.setText("Make"); + + market.setText("Market"); + + year.setText("Year"); + + model.setText("Model"); + + submodel.setText("Submodel"); + + transmission.setText("Transmission"); + + tableList.setModel(new javax.swing.AbstractListModel() { + /** + * + */ + private static final long serialVersionUID = -8498656966410761726L; + String[] strings = {"Item 1", "Item 2", "Item 3", "Item 4", "Item 5"}; + + public int getSize() { + return strings.length; + } + + public Object getElementAt(int i) { + return strings[i]; + } + }); + jScrollPane1.setViewportView(tableList); + + lblTables.setText("Tables:"); + + org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .addContainerGap() + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .add(lblFilename) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(fileName, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 302, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .add(layout.createSequentialGroup() + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(lblECURevision) + .add(lblEcuVersion) + .add(lblFilesize)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(fileSize) + .add(ecuVersion) + .add(xmlID))) + .add(layout.createSequentialGroup() + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(lblYear) + .add(lblModel) + .add(lblSubmodel) + .add(lblTransmission) + .add(lblMarket) + .add(lblMake)) + .add(7, 7, 7) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(make) + .add(market) + .add(year) + .add(layout.createSequentialGroup() + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(transmission) + .add(submodel))) + .add(model)))) + .add(32, 32, 32) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(lblInternalId) + .add(lblStorageAddress) + .add(lblEditStamp)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 53, Short.MAX_VALUE) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(internalID) + .add(storageAddress) + .add(editStamp)) + .add(36, 36, 36)) + .add(lblTables) + .add(jScrollPane1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 226, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup() + .add(21, 21, 21) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(lblFilename) + .add(fileName)) + .add(layout.createSequentialGroup() + .add(40, 40, 40) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(lblECURevision) + .add(xmlID) + .add(lblInternalId) + .add(internalID)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(ecuVersion) + .add(lblEcuVersion) + .add(storageAddress) + .add(lblStorageAddress)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(lblFilesize) + .add(fileSize) + .add(lblEditStamp) + .add(editStamp)))) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(lblTables) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(lblMake) + .add(make)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(lblMarket) + .add(market)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(lblYear) + .add(year)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(lblModel) + .add(model)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(lblSubmodel) + .add(submodel)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(lblTransmission) + .add(transmission))) + .add(jScrollPane1, 0, 0, Short.MAX_VALUE)) + .addContainerGap()) + ); + }// //GEN-END:initComponents + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel ecuVersion; + private javax.swing.JLabel fileName; + private javax.swing.JLabel fileSize; + private javax.swing.JLabel internalID; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JLabel lblECURevision; + private javax.swing.JLabel lblEcuVersion; + private javax.swing.JLabel lblFilename; + private javax.swing.JLabel lblFilesize; + private javax.swing.JLabel lblInternalId; + private javax.swing.JLabel lblMake; + private javax.swing.JLabel lblMarket; + private javax.swing.JLabel lblModel; + private javax.swing.JLabel lblStorageAddress; + private javax.swing.JLabel lblSubmodel; + private javax.swing.JLabel lblTables; + private javax.swing.JLabel lblTransmission; + private javax.swing.JLabel lblYear; + private javax.swing.JLabel make; + private javax.swing.JLabel market; + private javax.swing.JLabel model; + private javax.swing.JLabel storageAddress; + private javax.swing.JLabel submodel; + private javax.swing.JList tableList; + private javax.swing.JLabel transmission; + private javax.swing.JLabel xmlID; + private javax.swing.JLabel year; + private javax.swing.JLabel lblEditStamp; + private javax.swing.JLabel editStamp; + // End of variables declaration//GEN-END:variables + +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/swing/RomTree.java b/java_console/romraider/src/com/romraider/swing/RomTree.java new file mode 100644 index 0000000000..ea5231e560 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/RomTree.java @@ -0,0 +1,142 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JTree; +import javax.swing.KeyStroke; +import javax.swing.tree.DefaultMutableTreeNode; + +import com.romraider.editor.ecu.ECUEditor; +import com.romraider.editor.ecu.ECUEditorManager; +import com.romraider.maps.Rom; +import com.romraider.util.SettingsManager; + +public class RomTree extends JTree implements MouseListener { + + private static final long serialVersionUID = 1630446543383498886L; + + public RomTree(DefaultMutableTreeNode input) { + super(input); + setRootVisible(false); + setRowHeight(0); + addMouseListener(this); + setCellRenderer(new RomCellRenderer()); + setFont(new Font("Tahoma", Font.PLAIN, 11)); + + // key binding actions + Action tableSelectAction = new AbstractAction() { + private static final long serialVersionUID = -6008026264821746092L; + + @Override + public void actionPerformed(ActionEvent e) { + try{ + Object selectedRow = getSelectionPath().getLastPathComponent(); + showTable(selectedRow); + setLastSelectedRom(selectedRow); + }catch(NullPointerException ex) { + } + } + }; + + this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "enter"); + this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "space"); + this.getActionMap().put("enter", tableSelectAction); + this.getActionMap().put("space", tableSelectAction); + } + + public ECUEditor getEditor() { + return ECUEditorManager.getECUEditor(); + } + + @Override + public void mouseClicked(MouseEvent e) { + try{ + Object selectedRow = getPathForLocation(e.getX(), e.getY()).getLastPathComponent(); + + if(selectedRow instanceof TableTreeNode) { + if (e.getClickCount() >= SettingsManager.getSettings().getTableClickCount()) { + showTable(selectedRow); + } + } else if(selectedRow instanceof Rom || selectedRow instanceof CategoryTreeNode) { + if (e.getClickCount() >= SettingsManager.getSettings().getTableClickCount()) { + if(isCollapsed(getRowForLocation(e.getX(),e.getY()))) { + expandRow(getRowForLocation(e.getX(),e.getY())); + } + else { + collapseRow(getRowForLocation(e.getX(),e.getY())); + } + } + } + + setLastSelectedRom(selectedRow); + }catch(NullPointerException ex) { + } + } + + private void showTable(Object selectedRow) { + try{ + if(selectedRow instanceof TableTreeNode) { + TableTreeNode node = (TableTreeNode) selectedRow; + if(null != node) { + getEditor().displayTable(node.getFrame()); + } + } + } catch (NullPointerException ex) { + } + } + + private void setLastSelectedRom(Object selectedNode) { + if (selectedNode instanceof TableTreeNode || selectedNode instanceof CategoryTreeNode || selectedNode instanceof Rom) { + Object lastSelectedPathComponent = getLastSelectedPathComponent(); + if(lastSelectedPathComponent instanceof Rom) { + Rom node = (Rom) lastSelectedPathComponent; + if(null != node) { + getEditor().setLastSelectedRom(node); + } + } + } + getEditor().refreshUI(); + } + + @Override + public void mousePressed(MouseEvent e) { + } + + @Override + public void mouseReleased(MouseEvent e) { + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + } + +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/swing/RomTreeRootNode.java b/java_console/romraider/src/com/romraider/swing/RomTreeRootNode.java new file mode 100644 index 0000000000..a184625403 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/RomTreeRootNode.java @@ -0,0 +1,31 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import javax.swing.tree.DefaultMutableTreeNode; + +public class RomTreeRootNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = 6810217325725782803L; + + public RomTreeRootNode(String name) { + super(name); + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/swing/ScalesTableModel.java b/java_console/romraider/src/com/romraider/swing/ScalesTableModel.java new file mode 100644 index 0000000000..63397d1de0 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/ScalesTableModel.java @@ -0,0 +1,95 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2014 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import java.util.Vector; + +import javax.swing.table.DefaultTableModel; + +import com.romraider.maps.Scale; + +public final class ScalesTableModel extends DefaultTableModel { + private static final long serialVersionUID = -5967359776053559125L; + private static final String[] colNames = { + "Name", "Expression", "Byte Expression", + "Unit", "Format", "Coarse", "Fine", "Min", "Max"}; + private Vector scales; + + @Override + public final int getColumnCount() { + return colNames.length; + } + + @Override + public final String getColumnName(int column) { + return colNames[column].toString(); + } + + @Override + public final Object getValueAt(int row, int column) { + if (null != scales) { + final Scale scale = scales.get(row); + switch (column) { + case 0: + return scale.getName(); + case 1: + return scale.getExpression(); + case 2: + return scale.getByteExpression(); + case 3: + return scale.getUnit(); + case 4: + return scale.getFormat(); + case 5: + return scale.getCoarseIncrement(); + case 6: + return scale.getFineIncrement(); + case 7: + return scale.getMin(); + case 8: + return scale.getMax(); + default: + return null; + } + } + else { + return null; + } + } + + @Override + public final int getRowCount() { + return (null != scales) ? scales.size() : 0; + } + + @Override + public final Class getColumnClass(int column) { + return getValueAt(0, column).getClass(); + } + + @Override + public final boolean isCellEditable(int row, int column) { + return false; + } + + public final void setScalesList(Vector scales) { + this.scales = scales; + } +} diff --git a/java_console/romraider/src/com/romraider/swing/SetFont.java b/java_console/romraider/src/com/romraider/swing/SetFont.java new file mode 100644 index 0000000000..52df65976b --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/SetFont.java @@ -0,0 +1,90 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import java.awt.Font; + +import javax.swing.JComponent; + +/** + * Methods of this class can be used to change the style and/or size of a + * JComponent within the same font family. + */ +public class SetFont { + public SetFont() {} + + /** + * Set the style of JComponent to plain. + * @param component - item to change. + */ + public static final void plain(JComponent component) { + component.setFont( + new Font( + component.getFont().getFamily(), + Font.PLAIN, + component.getFont().getSize() + ) + ); + } + + /** + * Set the style of JComponent to plain using new size. + * @param component - item to change. + * @param size - the new font size. + */ + public static final void plain(JComponent component, int size) { + component.setFont( + new Font( + component.getFont().getFamily(), + Font.PLAIN, + size + ) + ); + } + + /** + * Set the style of JComponent to bold. + * @param component - item to change. + */ + public static final void bold(JComponent component) { + component.setFont( + new Font( + component.getFont().getFamily(), + Font.BOLD, + component.getFont().getSize() + ) + ); + } + + /** + * Set the style of JComponent to bold using new size. + * @param component - item to change. + * @param size - the new font size. + */ + public static final void bold(JComponent component, int size) { + component.setFont( + new Font( + component.getFont().getFamily(), + Font.BOLD, + size + ) + ); + } +} diff --git a/java_console/romraider/src/com/romraider/swing/SettingsForm.java b/java_console/romraider/src/com/romraider/swing/SettingsForm.java new file mode 100644 index 0000000000..fb78becf95 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/SettingsForm.java @@ -0,0 +1,1144 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import static com.romraider.Version.PRODUCT_NAME; +import static java.io.File.separator; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.io.File; +import java.util.StringTokenizer; + +import javax.swing.ButtonGroup; +import javax.swing.DefaultComboBoxModel; +import javax.swing.GroupLayout; +import javax.swing.GroupLayout.Alignment; +import javax.swing.JCheckBox; +import javax.swing.JColorChooser; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextField; +import javax.swing.LayoutStyle.ComponentPlacement; +import javax.swing.UIManager; +import javax.swing.border.TitledBorder; + +import ZoeloeSoft.projects.JFontChooser.JFontChooser; + +import com.romraider.Settings; +import com.romraider.editor.ecu.ECUEditor; +import com.romraider.editor.ecu.ECUEditorManager; +//import com.romraider.util.FileAssociator; +import com.romraider.util.SettingsManager; + +public class SettingsForm extends JFrame implements MouseListener { + + private static final long serialVersionUID = 3910602424260147767L; + + public SettingsForm() { + this.setIconImage(getEditor().getIconImage()); + initComponents(); + initSettings(); + + maxColor.addMouseListener(this); + minColor.addMouseListener(this); + highlightColor.addMouseListener(this); + selectColor.addMouseListener(this); + axisColor.addMouseListener(this); + increaseColor.addMouseListener(this); + decreaseColor.addMouseListener(this); + warningColor.addMouseListener(this); + + btnOk.addMouseListener(this); + btnApply.addMouseListener(this); + btnCancel.addMouseListener(this); + btnChooseFont.addMouseListener(this); + reset.addMouseListener(this); + btnAddAssocs.addMouseListener(this); + btnRemoveAssocs.addMouseListener(this); + + tableClickCount.setBackground(Color.WHITE); + tableClickBehavior.setBackground(Color.WHITE); + + // disable file association buttons if user is not in Windows + StringTokenizer osName = new StringTokenizer(System.getProperties().getProperty("os.name")); + if (!osName.nextToken().equalsIgnoreCase("windows")) { + btnAddAssocs.setEnabled(false); + btnRemoveAssocs.setEnabled(false); + extensionHex.setEnabled(false); + extensionBin.setEnabled(false); + } + + } + + private void initSettings() { + Settings settings = getSettings(); + obsoleteWarning.setSelected(settings.isObsoleteWarning()); + calcConflictWarning.setSelected(settings.isCalcConflictWarning()); + displayHighTables.setSelected(settings.isDisplayHighTables()); + saveDebugTables.setSelected(settings.isSaveDebugTables()); + debug.setSelected(settings.isDebug()); + + maxColor.setBackground(settings.getMaxColor()); + minColor.setBackground(settings.getMinColor()); + highlightColor.setBackground(settings.getHighlightColor()); + selectColor.setBackground(settings.getSelectColor()); + axisColor.setBackground(settings.getAxisColor()); + increaseColor.setBackground(settings.getIncreaseBorder()); + decreaseColor.setBackground(settings.getDecreaseBorder()); + + cellWidth.setText(((int) settings.getCellSize().getWidth()) + ""); + cellHeight.setText(((int) settings.getCellSize().getHeight()) + ""); + + btnChooseFont.setFont(settings.getTableFont()); + btnChooseFont.setText(settings.getTableFont().getFontName()); + + if (settings.getTableClickCount() == 1) { // single click opens table + tableClickCount.setSelectedIndex(0); + } else { // double click opens table + tableClickCount.setSelectedIndex(1); + } + + if(1 == settings.getTableClickBehavior()) { // open/focus + tableClickBehavior.setSelectedIndex(1); + } else { // open/close + tableClickBehavior.setSelectedIndex(0); + } + + valueLimitWarning.setSelected(settings.isValueLimitWarning()); + warningColor.setBackground(settings.getWarningColor()); + chckbxColorAxis.setSelected(settings.isColorAxis()); + + defaultScale.setText(settings.getDefaultScale()); + + if(settings.getTableClipboardFormat().equalsIgnoreCase(Settings.AIRBOYS_CLIPBOARD_FORMAT)) { + this.rdbtnAirboys.setSelected(true); + } else if(settings.getTableClipboardFormat().equalsIgnoreCase(Settings.CUSTOM_CLIPBOARD_FORMAT)) { + this.rdbtnCustom.setSelected(true); + } else { + this.rdbtnDefault.setSelected(true); + } + + this.textFieldEditorIconScale.setText(String.valueOf(settings.getEditorIconScale())); + this.textFieldTableIconScale.setText(String.valueOf(settings.getTableIconScale())); + + chckbxShowTableToolbar.setSelected(settings.isShowTableToolbarBorder()); + chckbxOpenRomNode.setSelected(settings.isOpenExpanded()); + chckbxOpenTablesAt.setSelected(settings.isAlwaysOpenTableAtZero()); + + if(settings.getDefaultScale().equalsIgnoreCase(Settings.DEFAULT_SCALE)) { + comboBoxDefaultScale.setSelectedItem(Settings.DEFAULT_SCALE); + } else if (settings.getDefaultScale().equalsIgnoreCase(Settings.METRIC_SCALE)) { + comboBoxDefaultScale.setSelectedItem(Settings.METRIC_SCALE); + } else if (settings.getDefaultScale().equalsIgnoreCase(Settings.STANDARD_SCALE)) { + comboBoxDefaultScale.setSelectedItem(Settings.STANDARD_SCALE); + } else { + comboBoxDefaultScale.setSelectedIndex(0); + } + + cbScaleHeaderAndData.setSelected(settings.isScaleHeadersAndData()); + } + + // //GEN-BEGIN:initComponents + private void initComponents() { + obsoleteWarning = new javax.swing.JCheckBox(); + calcConflictWarning = new javax.swing.JCheckBox(); + debug = new javax.swing.JCheckBox(); + btnCancel = new javax.swing.JButton(); + btnOk = new javax.swing.JButton(); + btnApply = new javax.swing.JButton(); + reset = new javax.swing.JButton(); + settingsTabbedPane = new javax.swing.JTabbedPane(); + jPanelClipboard = new javax.swing.JPanel(); + jPanelDefault = new javax.swing.JPanel(); + jPanelTableDisplay = new javax.swing.JPanel(); + jPanelIcons = new javax.swing.JPanel(); + jPanelScale = new javax.swing.JPanel(); + jPanel2 = new javax.swing.JPanel(); + lblAxis = new javax.swing.JLabel(); + lblHighlight = new javax.swing.JLabel(); + lblSelect = new javax.swing.JLabel(); + lblMin = new javax.swing.JLabel(); + lblMax = new javax.swing.JLabel(); + maxColor = new javax.swing.JLabel(); + minColor = new javax.swing.JLabel(); + highlightColor = new javax.swing.JLabel(); + selectColor = new javax.swing.JLabel(); + axisColor = new javax.swing.JLabel(); + warningColor = new javax.swing.JLabel(); + lblWarning = new javax.swing.JLabel(); + jPanel3 = new javax.swing.JPanel(); + lblIncrease = new javax.swing.JLabel(); + increaseColor = new javax.swing.JLabel(); + decreaseColor = new javax.swing.JLabel(); + lblDecrease = new javax.swing.JLabel(); + lblCellHeight = new javax.swing.JLabel(); + cellHeight = new javax.swing.JTextField(); + cellWidth = new javax.swing.JTextField(); + lblCellWidth = new javax.swing.JLabel(); + lblFont = new javax.swing.JLabel(); + btnChooseFont = new javax.swing.JButton(); + saveDebugTables = new javax.swing.JCheckBox(); + displayHighTables = new javax.swing.JCheckBox(); + valueLimitWarning = new javax.swing.JCheckBox(); + chckbxColorAxis = new javax.swing.JCheckBox(); + jPanel4 = new javax.swing.JPanel(); + extensionHex = new javax.swing.JCheckBox(); + extensionBin = new javax.swing.JCheckBox(); + btnAddAssocs = new javax.swing.JButton(); + btnRemoveAssocs = new javax.swing.JButton(); + editorIconsPanel = new javax.swing.JPanel(); + tableIconsPanel = new javax.swing.JPanel(); + tableClickBehavior = new javax.swing.JComboBox(); + labelTableClick = new javax.swing.JLabel(); + tableClickCount = new javax.swing.JComboBox(); + defaultScale = new javax.swing.JTextField(); + comboBoxDefaultScale = new javax.swing.JComboBox(); + cbScaleHeaderAndData = new javax.swing.JCheckBox(); + + clipboardButtonGroup = new ButtonGroup(); + rdbtnDefault = new JRadioButton("RomRaider Default"); + rdbtnAirboys = new JRadioButton("Airboys Spreadsheet"); + rdbtnCustom = new JRadioButton("Custom (manually specify formats in settings.xml)"); + clipboardButtonGroup.add(this.rdbtnDefault); + clipboardButtonGroup.add(this.rdbtnAirboys); + clipboardButtonGroup.add(this.rdbtnCustom); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle(PRODUCT_NAME + " Settings"); + setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); + setFont(new java.awt.Font("Tahoma", 0, 12)); + obsoleteWarning.setText("Warn me when opening out of date ECU image revision"); + obsoleteWarning.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + obsoleteWarning.setMargin(new java.awt.Insets(0, 0, 0, 0)); + + calcConflictWarning.setText("Warn me when real and byte value calculations conflict"); + calcConflictWarning.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + calcConflictWarning.setMargin(new java.awt.Insets(0, 0, 0, 0)); + + debug.setText("Debug mode"); + debug.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + debug.setEnabled(false); + debug.setMargin(new java.awt.Insets(0, 0, 0, 0)); + + btnCancel.setMnemonic('C'); + btnCancel.setText("Cancel"); + + btnOk.setMnemonic('O'); + btnOk.setText("OK"); + + btnApply.setMnemonic('A'); + btnApply.setText("Apply"); + + reset.setText("Restore Defaults"); + + jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder("Background")); + lblAxis.setText("Axis Cell:"); + + lblHighlight.setText("Highlighted Cell:"); + lblSelect.setText("Selected Cell:"); + + lblMin.setText("Minimum Value:"); + + lblMax.setText("Maximum Value:"); + + maxColor.setBackground(new java.awt.Color(255, 0, 0)); + maxColor.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); + maxColor.setOpaque(true); + + minColor.setBackground(new java.awt.Color(255, 0, 0)); + minColor.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); + minColor.setOpaque(true); + + highlightColor.setBackground(new java.awt.Color(255, 0, 0)); + highlightColor.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); + highlightColor.setOpaque(true); + + selectColor.setBackground(new java.awt.Color(255, 0, 0)); + selectColor.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); + selectColor.setOpaque(true); + + axisColor.setBackground(new java.awt.Color(255, 0, 0)); + axisColor.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); + axisColor.setOpaque(true); + + warningColor.setBackground(new java.awt.Color(255, 0, 0)); + warningColor.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); + warningColor.setOpaque(true); + + lblWarning.setText("Warning:"); + + GroupLayout jPanel2Layout = new GroupLayout(jPanel2); + jPanel2Layout.setHorizontalGroup( + jPanel2Layout.createParallelGroup(Alignment.TRAILING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addGroup(jPanel2Layout.createParallelGroup(Alignment.TRAILING) + .addComponent(lblWarning) + .addGroup(jPanel2Layout.createParallelGroup(Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addGap(4) + .addComponent(lblMin)) + .addComponent(lblMax))) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(jPanel2Layout.createParallelGroup(Alignment.TRAILING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addComponent(maxColor, GroupLayout.PREFERRED_SIZE, 50, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(ComponentPlacement.RELATED, 139, Short.MAX_VALUE) + .addComponent(lblHighlight) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(highlightColor, GroupLayout.PREFERRED_SIZE, 50, GroupLayout.PREFERRED_SIZE)) + .addGroup(jPanel2Layout.createSequentialGroup() + .addGroup(jPanel2Layout.createParallelGroup(Alignment.LEADING) + .addGroup(Alignment.TRAILING, jPanel2Layout.createSequentialGroup() + .addComponent(minColor, GroupLayout.PREFERRED_SIZE, 50, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(ComponentPlacement.RELATED, 172, Short.MAX_VALUE) + .addComponent(lblAxis)) + .addGroup(jPanel2Layout.createSequentialGroup() + .addComponent(warningColor, GroupLayout.PREFERRED_SIZE, 50, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(ComponentPlacement.RELATED, 151, Short.MAX_VALUE) + .addComponent(lblSelect))) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(jPanel2Layout.createParallelGroup(Alignment.LEADING) + .addComponent(selectColor, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(axisColor, GroupLayout.PREFERRED_SIZE, 50, GroupLayout.PREFERRED_SIZE)))) + .addContainerGap()) + ); + jPanel2Layout.setVerticalGroup( + jPanel2Layout.createParallelGroup(Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addGroup(jPanel2Layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblMax) + .addComponent(maxColor, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE) + .addComponent(highlightColor, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE) + .addComponent(lblHighlight)) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(jPanel2Layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblMin) + .addComponent(minColor, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE) + .addComponent(axisColor, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE) + .addComponent(lblAxis)) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(jPanel2Layout.createParallelGroup(Alignment.LEADING) + .addGroup(jPanel2Layout.createParallelGroup(Alignment.BASELINE) + .addComponent(warningColor, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE) + .addComponent(lblWarning)) + .addComponent(selectColor, GroupLayout.DEFAULT_SIZE, 20, Short.MAX_VALUE) + .addComponent(lblSelect)) + .addContainerGap()) + ); + jPanel2.setLayout(jPanel2Layout); + + jPanel3.setBorder(javax.swing.BorderFactory.createTitledBorder("Cell Borders")); + lblIncrease.setText("Increased:"); + + increaseColor.setBackground(new java.awt.Color(255, 0, 0)); + increaseColor.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); + increaseColor.setOpaque(true); + + decreaseColor.setBackground(new java.awt.Color(255, 0, 0)); + decreaseColor.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); + decreaseColor.setOpaque(true); + + lblDecrease.setText("Decreased:"); + +// org.jdesktop.layout.GroupLayout jPanel3Layout = new org.jdesktop.layout.GroupLayout(jPanel3); +// jPanel3.setLayout(jPanel3Layout); +// jPanel3Layout.setHorizontalGroup( +// jPanel3Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) +// .add(org.jdesktop.layout.GroupLayout.TRAILING, jPanel3Layout.createSequentialGroup() +// .addContainerGap() +// .add(lblIncrease) +// .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) +// .add(increaseColor, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 50, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) +// .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 59, Short.MAX_VALUE) +// .add(lblDecrease) +// .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) +// .add(decreaseColor, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 50, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) +// .addContainerGap()) +// ); +// jPanel3Layout.setVerticalGroup( +// jPanel3Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) +// .add(jPanel3Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) +// .add(decreaseColor, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 15, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) +// .add(lblDecrease) +// .add(lblIncrease) +// .add(increaseColor, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 15, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) +// ); + + lblCellHeight.setText("Cell Height:"); + + lblCellWidth.setText("Cell Width:"); + + lblFont.setText("Font:"); + + btnChooseFont.setText("Choose"); + + saveDebugTables.setText("Save changes made on tables in debug mode"); + saveDebugTables.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + saveDebugTables.setMargin(new java.awt.Insets(0, 0, 0, 0)); + + displayHighTables.setText("List tables that are above my userlevel"); + displayHighTables.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + displayHighTables.setMargin(new java.awt.Insets(0, 0, 0, 0)); + + valueLimitWarning.setText("Warn when values exceed limits"); + valueLimitWarning.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + valueLimitWarning.setMargin(new java.awt.Insets(0, 0, 0, 0)); + + chckbxColorAxis.setText("Color Axis"); + chckbxColorAxis.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + chckbxColorAxis.setMargin(new java.awt.Insets(0, 0, 0, 0)); + + jPanel4.setBorder(javax.swing.BorderFactory.createTitledBorder("File Associations")); + extensionHex.setText("HEX"); + extensionHex.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + extensionHex.setMargin(new java.awt.Insets(0, 0, 0, 0)); + + extensionBin.setText("BIN"); + extensionBin.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + extensionBin.setMargin(new java.awt.Insets(0, 0, 0, 0)); + + btnAddAssocs.setText("Add Associations"); + + btnRemoveAssocs.setText("Remove Associations"); + +// org.jdesktop.layout.GroupLayout jPanel4Layout = new org.jdesktop.layout.GroupLayout(jPanel4); +// jPanel4.setLayout(jPanel4Layout); +// jPanel4Layout.setHorizontalGroup( +// jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) +// .add(org.jdesktop.layout.GroupLayout.TRAILING, jPanel4Layout.createSequentialGroup() +// .addContainerGap() +// .add(jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) +// .add(extensionBin) +// .add(extensionHex)) +// .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 93, Short.MAX_VALUE) +// .add(jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING, false) +// .add(btnAddAssocs, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) +// .add(btnRemoveAssocs)) +// .add(25, 25, 25)) +// ); +// jPanel4Layout.setVerticalGroup( +// jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) +// .add(jPanel4Layout.createSequentialGroup() +// .add(jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) +// .add(btnAddAssocs) +// .add(extensionHex)) +// .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) +// .add(jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) +// .add(btnRemoveAssocs) +// .add(extensionBin))) +// ); + + initTabs(); + + settingsTabbedPane.addTab("General", jPanelDefault); + settingsTabbedPane.addTab("Table Display", jPanelTableDisplay); + settingsTabbedPane.addTab("Clipboard", jPanelClipboard); + settingsTabbedPane.addTab("Icons", jPanelIcons); + settingsTabbedPane.addTab("Scale", jPanelScale); + + this.cbScaleHeaderAndData = new JCheckBox("Scale Headers and Data."); + this.cbScaleHeaderAndData.setToolTipText("If checked, the header scale will change when the data scale is selected. Otherwise click on a header row or column to select the scale."); + + JLabel lblDefaultScale = new JLabel("Default Scale:"); + + comboBoxDefaultScale.setModel(new DefaultComboBoxModel(new String[] { Settings.DEFAULT_SCALE, Settings.METRIC_SCALE, Settings.STANDARD_SCALE})); + + GroupLayout gl_jPanelScale = new GroupLayout(jPanelScale); + gl_jPanelScale.setHorizontalGroup( + gl_jPanelScale.createParallelGroup(Alignment.LEADING) + .addGroup(gl_jPanelScale.createSequentialGroup() + .addContainerGap() + .addGroup(gl_jPanelScale.createParallelGroup(Alignment.LEADING) + .addComponent(cbScaleHeaderAndData) + .addGroup(gl_jPanelScale.createSequentialGroup() + .addComponent(lblDefaultScale) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(comboBoxDefaultScale, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))) + .addContainerGap(277, Short.MAX_VALUE)) + ); + gl_jPanelScale.setVerticalGroup( + gl_jPanelScale.createParallelGroup(Alignment.LEADING) + .addGroup(gl_jPanelScale.createSequentialGroup() + .addContainerGap() + .addGroup(gl_jPanelScale.createParallelGroup(Alignment.BASELINE) + .addComponent(lblDefaultScale) + .addComponent(comboBoxDefaultScale, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(ComponentPlacement.UNRELATED) + .addComponent(cbScaleHeaderAndData) + .addContainerGap(453, Short.MAX_VALUE)) + ); + jPanelScale.setLayout(gl_jPanelScale); + + editorIconsPanel = new JPanel(); + + // Content Pane Layout + GroupLayout layout = new GroupLayout(getContentPane()); + layout.setHorizontalGroup( + layout.createParallelGroup(Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(Alignment.LEADING) + .addComponent(settingsTabbedPane, Alignment.TRAILING, GroupLayout.PREFERRED_SIZE, 432, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addComponent(reset) + .addPreferredGap(ComponentPlacement.RELATED, 136, Short.MAX_VALUE) + .addComponent(btnApply) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(btnOk) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(btnCancel))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(settingsTabbedPane, GroupLayout.PREFERRED_SIZE, 542, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(btnCancel) + .addComponent(btnOk) + .addComponent(btnApply) + .addComponent(reset)) + .addContainerGap()) + ); + getContentPane().setLayout(layout); + pack(); + }// //GEN-END:initComponents + + private void initTabs() { + + JPanel panelUISettings = new JPanel(); + panelUISettings.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "UI Settings", TitledBorder.LEADING, TitledBorder.TOP, null, null)); + // Init Default Tab Panel + GroupLayout jPanelDefaultLayout = new GroupLayout(jPanelDefault); + jPanelDefaultLayout.setHorizontalGroup( + jPanelDefaultLayout.createParallelGroup(Alignment.LEADING) + .addGroup(jPanelDefaultLayout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanelDefaultLayout.createParallelGroup(Alignment.LEADING) + .addComponent(panelUISettings, GroupLayout.DEFAULT_SIZE, 407, Short.MAX_VALUE) + .addComponent(obsoleteWarning) + .addComponent(calcConflictWarning) + .addComponent(debug)) + .addContainerGap()) + ); + jPanelDefaultLayout.setVerticalGroup( + jPanelDefaultLayout.createParallelGroup(Alignment.LEADING) + .addGroup(jPanelDefaultLayout.createSequentialGroup() + .addContainerGap() + .addComponent(obsoleteWarning) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(calcConflictWarning) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(debug) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(panelUISettings, GroupLayout.PREFERRED_SIZE, 173, GroupLayout.PREFERRED_SIZE) + .addContainerGap(267, Short.MAX_VALUE)) + ); + chckbxOpenRomNode = new JCheckBox("Open rom node expanded"); + + chckbxShowTableToolbar = new JCheckBox("Show table toolbar border"); + + panelTreeSettings = new JPanel(); + panelTreeSettings.setBorder(new TitledBorder(null, "Rom Tree Settings", TitledBorder.LEADING, TitledBorder.TOP, null, null)); + + chckbxOpenTablesAt = new JCheckBox("Always open tables at [0,0]"); + GroupLayout gl_panelUISettings = new GroupLayout(panelUISettings); + gl_panelUISettings.setHorizontalGroup( + gl_panelUISettings.createParallelGroup(Alignment.LEADING) + .addGroup(gl_panelUISettings.createSequentialGroup() + .addGroup(gl_panelUISettings.createParallelGroup(Alignment.LEADING) + .addComponent(chckbxOpenRomNode) + .addComponent(chckbxShowTableToolbar)) + .addContainerGap(244, Short.MAX_VALUE)) + .addComponent(panelTreeSettings, GroupLayout.DEFAULT_SIZE, 395, Short.MAX_VALUE) + .addGroup(gl_panelUISettings.createSequentialGroup() + .addComponent(chckbxOpenTablesAt) + .addContainerGap()) + ); + gl_panelUISettings.setVerticalGroup( + gl_panelUISettings.createParallelGroup(Alignment.LEADING) + .addGroup(gl_panelUISettings.createSequentialGroup() + .addComponent(chckbxOpenRomNode) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(chckbxShowTableToolbar) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(chckbxOpenTablesAt) + .addPreferredGap(ComponentPlacement.RELATED, 12, Short.MAX_VALUE) + .addComponent(panelTreeSettings, GroupLayout.PREFERRED_SIZE, 69, GroupLayout.PREFERRED_SIZE)) + ); + + labelTableClick.setText("Click to open tables"); + tableClickCount.setModel(new javax.swing.DefaultComboBoxModel(new String[]{"Single", "Double"})); + tableClickBehavior.setModel(new DefaultComboBoxModel(new String[] {"open/close", "open/focus"})); + + lblClickBehavior = new JLabel("Table click behavior"); + GroupLayout gl_panelTreeSettings = new GroupLayout(panelTreeSettings); + gl_panelTreeSettings.setHorizontalGroup( + gl_panelTreeSettings.createParallelGroup(Alignment.LEADING) + .addGroup(gl_panelTreeSettings.createSequentialGroup() + .addGroup(gl_panelTreeSettings.createParallelGroup(Alignment.LEADING) + .addComponent(tableClickCount, 0, 72, Short.MAX_VALUE) + .addComponent(tableClickBehavior, 0, 86, Short.MAX_VALUE)) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(gl_panelTreeSettings.createParallelGroup(Alignment.LEADING) + .addComponent(labelTableClick) + .addComponent(lblClickBehavior)) + .addGap(200)) + ); + gl_panelTreeSettings.setVerticalGroup( + gl_panelTreeSettings.createParallelGroup(Alignment.LEADING) + .addGroup(gl_panelTreeSettings.createSequentialGroup() + .addGroup(gl_panelTreeSettings.createParallelGroup(Alignment.BASELINE) + .addComponent(labelTableClick) + .addComponent(tableClickCount, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(gl_panelTreeSettings.createParallelGroup(Alignment.BASELINE) + .addComponent(lblClickBehavior) + .addComponent(tableClickBehavior, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) + .addContainerGap(23, Short.MAX_VALUE)) + ); + panelTreeSettings.setLayout(gl_panelTreeSettings); + panelUISettings.setLayout(gl_panelUISettings); + jPanelDefault.setLayout(jPanelDefaultLayout); + + // Init Table Display Tab + GroupLayout jPanelTableDisplayLayout = new GroupLayout(jPanelTableDisplay); + jPanelTableDisplayLayout.setHorizontalGroup( + jPanelTableDisplayLayout.createParallelGroup(Alignment.LEADING) + .addGroup(jPanelTableDisplayLayout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanelTableDisplayLayout.createParallelGroup(Alignment.LEADING) + .addComponent(lblCellHeight) + .addComponent(lblFont)) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(jPanelTableDisplayLayout.createParallelGroup(Alignment.LEADING) + .addComponent(btnChooseFont) + .addGroup(jPanelTableDisplayLayout.createSequentialGroup() + .addComponent(cellHeight, GroupLayout.PREFERRED_SIZE, 50, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(ComponentPlacement.RELATED, 155, Short.MAX_VALUE) + .addComponent(lblCellWidth) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(cellWidth, GroupLayout.PREFERRED_SIZE, 50, GroupLayout.PREFERRED_SIZE))) + .addGap(47)) + .addGroup(jPanelTableDisplayLayout.createSequentialGroup() + .addComponent(jPanel4, GroupLayout.DEFAULT_SIZE, 417, Short.MAX_VALUE) + .addContainerGap()) + .addGroup(jPanelTableDisplayLayout.createSequentialGroup() + .addComponent(jPanel3, GroupLayout.DEFAULT_SIZE, 417, Short.MAX_VALUE) + .addContainerGap()) + .addComponent(jPanel2, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(jPanelTableDisplayLayout.createSequentialGroup() + .addContainerGap() + .addComponent(saveDebugTables) + .addContainerGap(184, Short.MAX_VALUE)) + .addGroup(jPanelTableDisplayLayout.createSequentialGroup() + .addContainerGap() + .addComponent(displayHighTables) + .addContainerGap(214, Short.MAX_VALUE)) + .addGroup(jPanelTableDisplayLayout.createSequentialGroup() + .addContainerGap() + .addComponent(valueLimitWarning) + .addContainerGap(246, Short.MAX_VALUE)) + .addGroup(jPanelTableDisplayLayout.createSequentialGroup() + .addContainerGap() + .addComponent(chckbxColorAxis) + .addContainerGap(324, Short.MAX_VALUE)) + ); + jPanelTableDisplayLayout.setVerticalGroup( + jPanelTableDisplayLayout.createParallelGroup(Alignment.LEADING) + .addGroup(jPanelTableDisplayLayout.createSequentialGroup() + .addContainerGap() + .addComponent(jPanel2, GroupLayout.PREFERRED_SIZE, 85, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(jPanel3, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(jPanel4, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(saveDebugTables) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(displayHighTables) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(valueLimitWarning) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(chckbxColorAxis) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(jPanelTableDisplayLayout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblCellWidth) + .addComponent(cellWidth, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addComponent(lblCellHeight) + .addComponent(cellHeight, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(jPanelTableDisplayLayout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblFont) + .addComponent(btnChooseFont, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE)) + .addContainerGap()) + ); + jPanelTableDisplay.setLayout(jPanelTableDisplayLayout); + + // Init Clipboard Tab Panel + GroupLayout jPanelClipboardLayout = new GroupLayout(jPanelClipboard); + jPanelClipboardLayout.setHorizontalGroup( + jPanelClipboardLayout.createParallelGroup(Alignment.TRAILING) + .addGroup(jPanelClipboardLayout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanelClipboardLayout.createParallelGroup(Alignment.LEADING) + .addGroup(jPanelClipboardLayout.createSequentialGroup() + .addGap(17) + .addGroup(jPanelClipboardLayout.createParallelGroup(Alignment.LEADING) + .addComponent(rdbtnAirboys) + .addComponent(rdbtnDefault) + .addComponent(rdbtnCustom)))) + .addGap(157)) + ); + jPanelClipboardLayout.setVerticalGroup( + jPanelClipboardLayout.createParallelGroup(Alignment.LEADING) + .addGroup(jPanelClipboardLayout.createSequentialGroup() + .addContainerGap() + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(rdbtnDefault) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(rdbtnAirboys) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(rdbtnCustom) + .addGap(435)) + ); + jPanelClipboard.setLayout(jPanelClipboardLayout); + + // Init Icons Tab panel + editorIconsPanel.setBorder(new TitledBorder(null, "Editor Toolbar Icons", TitledBorder.LEADING, TitledBorder.TOP, null, null)); + tableIconsPanel.setBorder(new TitledBorder(null, "Table Toolbar Icons", TitledBorder.LEADING, TitledBorder.TOP, null, null)); + + GroupLayout jPanelIconsLayout = new GroupLayout(jPanelIcons); + jPanelIconsLayout.setHorizontalGroup( + jPanelIconsLayout.createParallelGroup(Alignment.TRAILING) + .addGroup(jPanelIconsLayout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanelIconsLayout.createParallelGroup(Alignment.LEADING) + .addComponent(editorIconsPanel, GroupLayout.DEFAULT_SIZE, 407, Short.MAX_VALUE) + .addComponent(tableIconsPanel, GroupLayout.DEFAULT_SIZE, 407, Short.MAX_VALUE)) + .addContainerGap()) + ); + jPanelIconsLayout.setVerticalGroup( + jPanelIconsLayout.createParallelGroup(Alignment.LEADING) + .addGroup(jPanelIconsLayout.createSequentialGroup() + .addContainerGap() + .addComponent(editorIconsPanel, GroupLayout.PREFERRED_SIZE, 66, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(tableIconsPanel, GroupLayout.PREFERRED_SIZE, 64, GroupLayout.PREFERRED_SIZE) + .addContainerGap(367, Short.MAX_VALUE)) + ); + + JLabel lblTableIconScale = new JLabel("Scale:"); + + textFieldTableIconScale = new JTextField(); + textFieldTableIconScale.setToolTipText("The percentage of the icons original size."); + textFieldTableIconScale.setColumns(10); + + JLabel labelTableScalePercent = new JLabel("%"); + GroupLayout tableIconsPanelLayout = new GroupLayout(tableIconsPanel); + tableIconsPanelLayout.setHorizontalGroup( + tableIconsPanelLayout.createParallelGroup(Alignment.LEADING) + .addGroup(tableIconsPanelLayout.createSequentialGroup() + .addContainerGap() + .addComponent(lblTableIconScale) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(textFieldTableIconScale, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(labelTableScalePercent) + .addContainerGap(216, Short.MAX_VALUE)) + ); + tableIconsPanelLayout.setVerticalGroup( + tableIconsPanelLayout.createParallelGroup(Alignment.LEADING) + .addGroup(tableIconsPanelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(tableIconsPanelLayout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblTableIconScale) + .addComponent(textFieldTableIconScale, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addComponent(labelTableScalePercent)) + .addContainerGap(116, Short.MAX_VALUE)) + ); + tableIconsPanel.setLayout(tableIconsPanelLayout); + + JLabel lblEditorIconScale = new JLabel("Scale:"); + + textFieldEditorIconScale = new JTextField(); + textFieldEditorIconScale.setToolTipText("The percentage of the icons original size."); + textFieldEditorIconScale.setColumns(10); + + JLabel labelEditorScalePercent = new JLabel("%"); + GroupLayout editorIconsPanelLayout = new GroupLayout(editorIconsPanel); + editorIconsPanelLayout.setHorizontalGroup( + editorIconsPanelLayout.createParallelGroup(Alignment.LEADING) + .addGroup(editorIconsPanelLayout.createSequentialGroup() + .addContainerGap() + .addComponent(lblEditorIconScale) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(textFieldEditorIconScale, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(labelEditorScalePercent) + .addContainerGap(216, Short.MAX_VALUE)) + ); + editorIconsPanelLayout.setVerticalGroup( + editorIconsPanelLayout.createParallelGroup(Alignment.LEADING) + .addGroup(editorIconsPanelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(editorIconsPanelLayout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblEditorIconScale) + .addComponent(textFieldEditorIconScale, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addComponent(labelEditorScalePercent)) + .addContainerGap(95, Short.MAX_VALUE)) + ); + editorIconsPanel.setLayout(editorIconsPanelLayout); + + jPanelIcons.setLayout(jPanelIconsLayout); + } + + @Override + public void mouseClicked(MouseEvent e) { + Settings settings = getSettings(); + if (e.getSource() == maxColor) { + Color color = JColorChooser.showDialog(this.getContentPane(), + "Background Color", settings.getMaxColor()); + if (color != null) { + maxColor.setBackground(color); + } + } else if (e.getSource() == minColor) { + Color color = JColorChooser.showDialog(this.getContentPane(), + "Background Color", settings.getMinColor()); + if (color != null) { + minColor.setBackground(color); + } + } else if (e.getSource() == highlightColor) { + Color color = JColorChooser.showDialog(this.getContentPane(), + "Background Color", settings.getHighlightColor()); + if (color != null) { + highlightColor.setBackground(color); + } + } else if (e.getSource() == selectColor) { + Color color = JColorChooser.showDialog(this.getContentPane(), + "Background Color", settings.getSelectColor()); + if (color != null) { + selectColor.setBackground(color); + } + } else if (e.getSource() == axisColor) { + Color color = JColorChooser.showDialog(this.getContentPane(), + "Background Color", settings.getAxisColor()); + if (color != null) { + axisColor.setBackground(color); + } + } else if (e.getSource() == increaseColor) { + Color color = JColorChooser.showDialog(this.getContentPane(), + "Background Color", settings.getIncreaseBorder()); + if (color != null) { + increaseColor.setBackground(color); + } + } else if (e.getSource() == decreaseColor) { + Color color = JColorChooser.showDialog(this.getContentPane(), + "Background Color", settings.getDecreaseBorder()); + if (color != null) { + decreaseColor.setBackground(color); + } + } else if (e.getSource() == warningColor) { + Color color = JColorChooser.showDialog(this.getContentPane(), + "Warning Color", settings.getWarningColor()); + if (color != null) { + warningColor.setBackground(color); + } + } else if (e.getSource() == btnApply) { + applySettings(); + } else if (e.getSource() == btnOk) { + // Apply settings to Settings object. + applySettings(); + // Write settings to file. + saveSettings(); + this.dispose(); + } else if (e.getSource() == btnCancel) { + this.dispose(); + } else if (e.getSource() == btnChooseFont) { + JFontChooser fc = new JFontChooser(this); + fc.setLocationRelativeTo(this); + if (fc.showDialog(settings.getTableFont()) == JFontChooser.OK_OPTION) { + btnChooseFont.setFont(fc.getFont()); + btnChooseFont.setText(fc.getFont().getFontName()); + } + } else if (e.getSource() == reset) { + Settings newSettings = new Settings(); + Settings curSettings = getSettings(); + + newSettings.setEcuDefinitionFiles(curSettings.getEcuDefinitionFiles()); + newSettings.setLastImageDir(curSettings.getLastImageDir()); + newSettings.setLastRepositoryDir(curSettings.getLastRepositoryDir()); + newSettings.setUserLevel(curSettings.getUserLevel()); + newSettings.setLoggerDefinitionFilePath(curSettings.getLoggerDefinitionFilePath()); + newSettings.setLoggerDebuggingLevel(curSettings.getLoggerDebuggingLevel()); + newSettings.setLoggerProfileFilePath(curSettings.getLoggerProfileFilePath()); + newSettings.setLoggerOutputDirPath(curSettings.getLoggerOutputDirPath()); + + SettingsManager.save(newSettings); + + initSettings(); + } else if (e.getSource() == btnAddAssocs) { + // add file associations for selected file types + boolean added = false; + try { + if (extensionHex.isSelected()) { + //FileAssociator.addAssociation("HEX", new File(".").getCanonicalPath() + separator + PRODUCT_NAME + ".exe", "ECU Image"); + added = true; + } + + if (extensionBin.isSelected()) { + //FileAssociator.addAssociation("BIN", new File(".").getCanonicalPath() + separator + PRODUCT_NAME + ".exe", "ECU Image"); + added = true; + } + } catch (Exception ex) { + added = false; + } finally { + if(added) { + JOptionPane.showMessageDialog(null, "Association(s) added.", "Add Association Success", JOptionPane.INFORMATION_MESSAGE); + } else { + JOptionPane.showMessageDialog(null, "Failed to add association(s).", "Add Association Failure", JOptionPane.ERROR_MESSAGE); + } + } + + } else if (e.getSource() == btnRemoveAssocs) { + // remove file associations for selected file types + boolean removed = false; + if (extensionHex.isSelected()) { + //removed = FileAssociator.removeAssociation("HEX"); + } + + if (extensionBin.isSelected()) { + //removed = FileAssociator.removeAssociation("BIN"); + } + + if(removed) { + JOptionPane.showMessageDialog(null, "Association removed.", "Remove Association Success", JOptionPane.INFORMATION_MESSAGE); + } else { + JOptionPane.showMessageDialog(null, "Failed to remove association.", "Remove Association Failure", JOptionPane.ERROR_MESSAGE); + } + } + } + + public void applySettings() { + try { + Integer.parseInt(cellHeight.getText()); + } catch (NumberFormatException ex) { + // number formatted imporperly, reset + cellHeight.setText((int) (getSettings().getCellSize().getHeight()) + ""); + } + try { + Integer.parseInt(cellWidth.getText()); + } catch (NumberFormatException ex) { + // number formatted imporperly, reset + cellWidth.setText((int) (getSettings().getCellSize().getWidth()) + ""); + } + + getSettings().setObsoleteWarning(obsoleteWarning.isSelected()); + getSettings().setCalcConflictWarning(calcConflictWarning.isSelected()); + getSettings().setDisplayHighTables(displayHighTables.isSelected()); + getSettings().setSaveDebugTables(saveDebugTables.isSelected()); + getSettings().setDebug(debug.isSelected()); + getSettings().setOpenExpanded(chckbxOpenRomNode.isSelected()); + getSettings().setAlwaysOpenTableAtZero(chckbxOpenTablesAt.isSelected()); + getSettings().setShowTableToolbarBorder(chckbxShowTableToolbar.isSelected()); + + getSettings().setMaxColor(maxColor.getBackground()); + getSettings().setMinColor(minColor.getBackground()); + getSettings().setHighlightColor(highlightColor.getBackground()); + getSettings().setSelectColor(selectColor.getBackground()); + getSettings().setAxisColor(axisColor.getBackground()); + getSettings().setIncreaseBorder(increaseColor.getBackground()); + getSettings().setDecreaseBorder(decreaseColor.getBackground()); + + getSettings().setScaleHeadersAndData(this.cbScaleHeaderAndData.isSelected()); + + getSettings().setCellSize(new Dimension(Integer.parseInt(cellWidth.getText()), + Integer.parseInt(cellHeight.getText()))); + + getSettings().setTableFont(btnChooseFont.getFont()); + + if (tableClickCount.getSelectedIndex() == 0) { // single click opens table + getSettings().setTableClickCount(1); + } else { // double click opens table + getSettings().setTableClickCount(2); + } + + if(1 == tableClickBehavior.getSelectedIndex()) { // open/close frame + getSettings().setTableClickBehavior(1); + } else { // open/focus frame + getSettings().setTableClickBehavior(0); + } + + getSettings().setValueLimitWarning(valueLimitWarning.isSelected()); + getSettings().setWarningColor(warningColor.getBackground()); + getSettings().setColorAxis(chckbxColorAxis.isSelected()); + getSettings().setDefaultScale(defaultScale.getText()); + + if(rdbtnAirboys.isSelected()) + { + getSettings().setAirboysFormat(); + } else if(rdbtnCustom.isSelected()) { + getSettings().setTableClipboardFormat(Settings.CUSTOM_CLIPBOARD_FORMAT); + // Table Header settings need to be manually edited in the settings.xml file; + } else { + getSettings().setDefaultFormat(); + } + + try{ + getSettings().setEditorIconScale(Integer.parseInt(textFieldEditorIconScale.getText())); + getEditor().getToolBar().updateIcons(); + } catch(NumberFormatException ex) { + // Number formatted incorrectly reset. + textFieldEditorIconScale.setText(String.valueOf(getSettings().getEditorIconScale())); + } + + try{ + getSettings().setTableIconScale(Integer.parseInt(textFieldTableIconScale.getText())); + getEditor().getTableToolBar().updateIcons(); + } catch(NumberFormatException ex) { + // Number formatted incorrectly reset. + textFieldTableIconScale.setText(String.valueOf(getSettings().getTableIconScale())); + } + + getSettings().setDefaultScale(comboBoxDefaultScale.getSelectedItem().toString()); + } + + private Settings getSettings() + { + return SettingsManager.getSettings(); + } + + private ECUEditor getEditor() + { + return ECUEditorManager.getECUEditor(); + } + + public void saveSettings() + { + saveSettings(getSettings()); + } + + public void saveSettings(Settings newSettings) { + SettingsManager.save(newSettings); + drawVisibleTables(); + getEditor().refreshUI(); + } + + private void drawVisibleTables() { + for(JInternalFrame frame : getEditor().getRightPanel().getAllFrames()) { + if(frame instanceof TableFrame && frame.isVisible()) { + TableFrame tableFrame = (TableFrame) frame; + tableFrame.getTable().drawTable(); + } + } + } + + @Override + public void mousePressed(MouseEvent e) { + } + + @Override + public void mouseReleased(MouseEvent e) { + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel axisColor; + private javax.swing.JButton btnAddAssocs; + private javax.swing.JButton btnApply; + private javax.swing.JButton btnCancel; + private javax.swing.JButton btnChooseFont; + private javax.swing.JButton btnOk; + private javax.swing.JButton btnRemoveAssocs; + private javax.swing.JCheckBox calcConflictWarning; + private javax.swing.JTextField cellHeight; + private javax.swing.JTextField cellWidth; + private javax.swing.JCheckBox debug; + private javax.swing.JLabel decreaseColor; + private javax.swing.JCheckBox displayHighTables; + private javax.swing.JCheckBox extensionBin; + private javax.swing.JCheckBox extensionHex; + private javax.swing.JLabel highlightColor; + private javax.swing.JLabel selectColor; + private javax.swing.JLabel increaseColor; + private javax.swing.JLabel labelTableClick; + private javax.swing.JLabel lblClickBehavior; + private javax.swing.JTabbedPane settingsTabbedPane; + private javax.swing.JPanel jPanelDefault; + private javax.swing.JPanel jPanelClipboard; + private javax.swing.JPanel jPanelTableDisplay; + private javax.swing.JPanel jPanelIcons; + private javax.swing.JPanel jPanelScale; + private javax.swing.JPanel jPanel2; + private javax.swing.JPanel jPanel3; + private javax.swing.JPanel jPanel4; + private javax.swing.JLabel lblAxis; + private javax.swing.JLabel lblCellHeight; + private javax.swing.JLabel lblCellWidth; + private javax.swing.JLabel lblDecrease; + private javax.swing.JLabel lblFont; + private javax.swing.JLabel lblHighlight; + private javax.swing.JLabel lblSelect; + private javax.swing.JLabel lblIncrease; + private javax.swing.JLabel lblMax; + private javax.swing.JLabel lblMin; + private javax.swing.JLabel lblWarning; + private javax.swing.JLabel maxColor; + private javax.swing.JLabel minColor; + private javax.swing.JCheckBox obsoleteWarning; + private javax.swing.JButton reset; + private javax.swing.JCheckBox saveDebugTables; + private javax.swing.JComboBox tableClickCount; + private javax.swing.JCheckBox valueLimitWarning; + private javax.swing.JCheckBox chckbxColorAxis; + private javax.swing.JLabel warningColor; + private ButtonGroup clipboardButtonGroup; + private javax.swing.JRadioButton rdbtnDefault; + private javax.swing.JRadioButton rdbtnAirboys; + private javax.swing.JRadioButton rdbtnCustom; + private JPanel editorIconsPanel; + private JPanel tableIconsPanel; + private JTextField textFieldTableIconScale; + private JTextField textFieldEditorIconScale; + private javax.swing.JCheckBox chckbxShowTableToolbar; + private javax.swing.JCheckBox chckbxOpenRomNode; + private JPanel panelTreeSettings; + private javax.swing.JComboBox tableClickBehavior; + private javax.swing.JCheckBox chckbxOpenTablesAt; + private javax.swing.JTextField defaultScale; + private javax.swing.JComboBox comboBoxDefaultScale; + private javax.swing.JCheckBox cbScaleHeaderAndData; +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/swing/SwitchStateTableModel.java b/java_console/romraider/src/com/romraider/swing/SwitchStateTableModel.java new file mode 100644 index 0000000000..9cc93bc46b --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/SwitchStateTableModel.java @@ -0,0 +1,81 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2014 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import java.util.Map; + +import javax.swing.table.DefaultTableModel; + +import com.romraider.util.HexUtil; + +public final class SwitchStateTableModel extends DefaultTableModel { + private static final long serialVersionUID = -6053559125596735977L; + private static final String[] colNames = { + "Switch State", "State Data"}; + private Map switchStates; + + @Override + public final int getColumnCount() { + return colNames.length; + } + + @Override + public final String getColumnName(int column) { + return colNames[column]; + } + + @Override + public final Object getValueAt(int row, int column) { + if (null != switchStates) { + final String key = (String) switchStates.keySet().toArray()[row]; + final byte[] value = switchStates.get(key); + switch (column) { + case 0: + return key; + case 1: + return HexUtil.asHex(value); + default: + return null; + } + } + else { + return null; + } + } + + @Override + public final int getRowCount() { + return (null != switchStates) ? switchStates.size() : 0; + } + + @Override + public final Class getColumnClass(int column) { + return getValueAt(0, column).getClass(); + } + + @Override + public final boolean isCellEditable(int row, int column) { + return false; + } + + public final void setScalesList(Map switchStates) { + this.switchStates = switchStates; + } +} diff --git a/java_console/romraider/src/com/romraider/swing/TableChooserTreeNode.java b/java_console/romraider/src/com/romraider/swing/TableChooserTreeNode.java new file mode 100644 index 0000000000..8a2e970671 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/TableChooserTreeNode.java @@ -0,0 +1,38 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import com.romraider.maps.Table; +import javax.swing.tree.DefaultMutableTreeNode; + +public class TableChooserTreeNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = 6660923635216155045L; + private Table table; + + public TableChooserTreeNode(String text, Table table) { + super(text); + this.table = table; + } + + public Table getTable() { + return table; + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/swing/TableFrame.java b/java_console/romraider/src/com/romraider/swing/TableFrame.java new file mode 100644 index 0000000000..53d08e9ed5 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/TableFrame.java @@ -0,0 +1,234 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import static javax.swing.BorderFactory.createBevelBorder; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JInternalFrame; +import javax.swing.JMenu; +import javax.swing.JOptionPane; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.event.InternalFrameEvent; +import javax.swing.event.InternalFrameListener; + +import com.romraider.Settings; +import com.romraider.editor.ecu.ECUEditor; +import com.romraider.editor.ecu.ECUEditorManager; +import com.romraider.logger.ecu.ui.handler.table.TableUpdateHandler; +import com.romraider.maps.Rom; +import com.romraider.maps.Table; + +public class TableFrame extends JInternalFrame implements InternalFrameListener, ActionListener { + + private static final long serialVersionUID = -2651279694660392351L; + private final Table table; + private TableMenuBar tableMenuBar = null; + + public TableFrame(String title, Table table) { + super(title, true, true); + this.table = table; + add(table); + setFrameIcon(null); + setBorder(createBevelBorder(0)); + if (System.getProperty("os.name").startsWith("Mac OS")) + putClientProperty("JInternalFrame.isPalette", true); + setVisible(false); + tableMenuBar = new TableMenuBar(this); + setJMenuBar(tableMenuBar); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + addInternalFrameListener(this); + } + + @Override + public void internalFrameActivated(InternalFrameEvent e) { + ECUEditor parent = getEditor(); + parent.getTableToolBar().updateTableToolBar(); + parent.getToolBar().updateButtons(); + parent.getEditorMenuBar().updateMenu(); + } + + @Override + public void internalFrameOpened(InternalFrameEvent e) { + RegisterTable(); + } + + @Override + public void internalFrameClosing(InternalFrameEvent e) { + TableUpdateHandler.getInstance().deregisterTable(this.getTable()); + } + + @Override + public void internalFrameClosed(InternalFrameEvent e) { + getEditor().getTableToolBar().updateTableToolBar(); + } + + @Override + public void internalFrameIconified(InternalFrameEvent e) { + ; + } + + @Override + public void internalFrameDeiconified(InternalFrameEvent e) { + ; + } + + @Override + public void internalFrameDeactivated(InternalFrameEvent e) { + getEditor().getTableToolBar().updateTableToolBar(); + } + + public void RegisterTable() { + TableUpdateHandler.getInstance().registerTable(this.getTable()); + } + + public Table getTable() { + return table; + } + + public ECUEditor getEditor() { + return ECUEditorManager.getECUEditor(); + } + + public TableMenuBar getTableMenuBar() { + return this.tableMenuBar; + } + + @Override + public void actionPerformed(ActionEvent e) { + TableMenuBar menu = getTableMenuBar(); + + if (e.getSource() == menu.getUndoAll()) { + getTable().undoAll(); + + } else if (e.getSource() == menu.getRevert()) { + getTable().setRevertPoint(); + + } else if (e.getSource() == menu.getUndoSel()) { + getTable().undoSelected(); + + } else if (e.getSource() == menu.getClose()) { + ECUEditorManager.getECUEditor().removeDisplayTable(this); + + } else if (e.getSource() == menu.getTableProperties()) { + JOptionPane.showMessageDialog(getTable(), new TablePropertyPanel(getTable()), + getTable().getName() + " Table Properties", JOptionPane.INFORMATION_MESSAGE); + + } else if (e.getSource() == menu.getCopySel()) { + getTable().copySelection(); + + } else if (e.getSource() == menu.getCopyTable()) { + getTable().copyTable(); + + } else if (e.getSource() == menu.getPaste()) { + getTable().paste(); + + } else if (e.getSource() == menu.getCompareOff()) { + getTable().setCompareTable(null); + getTable().setCompareValueType(Settings.DATA_TYPE_BIN); + getTableMenuBar().getCompareToBin().setSelected(true); + + } else if (e.getSource() == menu.getCompareAbsolute()) { + getTable().setCompareDisplay(Settings.COMPARE_DISPLAY_ABSOLUTE); + + } else if (e.getSource() == menu.getComparePercent()) { + getTable().setCompareDisplay(Settings.COMPARE_DISPLAY_PERCENT); + + } else if (e.getSource() == menu.getCompareOriginal()) { + getTable().setCompareValueType(Settings.DATA_TYPE_ORIGINAL); + getTableMenuBar().getCompareToOriginal().setSelected(true); + compareByTable(getTable()); + + } else if (e.getSource() == menu.getCompareMap()) { + JTableChooser chooser = new JTableChooser(); + Table selectedTable = chooser.showChooser(getTable()); + if(null != selectedTable) { + compareByTable(selectedTable); + } + + } else if (e.getSource() instanceof TableMenuItem) { + Table selectedTable = findSimilarTable((TableMenuItem)e.getSource()); + if(null != e.getSource()) { + compareByTable(selectedTable); + } + + } else if (e.getSource() == menu.getCompareToOriginal()) { + getTable().setCompareValueType(Settings.DATA_TYPE_ORIGINAL); + getTable().refreshCompare(); + + } else if (e.getSource() == menu.getCompareToBin()) { + getTable().setCompareValueType(Settings.DATA_TYPE_BIN); + getTable().refreshCompare(); + + } else if (e.getSource() == menu.getInterp()) { + getTable().interpolate(); + + } else if (e.getSource() == menu.getVertInterp()) { + getTable().verticalInterpolate(); + + } else if (e.getSource() == menu.getHorizInterp()) { + getTable().horizontalInterpolate(); + } + } + + public void compareByTable(Table selectedTable) { + if(null == selectedTable) { + return; + } + getTable().setCompareTable(selectedTable); + ECUEditorManager.getECUEditor().getTableToolBar().updateTableToolBar(getTable()); + getTable().populateCompareValues(selectedTable); + } + + public void refreshSimilarOpenTables() { + JMenu similarTables = getTableMenuBar().getSimilarOpenTables(); + similarTables.removeAll(); + + for(Rom rom : ECUEditorManager.getECUEditor().getImages()) { + for(TableTreeNode tableNode : rom.getTableNodes()) { + if(tableNode.getTable().getName().equalsIgnoreCase(getTable().getName())) { + JRadioButtonMenuItem similarTable = new TableMenuItem(rom.getFileName()); + similarTable.setToolTipText(tableNode.getFrame().getTable().getName()); + similarTable.addActionListener(this); + similarTables.add(similarTable); + break; + } + } + } + + getTableMenuBar().initCompareGroup(); + getTableMenuBar().repaint(); + } + + private Table findSimilarTable(TableMenuItem menuItem) { + for(Rom rom : ECUEditorManager.getECUEditor().getImages()) { + if(menuItem.getText().equalsIgnoreCase(rom.getFileName())) { + for(TableTreeNode treeNode : rom.getTableNodes()) { + if(menuItem.getToolTipText().equalsIgnoreCase(treeNode.getFrame().getTable().getName())) { + return treeNode.getFrame().getTable(); + } + } + } + } + return null; + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/swing/TableMenuBar.java b/java_console/romraider/src/com/romraider/swing/TableMenuBar.java new file mode 100644 index 0000000000..3e14fd1283 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/TableMenuBar.java @@ -0,0 +1,407 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import javax.swing.ButtonGroup; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.JSeparator; + +import com.romraider.Settings; +import com.romraider.maps.Table3D; + +public class TableMenuBar extends JMenuBar { + + private static final long serialVersionUID = -695692646459410510L; + private JMenu fileMenu; + private JMenuItem graph; + + private JMenu compareMenu; + private JRadioButtonMenuItem compareOriginal; + private JRadioButtonMenuItem compareMap; + private JMenu similarOpenTables; + private JRadioButtonMenuItem compareOff; + private JMenu compareDisplay; + private JRadioButtonMenuItem comparePercent; + private JRadioButtonMenuItem compareAbsolute; + private JMenu compareToValue; + private JRadioButtonMenuItem compareToOriginal; + private JRadioButtonMenuItem compareToBin; + + private JMenuItem close; + private JMenu editMenu; + private JMenuItem undoSel; + private JMenuItem undoAll; + private JMenuItem revert; + private JMenuItem copySel; + private JMenuItem copyTable; + private JMenuItem paste; + private JMenuItem interp; + private JMenuItem vertInterp; + private JMenuItem horizInterp; + private JMenu viewMenu; + private JMenuItem tableProperties; + + private ButtonGroup compareGroup; + private ButtonGroup compareDisplayGroup; + private ButtonGroup compareToGroup; + + public TableMenuBar(TableFrame frame) { + initFileMenu(frame); + initEditMenu(frame); + initViewMenu(frame); + applyTableTypeRules(frame); + } + + private void initFileMenu(TableFrame frame) { + fileMenu = new JMenu("Table"); + graph = new JMenuItem("View Graph"); + compareMenu = new JMenu("Compare"); + setClose(new JMenuItem("Close Table")); + + initCompareMenu(frame); + getClose().setText("Close " + frame.getTable().getName()); + + graph.addActionListener(frame); + getClose().addActionListener(frame); + + graph.setMnemonic('G'); + getClose().setMnemonic('X'); + graph.setEnabled(false); + + fileMenu.add(graph); + fileMenu.add(compareMenu); + fileMenu.add(new JSeparator()); + fileMenu.add(getClose()); + + this.add(fileMenu); + } + + private void initEditMenu(TableFrame frame) { + editMenu = new JMenu("Edit"); + setUndoSel(new JMenuItem("Undo Selected Changes")); + setUndoAll(new JMenuItem("Undo All Changes")); + setRevert(new JMenuItem("Set Revert Point")); + setCopySel(new JMenuItem("Copy Selection")); + setCopyTable(new JMenuItem("Copy Table")); + setPaste(new JMenuItem("Paste")); + setVertInterp(new JMenuItem("Vertical Interpolate")); + setHorizInterp(new JMenuItem("Horizontal Interpolate")); + setInterp(new JMenuItem("Interpolate")); + + editMenu.add(getUndoSel()); + editMenu.add(getUndoAll()); + editMenu.add(getRevert()); + editMenu.add(new JSeparator()); + editMenu.add(getCopySel()); + editMenu.add(getCopyTable()); + editMenu.add(new JSeparator()); + editMenu.add(getPaste()); + editMenu.add(new JSeparator()); + if (frame.getTable() instanceof Table3D) { + editMenu.add(getVertInterp()); + editMenu.add(getHorizInterp()); + } + editMenu.add(getInterp()); + + editMenu.setMnemonic('E'); + getUndoSel().setMnemonic('U'); + getUndoAll().setMnemonic('A'); + getRevert().setMnemonic('R'); + getCopySel().setMnemonic('C'); + getCopyTable().setMnemonic('T'); + getPaste().setMnemonic('P'); + getInterp().setMnemonic('I'); + getVertInterp().setMnemonic('V'); + getHorizInterp().setMnemonic('H'); + + getUndoSel().addActionListener(frame); + getUndoAll().addActionListener(frame); + getRevert().addActionListener(frame); + getCopySel().addActionListener(frame); + getCopyTable().addActionListener(frame); + getPaste().addActionListener(frame); + getInterp().addActionListener(frame); + getVertInterp().addActionListener(frame); + getHorizInterp().addActionListener(frame); + this.add(editMenu); + } + + private void initViewMenu(TableFrame frame) { + + viewMenu = new JMenu("View"); + viewMenu.setMnemonic('V'); + + setTableProperties(new JMenuItem("Table Properties")); + getTableProperties().setToolTipText("Select to view the table properties."); + getTableProperties().setMnemonic('P'); + getTableProperties().addActionListener(frame); + + fileMenu.setMnemonic('F'); + fileMenu.setMnemonic('T'); + + viewMenu.add(getTableProperties()); + + this.add(viewMenu); + } + + private void initCompareMenu(TableFrame frame) { + setCompareOriginal(new JRadioButtonMenuItem("Show Changes")); + getCompareOriginal().setToolTipText("Compares the current values to the original or revert point values."); + setCompareMap(new JRadioButtonMenuItem("Compare to Another Map")); + getCompareMap().setToolTipText("Compares this table and a selected table."); + setSimilarOpenTables(new JMenu("Compare to Table")); + getSimilarOpenTables().setToolTipText("Compares this table to a similar table."); + + setCompareOff(new JRadioButtonMenuItem("Off")); + + setComparePercent(new JRadioButtonMenuItem("Percent Difference")); + setCompareAbsolute(new JRadioButtonMenuItem("Absolute Difference")); + compareDisplayGroup = new ButtonGroup(); + compareDisplayGroup.add(getComparePercent()); + compareDisplayGroup.add(getCompareAbsolute()); + compareDisplay = new JMenu("Display"); + compareDisplay.add(getComparePercent()); + compareDisplay.add(getCompareAbsolute()); + + setCompareToOriginal(new JRadioButtonMenuItem("Compare to Original Value")); + getCompareToOriginal().setToolTipText("Compares this table to the selected table's original or revert point values."); + setCompareToBin(new JRadioButtonMenuItem("Compare to Bin Value")); + getCompareToBin().setToolTipText("Compares this table to the selected table's current values."); + compareToGroup = new ButtonGroup(); + compareToGroup.add(getCompareToOriginal()); + compareToGroup.add(getCompareToBin()); + compareToValue = new JMenu("Compare to"); + compareToValue.add(getCompareToOriginal()); + compareToValue.add(getCompareToBin()); + + compareMenu.add(getCompareOriginal()); + compareMenu.add(getCompareMap()); + compareMenu.add(getSimilarOpenTables()); + compareMenu.add(getCompareOff()); + compareMenu.add(new JSeparator()); + compareMenu.add(compareDisplay); + compareMenu.add(new JSeparator()); + compareMenu.add(compareToValue); + + compareMenu.setMnemonic('C'); + getCompareOriginal().setMnemonic('C'); + getCompareMap().setMnemonic('M'); + getCompareOff().setMnemonic('O'); + compareDisplay.setMnemonic('D'); + getComparePercent().setMnemonic('P'); + getCompareAbsolute().setMnemonic('A'); + getSimilarOpenTables().setMnemonic('S'); + compareToValue.setMnemonic('T'); + getCompareToOriginal().setMnemonic('R'); + getCompareToOriginal().setMnemonic('B'); + + getCompareOff().setSelected(true); + getCompareAbsolute().setSelected(true); + getCompareToOriginal().setSelected(true); + + initCompareGroup(); + + getCompareOriginal().addActionListener(frame); + getCompareMap().addActionListener(frame); + getCompareOff().addActionListener(frame); + getComparePercent().addActionListener(frame); + getCompareAbsolute().addActionListener(frame); + getCompareToOriginal().addActionListener(frame); + getCompareToBin().addActionListener(frame); + } + + public void initCompareGroup() { + compareGroup = new ButtonGroup(); + + compareGroup.add(getCompareOriginal()); + compareGroup.add(getCompareMap()); + compareGroup.add(getCompareOff()); + + for(int i = 0; i< getSimilarOpenTables().getItemCount(); i++) { + compareGroup.add(getSimilarOpenTables().getItem(i)); + } + } + + private void applyTableTypeRules(TableFrame frame) { + // Hide items that don't work with a DTC tables. + if(frame.getTable().getType() == Settings.TABLE_SWITCH) { + editMenu.setEnabled(false); + getCompareOriginal().setEnabled(false); + getComparePercent().setEnabled(false); + getCompareAbsolute().setEnabled(false); + getCompareToOriginal().setEnabled(false); + getCompareToBin().setEnabled(false); + } + } + + public JMenuItem getUndoAll() { + return undoAll; + } + + public void setUndoAll(JMenuItem undoAll) { + this.undoAll = undoAll; + } + + public JMenuItem getRevert() { + return revert; + } + + public void setRevert(JMenuItem revert) { + this.revert = revert; + } + + public JMenuItem getUndoSel() { + return undoSel; + } + + public void setUndoSel(JMenuItem undoSel) { + this.undoSel = undoSel; + } + + public JMenuItem getClose() { + return close; + } + + public void setClose(JMenuItem close) { + this.close = close; + } + + public JMenuItem getTableProperties() { + return tableProperties; + } + public void setTableProperties(JMenuItem tableProperties) { + this.tableProperties = tableProperties; + } + + public JMenuItem getCopySel() { + return copySel; + } + + public void setCopySel(JMenuItem copySel) { + this.copySel = copySel; + } + + public JMenuItem getCopyTable() { + return copyTable; + } + + public void setCopyTable(JMenuItem copyTable) { + this.copyTable = copyTable; + } + + public JMenuItem getPaste() { + return paste; + } + + public void setPaste(JMenuItem paste) { + this.paste = paste; + } + + public JRadioButtonMenuItem getCompareOff() { + return compareOff; + } + + public void setCompareOff(JRadioButtonMenuItem compareOff) { + this.compareOff = compareOff; + } + + public JRadioButtonMenuItem getCompareAbsolute() { + return compareAbsolute; + } + + public void setCompareAbsolute(JRadioButtonMenuItem compareAbsolute) { + this.compareAbsolute = compareAbsolute; + } + + public JRadioButtonMenuItem getComparePercent() { + return comparePercent; + } + + public void setComparePercent(JRadioButtonMenuItem comparePercent) { + this.comparePercent = comparePercent; + } + + public JRadioButtonMenuItem getCompareOriginal() { + return compareOriginal; + } + + public void setCompareOriginal(JRadioButtonMenuItem compareOriginal) { + this.compareOriginal = compareOriginal; + } + + public JRadioButtonMenuItem getCompareToOriginal() { + return compareToOriginal; + } + + public void setCompareToOriginal(JRadioButtonMenuItem compareToOriginal) { + this.compareToOriginal = compareToOriginal; + } + + public JRadioButtonMenuItem getCompareMap() { + return compareMap; + } + + public void setCompareMap(JRadioButtonMenuItem compareMap) { + this.compareMap = compareMap; + } + + public JRadioButtonMenuItem getCompareToBin() { + return compareToBin; + } + + public void setCompareToBin(JRadioButtonMenuItem compareToBin) { + this.compareToBin = compareToBin; + } + + public JMenu getSimilarOpenTables() { + return similarOpenTables; + } + + public void setSimilarOpenTables(JMenu similarOpenTables) { + this.similarOpenTables = similarOpenTables; + } + + public JMenuItem getInterp() { + return interp; + } + + public void setInterp(JMenuItem interp) { + this.interp = interp; + } + + public JMenuItem getHorizInterp() { + return this.horizInterp; + } + + public void setHorizInterp(JMenuItem horizInterp) { + this.horizInterp = horizInterp; + } + + public JMenuItem getVertInterp() { + return this.vertInterp; + } + + public void setVertInterp(JMenuItem vertInterp) { + this.vertInterp = vertInterp; + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/swing/TableMenuItem.java b/java_console/romraider/src/com/romraider/swing/TableMenuItem.java new file mode 100644 index 0000000000..52bfaf514e --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/TableMenuItem.java @@ -0,0 +1,12 @@ +package com.romraider.swing; + +import javax.swing.JRadioButtonMenuItem; + +public class TableMenuItem extends JRadioButtonMenuItem{ + + private static final long serialVersionUID = -3618983591185294967L; + + public TableMenuItem(String itemName) { + super(itemName); + } +} diff --git a/java_console/romraider/src/com/romraider/swing/TablePropertyPanel.java b/java_console/romraider/src/com/romraider/swing/TablePropertyPanel.java new file mode 100644 index 0000000000..cee0f9cb02 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/TablePropertyPanel.java @@ -0,0 +1,362 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2014 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; + +import javax.swing.GroupLayout; +import javax.swing.GroupLayout.Alignment; +import javax.swing.JLabel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.LayoutStyle.ComponentPlacement; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableColumn; + +import com.romraider.Settings; +import com.romraider.maps.Scale; +import com.romraider.maps.Table; +import com.romraider.maps.TableSwitch; + +public class TablePropertyPanel extends javax.swing.JPanel { + + private static final long serialVersionUID = -5817685772039277602L; + + public TablePropertyPanel(Table table) { + initComponents(); + setVisible(true); + + category.setText(table.getCategory()); + + int dim; + if (Settings.TABLE_SWITCH == table.getType()) { + dim = 1; + storageSize.setText("switch"); + scrollPane.setViewportView(populateScalesTable( + ((TableSwitch) table).getSwitchStates())); + } + else { + if (Settings.STORAGE_TYPE_FLOAT == table.getStorageType()) { + storageSize.setText("float"); + } + else { + String dataType; + if (table.isSignedData()) { + dataType = "int"; + } + else { + dataType = "uint"; + } + storageSize.setText(dataType + (table.getStorageType() * 8)); + } + dim = table.getType(); + scrollPane.setViewportView(populateScalesTable(table.getScales())); + } + + tableName.setText(String.format("%s (%dD)", table.getName(), dim)); + storageAddress.setText("0x" + Integer.toHexString(table.getStorageAddress())); + + if (table.getEndian() == Settings.ENDIAN_BIG) { + endian.setText("big"); + } else { + endian.setText("little"); + } + + description.setText(table.getDescription()); + + if (table.getUserLevel() == 1) { + userLevel.setText("Beginner"); + } else if (table.getUserLevel() == 2) { + userLevel.setText("Intermediate"); + } else if (table.getUserLevel() == 3) { + userLevel.setText("Advanced"); + } else if (table.getUserLevel() == 4) { + userLevel.setText("All"); + } else if (table.getUserLevel() == 5) { + userLevel.setText("Debug"); + } + + logIDscrollPane.setViewportView(populateLogParamTable(table.getLogParamString())); +} + + // //GEN-BEGIN:initComponents + private void initComponents() { + lblTable = new javax.swing.JLabel(); + tableName = new javax.swing.JLabel(); + lblCategory = new javax.swing.JLabel(); + category = new javax.swing.JLabel(); + jPanel1 = new javax.swing.JPanel(); + jPanel2 = new javax.swing.JPanel(); + lblStorageAddress = new javax.swing.JLabel(); + lblStorageSize = new javax.swing.JLabel(); + lblEndian = new javax.swing.JLabel(); + endian = new javax.swing.JLabel(); + storageSize = new javax.swing.JLabel(); + storageAddress = new javax.swing.JLabel(); + jPanel3 = new javax.swing.JPanel(); + jScrollPane1 = new javax.swing.JScrollPane(); + description = new javax.swing.JTextArea(); + jLabel5 = new javax.swing.JLabel(); + userLevel = new javax.swing.JLabel(); + scrollPane = new JScrollPane(); + logIDscrollPane = new JScrollPane(); + + setAutoscrolls(true); + setFont(new java.awt.Font("Tahoma", 0, 12)); + setInheritsPopupMenu(true); + lblTable.setText("Table:"); + lblTable.setFocusable(false); + + tableName.setText("Tablename (3D)"); + tableName.setFocusable(false); + + lblCategory.setText("Category:"); + lblCategory.setFocusable(false); + + category.setText("Category"); + category.setFocusable(false); + + jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createTitledBorder("Conversions"))); + + GroupLayout jPanel1Layout = new GroupLayout(jPanel1); + jPanel1Layout.setHorizontalGroup( + jPanel1Layout.createParallelGroup(Alignment.LEADING) + .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 170, Short.MAX_VALUE) + ); + jPanel1Layout.setVerticalGroup( + jPanel1Layout.createParallelGroup(Alignment.LEADING) + .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 75, Short.MAX_VALUE) + ); + + jPanel1.setLayout(jPanel1Layout); + + jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder("Storage")); + lblStorageAddress.setText("Storage Address:"); + lblStorageAddress.setFocusable(false); + + lblStorageSize.setText("Data Type:"); + lblStorageSize.setFocusable(false); + + lblEndian.setText("Endian:"); + lblEndian.setFocusable(false); + + endian.setText("little"); + endian.setFocusable(false); + + storageSize.setText("unkn"); + storageSize.setFocusable(false); + + storageAddress.setText("0x00"); + storageAddress.setFocusable(false); + + org.jdesktop.layout.GroupLayout jPanel2Layout = new org.jdesktop.layout.GroupLayout(jPanel2); + jPanel2.setLayout(jPanel2Layout); + jPanel2Layout.setHorizontalGroup( + jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(jPanel2Layout.createSequentialGroup() + .addContainerGap() + .add(jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(lblStorageAddress) + .add(lblStorageSize) + .add(lblEndian)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(endian) + .add(storageSize) + .add(storageAddress)) + .addContainerGap(28, Short.MAX_VALUE)) + ); + jPanel2Layout.setVerticalGroup( + jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(jPanel2Layout.createSequentialGroup() + .add(jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(lblStorageSize) + .add(storageSize)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(lblStorageAddress) + .add(storageAddress)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(lblEndian) + .add(endian)) + .addContainerGap(37, Short.MAX_VALUE)) + ); + + jPanel3.setBorder(javax.swing.BorderFactory.createTitledBorder("Description")); + jScrollPane1.setBorder(null); + description.setBackground(new java.awt.Color(236, 233, 216)); + description.setColumns(20); + description.setEditable(false); + description.setFont(new java.awt.Font("Tahoma", 0, 11)); + description.setLineWrap(true); + description.setRows(5); + description.setText("Description"); + description.setWrapStyleWord(true); + description.setBorder(null); + description.setOpaque(false); + description.setRequestFocusEnabled(false); + jScrollPane1.setViewportView(description); + + org.jdesktop.layout.GroupLayout jPanel3Layout = new org.jdesktop.layout.GroupLayout(jPanel3); + jPanel3.setLayout(jPanel3Layout); + jPanel3Layout.setHorizontalGroup( + jPanel3Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(org.jdesktop.layout.GroupLayout.TRAILING, jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 360, Short.MAX_VALUE) + ); + jPanel3Layout.setVerticalGroup( + jPanel3Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(jPanel3Layout.createSequentialGroup() + .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 102, Short.MAX_VALUE) + .addContainerGap()) + ); + + jLabel5.setText("User Level:"); + + userLevel.setText("Beginner"); + + lblLogId = new JLabel("Log Param:"); + + GroupLayout layout = new GroupLayout(this); + layout.setHorizontalGroup( + layout.createParallelGroup(Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(Alignment.LEADING) + .addComponent(lblCategory) + .addComponent(lblTable) + .addComponent(lblLogId)) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(Alignment.TRAILING) + .addComponent(tableName, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 370, Short.MAX_VALUE) + .addComponent(category, GroupLayout.DEFAULT_SIZE, 370, Short.MAX_VALUE) + .addComponent(logIDscrollPane, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 300, 300))) + .addGroup(layout.createSequentialGroup() + .addComponent(jLabel5) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(userLevel, GroupLayout.DEFAULT_SIZE, 366, Short.MAX_VALUE)) + .addComponent(jPanel1, GroupLayout.DEFAULT_SIZE, 430, Short.MAX_VALUE) + .addComponent(jPanel2, GroupLayout.DEFAULT_SIZE, 430, Short.MAX_VALUE) + .addComponent(jPanel3, GroupLayout.DEFAULT_SIZE, 430, Short.MAX_VALUE)) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(tableName) + .addComponent(lblTable)) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblCategory) + .addComponent(category)) + .addGap(6) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(jLabel5) + .addComponent(userLevel)) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblLogId) + .addComponent(logIDscrollPane, GroupLayout.PREFERRED_SIZE, 75, GroupLayout.PREFERRED_SIZE)) + .addGap(8) + .addComponent(jPanel1, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(jPanel2, GroupLayout.PREFERRED_SIZE, 89, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(jPanel3, GroupLayout.PREFERRED_SIZE, 93, GroupLayout.PREFERRED_SIZE) + .addGap(23)) + ); + this.setLayout(layout); + }// //GEN-END:initComponents + private javax.swing.JLabel category; + private javax.swing.JTextArea description; + private javax.swing.JLabel endian; + private javax.swing.JLabel jLabel5; + private javax.swing.JPanel jPanel1; + private javax.swing.JPanel jPanel2; + private javax.swing.JPanel jPanel3; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JLabel lblCategory; + private javax.swing.JLabel lblEndian; + private javax.swing.JLabel lblStorageAddress; + private javax.swing.JLabel lblStorageSize; + private javax.swing.JLabel lblTable; + private javax.swing.JLabel storageAddress; + private javax.swing.JLabel storageSize; + private javax.swing.JLabel tableName; + private javax.swing.JLabel userLevel; + private JScrollPane scrollPane; + private JLabel lblLogId; + private JScrollPane logIDscrollPane; + + private JTable populateScalesTable(Vector scales) { + final ScalesTableModel scalesModel = new ScalesTableModel(); + scalesModel.setScalesList(scales); + return createScalesTable(scalesModel); + } + + private JTable populateScalesTable(Map switchStates) { + final SwitchStateTableModel scalesModel = new SwitchStateTableModel(); + scalesModel.setScalesList(switchStates); + return createScalesTable(scalesModel); + } + + private JTable createScalesTable(DefaultTableModel tableModel) { + final JTable table = new JTable(tableModel); + table.setAutoCreateRowSorter(false); + table.setColumnSelectionAllowed(false); + table.setRowSelectionAllowed(true); + table.setFillsViewportHeight(true); + return table; + } + + private JTable populateLogParamTable(String logParams) { + final Map paramMap = new HashMap(); + final String[] paramEntries = logParams.split(", "); + for (String entry : paramEntries) { + final String[] entries = entry.split(":"); + if(!paramMap.containsKey(entries[0])){ + paramMap.put(entries[0], entries.length > 1 ? entries[1] : "n/a"); + } + } + final ParameterIdsTableModel tableModel = new ParameterIdsTableModel(); + tableModel.setParameterList(paramMap); + final JTable table = new JTable(tableModel); + TableColumn column = null; + for (int i = 0; i < table.getColumnCount(); i++) { + column = table.getColumnModel().getColumn(i); + if (i == 0) { + column.setPreferredWidth(240); + } else { + column.setPreferredWidth(80); + } + } + table.setAutoCreateRowSorter(false); + table.setColumnSelectionAllowed(false); + table.setRowSelectionAllowed(true); + table.setFillsViewportHeight(true); + return table; + } +} diff --git a/java_console/romraider/src/com/romraider/swing/TableToolBar.java b/java_console/romraider/src/com/romraider/swing/TableToolBar.java new file mode 100644 index 0000000000..7679535c36 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/TableToolBar.java @@ -0,0 +1,794 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import static javax.swing.BorderFactory.createLineBorder; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.beans.PropertyVetoException; +import java.math.BigDecimal; +import java.net.URL; +import java.text.DecimalFormat; +import java.text.ParseException; +import java.util.Vector; + +import javax.naming.NameNotFoundException; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.ImageIcon; +import javax.swing.InputMap; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JFormattedTextField; +import javax.swing.JInternalFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.JToolBar; +import javax.swing.KeyStroke; +import javax.swing.border.Border; +import javax.swing.border.TitledBorder; + +import org.apache.log4j.Logger; + +//import com.ecm.graphics.Graph3dFrameManager; +//import com.ecm.graphics.data.GraphData; +//import com.ecm.graphics.data.GraphDataListener; +import com.romraider.Settings; +import com.romraider.editor.ecu.ECUEditorManager; +import com.romraider.maps.DataCell; +import com.romraider.maps.Scale; +import com.romraider.maps.Table; +import com.romraider.maps.Table1D; +import com.romraider.maps.Table3D; +import com.romraider.util.SettingsManager; + +public class TableToolBar extends JToolBar implements MouseListener, ItemListener, ActionListener +// , GraphDataListener +{ + + private static final long serialVersionUID = 8697645329367637930L; + private static final Logger LOGGER = Logger.getLogger(TableToolBar.class); + private final JButton incrementFine = new JButton(); + private final JButton decrementFine = new JButton(); + private final JButton incrementCoarse = new JButton(); + private final JButton decrementCoarse = new JButton(); + private final JButton enable3d = new JButton(); + private final JButton colorCells = new JButton(); + private final JButton refreshCompare = new JButton(); + + private final JButton setValue = new JButton("Set"); + private final JButton multiply = new JButton("Mul"); + + private final JFormattedTextField incrementByFine = new JFormattedTextField(new DecimalFormat("#.####")); + private final JFormattedTextField incrementByCoarse = new JFormattedTextField(new DecimalFormat("#.####")); + private final JFormattedTextField setValueText = new JFormattedTextField(new DecimalFormat("#.####")); + + private final JComboBox scaleSelection = new JComboBox(); + + private final JPanel liveDataPanel = new JPanel(); + private final JCheckBox overlayLog = new JCheckBox("Overlay Log"); + private final JButton clearOverlay = new JButton("Clear Overlay"); + private final JLabel liveDataValue = new JLabel(); + + private final URL incrementFineImage = getClass().getResource("/graphics/icon-incfine.png"); + private final URL decrementFineImage = getClass().getResource("/graphics/icon-decfine.png"); + private final URL incrementCoarseImage = getClass().getResource("/graphics/icon-inccoarse.png"); + private final URL decrementCoarseImage = getClass().getResource("/graphics/icon-deccoarse.png"); + private final URL enable3dImage = getClass().getResource("/graphics/3d_render.png"); + private final URL colorCellImage = getClass().getResource("/graphics/icon-palette.png"); + private final URL refreshCompareImage = getClass().getResource("/graphics/table_refresh.png"); + + private final TitledBorder toolbarBorder = BorderFactory.createTitledBorder(Settings.defaultTableToolBarName); + + private Table selectedTable = null; + + public TableToolBar() { + super(Settings.defaultTableToolBarName); + this.setFloatable(true); + this.setRollover(true); + FlowLayout toolBarLayout = new FlowLayout(FlowLayout.LEFT, 0, 0); + this.setLayout(toolBarLayout); + + setBorder(toolbarBorder); + + this.updateIcons(); + + JPanel finePanel = new JPanel(); + finePanel.add(incrementFine); + finePanel.add(decrementFine); + finePanel.add(incrementByFine); + this.add(finePanel); + + JPanel coarsePanel = new JPanel(); + coarsePanel.add(incrementCoarse); + coarsePanel.add(decrementCoarse); + coarsePanel.add(incrementByCoarse); + this.add(coarsePanel); + + JPanel setValuePanel = new JPanel(); + setValuePanel.add(setValueText); + setValuePanel.add(setValue); + setValuePanel.add(multiply); + this.add(setValuePanel); + + colorCells.setEnabled(false); + refreshCompare.setEnabled(false); + enable3d.setEnabled(false); + + JPanel otherPanel = new JPanel(); + otherPanel.add(colorCells); + otherPanel.add(refreshCompare); + otherPanel.add(enable3d); + this.add(otherPanel); + + JPanel scaleSelectionPanel = new JPanel(); + scaleSelectionPanel.add(scaleSelection); + this.add(scaleSelectionPanel); + + incrementFine.setBorder(createLineBorder(new Color(150, 150, 150), 1)); + decrementFine.setBorder(createLineBorder(new Color(150, 150, 150), 1)); + incrementCoarse.setBorder(createLineBorder(new Color(150, 150, 150), 1)); + decrementCoarse.setBorder(createLineBorder(new Color(150, 150, 150), 1)); + + enable3d.setBorder(createLineBorder(new Color(150, 150, 150), 1)); + setValue.setPreferredSize(new Dimension(33, 23)); + setValue.setBorder(createLineBorder(new Color(150, 150, 150), 1)); + multiply.setPreferredSize(new Dimension(33, 23)); + multiply.setBorder(createLineBorder(new Color(150, 150, 150), 1)); + + colorCells.setBorder(createLineBorder(new Color(150, 150, 150), 1)); + refreshCompare.setBorder(createLineBorder(new Color(150, 150, 150), 1)); + + scaleSelection.setPreferredSize(new Dimension(80, 23)); + + clearOverlay.setPreferredSize(new Dimension(75, 23)); + clearOverlay.setBorder(createLineBorder(new Color(150, 150, 150), 1)); + + incrementByFine.setAlignmentX(JTextArea.CENTER_ALIGNMENT); + incrementByFine.setAlignmentY(JTextArea.CENTER_ALIGNMENT); + incrementByFine.setPreferredSize(new Dimension(45, 23)); + incrementByCoarse.setAlignmentX(JTextArea.CENTER_ALIGNMENT); + incrementByCoarse.setAlignmentY(JTextArea.CENTER_ALIGNMENT); + incrementByCoarse.setPreferredSize(new Dimension(45, 23)); + setValueText.setAlignmentX(JTextArea.CENTER_ALIGNMENT); + setValueText.setAlignmentY(JTextArea.CENTER_ALIGNMENT); + setValueText.setPreferredSize(new Dimension(45, 23)); + + incrementFine.setToolTipText("Increment Value (Fine)"); + decrementFine.setToolTipText("Decrement Value (Fine)"); + incrementCoarse.setToolTipText("Increment Value (Coarse)"); + decrementCoarse.setToolTipText("Decrement Value (Coarse)"); + enable3d.setToolTipText("Render data in 3d"); + setValue.setToolTipText("Set Absolute Value"); + setValueText.setToolTipText("Set Absolute Value"); + incrementByFine.setToolTipText("Fine Value Adjustment"); + incrementByCoarse.setToolTipText("Coarse Value Adjustment"); + multiply.setToolTipText("Multiply Value"); + overlayLog.setToolTipText("Enable Overlay Of Real Time Log Data"); + clearOverlay.setToolTipText("Clear Log Data Overlay Highlights"); + colorCells.setToolTipText("Color Table Cells"); + refreshCompare.setToolTipText("Refresh Table Compare"); + + incrementFine.addMouseListener(this); + decrementFine.addMouseListener(this); + incrementCoarse.addMouseListener(this); + decrementCoarse.addMouseListener(this); + enable3d.addMouseListener(this); + setValue.addMouseListener(this); + multiply.addMouseListener(this); + scaleSelection.addItemListener(this); + overlayLog.addItemListener(this); + clearOverlay.addActionListener(this); + colorCells.addMouseListener(this); + refreshCompare.addMouseListener(this); + + // key binding actions + Action enterAction = new AbstractAction() { + private static final long serialVersionUID = -6008026264821746092L; + + @Override + public void actionPerformed(ActionEvent e) { + JInternalFrame selectedFrame = ECUEditorManager.getECUEditor().getRightPanel().getSelectedFrame(); + if(selectedFrame == null || !(selectedFrame instanceof TableFrame)) { + return; + } + TableFrame frame = (TableFrame)selectedFrame; + frame.toFront(); + try { + frame.setSelected(true); + } catch (PropertyVetoException ex) { + } + frame.requestFocusInWindow(); + setValue(frame.getTable()); + } + }; + + // set input mapping + InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW); + + KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0); + + im.put(enter, "enterAction"); + getActionMap().put(im.get(enter), enterAction); + + liveDataPanel.add(overlayLog); + liveDataPanel.add(clearOverlay); + //liveDataPanel.add(liveDataValue); + this.add(liveDataPanel); + overlayLog.setEnabled(false); + clearOverlay.setEnabled(false); + + incrementFine.getInputMap().put(enter, "enterAction"); + decrementFine.getInputMap().put(enter, "enterAction"); + incrementCoarse.getInputMap().put(enter, "enterAction"); + decrementCoarse.getInputMap().put(enter, "enterAction"); + incrementByFine.getInputMap().put(enter, "enterAction"); + incrementByCoarse.getInputMap().put(enter, "enterAction"); + setValueText.getInputMap().put(enter, "enterAction"); + setValue.getInputMap().put(enter, "enterAction"); + + this.setEnabled(true); + } + + public void updateIcons() { + Settings settings = SettingsManager.getSettings(); + incrementFine.setIcon(rescaleImageIcon(new ImageIcon(incrementFineImage), settings.getTableIconScale())); + decrementFine.setIcon(rescaleImageIcon(new ImageIcon(decrementFineImage), settings.getTableIconScale())); + incrementCoarse.setIcon(rescaleImageIcon(new ImageIcon(incrementCoarseImage), settings.getTableIconScale())); + decrementCoarse.setIcon(rescaleImageIcon(new ImageIcon(decrementCoarseImage), settings.getTableIconScale())); + enable3d.setIcon(rescaleImageIcon(new ImageIcon(enable3dImage), settings.getTableIconScale())); + colorCells.setIcon(rescaleImageIcon(new ImageIcon(colorCellImage), settings.getTableIconScale())); + refreshCompare.setIcon(rescaleImageIcon(new ImageIcon(refreshCompareImage), settings.getTableIconScale())); + } + + private ImageIcon rescaleImageIcon(ImageIcon imageIcon, int percentOfOriginal) { + int newHeight = (int) (imageIcon.getImage().getHeight(this) * (percentOfOriginal * .01)); + int newWidth = (int) (imageIcon.getImage().getWidth(this) * (percentOfOriginal * .01)); + + imageIcon.setImage(imageIcon.getImage().getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH)); + return imageIcon; + } + + public Table getTable() { + JInternalFrame frame = ECUEditorManager.getECUEditor().getRightPanel().getSelectedFrame(); + if(null == frame) { + return null; + } + + if( !(frame instanceof TableFrame) ) { + return null; + } + + + return ((TableFrame)frame).getTable(); + } + + @Override + public void setBorder(Border border) { + if(SettingsManager.getSettings().isShowTableToolbarBorder()) { + super.setBorder(toolbarBorder); + } else { + super.setBorder(BorderFactory.createEmptyBorder()); + } + } + + public void updateTableToolBar() { + this.updateTableToolBar(getTable()); + } + + public void updateTableToolBar(Table selectedTable) { + if(selectedTable == null && this.selectedTable == null) { + // Skip if the table is the same to avoid multiple updates + return; + } else if(selectedTable == null || this.selectedTable == null) { + // Update the toolbar. + } else if(this.selectedTable.equals(selectedTable)) { + // Skip if the table is the same to avoid multiple updates + return; + } + + this.selectedTable = selectedTable; + + setBorder(toolbarBorder); + + if(null == selectedTable) + { + // disable the toolbar. + toggleTableToolBar(selectedTable); + return; + } + + updateToolbarIncrementDecrementValues(); + + this.overlayLog.setSelected(selectedTable.getOverlayLog()); + this.enable3d.setEnabled(selectedTable.getType() == Settings.TABLE_3D); + + setScales(selectedTable.getScales()); + + if(null == selectedTable.getCurrentScale()) + { + this.scaleSelection.setSelectedItem("Default"); + } else { + this.scaleSelection.setSelectedItem(selectedTable.getCurrentScale().getName()); + } + + toggleTableToolBar(selectedTable); + } + + private void updateToolbarIncrementDecrementValues() { + if(null == selectedTable) { + return; + } + + double fineIncrement = 0; + double coarseIncrement = 0; + + try { + // enable the toolbar. + fineIncrement = Math.abs(selectedTable.getCurrentScale().getFineIncrement()); + coarseIncrement = Math.abs(selectedTable.getCurrentScale().getCoarseIncrement()); + } catch (Exception ex) { + // scaling units haven't been added yet -- no problem + } + + incrementByFine.setValue(fineIncrement); + incrementByCoarse.setValue(coarseIncrement); + } + + private void toggleTableToolBar(Table currentTable) { + String newTitle = ""; + boolean enabled; + + if(null == currentTable) { + enabled = false; + } else { + if(currentTable instanceof Table1D) { + Table1D cur1DTable = (Table1D)currentTable; + if(cur1DTable.isAxis()) { + newTitle = cur1DTable.getAxisParent().getName() + " ("+ cur1DTable.getName() +")"; + } else { + newTitle = currentTable.getName(); + } + } else { + newTitle = currentTable.getName(); + } + + enabled = true; + } + + toolbarBorder.setTitle(newTitle); + + incrementFine.setEnabled(enabled); + decrementFine.setEnabled(enabled); + incrementCoarse.setEnabled(enabled); + decrementCoarse.setEnabled(enabled); + + setValue.setEnabled(enabled); + multiply.setEnabled(enabled); + + incrementByFine.setEnabled(enabled); + incrementByCoarse.setEnabled(enabled); + setValueText.setEnabled(enabled); + + scaleSelection.setEnabled(enabled); + + liveDataValue.setEnabled(enabled); + + colorCells.setEnabled(enabled); + refreshCompare.setEnabled(enabled); + + //Only enable the 3d button if table includes 3d data + if (null != currentTable && currentTable.getType() == Settings.TABLE_3D && enabled) { + enable3d.setEnabled(true); + } + else{ + enable3d.setEnabled(false); + } + + if(null != currentTable && null != currentTable.getCompareTable() && enabled) { + refreshCompare.setEnabled(true); + } else { + refreshCompare.setEnabled(false); + } + + if (null != currentTable && currentTable.isLiveDataSupported() && enabled) { + overlayLog.setEnabled(true); + clearOverlay.setEnabled(true); + } + else{ + overlayLog.setEnabled(false); + clearOverlay.setEnabled(false); + } + + if(null != currentTable && currentTable.isStaticDataTable()) { + if(enabled) { + scaleSelection.setEnabled(true); + } else { + scaleSelection.setEnabled(false); + } + + // Disable everything that does not apply to static value tables. + colorCells.setEnabled(false); + refreshCompare.setEnabled(false); + + incrementFine.setEnabled(false); + decrementFine.setEnabled(false); + incrementCoarse.setEnabled(false); + decrementCoarse.setEnabled(false); + incrementByFine.setEnabled(false); + incrementByCoarse.setEnabled(false); + setValue.setEnabled(false); + setValueText.setEnabled(false); + multiply.setEnabled(false); + enable3d.setEnabled(false); + } + + + repaint(); + } + + public void setScales(Vector scales) { + + // remove item listener to avoid null pointer exception when populating + scaleSelection.removeItemListener(this); + scaleSelection.removeAllItems(); + + for (Scale scale : scales) { + scaleSelection.addItem(scale.getName()); + } + + // and put it back + scaleSelection.addItemListener(this); + } + + @Override + public void mouseClicked(MouseEvent e) { + Table curTable = getSelectedTable(); + if(null == curTable) + { + return; + } + + if (e.getSource() == incrementCoarse) { + incrementCoarse(curTable); + } else if (e.getSource() == decrementCoarse) { + decrementCoarse(curTable); + } else if (e.getSource() == enable3d) { + enable3d(curTable); + } else if (e.getSource() == incrementFine) { + incrementFine(curTable); + } else if (e.getSource() == decrementFine) { + decrementFine(curTable); + } else if (e.getSource() == multiply) { + multiply(curTable); + } else if (e.getSource() == setValue) { + setValue(curTable); + } else if (e.getSource() == colorCells) { + colorCells(curTable); + } else if (e.getSource() == refreshCompare) { + refreshCompare(curTable); + } + } + + public void setValue(Table currentTable) { + currentTable.setRealValue(setValueText.getText()); + } + + public void multiply() { + Table curTable = getSelectedTable(); + if(null == curTable) { + return; + } + multiply(curTable); + } + + public void multiply(Table currentTable) { + try{ + currentTable.multiply(Double.parseDouble(setValueText.getText())); + }catch(NumberFormatException nex) { + // Do Nothing. setValueText is null or not a valid double. + } + } + + public void incrementFine() { + Table curTable = getSelectedTable(); + if(null == curTable) { + return; + } + incrementFine(curTable); + } + + public void incrementFine(Table currentTable) { + currentTable.increment(Double.parseDouble(String.valueOf(incrementByFine.getValue()))); + } + + public void decrementFine() { + Table curTable = getSelectedTable(); + if(null == curTable) { + return; + } + decrementFine(curTable); + } + + public void decrementFine(Table currentTable) { + currentTable.increment(0 - Double.parseDouble(String.valueOf(incrementByFine.getValue()))); + } + + public void incrementCoarse() { + Table curTable = getSelectedTable(); + if(null == curTable) { + return; + } + incrementCoarse(curTable); + } + + public void incrementCoarse(Table currentTable) { + currentTable.increment(Double.parseDouble(String.valueOf(incrementByCoarse.getValue()))); + } + + public void decrementCoarse() { + Table curTable = getSelectedTable(); + if(null == curTable) { + return; + } + decrementCoarse(curTable); + } + + public void decrementCoarse(Table currentTable) { + currentTable.increment(0 - Double.parseDouble(String.valueOf(incrementByCoarse.getValue()))); + } + + /** + * Method launches a 3d Frame. + */ + public void enable3d(Table currentTable) { + int rowCount = 0; + int valueCount = 0; + + //Pull data into format 3d graph understands + Vector graphValues = new Vector(); + graphValues.clear(); + + if (currentTable.getType() == Settings.TABLE_3D) { + Table3D table3d = (Table3D) currentTable; + DataCell[][] tableData = table3d.get3dData(); + valueCount = tableData.length; + DataCell[] dataRow = tableData[0]; + rowCount = dataRow.length; + + for (int j = (rowCount - 1); j >= 0; j--) { + float[] rowValues = new float[valueCount]; + for (int i = 0; i < valueCount; i++) { + DataCell theCell = tableData[i][j]; + rowValues[i] = (float) theCell.getRealValue(); + } + graphValues.add(rowValues); + } + + Table1D xAxisTable1D = table3d.getXAxis(); + Table1D yAxisTable1D = table3d.getYAxis(); + + //Gather x axis values + DataCell[] dataCells = xAxisTable1D.getData(); + int length = dataCells.length; + double[] xValues = new double[length]; + + for (int i = 0; i < length; i++) { + xValues[i] = dataCells[i].getRealValue(); + } + + //Gather y/z axis values + dataCells = yAxisTable1D.getData(); + length = dataCells.length; + double[] yValues = new double[length]; + + for (int i = 0; i < length; i++) { + double theValue = dataCells[i].getRealValue(); + BigDecimal finalRoundedValue = new BigDecimal(theValue).setScale(2, BigDecimal.ROUND_HALF_UP); + yValues[i] = finalRoundedValue.doubleValue(); + } + + //Define Labels for graph + String xLabel = table3d.getXAxis().getName(); + String zLabel = table3d.getYAxis().getName(); + String yLabel = table3d.getCategory(); + + //TODO Figure out mix between heavy weight and lightweight components + //Below is initial work on making graph3d a JInternal Frame + /* + Graph3dJPanel graph3dJPanel = new Graph3dJPanel(graphValues, testX, testZ,xLabel, yLabel, zLabel); + graph3dJPanel.addModifiedDataListener(this); + JInternalFrame graphFrame = new JInternalFrame(); + graphFrame.add(graph3dJPanel); + graphFrame.setSize(200, 200); + + graphFrame.setFrameIcon(null); + graphFrame.setBorder(BorderFactory.createBevelBorder(0)); + graphFrame.setVisible(true); + graphFrame.setDefaultCloseOperation(JInternalFrame.HIDE_ON_CLOSE); + ECUEditorManager.getECUEditor().rightPanel.add(graphFrame); + */ + + // TODO: do we want to get the max/min allowed or the max/min current? + table3d.calcCellRanges(); + double maxV = table3d.getMaxReal(); + double minV = table3d.getMinReal(); + //TODO Remove this when above is working + //*********** + /*minV = 0.0; + maxV = 13.01;*/ + LOGGER.debug("Scale: " + maxV + "," + minV); + //*********** + + throw new UnsupportedOperationException(); +// Graph3dFrameManager.openGraph3dFrame(graphValues, minV, maxV, xValues, yValues, xLabel, yLabel, zLabel, currentTable.getName()); +// GraphData.addGraphDataListener(this); + } + } + + public void colorCells(Table currentTable) { + currentTable.colorCells(); + } + + public void refreshCompare(Table currentTable) { + currentTable.populateCompareValues(currentTable.getCompareTable()); + } + + public void setCoarseValue(double input) { + incrementByCoarse.setText(String.valueOf(input)); + try { + incrementByCoarse.commitEdit(); + } catch (ParseException ex) { + } + } + + public void setFineValue(double input) { + incrementByFine.setText(String.valueOf(input)); + try { + incrementByFine.commitEdit(); + } catch (ParseException ex) { + } + } + + public void focusSetValue(char input) { + setValueText.requestFocus(); + setValueText.setText(String.valueOf(input)); + } + + public void setInputMap(InputMap im) { + incrementFine.setInputMap(WHEN_FOCUSED, im); + decrementFine.setInputMap(WHEN_FOCUSED, im); + incrementCoarse.setInputMap(WHEN_FOCUSED, im); + decrementCoarse.setInputMap(WHEN_FOCUSED, im); + setValue.setInputMap(WHEN_FOCUSED, im); + } + + @Override + public void mousePressed(MouseEvent e) { + } + + @Override + public void mouseReleased(MouseEvent e) { + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + } + + @Override + public void itemStateChanged(ItemEvent e) { + Table curTable = getSelectedTable(); + if(null == curTable) { + return; + } + + if (e.getSource() == scaleSelection) { + // scale changed + try { + + curTable.setScaleByName((String)scaleSelection.getSelectedItem()); + updateToolbarIncrementDecrementValues(); + } catch (NameNotFoundException e1) { + e1.printStackTrace(); + } + } else if (e.getSource() == overlayLog) { + // enable/disable log overlay and live data display + curTable.setOverlayLog(overlayLog.isSelected()); + } + } + + + @Override + public void actionPerformed(ActionEvent e) { + Table curTable = getSelectedTable(); + if(null == curTable) { + return; + } + + if (e.getSource() == clearOverlay) { + // clear log overlay + curTable.clearLiveDataTrace(); + } + } + + public void setLiveDataValue(String value) { + liveDataValue.setText(value); + } + + + // ****************************************** + // Code for listening to graph3d data changes + // ****************************************** + + //@Override + public void newGraphData(int x, int z, float value) { + Table curTable = getSelectedTable(); + if(null == curTable) { + return; + } + + if(curTable.getType() == Settings.TABLE_3D) { + Table3D table3d = (Table3D) curTable; + table3d.selectCellAt(x, table3d.getSizeY() - z - 1); + + //Set the value + table3d.setRealValue(String.valueOf(value)); + } + } + + //@Override + public void selectStateChange(int x, int z, boolean value) { + Table curTable = getSelectedTable(); + if(null == curTable) { + return; + } + + if(curTable.getType() == Settings.TABLE_3D) { + if (value) { + Table3D table3d = (Table3D) curTable; + table3d.selectCellAtWithoutClear(x, table3d.getSizeY() - z - 1); + } else { + Table3D table3d = (Table3D) curTable; + table3d.deSelectCellAt(x, table3d.getSizeY() - z - 1); + } + } + } + + public Table getSelectedTable() { + return this.selectedTable; + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/swing/TableTreeNode.java b/java_console/romraider/src/com/romraider/swing/TableTreeNode.java new file mode 100644 index 0000000000..0d35d80951 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/TableTreeNode.java @@ -0,0 +1,57 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import javax.swing.tree.DefaultMutableTreeNode; + +import com.romraider.maps.Table; + +public class TableTreeNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = 2824050968863990871L; + private String toolTip; + + public TableTreeNode(TableFrame tableFrame) { + super(tableFrame); + } + + public TableFrame getFrame() { + if(getUserObject() instanceof TableFrame) { + return (TableFrame)getUserObject(); + } + return null; + } + + public void setFrame(TableFrame tableFrame) { + this.setUserObject(tableFrame); + } + + public Table getTable() { + return this.getFrame().getTable(); + } + + public void setToolTipText(String input) { + toolTip = input; + } + + public String getToolTipText() { + return toolTip; + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/swing/XMLFilter.java b/java_console/romraider/src/com/romraider/swing/XMLFilter.java new file mode 100644 index 0000000000..d0d65e90a5 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/XMLFilter.java @@ -0,0 +1,106 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing; + +import javax.swing.filechooser.FileFilter; +import java.io.File; +import java.util.Enumeration; +import java.util.Hashtable; + +public class XMLFilter extends FileFilter { + + private Hashtable filters = null; + private String description = null; + private String fullDescription = null; + private boolean useExtensionsInDescription = true; + + public XMLFilter() { + this.filters = new Hashtable(); + this.addExtension("xml"); + this.setDescription("ECU Definition Files"); + } + + public boolean accept(File f) { + if (f != null) { + if (f.isDirectory()) { + return true; + } + String extension = getExtension(f); + if (extension != null && filters.get(getExtension(f)) != null) { + return true; + } + ; + } + return false; + } + + public String getExtension(File f) { + if (f != null) { + String filename = f.getName(); + int i = filename.lastIndexOf('.'); + if (i > 0 && i < filename.length() - 1) { + return filename.substring(i + 1).toLowerCase(); + } + ; + } + return null; + } + + public void addExtension(String extension) { + filters.put(extension.toLowerCase(), this); + fullDescription = null; + } + + public String getDescription() { + if (fullDescription == null) { + if (description == null || isExtensionListInDescription()) { + fullDescription = description == null ? "(" : description + + " ("; + // build the description from the extension list + Enumeration extensions = filters.keys(); + if (extensions != null) { + fullDescription += "." + extensions.nextElement(); + while (extensions.hasMoreElements()) { + fullDescription += ", ." + + extensions.nextElement(); + } + } + fullDescription += ")"; + } else { + fullDescription = description; + } + } + return fullDescription; + } + + public void setDescription(String description) { + this.description = description; + fullDescription = null; + } + + public void setExtensionListInDescription(boolean b) { + useExtensionsInDescription = b; + fullDescription = null; + } + + public boolean isExtensionListInDescription() { + return useExtensionsInDescription; + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/swing/menubar/Menu.java b/java_console/romraider/src/com/romraider/swing/menubar/Menu.java new file mode 100644 index 0000000000..a9de5ffa22 --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/menubar/Menu.java @@ -0,0 +1,32 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing.menubar; + +import javax.swing.JMenu; + +public final class Menu extends JMenu { + + private static final long serialVersionUID = -5058943622469501273L; + + public Menu(String text, int mnemonic) { + setText(text); + setMnemonic(mnemonic); + } +} diff --git a/java_console/romraider/src/com/romraider/swing/menubar/MenuItem.java b/java_console/romraider/src/com/romraider/swing/menubar/MenuItem.java new file mode 100644 index 0000000000..748e33c2ab --- /dev/null +++ b/java_console/romraider/src/com/romraider/swing/menubar/MenuItem.java @@ -0,0 +1,46 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.swing.menubar; + +import javax.swing.Action; +import javax.swing.JMenuItem; +import javax.swing.KeyStroke; + +public final class MenuItem extends JMenuItem { + + private static final long serialVersionUID = 8944116003490787227L; + + public MenuItem(String text, Action action) { + super(action); + setText(text); + } + + public MenuItem(String text, Action action, int mnemonic) { + super(action); + setText(text); + setMnemonic(mnemonic); + } + + public MenuItem(String text, Action action, int mnemonic, KeyStroke accelerator) { + this(text, action, mnemonic); + setAccelerator(accelerator); + } + +} diff --git a/java_console/romraider/src/com/romraider/util/AxisRange.java b/java_console/romraider/src/com/romraider/util/AxisRange.java new file mode 100644 index 0000000000..006e6a3557 --- /dev/null +++ b/java_console/romraider/src/com/romraider/util/AxisRange.java @@ -0,0 +1,39 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.util; + +public final class AxisRange { + private final int startIndex; + private final int endIndex; + + public AxisRange(int startIndex, int endIndex) { + this.startIndex = startIndex; + this.endIndex = endIndex; + } + + public int getStartIndex() { + return startIndex; + } + + public int getEndIndex() { + return endIndex; + } + +} diff --git a/java_console/romraider/src/com/romraider/util/ByteUtil.java b/java_console/romraider/src/com/romraider/util/ByteUtil.java new file mode 100644 index 0000000000..b8a5f8b486 --- /dev/null +++ b/java_console/romraider/src/com/romraider/util/ByteUtil.java @@ -0,0 +1,118 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.util; + +import java.nio.ByteBuffer; +import java.util.List; + +public final class ByteUtil { + + private ByteUtil() { + throw new UnsupportedOperationException(); + } + + public static int asUnsignedInt(byte b) { + return asUnsignedInt(new byte[]{b}); + } + + public static int asSignedInt(byte[] bytes) { + int i = 0; + for (int j = 0; j < bytes.length; j++) { + if (j == 0) { + i |= bytes[j]; + } + else { + i <<= 8; + i |= bytes[j] & 0xFF; + } + } + return i; + } + + public static int asUnsignedInt(byte[] bytes) { + int i = 0; + for (int j = 0; j < bytes.length; j++) { + if (j > 0) { + i <<= 8; + } + i |= bytes[j] & 0xFF; + } + return i; + } + + public static byte asByte(int i) { + return Integer.valueOf(i).byteValue(); + } + + public static float asFloat(byte[] b, int offset, int length) { + final ByteBuffer buf = ByteBuffer.wrap(b, offset, length); + return buf.getFloat(); + } + + public static int asInt(byte b) { + return Byte.valueOf(b).intValue(); + } + + public static boolean matchOnes(byte b, int mask) { + return (b & mask) == mask; + } + + public static boolean matchZeroes(byte b, int mask) { + return (b & mask) == 0; + } + + public static void byteListToBytes(List buffer, byte[] response) { + for (int i = 0; i < buffer.size(); i++) { + response[i] = buffer.get(i); + } + } + + public static int indexOfBytes(byte[] bytes, byte[] pattern) { + int[] failure = computeFailure(pattern); + int j = 0; + for (int i = 0; i < bytes.length; i++) { + while (j > 0 && pattern[j] != bytes[i]) { + j = failure[j - 1]; + } + if (pattern[j] == bytes[i]) { + j++; + } + if (j == pattern.length) { + return i - pattern.length + 1; + } + } + return -1; + } + + private static int[] computeFailure(byte[] pattern) { + int[] failure = new int[pattern.length]; + int j = 0; + for (int i = 1; i < pattern.length; i++) { + while (j>0 && pattern[j] != pattern[i]) { + j = failure[j - 1]; + } + if (pattern[j] == pattern[i]) { + j++; + } + failure[i] = j; + } + return failure; + } +} diff --git a/java_console/romraider/src/com/romraider/util/ColorScaler.java b/java_console/romraider/src/com/romraider/util/ColorScaler.java new file mode 100644 index 0000000000..42b2a0afec --- /dev/null +++ b/java_console/romraider/src/com/romraider/util/ColorScaler.java @@ -0,0 +1,62 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.util; + +import java.awt.Color; + +import com.romraider.Settings; + +public final class ColorScaler { + + private ColorScaler() { + } + + public static Color getScaledColor(double scale) { + + Settings settings = SettingsManager.getSettings(); + if(0.0 == scale) { + return settings.getMinColor(); + } + + if(Double.NaN == scale) { + return settings.getMaxColor(); + } + + Color minColor = settings.getMinColor(); + Color maxColor = settings.getMaxColor(); + + float[] minColorHSB = new float[3]; + float[] maxColorHSB = new float[3]; + + rgbToHsb(minColor, minColorHSB); + rgbToHsb(maxColor, maxColorHSB); + + float h = minColorHSB[0] + (maxColorHSB[0] - minColorHSB[0]) * (float) scale; + float s = minColorHSB[1] + (maxColorHSB[1] - minColorHSB[1]) * (float) scale; + float b = minColorHSB[2] + (maxColorHSB[2] - minColorHSB[2]) * (float) scale; + + return Color.getHSBColor(h, s, b); + + } + + private static void rgbToHsb(Color color, float[] colorHSB) { + Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), colorHSB); + } +} diff --git a/java_console/romraider/src/com/romraider/util/FormatFilename.java b/java_console/romraider/src/com/romraider/util/FormatFilename.java new file mode 100644 index 0000000000..42f68e2835 --- /dev/null +++ b/java_console/romraider/src/com/romraider/util/FormatFilename.java @@ -0,0 +1,68 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.util; + +import java.io.File; + +/** + * Shorten a long text file path for use in title bar or limited width display + * component. + */ +public final class FormatFilename { + private final static String separator = System.getProperty("file.separator"); + + private FormatFilename() { + } + + /** + * Shorten a text file path name if > 45 characters. + * @param file - the File to shorten the path name for. + * @return the shortened name + */ + public final static String getShortName(File file) { + String filePath = file.getAbsolutePath(); + if (filePath.length() > 45) { + filePath = getShortName(filePath); + } + return filePath; + } + + public final static String getShortName(String filePath) { + String regex = separator; + if (separator.equals("\\")) { + regex = "\\" + separator; + } + final String[] filePathParts = filePath.split(regex); + String logFileName = filePath; + if (filePathParts.length > 3) { + logFileName = String.format( + "%s%s%s...%s%s%s%s", + filePathParts[0], + separator, + filePathParts[1].substring(0, + Math.min(4, filePathParts[1].length())), + separator, + filePathParts[filePathParts.length - 2], + separator, + filePathParts[filePathParts.length - 1]); + } + return logFileName; + } +} diff --git a/java_console/romraider/src/com/romraider/util/HexUtil.java b/java_console/romraider/src/com/romraider/util/HexUtil.java new file mode 100644 index 0000000000..d40c7ead16 --- /dev/null +++ b/java_console/romraider/src/com/romraider/util/HexUtil.java @@ -0,0 +1,104 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2014 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.util; + +public final class HexUtil { + + private HexUtil() { + } + + public static String asHex(byte b) { + return asHex(new byte[]{b}); + } + + public static String asHex(byte[] in) { + return bytesToHex(in).toUpperCase(); + } + + public static byte[] asBytes(String hex) { + if (hex.indexOf(' ') >= 0) { + hex = hex.replaceAll(" ", ""); + } + if (hex.startsWith("0x")) { + hex = hex.substring(2); + } + return hexToBytes(hex); + } + + public static String bytesToHex(byte[] bs, int off, int length) { + StringBuffer sb = new StringBuffer(length * 2); + bytesToHexAppend(bs, off, length, sb); + return sb.toString(); + } + + public static void bytesToHexAppend(byte[] bs, int off, int length, StringBuffer sb) { + sb.ensureCapacity(sb.length() + length * 2); + for (int i = off; (i < (off + length)) && (i < bs.length); i++) { + sb.append(Character.forDigit((bs[i] >>> 4) & 0xf, 16)); + sb.append(Character.forDigit(bs[i] & 0xf, 16)); + } + } + + public static String bytesToHex(byte[] bs) { + return bytesToHex(bs, 0, bs.length); + } + + public static byte[] hexToBytes(String s) { + return hexToBytes(s, 0); + } + + public static byte[] hexToBytes(String s, int off) { + byte[] bs = new byte[off + (1 + s.length()) / 2]; + hexToBytes(s, bs, off); + return bs; + } + + public static void hexToBytes(String s, byte[] out, int off) throws NumberFormatException, IndexOutOfBoundsException { + int slen = s.length(); + if ((slen % 2) != 0) { + s = '0' + s; + } + if (out.length < off + slen / 2) { + throw new IndexOutOfBoundsException("Output buffer too small for input (" + out.length + "<" + off + slen / 2 + ")"); + } + // Safe to assume the string is even length + byte b1, b2; + for (int i = 0; i < slen; i += 2) { + b1 = (byte) Character.digit(s.charAt(i), 16); + b2 = (byte) Character.digit(s.charAt(i + 1), 16); + if ((b1 < 0) || (b2 < 0)) { + throw new NumberFormatException(); + } + out[off + i / 2] = (byte) (b1 << 4 | b2); + } + } + + public static int hexToInt(String input) { + if (input.length() > 2 && input.substring(0, 2).equalsIgnoreCase("0x")) { + return Integer.parseInt(input.substring(2), 16); + } else { + return Integer.parseInt(input, 16); + } + } + + public static String intToHexString(int input) { + return "0x" + Integer.toHexString(input).toUpperCase(); + } +} diff --git a/java_console/romraider/src/com/romraider/util/JEPUtil.java b/java_console/romraider/src/com/romraider/util/JEPUtil.java new file mode 100644 index 0000000000..bb7f128717 --- /dev/null +++ b/java_console/romraider/src/com/romraider/util/JEPUtil.java @@ -0,0 +1,48 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2014 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.util; + +import org.nfunk.jep.JEP; +import java.util.Map; + +public final class JEPUtil { + + private JEPUtil() { + } + + public static double evaluate(String expression, double value) { + final JEP parser = new JEP(); + parser.addStandardFunctions(); + parser.initSymTab(); // clear the contents of the symbol table + parser.addVariable("x", value); + parser.parseExpression(expression); + return parser.getValue(); + } + + public static double evaluate(String expression, Map valueMap) { + final JEP parser = new JEP(); + parser.initSymTab(); // clear the contents of the symbol table + for (String id : valueMap.keySet()) { + parser.addVariable(id, valueMap.get(id)); + } + parser.parseExpression(expression); + return parser.getValue(); + } +} diff --git a/java_console/romraider/src/com/romraider/util/JREChecker.java b/java_console/romraider/src/com/romraider/util/JREChecker.java new file mode 100644 index 0000000000..571af836c5 --- /dev/null +++ b/java_console/romraider/src/com/romraider/util/JREChecker.java @@ -0,0 +1,59 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.util; + +/** + * @author Steve Wadsworth + * @since 2012-12-30 + * + * This class is intended to contain utility methods needed to determine various + * parameters of the JVM in which the application is running. + * + */ + +public final class JREChecker { + + /** + * Check to see if the JVM is 32-bit + * + * @return true if JVM is 32-bit + */ + public static boolean is32bit() { + // determine if we're running in a 32-bit JVM + // this may need to be extended for other JVM providers and versions + String bitness = System.getProperty("sun.arch.data.model", "unknown"); + // if we don't know, try harder - additional tests can be added here as + // necessary + if (bitness.equals("unknown")) { + // if sun.arch.data.model isn't found, we may be on a non-Sun + // (Oracle) VM try some other properties + if (System.getProperty("java.vm.name").indexOf("64") >= 0 || + System.getProperty("sun.cpu.isalist").indexOf("64") >= 0) { + bitness = "64"; + } + } + // should be either 32, 64, or still unknown. only known 32 should + // return true + if (bitness.equals("32")) { + return true; + } + return false; + } +} diff --git a/java_console/romraider/src/com/romraider/util/LogManager.java b/java_console/romraider/src/com/romraider/util/LogManager.java new file mode 100644 index 0000000000..4d3834ed1e --- /dev/null +++ b/java_console/romraider/src/com/romraider/util/LogManager.java @@ -0,0 +1,33 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.util; + +import static org.apache.log4j.PropertyConfigurator.configureAndWatch; + +public final class LogManager { + + private LogManager() { + throw new UnsupportedOperationException(); + } + + public static void initDebugLogging() { + configureAndWatch("log4j.properties"); + } +} diff --git a/java_console/romraider/src/com/romraider/util/MD5Checksum.java b/java_console/romraider/src/com/romraider/util/MD5Checksum.java new file mode 100644 index 0000000000..d283355b35 --- /dev/null +++ b/java_console/romraider/src/com/romraider/util/MD5Checksum.java @@ -0,0 +1,53 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.util; + +import java.io.FileInputStream; +import java.io.InputStream; +import java.security.MessageDigest; + +public class MD5Checksum { + + public static byte[] createChecksum(String filename) throws Exception { + InputStream fis = new FileInputStream(filename); + + byte[] buffer = new byte[1024]; + MessageDigest complete = MessageDigest.getInstance("MD5"); + int numRead; + do { + numRead = fis.read(buffer); + if (numRead > 0) { + complete.update(buffer, 0, numRead); + } + } while (numRead != -1); + fis.close(); + return complete.digest(); + } + + public static String getMD5Checksum(String filename) throws Exception { + byte[] b = createChecksum(filename); + String result = ""; + for (int i = 0; i < b.length; i++) { + result += + Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1); + } + return result; + } +} diff --git a/java_console/romraider/src/com/romraider/util/ObjectCloner.java b/java_console/romraider/src/com/romraider/util/ObjectCloner.java new file mode 100644 index 0000000000..46b12425da --- /dev/null +++ b/java_console/romraider/src/com/romraider/util/ObjectCloner.java @@ -0,0 +1,96 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +public final class ObjectCloner { + + private ObjectCloner() { + } + + // returns a deep copy of an object + public static Object deepCopy(Object obj) throws Exception { + /*ByteArrayOutputStream bos = new ByteArrayOutputStream(); + try { + ObjectOutputStream oos = new ObjectOutputStream(bos); + try { + ByteArrayInputStream bin = new ByteArrayInputStream(bos.toByteArray()); + try { + ObjectInputStream ois = new ObjectInputStream(bin); + try { + // serialize and pass the object + oos.writeObject(obj); + oos.flush(); + + // return the new object + return ois.readObject(); + + } finally { + ois.close(); + } + } finally { + bin.close(); + } + } finally { + oos.close(); + } + } finally { + bos.close(); + }*/ + + /*ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(obj); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(bais); + Object deepCopy = ois.readObject(); + return deepCopy;*/ + + //obj2DeepCopy must be serializable + ObjectOutputStream outStream = null; + ObjectInputStream inStream = null; + + try { + ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); + outStream = new ObjectOutputStream(byteOut); + outStream.writeObject(obj); + outStream.flush(); + ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray()); + inStream = new ObjectInputStream(byteIn); + + // read the serialized, and deep copied, object and return it + return inStream.readObject(); + + } catch (Exception e) { + throw (e); + + } finally { + //always close your streams in finally clauses + outStream.close(); + inStream.close(); + } + + + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/util/ParamChecker.java b/java_console/romraider/src/com/romraider/util/ParamChecker.java new file mode 100644 index 0000000000..9e6c39c6c2 --- /dev/null +++ b/java_console/romraider/src/com/romraider/util/ParamChecker.java @@ -0,0 +1,99 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.util; + +import java.util.Collection; +import java.util.Map; + +public final class ParamChecker { + + private ParamChecker() { + } + + public static void checkNotNull(Object param, String paramName) { + if (param == null) { + throw new IllegalArgumentException("Parameter " + paramName + " must not be null"); + } + } + + public static void checkNotNull(Object... params) { + for (int i = 0; i < params.length; i++) { + checkNotNull(params[i], "arg" + i); + } + } + + public static void checkNotNullOrEmpty(String param, String paramName) { + if (isNullOrEmpty(param)) { + throw new IllegalArgumentException("Parameter " + paramName + " must not be null or empty"); + } + } + + public static void checkNotNullOrEmpty(Object[] param, String paramName) { + if (param == null || param.length == 0) { + throw new IllegalArgumentException("Parameter " + paramName + " must not be null or empty"); + } + } + + public static void checkNotNullOrEmpty(Collection param, String paramName) { + if (param == null || param.isEmpty()) { + throw new IllegalArgumentException("Parameter " + paramName + " must not be null or empty"); + } + } + + public static void checkNotNullOrEmpty(Map param, String paramName) { + if (param == null || param.isEmpty()) { + throw new IllegalArgumentException("Parameter " + paramName + " must not be null or empty"); + } + } + + public static void checkGreaterThanZero(int param, String paramName) { + if (param <= 0) { + throw new IllegalArgumentException("Parameter " + paramName + " must be > 0"); + } + } + + public static void checkNotNullOrEmpty(byte[] param, String paramName) { + if (param == null || param.length == 0) { + throw new IllegalArgumentException("Parameter " + paramName + " must not be null or empty"); + } + } + + public static void checkBit(int bit) { + if (!isValidBit(bit)) { + throw new IllegalArgumentException("Bit must be between 0 and 7 inclusive."); + } + } + + public static boolean isNullOrEmpty(String param) { + return param == null || param.length() == 0; + } + + public static boolean isNullOrEmpty(Collection param) { + return param == null || param.isEmpty(); + } + + public static boolean isNullOrEmpty(Map param) { + return param == null || param.isEmpty(); + } + + public static boolean isValidBit(int bit) { + return bit >= 0 && bit <= 7; + } +} diff --git a/java_console/romraider/src/com/romraider/util/Platform.java b/java_console/romraider/src/com/romraider/util/Platform.java new file mode 100644 index 0000000000..97aaac65ad --- /dev/null +++ b/java_console/romraider/src/com/romraider/util/Platform.java @@ -0,0 +1,31 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.util; + +public final class Platform { + public static final String LINUX = "Linux"; + public static final String MAC_OS_X = "Mac OS X"; + public static final String WINDOWS = "Windows"; + private static final String OS_NAME = "os.name"; + + public static boolean isPlatform(String platform) { + return System.getProperties().getProperty(OS_NAME).toLowerCase().contains(platform.toLowerCase()); + } +} diff --git a/java_console/romraider/src/com/romraider/util/RomServer.java b/java_console/romraider/src/com/romraider/util/RomServer.java new file mode 100644 index 0000000000..6d689eb1ba --- /dev/null +++ b/java_console/romraider/src/com/romraider/util/RomServer.java @@ -0,0 +1,96 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.util; + +import org.apache.log4j.Logger; +import static org.apache.log4j.Logger.getLogger; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.net.ServerSocket; +import java.net.Socket; + +public final class RomServer { + private static final Logger LOGGER = getLogger(RomServer.class); + private static final String HOST = "localhost"; + private static final int PORT = 50001; + + public static boolean isRunning() { + try { + ServerSocket sock = new ServerSocket(PORT); + sock.close(); + return false; + } catch (Exception ex) { + return true; + } + } + + public static String waitForRom() throws IOException { + ServerSocket sock = new ServerSocket(PORT); + try { + return waitForRom(sock); + } finally { + sock.close(); + } + } + + public static void sendRomToOpenInstance(String rom) { + try { + Socket socket = new Socket(HOST, PORT); + OutputStream os = socket.getOutputStream(); + try { + write(os, rom); + } finally { + socket.close(); + } + } catch (Throwable e) { + LOGGER.error("Error occurred", e); + } + } + + private static void write(OutputStream os, String rom) { + PrintWriter pw = new PrintWriter(os, true); + try { + pw.println(rom); + } finally { + pw.close(); + } + } + + private static String waitForRom(ServerSocket sock) throws IOException { + Socket client = sock.accept(); + try { + return getRom(client); + } finally { + client.close(); + } + } + + private static String getRom(Socket client) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream())); + try { + return br.readLine(); + } finally { + br.close(); + } + } +} diff --git a/java_console/romraider/src/com/romraider/util/SaxParserFactory.java b/java_console/romraider/src/com/romraider/util/SaxParserFactory.java new file mode 100644 index 0000000000..843c7fb750 --- /dev/null +++ b/java_console/romraider/src/com/romraider/util/SaxParserFactory.java @@ -0,0 +1,57 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.util; + +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; + +public final class SaxParserFactory { + + private SaxParserFactory() { + throw new UnsupportedOperationException(); + } + + public static SAXParser getSaxParser() throws ParserConfigurationException, SAXException { + SAXParserFactory parserFactory = SAXParserFactory.newInstance(); + parserFactory.setNamespaceAware(false); + parserFactory.setValidating(true); + parserFactory.setXIncludeAware(false); + return parserFactory.newSAXParser(); + } + + public static void main(String args[]) { + try { + SAXParser parser = SaxParserFactory.getSaxParser(); + BufferedInputStream b = new BufferedInputStream(new FileInputStream(new File("/ecu_defs.xml"))); + System.out.println(b.available()); + parser.parse(b, new DefaultHandler()); + System.out.println(parser.isValidating()); + + } catch (Exception ex) { + System.err.println(ex); + } + } +} diff --git a/java_console/romraider/src/com/romraider/util/SettingsManager.java b/java_console/romraider/src/com/romraider/util/SettingsManager.java new file mode 100644 index 0000000000..9c98e166c2 --- /dev/null +++ b/java_console/romraider/src/com/romraider/util/SettingsManager.java @@ -0,0 +1,103 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.util; + +import static com.romraider.Version.VERSION; +import static javax.swing.JOptionPane.INFORMATION_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; + +import org.w3c.dom.Document; +import org.xml.sax.InputSource; + +import com.romraider.Settings; +import com.romraider.swing.JProgressPane; +import com.romraider.xml.DOMSettingsBuilder; +import com.romraider.xml.DOMSettingsUnmarshaller; +import com.sun.org.apache.xerces.internal.parsers.DOMParser; + +public class SettingsManager { + private static final String SETTINGS_FILE = "/settings.xml"; + private static final String USER_HOME = + System.getProperty("user.home") + "/.RomRaider"; + private static final String START_DIR = System.getProperty("user.dir"); + private static String settingsDir = USER_HOME; + + private static Settings settings = null; + + public static Settings getSettings() { + if(null == settings) { + settings = load(); + } + return settings; + } + + private static Settings load() { + Settings loadedSettings; + try { + FileInputStream settingsFileIn = null; + try { + final File sf = new File(USER_HOME + SETTINGS_FILE); + settingsFileIn = new FileInputStream(sf); + } + catch (Exception e) { + final File sf = new File(START_DIR + SETTINGS_FILE); + settingsFileIn = new FileInputStream(sf); + settingsDir = START_DIR; + } + final InputSource src = new InputSource(settingsFileIn); + final DOMSettingsUnmarshaller domUms = new DOMSettingsUnmarshaller(); + final DOMParser parser = new DOMParser(); + parser.parse(src); + final Document doc = parser.getDocument(); + loadedSettings = domUms.unmarshallSettings(doc.getDocumentElement()); + } catch (FileNotFoundException e) { + showMessageDialog(null, + "Settings file not found.\nUsing default settings.", + "Error Loading Settings", INFORMATION_MESSAGE); + loadedSettings = new Settings(); + } catch (Exception e) { + throw new RuntimeException(e); + } + return loadedSettings; + } + + public static void save(Settings newSettings) { + save(newSettings, new JProgressPane()); + } + + public static void save(Settings newSettings, JProgressPane progress) { + final DOMSettingsBuilder builder = new DOMSettingsBuilder(); + try { + final File newDir = new File(settingsDir); + newDir.mkdir(); // Creates directory if it does not exist + final File sf = new File(settingsDir + SETTINGS_FILE); + builder.buildSettings(newSettings, sf, progress, VERSION); + settings = newSettings; + } catch (Exception e) { + // Load the settings from disk. + settings = load(); + throw new RuntimeException(e); + } + } +} diff --git a/java_console/romraider/src/com/romraider/util/ThreadCheckingRepaintManager.java b/java_console/romraider/src/com/romraider/util/ThreadCheckingRepaintManager.java new file mode 100644 index 0000000000..67f111da37 --- /dev/null +++ b/java_console/romraider/src/com/romraider/util/ThreadCheckingRepaintManager.java @@ -0,0 +1,96 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.util; + +import javax.swing.JComponent; +import javax.swing.RepaintManager; +import javax.swing.SwingUtilities; +import java.awt.Component; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +public class ThreadCheckingRepaintManager extends RepaintManager { + private int tabCount = 0; + private boolean checkIsShowing = false; + + public ThreadCheckingRepaintManager() { + } + + public ThreadCheckingRepaintManager(boolean checkIsShowing) { + this.checkIsShowing = checkIsShowing; + } + + public synchronized void addInvalidComponent(JComponent jComponent) { + checkThread(jComponent); + super.addInvalidComponent(jComponent); + } + + private void checkThread(JComponent c) { + if (!SwingUtilities.isEventDispatchThread() && checkIsShowing(c)) { + System.out.println("----------Wrong Thread START"); + System.out.println(getStracktraceAsString(new Exception())); + dumpComponentTree(c); + System.out.println("----------Wrong Thread END"); + } + } + + private String getStracktraceAsString(Exception e) { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + PrintStream printStream = new PrintStream(byteArrayOutputStream); + e.printStackTrace(printStream); + printStream.flush(); + return byteArrayOutputStream.toString(); + } + + private boolean checkIsShowing(JComponent c) { + return !this.checkIsShowing || c.isShowing(); + } + + public synchronized void addDirtyRegion(JComponent jComponent, int i, int i1, int i2, int i3) { + checkThread(jComponent); + super.addDirtyRegion(jComponent, i, i1, i2, i3); + } + + private void dumpComponentTree(Component c) { + System.out.println("----------Component Tree"); + resetTabCount(); + for (; c != null; c = c.getParent()) { + printTabIndent(); + System.out.println(c); + printTabIndent(); + System.out.println("Showing:" + c.isShowing() + " Visible: " + c.isVisible()); + incrementTabCount(); + } + } + + private void resetTabCount() { + this.tabCount = 0; + } + + private void incrementTabCount() { + this.tabCount++; + } + + private void printTabIndent() { + for (int i = 0; i < this.tabCount; i++) { + System.out.print("\t"); + } + } +} diff --git a/java_console/romraider/src/com/romraider/util/ThreadUtil.java b/java_console/romraider/src/com/romraider/util/ThreadUtil.java new file mode 100644 index 0000000000..dcc374a1fc --- /dev/null +++ b/java_console/romraider/src/com/romraider/util/ThreadUtil.java @@ -0,0 +1,50 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.util; + +import org.apache.log4j.Logger; +import java.util.concurrent.TimeUnit; + +public final class ThreadUtil { + private static final Logger LOGGER = Logger.getLogger(ThreadUtil.class); + + private ThreadUtil() { + throw new UnsupportedOperationException(); + } + + public static void sleep(long millis) { + try { + TimeUnit.MILLISECONDS.sleep(millis); + } catch (InterruptedException e) { + LOGGER.trace("Sleep interrupted", e); + } + } + + public static void run(Runnable runnable) { + new Thread(runnable).start(); + } + + public static void runAsDaemon(Runnable runnable) { + Thread thread = new Thread(runnable); + thread.setDaemon(true); + thread.start(); + } + +} diff --git a/java_console/romraider/src/com/romraider/util/proxy/Proxifier.java b/java_console/romraider/src/com/romraider/util/proxy/Proxifier.java new file mode 100644 index 0000000000..d8b2bed70a --- /dev/null +++ b/java_console/romraider/src/com/romraider/util/proxy/Proxifier.java @@ -0,0 +1,46 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.util.proxy; + +import static java.lang.reflect.Proxy.newProxyInstance; +import java.lang.reflect.Constructor; + +public final class Proxifier { + public static T proxy(T t, Class cls) { + Wrapper wrapper = instantiate(cls, t); + return proxy(t, wrapper); + } + + private static T proxy(T t, Wrapper wrapper) { + Class cls = t.getClass(); + ClassLoader loader = cls.getClassLoader(); + Class[] interfaces = cls.getInterfaces(); + return (T) newProxyInstance(loader, interfaces, wrapper); + } + + private static Wrapper instantiate(Class wrapper, T t) { + try { + Constructor constructor = wrapper.getConstructor(Object.class); + return (Wrapper) constructor.newInstance(t); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/java_console/romraider/src/com/romraider/util/proxy/TimerWrapper.java b/java_console/romraider/src/com/romraider/util/proxy/TimerWrapper.java new file mode 100644 index 0000000000..01b8582696 --- /dev/null +++ b/java_console/romraider/src/com/romraider/util/proxy/TimerWrapper.java @@ -0,0 +1,52 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.util.proxy; + +import static com.romraider.util.ParamChecker.checkNotNull; +import static java.lang.System.currentTimeMillis; +import org.apache.log4j.Logger; +import static org.apache.log4j.Logger.getLogger; +import java.lang.reflect.Method; + +public final class TimerWrapper implements Wrapper { + private static final Logger LOGGER = getLogger(TimerWrapper.class); + private final Object delegate; + + public TimerWrapper(Object delegate) { + checkNotNull(delegate); + this.delegate = delegate; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + long start = currentTimeMillis(); + try { + return method.invoke(delegate, args); + } finally { + long time = currentTimeMillis() - start; + log(method, time); + } + } + + private void log(Method method, long time) { + String c = delegate.getClass().getSimpleName(); + String m = method.getName(); + LOGGER.error("[TIMER] - " + c + "." + m + ": " + time + "ms"); + } +} diff --git a/java_console/romraider/src/com/romraider/util/proxy/Wrapper.java b/java_console/romraider/src/com/romraider/util/proxy/Wrapper.java new file mode 100644 index 0000000000..3650c03dc2 --- /dev/null +++ b/java_console/romraider/src/com/romraider/util/proxy/Wrapper.java @@ -0,0 +1,25 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.util.proxy; + +import java.lang.reflect.InvocationHandler; + +public interface Wrapper extends InvocationHandler { +} diff --git a/java_console/romraider/src/com/romraider/xml/DOMHelper.java b/java_console/romraider/src/com/romraider/xml/DOMHelper.java new file mode 100644 index 0000000000..ad84a14242 --- /dev/null +++ b/java_console/romraider/src/com/romraider/xml/DOMHelper.java @@ -0,0 +1,65 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.xml; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +public final class DOMHelper { + + private DOMHelper() { + } + + public static String unmarshallText(Node textNode) { + StringBuffer buf = new StringBuffer(); + + Node n; + NodeList nodes = textNode.getChildNodes(); + + for (int i = 0; i < nodes.getLength(); i++) { + n = nodes.item(i); + + if (n.getNodeType() == Node.TEXT_NODE) { + buf.append(n.getNodeValue()); + } else { + // expected a text-only node (skip) + } + } + return buf.toString(); + } + + public static String unmarshallAttribute(Node node, String name, String defaultValue) { + Node n = node.getAttributes().getNamedItem(name); + return (n != null) ? (n.getNodeValue()) : (defaultValue); + } + + public static Double unmarshallAttribute(Node node, String name, double defaultValue) { + return Double.parseDouble(unmarshallAttribute(node, name, String.valueOf(defaultValue))); + } + + public static int unmarshallAttribute(Node node, String name, int defaultValue) { + return Integer.parseInt(unmarshallAttribute(node, name, String.valueOf(defaultValue))); + } + + public static boolean unmarshallAttribute(Node node, String name, boolean defaultValue) { + return Boolean.parseBoolean(unmarshallAttribute(node, name, String.valueOf(defaultValue))); + } + +} diff --git a/java_console/romraider/src/com/romraider/xml/DOMRomUnmarshaller.java b/java_console/romraider/src/com/romraider/xml/DOMRomUnmarshaller.java new file mode 100644 index 0000000000..9b60b6454a --- /dev/null +++ b/java_console/romraider/src/com/romraider/xml/DOMRomUnmarshaller.java @@ -0,0 +1,617 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +//DOM XML parser for ROMs + +package com.romraider.xml; + +import static com.romraider.xml.DOMHelper.unmarshallAttribute; +import static com.romraider.xml.DOMHelper.unmarshallText; +import static org.w3c.dom.Node.ELEMENT_NODE; + +import java.util.ArrayList; +import java.util.List; + +import javax.management.modelmbean.XMLParseException; +import javax.swing.JOptionPane; + +import org.apache.log4j.Logger; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import com.romraider.Settings; +import com.romraider.editor.ecu.ECUEditorManager; +import com.romraider.maps.DataCell; +import com.romraider.maps.Rom; +import com.romraider.maps.RomID; +import com.romraider.maps.Scale; +import com.romraider.maps.Table; +import com.romraider.maps.Table1D; +import com.romraider.maps.Table2D; +import com.romraider.maps.Table3D; +import com.romraider.maps.TableSwitch; +import com.romraider.swing.DebugPanel; +import com.romraider.swing.JProgressPane; +import com.romraider.util.LogManager; +import com.romraider.util.ObjectCloner; +import com.romraider.util.SettingsManager; + +public final class DOMRomUnmarshaller { + private static final Logger LOGGER = Logger + .getLogger(DOMRomUnmarshaller.class); + private JProgressPane progress = null; + private final List scales = new ArrayList(); + + public DOMRomUnmarshaller() { + } + + public Rom unmarshallXMLDefinition(Node rootNode, byte[] input, + JProgressPane progress) throws RomNotFoundException, + XMLParseException, StackOverflowError, Exception { + + this.progress = progress; + Node n; + NodeList nodes = rootNode.getChildNodes(); + + // unmarshall scales first + for (int i = 0; i < nodes.getLength(); i++) { + n = nodes.item(i); + + if (n.getNodeType() == ELEMENT_NODE + && n.getNodeName().equalsIgnoreCase("scalingbase")) { + scales.add(unmarshallScale(n, new Scale())); + } + } + + // now unmarshall roms + for (int i = 0; i < nodes.getLength(); i++) { + n = nodes.item(i); + + if (n.getNodeType() == ELEMENT_NODE + && n.getNodeName().equalsIgnoreCase("rom")) { + Node n2; + NodeList nodes2 = n.getChildNodes(); + + for (int z = 0; z < nodes2.getLength(); z++) { + n2 = nodes2.item(z); + if (n2.getNodeType() == ELEMENT_NODE + && n2.getNodeName().equalsIgnoreCase("romid")) { + + RomID romID = unmarshallRomID(n2, new RomID()); + + if (romID.getInternalIdString().length() > 0 + && foundMatch(romID, input)) { + Rom output = unmarshallRom(n, new Rom()); + + // set ram offset + output.getRomID().setRamOffset( + output.getRomID().getFileSize() + - input.length); + return output; + } + } + } + } + } + throw new RomNotFoundException(); + } + + public static boolean foundMatch(RomID romID, byte[] file) { + + String idString = romID.getInternalIdString(); + + // romid is hex string + if (idString.length() > 2 + && idString.substring(0, 2).equalsIgnoreCase("0x")) { + + try { + // put romid in to byte array to check for match + idString = idString.substring(2); // remove "0x" + int[] romIDBytes = new int[idString.length() / 2]; + + for (int i = 0; i < romIDBytes.length; i++) { + // check to see if each byte matches + + if ((file[romID.getInternalIdAddress() + i] & 0xff) != Integer + .parseInt(idString.substring(i * 2, i * 2 + 2), 16)) { + + return false; + } + } + // if no mismatched bytes found, return true + return true; + } catch (Exception ex) { + // if any exception is encountered, names do not match + LOGGER.warn("Error finding match", ex); + return false; + } + + // else romid is NOT hex string + } else { + try { + String ecuID = new String(file, romID.getInternalIdAddress(), + romID.getInternalIdString().length()); + return foundMatchByString(romID, ecuID); + } catch (Exception ex) { + // if any exception is encountered, names do not match + return false; + } + } + } + + public static boolean foundMatchByString(RomID romID, String ecuID) { + + try { + if (ecuID.equalsIgnoreCase(romID.getInternalIdString())) { + return true; + } else { + return false; + } + } catch (Exception ex) { + // if any exception is encountered, names do not match + return false; + } + } + + public static void main(String args[]) { + LogManager.initDebugLogging(); + RomID romID = new RomID(); + romID.setInternalIdString("Asdfd"); + + byte[] file = "Asdfd".getBytes(); + LOGGER.debug(foundMatch(romID, file)); + + file[0] = 1; + file[1] = 1; + file[2] = 1; + file[3] = 1; + LOGGER.debug(foundMatch(romID, file)); + + romID.setInternalIdString("0x010101"); + LOGGER.debug(foundMatch(romID, file)); + } + + public Rom unmarshallRom(Node rootNode, Rom rom) throws XMLParseException, + RomNotFoundException, StackOverflowError, Exception { + Node n; + NodeList nodes = rootNode.getChildNodes(); + + progress.update("Creating tables...", 15); + + if (!unmarshallAttribute(rootNode, "base", "none").equalsIgnoreCase( + "none")) { + rom = getBaseRom(rootNode.getParentNode(), + unmarshallAttribute(rootNode, "base", "none"), rom); + rom.getRomID().setObsolete(false); + } + + for (int i = 0; i < nodes.getLength(); i++) { + n = nodes.item(i); + + // update progress + int currProgress = (int) ((double) i / (double) nodes.getLength() * 40); + progress.update("Creating tables...", 10 + currProgress); + + if (n.getNodeType() == ELEMENT_NODE) { + if (n.getNodeName().equalsIgnoreCase("romid")) { + rom.setRomID(unmarshallRomID(n, rom.getRomID())); + + } else if (n.getNodeName().equalsIgnoreCase("table")) { + Table table = null; + try { + table = rom.getTableByName(unmarshallAttribute(n, "name", + null)); + } catch (TableNotFoundException e) { + /* + * table does not + * already exist (do + * nothing) + */ + } catch (InvalidTableNameException iex) { + // Table name is null or empty. Do nothing. + } + + try { + table = unmarshallTable(n, table, rom); + //rom.addTableByName(table); + rom.addTable(table); + } catch (TableIsOmittedException ex) { + // table is not supported in inherited def (skip) + if (table != null) { + //rom.removeTableByName(table); + rom.removeTable(table); + } + } catch (XMLParseException ex) { + LOGGER.error("Error unmarshalling rom", ex); + } + + } else { /* unexpected element in Rom (skip) */ + } + } else { /* unexpected node-type in Rom (skip) */ + } + } + return rom; + } + + public Rom getBaseRom(Node rootNode, String xmlID, Rom rom) + throws XMLParseException, RomNotFoundException, StackOverflowError, + Exception { + Node n; + NodeList nodes = rootNode.getChildNodes(); + + for (int i = 0; i < nodes.getLength(); i++) { + n = nodes.item(i); + + if (n.getNodeType() == ELEMENT_NODE + && n.getNodeName().equalsIgnoreCase("rom")) { + Node n2; + NodeList nodes2 = n.getChildNodes(); + + for (int z = 0; z < nodes2.getLength(); z++) { + n2 = nodes2.item(z); + if (n2.getNodeType() == ELEMENT_NODE + && n2.getNodeName().equalsIgnoreCase("romid")) { + + RomID romID = unmarshallRomID(n2, new RomID()); + if (romID.getXmlid().equalsIgnoreCase(xmlID)) { + Rom returnrom = unmarshallRom(n, rom); + returnrom.getRomID().setObsolete(false); + return returnrom; + } + } + } + } + } + throw new RomNotFoundException(); + } + + public RomID unmarshallRomID(Node romIDNode, RomID romID) { + Node n; + NodeList nodes = romIDNode.getChildNodes(); + + for (int i = 0; i < nodes.getLength(); i++) { + n = nodes.item(i); + + if (n.getNodeType() == ELEMENT_NODE) { + + if (n.getNodeName().equalsIgnoreCase("xmlid")) { + romID.setXmlid(unmarshallText(n)); + + } else if (n.getNodeName() + .equalsIgnoreCase("internalidaddress")) { + romID.setInternalIdAddress(RomAttributeParser + .parseHexString(unmarshallText(n))); + + } else if (n.getNodeName().equalsIgnoreCase("internalidstring")) { + romID.setInternalIdString(unmarshallText(n)); + if (romID.getInternalIdString() == null) { + romID.setInternalIdString(""); + } + + } else if (n.getNodeName().equalsIgnoreCase("caseid")) { + romID.setCaseId(unmarshallText(n)); + + } else if (n.getNodeName().equalsIgnoreCase("ecuid")) { + romID.setEcuId(unmarshallText(n)); + + } else if (n.getNodeName().equalsIgnoreCase("make")) { + romID.setMake(unmarshallText(n)); + + } else if (n.getNodeName().equalsIgnoreCase("market")) { + romID.setMarket(unmarshallText(n)); + + } else if (n.getNodeName().equalsIgnoreCase("model")) { + romID.setModel(unmarshallText(n)); + + } else if (n.getNodeName().equalsIgnoreCase("submodel")) { + romID.setSubModel(unmarshallText(n)); + + } else if (n.getNodeName().equalsIgnoreCase("transmission")) { + romID.setTransmission(unmarshallText(n)); + + } else if (n.getNodeName().equalsIgnoreCase("year")) { + romID.setYear(unmarshallText(n)); + + } else if (n.getNodeName().equalsIgnoreCase("flashmethod")) { + romID.setFlashMethod(unmarshallText(n)); + + } else if (n.getNodeName().equalsIgnoreCase("memmodel")) { + romID.setMemModel(unmarshallText(n)); + + } else if (n.getNodeName().equalsIgnoreCase("filesize")) { + romID.setFileSize(RomAttributeParser + .parseFileSize(unmarshallText(n))); + + } else if (n.getNodeName().equalsIgnoreCase("obsolete")) { + romID.setObsolete(Boolean.parseBoolean(unmarshallText(n))); + + } else { /* unexpected element in RomID (skip) */ + } + } else { /* unexpected node-type in RomID (skip) */ + } + } + return romID; + } + + private Table unmarshallTable(Node tableNode, Table table, Rom rom) + throws XMLParseException, TableIsOmittedException, Exception { + + if (unmarshallAttribute(tableNode, "omit", "false").equalsIgnoreCase( + "true")) { // remove table if omitted + throw new TableIsOmittedException(); + } + + if (!unmarshallAttribute(tableNode, "base", "none").equalsIgnoreCase( + "none")) { // copy base table for inheritance + try { + table = (Table) ObjectCloner + .deepCopy(rom.getTableByName(unmarshallAttribute(tableNode, + "base", "none"))); + + } catch (TableNotFoundException ex) { /* table not found, do nothing */ + + } catch (InvalidTableNameException ex) { // Table name is invalid, do nothing. + + } catch (NullPointerException ex) { + JOptionPane.showMessageDialog(ECUEditorManager.getECUEditor(), + new DebugPanel(ex, SettingsManager.getSettings().getSupportURL()), "Exception", + JOptionPane.ERROR_MESSAGE); + + } + } + + try { + if (table.getType() < 1) { + } + } catch (NullPointerException ex) { // if type is null or less than 0, + // create new instance (otherwise it + // is inherited) + if (unmarshallAttribute(tableNode, "type", "unknown") + .equalsIgnoreCase("3D")) { + table = new Table3D(); + + } else if (unmarshallAttribute(tableNode, "type", "unknown") + .equalsIgnoreCase("2D")) { + table = new Table2D(); + + } else if (unmarshallAttribute(tableNode, "type", "unknown") + .equalsIgnoreCase("1D")) { + table = new Table1D(); + + } else if (unmarshallAttribute(tableNode, "type", "unknown") + .equalsIgnoreCase("X Axis") + || unmarshallAttribute(tableNode, "type", "unknown") + .equalsIgnoreCase("Y Axis")) { + table = new Table1D(); + + } else if (unmarshallAttribute(tableNode, "type", "unknown") + .equalsIgnoreCase("Static Y Axis") + || unmarshallAttribute(tableNode, "type", "unknown") + .equalsIgnoreCase("Static X Axis")) { + table = new Table1D(); + + } else if (unmarshallAttribute(tableNode, "type", "unknown") + .equalsIgnoreCase("Switch")) { + table = new TableSwitch(); + + } else { + throw new XMLParseException("Error loading table, " + + tableNode.getAttributes().getNamedItem("name")); + } + } + + // unmarshall table attributes + table.setName(unmarshallAttribute(tableNode, "name", table.getName())); + table.setType(RomAttributeParser.parseTableType(unmarshallAttribute( + tableNode, "type", String.valueOf(table.getType())))); + if (unmarshallAttribute(tableNode, "beforeram", "false") + .equalsIgnoreCase("true")) { + table.setBeforeRam(true); + } + + table.setCategory(unmarshallAttribute(tableNode, "category", + table.getCategory())); + if (table.getStorageType() < 1) { + table.setSignedData(RomAttributeParser + .parseStorageDataSign(unmarshallAttribute(tableNode, + "storagetype", + String.valueOf(table.getStorageType())))); + } + table.setStorageType(RomAttributeParser + .parseStorageType(unmarshallAttribute(tableNode, "storagetype", + String.valueOf(table.getStorageType())))); + table.setEndian(RomAttributeParser.parseEndian(unmarshallAttribute( + tableNode, "endian", String.valueOf(table.getEndian())))); + table.setStorageAddress(RomAttributeParser + .parseHexString(unmarshallAttribute(tableNode, + "storageaddress", + String.valueOf(table.getStorageAddress())))); + table.setDescription(unmarshallAttribute(tableNode, "description", + table.getDescription())); + table.setDataSize(unmarshallAttribute(tableNode, "sizey", + unmarshallAttribute(tableNode, "sizex", table.getDataSize()))); + table.setFlip(unmarshallAttribute(tableNode, "flipy", + unmarshallAttribute(tableNode, "flipx", table.getFlip()))); + table.setUserLevel(unmarshallAttribute(tableNode, "userlevel", + table.getUserLevel())); + table.setLocked(unmarshallAttribute(tableNode, "locked", + table.isLocked())); + table.setLogParam(unmarshallAttribute(tableNode, "logparam", + table.getLogParam())); + + if (table.getType() == Settings.TABLE_3D) { + ((Table3D) table).setSwapXY(unmarshallAttribute(tableNode, + "swapxy", ((Table3D) table).getSwapXY())); + ((Table3D) table).setFlipX(unmarshallAttribute(tableNode, "flipx", + ((Table3D) table).getFlipX())); + ((Table3D) table).setFlipY(unmarshallAttribute(tableNode, "flipy", + ((Table3D) table).getFlipY())); + ((Table3D) table).setSizeX(unmarshallAttribute(tableNode, "sizex", + ((Table3D) table).getSizeX())); + ((Table3D) table).setSizeY(unmarshallAttribute(tableNode, "sizey", + ((Table3D) table).getSizeY())); + } + + Node n; + NodeList nodes = tableNode.getChildNodes(); + + for (int i = 0; i < nodes.getLength(); i++) { + n = nodes.item(i); + + if (n.getNodeType() == ELEMENT_NODE) { + if (n.getNodeName().equalsIgnoreCase("table")) { + + if (table.getType() == Settings.TABLE_2D) { // if table is 2D, + // parse axis + + if (RomAttributeParser + .parseTableType(unmarshallAttribute(n, "type", + "unknown")) == Settings.TABLE_Y_AXIS + || RomAttributeParser + .parseTableType(unmarshallAttribute(n, + "type", "unknown")) == Settings.TABLE_X_AXIS) { + + Table1D tempTable = (Table1D) unmarshallTable(n, + ((Table2D) table).getAxis(), rom); + if (tempTable.getDataSize() != table.getDataSize()) { + tempTable.setDataSize(table.getDataSize()); + } + tempTable.setData(((Table2D) table).getAxis() + .getData()); + ((Table2D) table).setAxis(tempTable); + + } + } else if (table.getType() == Settings.TABLE_3D) { // if table + // is 3D, + // populate + // xAxis + if (RomAttributeParser + .parseTableType(unmarshallAttribute(n, "type", + "unknown")) == Settings.TABLE_X_AXIS) { + + Table1D tempTable = (Table1D) unmarshallTable(n, + ((Table3D) table).getXAxis(), rom); + if (tempTable.getDataSize() != ((Table3D) table) + .getSizeX()) { + tempTable.setDataSize(((Table3D) table) + .getSizeX()); + } + tempTable.setData(((Table3D) table).getXAxis() + .getData()); + ((Table3D) table).setXAxis(tempTable); + + } else if (RomAttributeParser + .parseTableType(unmarshallAttribute(n, "type", + "unknown")) == Settings.TABLE_Y_AXIS) { + + Table1D tempTable = (Table1D) unmarshallTable(n, + ((Table3D) table).getYAxis(), rom); + if (tempTable.getDataSize() != ((Table3D) table) + .getSizeY()) { + tempTable.setDataSize(((Table3D) table) + .getSizeY()); + } + tempTable.setData(((Table3D) table).getYAxis() + .getData()); + ((Table3D) table).setYAxis(tempTable); + + } + } + + } else if (n.getNodeName().equalsIgnoreCase("scaling")) { + // check whether scale already exists. if so, modify, else + // use new instance + Scale baseScale = table.getScale(unmarshallAttribute(n,"name", "Default")); + table.addScale(unmarshallScale(n, baseScale)); + + } else if (n.getNodeName().equalsIgnoreCase("data")) { + // parse and add data to table + DataCell dataCell = new DataCell(table, unmarshallText(n)); + if(table instanceof Table1D) { + ((Table1D)table).addStaticDataCell(dataCell); + } else { + // Why would this happen. Static should only be for axis. + LOGGER.error("Error adding static data cell."); + } + + } else if (n.getNodeName().equalsIgnoreCase("description")) { + table.setDescription(unmarshallText(n)); + + } else if (n.getNodeName().equalsIgnoreCase("state")) { + ((TableSwitch) table).setValues( + unmarshallAttribute(n, "name", ""), + unmarshallAttribute(n, "data", "0.0")); + + } else { /* unexpected element in Table (skip) */ + } + } else { /* unexpected node-type in Table (skip) */ + } + } + + return table; + } + + private Scale unmarshallScale(Node scaleNode, Scale scale) { + + // look for base scale first + String base = unmarshallAttribute(scaleNode, "base", "none"); + if (!base.equalsIgnoreCase("none")) { + for (Scale scaleItem : scales) { + + // check whether name matches base and set scale if so + if (scaleItem.getName().equalsIgnoreCase(base)) { + try { + scale = (Scale) ObjectCloner.deepCopy(scaleItem); + + } catch (Exception ex) { + JOptionPane.showMessageDialog( + ECUEditorManager.getECUEditor(), + new DebugPanel(ex, SettingsManager.getSettings() + .getSupportURL()), "Exception", + JOptionPane.ERROR_MESSAGE); + } + } + } + } + + // set remaining attributes + scale.setName(unmarshallAttribute(scaleNode, "name", "Default")); + scale.setUnit(unmarshallAttribute(scaleNode, "units", scale.getUnit())); + scale.setExpression(unmarshallAttribute(scaleNode, "expression", + scale.getExpression())); + scale.setByteExpression(unmarshallAttribute(scaleNode, "to_byte", + scale.getByteExpression())); + scale.setFormat(unmarshallAttribute(scaleNode, "format", "#")); + scale.setMax(unmarshallAttribute(scaleNode, "max", 0.0)); + scale.setMin(unmarshallAttribute(scaleNode, "min", 0.0)); + + // get coarse increment with new attribute name (coarseincrement), else + // look for old (increment) + scale.setCoarseIncrement(unmarshallAttribute( + scaleNode, + "coarseincrement", + unmarshallAttribute(scaleNode, "increment", + scale.getCoarseIncrement()))); + + scale.setFineIncrement(unmarshallAttribute(scaleNode, "fineincrement", + scale.getFineIncrement())); + + return scale; + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/xml/DOMSettingsBuilder.java b/java_console/romraider/src/com/romraider/xml/DOMSettingsBuilder.java new file mode 100644 index 0000000000..46cb8d85aa --- /dev/null +++ b/java_console/romraider/src/com/romraider/xml/DOMSettingsBuilder.java @@ -0,0 +1,430 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2013 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.xml; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Collection; +import java.util.Map; +import java.util.Vector; + +import javax.imageio.metadata.IIOMetadataNode; + +import com.romraider.Settings; +//import com.romraider.logger.external.phidget.interfacekit.io.IntfKitSensor; +import com.romraider.swing.JProgressPane; +import com.sun.org.apache.xml.internal.serialize.OutputFormat; +import com.sun.org.apache.xml.internal.serialize.XMLSerializer; + +public final class DOMSettingsBuilder { + + public void buildSettings(Settings settings, File output, JProgressPane progress, String versionNumber) throws IOException { + + IIOMetadataNode settingsNode = new IIOMetadataNode("settings"); + + // create settings + progress.update("Saving window settings...", 15); + settingsNode.appendChild(buildWindow(settings)); + progress.update("Saving file settings...", 30); + settingsNode.appendChild(buildFiles(settings)); + progress.update("Saving options...", 45); + settingsNode.appendChild(buildOptions(settings, versionNumber)); + progress.update("Saving display settings...", 60); + settingsNode.appendChild(buildTableDisplay(settings)); + progress.update("Saving logger settings...", 75); + settingsNode.appendChild(buildLogger(settings)); + progress.update("Saving table clipboard format settings...", 80); + settingsNode.appendChild(buildTableClipboardFormat(settings)); + progress.update("Saving icon scale settings...", 85); + settingsNode.appendChild(buildIcons(settings)); + + OutputFormat of = new OutputFormat("XML", "ISO-8859-1", true); + of.setIndent(1); + of.setIndenting(true); + + progress.update("Writing to file...", 90); + + FileOutputStream fos = new FileOutputStream(output); + try { + XMLSerializer serializer = new XMLSerializer(fos, of); + serializer.serialize(settingsNode); + fos.flush(); + } finally { + fos.close(); + } + } + + private IIOMetadataNode buildWindow(Settings settings) { + IIOMetadataNode windowSettings = new IIOMetadataNode("window"); + + // window maximized + IIOMetadataNode maximized = new IIOMetadataNode("maximized"); + maximized.setAttribute("value", String.valueOf((settings.isWindowMaximized()))); + windowSettings.appendChild(maximized); + + // window size + IIOMetadataNode size = new IIOMetadataNode("size"); + size.setAttribute("x", String.valueOf(((int) settings.getWindowSize().getHeight()))); + size.setAttribute("y", String.valueOf(((int) settings.getWindowSize().getWidth()))); + windowSettings.appendChild(size); + + // window location + IIOMetadataNode location = new IIOMetadataNode("location"); + location.setAttribute("x", String.valueOf(((int) settings.getWindowLocation().getX()))); + location.setAttribute("y", String.valueOf(((int) settings.getWindowLocation().getY()))); + windowSettings.appendChild(location); + + // splitpane location + IIOMetadataNode splitpane = new IIOMetadataNode("splitpane"); + splitpane.setAttribute("location", String.valueOf(settings.getSplitPaneLocation())); + windowSettings.appendChild(splitpane); + + return windowSettings; + } + + private IIOMetadataNode buildFiles(Settings settings) { + IIOMetadataNode files = new IIOMetadataNode("files"); + + // image directory + IIOMetadataNode imageDir = new IIOMetadataNode("image_dir"); + imageDir.setAttribute("path", settings.getLastImageDir().getAbsolutePath()); + files.appendChild(imageDir); + + // repository directory + IIOMetadataNode repositoryDir = new IIOMetadataNode(Settings.REPOSITORY_ELEMENT_NAME); + repositoryDir.setAttribute(Settings.REPOSITORY_ATTRIBUTE_NAME, settings.getLastRepositoryDir().getAbsolutePath()); + files.appendChild(repositoryDir); + + // ecu definition files + Vector defFiles = settings.getEcuDefinitionFiles(); + + for (File defFile : defFiles) { + IIOMetadataNode ecuDef = new IIOMetadataNode("ecudefinitionfile"); + ecuDef.setAttribute("name", defFile.getAbsolutePath()); + files.appendChild(ecuDef); + } + + return files; + } + + private IIOMetadataNode buildOptions(Settings settings, String versionNumber) { + IIOMetadataNode options = new IIOMetadataNode("options"); + + // obsolete warning + IIOMetadataNode obsoleteWarning = new IIOMetadataNode("obsoletewarning"); + obsoleteWarning.setAttribute("value", String.valueOf(settings.isObsoleteWarning())); + options.appendChild(obsoleteWarning); + + // calcultion conflicting warning + IIOMetadataNode calcConflictWarning = new IIOMetadataNode("calcconflictwarning"); + calcConflictWarning.setAttribute("value", String.valueOf(settings.isCalcConflictWarning())); + options.appendChild(calcConflictWarning); + + // debug mode + IIOMetadataNode debug = new IIOMetadataNode("debug"); + debug.setAttribute("value", String.valueOf(settings.isDebug())); + options.appendChild(debug); + + // userlevel + IIOMetadataNode userLevel = new IIOMetadataNode("userlevel"); + userLevel.setAttribute("value", String.valueOf(settings.getUserLevel())); + options.appendChild(userLevel); + + // table click count + IIOMetadataNode tableClickCount = new IIOMetadataNode("tableclickcount"); + tableClickCount.setAttribute("value", String.valueOf(settings.getTableClickCount())); + options.appendChild(tableClickCount); + + // table click behavior + IIOMetadataNode tableClickBehavior = new IIOMetadataNode("tableclickbehavior"); + tableClickBehavior.setAttribute("value", String.valueOf(settings.getTableClickBehavior())); + options.appendChild(tableClickBehavior); + + // last version used + IIOMetadataNode version = new IIOMetadataNode("version"); + version.setAttribute("value", versionNumber); + options.appendChild(version); + + // save debug level tables + IIOMetadataNode saveDebugTables = new IIOMetadataNode("savedebugtables"); + saveDebugTables.setAttribute("value", String.valueOf(settings.isSaveDebugTables())); + options.appendChild(saveDebugTables); + + // display tables higher than userlevel + IIOMetadataNode displayHighTables = new IIOMetadataNode("displayhightables"); + displayHighTables.setAttribute("value", String.valueOf(settings.isDisplayHighTables())); + options.appendChild(displayHighTables); + + // warning when exceeding limits + IIOMetadataNode valueLimitWarning = new IIOMetadataNode("valuelimitwarning"); + valueLimitWarning.setAttribute("value", String.valueOf(settings.isValueLimitWarning())); + options.appendChild(valueLimitWarning); + + // color axis + IIOMetadataNode colorAxis = new IIOMetadataNode("coloraxis"); + colorAxis.setAttribute("value", String.valueOf(settings.isColorAxis())); + options.appendChild(colorAxis); + + // show table toolbar border + IIOMetadataNode showTableToolbarBorder = new IIOMetadataNode("showtabletoolbarborder"); + showTableToolbarBorder.setAttribute("value", String.valueOf(settings.isShowTableToolbarBorder())); + options.appendChild(showTableToolbarBorder); + + // open top level rom node expanded + IIOMetadataNode openRomExpanded = new IIOMetadataNode("openromexpanded"); + openRomExpanded.setAttribute("value", String.valueOf(settings.isOpenExpanded())); + options.appendChild(openRomExpanded); + + // always open at 0 + IIOMetadataNode alwaysOpenTableAtZero = new IIOMetadataNode("alwaysopentableatzero"); + alwaysOpenTableAtZero.setAttribute("value", String.valueOf(settings.isAlwaysOpenTableAtZero())); + options.appendChild(alwaysOpenTableAtZero); + + // defaultScale + IIOMetadataNode defaultScale = new IIOMetadataNode("defaultscale"); + defaultScale.setAttribute("value", String.valueOf(settings.getDefaultScale())); + options.appendChild(defaultScale); + + // scale headers with data + IIOMetadataNode scaleHeaderAndData = new IIOMetadataNode("scaleHeadersAndData"); + scaleHeaderAndData.setAttribute("value", String.valueOf(settings.isScaleHeadersAndData())); + options.appendChild(scaleHeaderAndData); + + return options; + } + + private IIOMetadataNode buildTableDisplay(Settings settings) { + IIOMetadataNode tableDisplay = new IIOMetadataNode("tabledisplay"); + + // font + IIOMetadataNode font = new IIOMetadataNode("font"); + font.setAttribute("face", settings.getTableFont().getName()); + font.setAttribute("size", String.valueOf(settings.getTableFont().getSize())); + font.setAttribute("decoration", String.valueOf(settings.getTableFont().getStyle())); + tableDisplay.appendChild(font); + + // table cell size + IIOMetadataNode cellSize = new IIOMetadataNode("cellsize"); + cellSize.setAttribute("height", String.valueOf((int) settings.getCellSize().getHeight())); + cellSize.setAttribute("width", String.valueOf(((int) settings.getCellSize().getWidth()))); + tableDisplay.appendChild(cellSize); + + // colors + IIOMetadataNode colors = new IIOMetadataNode("colors"); + // max + IIOMetadataNode max = new IIOMetadataNode("max"); + max.setAttribute("r", String.valueOf(settings.getMaxColor().getRed())); + max.setAttribute("g", String.valueOf(settings.getMaxColor().getGreen())); + max.setAttribute("b", String.valueOf(settings.getMaxColor().getBlue())); + colors.appendChild(max); + // min + IIOMetadataNode min = new IIOMetadataNode("min"); + min.setAttribute("r", String.valueOf(settings.getMinColor().getRed())); + min.setAttribute("g", String.valueOf(settings.getMinColor().getGreen())); + min.setAttribute("b", String.valueOf(settings.getMinColor().getBlue())); + colors.appendChild(min); + // highlight + IIOMetadataNode highlight = new IIOMetadataNode("highlight"); + highlight.setAttribute("r", String.valueOf(settings.getHighlightColor().getRed())); + highlight.setAttribute("g", String.valueOf(settings.getHighlightColor().getGreen())); + highlight.setAttribute("b", String.valueOf(settings.getHighlightColor().getBlue())); + colors.appendChild(highlight); + // select + IIOMetadataNode select = new IIOMetadataNode("select"); + select.setAttribute("r", String.valueOf(settings.getSelectColor().getRed())); + select.setAttribute("g", String.valueOf(settings.getSelectColor().getGreen())); + select.setAttribute("b", String.valueOf(settings.getSelectColor().getBlue())); + colors.appendChild(select); + // increased cell border + IIOMetadataNode increaseBorder = new IIOMetadataNode("increaseborder"); + increaseBorder.setAttribute("r", String.valueOf(settings.getIncreaseBorder().getRed())); + increaseBorder.setAttribute("g", String.valueOf(settings.getIncreaseBorder().getGreen())); + increaseBorder.setAttribute("b", String.valueOf(settings.getIncreaseBorder().getBlue())); + colors.appendChild(increaseBorder); + // decreased cell border + IIOMetadataNode decreaseBorder = new IIOMetadataNode("decreaseborder"); + decreaseBorder.setAttribute("r", String.valueOf(settings.getDecreaseBorder().getRed())); + decreaseBorder.setAttribute("g", String.valueOf(settings.getDecreaseBorder().getGreen())); + decreaseBorder.setAttribute("b", String.valueOf(settings.getDecreaseBorder().getBlue())); + colors.appendChild(decreaseBorder); + // axis cells + IIOMetadataNode axis = new IIOMetadataNode("axis"); + axis.setAttribute("r", String.valueOf(settings.getAxisColor().getRed())); + axis.setAttribute("g", String.valueOf(settings.getAxisColor().getGreen())); + axis.setAttribute("b", String.valueOf(settings.getAxisColor().getBlue())); + colors.appendChild(axis); + // warning cells + IIOMetadataNode warning = new IIOMetadataNode("warning"); + warning.setAttribute("r", String.valueOf(settings.getWarningColor().getRed())); + warning.setAttribute("g", String.valueOf(settings.getWarningColor().getGreen())); + warning.setAttribute("b", String.valueOf(settings.getWarningColor().getBlue())); + colors.appendChild(warning); + + tableDisplay.appendChild(colors); + + return tableDisplay; + } + + private IIOMetadataNode buildLogger(Settings settings) { + IIOMetadataNode loggerSettings = new IIOMetadataNode("logger"); + loggerSettings.setAttribute("locale", settings.getLocale()); + + // serial connection + IIOMetadataNode serial = new IIOMetadataNode("serial"); + serial.setAttribute("port", settings.getLoggerPortDefault()); + serial.setAttribute("refresh", String.valueOf(settings.getRefreshMode())); + serial.setAttribute("ecuid", String.valueOf(settings.getDestinationId())); + serial.setAttribute("fastpoll", String.valueOf(settings.isFastPoll())); + loggerSettings.appendChild(serial); + + // Protocol connection + IIOMetadataNode protocol = new IIOMetadataNode("protocol"); + protocol.setAttribute("name", settings.getLoggerProtocol()); + protocol.setAttribute("transport", settings.getTransportProtocol()); + protocol.setAttribute("library", settings.getJ2534Device()); + loggerSettings.appendChild(protocol); + + // window maximized + IIOMetadataNode maximized = new IIOMetadataNode("maximized"); + maximized.setAttribute("value", String.valueOf((settings.isLoggerWindowMaximized()))); + loggerSettings.appendChild(maximized); + + // window size + IIOMetadataNode size = new IIOMetadataNode("size"); + size.setAttribute("x", String.valueOf(((int) settings.getLoggerWindowSize().getHeight()))); + size.setAttribute("y", String.valueOf(((int) settings.getLoggerWindowSize().getWidth()))); + size.setAttribute("divider", String.valueOf(((int) settings.getDividerLocation()))); + loggerSettings.appendChild(size); + + // window location + IIOMetadataNode location = new IIOMetadataNode("location"); + location.setAttribute("x", String.valueOf(((int) settings.getLoggerWindowLocation().getX()))); + location.setAttribute("y", String.valueOf(((int) settings.getLoggerWindowLocation().getY()))); + loggerSettings.appendChild(location); + + // last tab index + IIOMetadataNode tabs = new IIOMetadataNode("tabs"); + tabs.setAttribute("selected", String.valueOf(settings.getLoggerSelectedTabIndex())); + tabs.setAttribute("showlist", String.valueOf(settings.getLoggerParameterListState())); + loggerSettings.appendChild(tabs); + + // definition path + IIOMetadataNode definition = new IIOMetadataNode("definition"); + definition.setAttribute("path", settings.getLoggerDefinitionFilePath()); + loggerSettings.appendChild(definition); + + // profile path + IIOMetadataNode profile = new IIOMetadataNode("profile"); + profile.setAttribute("path", settings.getLoggerProfileFilePath()); + loggerSettings.appendChild(profile); + + // file logging + IIOMetadataNode filelogging = new IIOMetadataNode("filelogging"); + filelogging.setAttribute("path", settings.getLoggerOutputDirPath()); + filelogging.setAttribute("switchid", settings.getFileLoggingControllerSwitchId()); + filelogging.setAttribute("active", String.valueOf(settings.isFileLoggingControllerSwitchActive())); + filelogging.setAttribute("absolutetimestamp", String.valueOf(settings.isFileLoggingAbsoluteTimestamp())); + loggerSettings.appendChild(filelogging); + + // debug level + IIOMetadataNode debug = new IIOMetadataNode("debug"); + debug.setAttribute("level", settings.getLoggerDebuggingLevel()); + loggerSettings.appendChild(debug); + + // plugin ports + Map pluginPorts = settings.getLoggerPluginPorts(); + if (pluginPorts != null && !pluginPorts.isEmpty()) { + IIOMetadataNode plugins = new IIOMetadataNode("plugins"); + for (Map.Entry entry : pluginPorts.entrySet()) { + IIOMetadataNode plugin = new IIOMetadataNode("plugin"); + plugin.setAttribute("id", entry.getKey()); + plugin.setAttribute("port", entry.getValue()); + plugins.appendChild(plugin); + } +// final Map phidgets = settings.getPhidgetSensors(); +// if (phidgets != null && !phidgets.isEmpty()) { +// final Collection sensors = phidgets.values(); +// IIOMetadataNode phidgetsNode = new IIOMetadataNode("phidgets"); +// for (IntfKitSensor entry : sensors) { +// IIOMetadataNode phidgetNode = new IIOMetadataNode("phidget"); +// phidgetNode.setAttribute("name", entry.getInputName()); +// phidgetNode.setAttribute("number", String.valueOf(entry.getInputNumber())); +// phidgetNode.setAttribute("units", entry.getUnits()); +// phidgetNode.setAttribute("expression", entry.getExpression()); +// phidgetNode.setAttribute("format", entry.getFormat()); +// phidgetNode.setAttribute("min", String.valueOf(entry.getMinValue())); +// phidgetNode.setAttribute("max", String.valueOf(entry.getMaxValue())); +// phidgetNode.setAttribute("step", String.valueOf(entry.getStepValue())); +// phidgetsNode.appendChild(phidgetNode); +// } +// plugins.appendChild(phidgetsNode); +// } +// loggerSettings.appendChild(plugins); + } + + return loggerSettings; + } + + private IIOMetadataNode buildTableClipboardFormat(Settings settings) { + // Head Node + IIOMetadataNode tableClipboardFormatSetting = new IIOMetadataNode(Settings.TABLE_CLIPBOARD_FORMAT_ELEMENT); + tableClipboardFormatSetting.setAttribute(Settings.TABLE_CLIPBOARD_FORMAT_ATTRIBUTE, settings.getTableClipboardFormat()); + + // Table Child + IIOMetadataNode tableFormatSetting = new IIOMetadataNode(Settings.TABLE_ELEMENT); + // Table1D Child + IIOMetadataNode table1DFormatSetting = new IIOMetadataNode(Settings.TABLE1D_ELEMENT); + // Table2D Child + IIOMetadataNode table2DFormatSetting = new IIOMetadataNode(Settings.TABLE2D_ELEMENT); + // Table3D Child + IIOMetadataNode table3DFormatSetting = new IIOMetadataNode(Settings.TABLE3D_ELEMENT); + + tableFormatSetting.setAttribute(Settings.TABLE_HEADER_ATTRIBUTE, settings.getTableHeader()); + table1DFormatSetting.setAttribute(Settings.TABLE_HEADER_ATTRIBUTE, settings.getTable1DHeader()); + table2DFormatSetting.setAttribute(Settings.TABLE_HEADER_ATTRIBUTE, settings.getTable2DHeader()); + table3DFormatSetting.setAttribute(Settings.TABLE_HEADER_ATTRIBUTE, settings.getTable3DHeader()); + + tableClipboardFormatSetting.appendChild(tableFormatSetting); + tableClipboardFormatSetting.appendChild(table1DFormatSetting); + tableClipboardFormatSetting.appendChild(table2DFormatSetting); + tableClipboardFormatSetting.appendChild(table3DFormatSetting); + + return tableClipboardFormatSetting; + } + + private IIOMetadataNode buildIcons(Settings settings) { + // Head Node + IIOMetadataNode iconsSettings = new IIOMetadataNode(Settings.ICONS_ELEMENT_NAME); + + // Editor Icons Child + IIOMetadataNode editorIconsScaleSettings = new IIOMetadataNode(Settings.EDITOR_ICONS_ELEMENT_NAME); + editorIconsScaleSettings.setAttribute(Settings.EDITOR_ICONS_SCALE_ATTRIBUTE_NAME, String.valueOf(settings.getEditorIconScale())); + + // Table Icons Child + IIOMetadataNode tableIconsScaleSettings = new IIOMetadataNode(Settings.TABLE_ICONS_ELEMENT_NAME); + tableIconsScaleSettings.setAttribute(Settings.TABLE_ICONS_SCALE_ATTRIBUTE_NAME, String.valueOf(settings.getTableIconScale())); + + iconsSettings.appendChild(editorIconsScaleSettings); + iconsSettings.appendChild(tableIconsScaleSettings); + + return iconsSettings; + } +} diff --git a/java_console/romraider/src/com/romraider/xml/DOMSettingsUnmarshaller.java b/java_console/romraider/src/com/romraider/xml/DOMSettingsUnmarshaller.java new file mode 100644 index 0000000000..6a01918cbb --- /dev/null +++ b/java_console/romraider/src/com/romraider/xml/DOMSettingsUnmarshaller.java @@ -0,0 +1,402 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.xml; + +import static com.romraider.xml.DOMHelper.unmarshallAttribute; +import static java.awt.Font.BOLD; +import static org.w3c.dom.Node.ELEMENT_NODE; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Point; +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import com.romraider.Settings; + +public final class DOMSettingsUnmarshaller { + + public Settings unmarshallSettings(Node rootNode) { + Settings settings = new Settings(); + Node n; + NodeList nodes = rootNode.getChildNodes(); + + for (int i = 0; i < nodes.getLength(); i++) { + n = nodes.item(i); + + if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("window")) { + settings = unmarshallWindow(n, settings); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("files")) { + settings = unmarshallFiles(n, settings); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("options")) { + settings = unmarshallOptions(n, settings); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("tabledisplay")) { + settings = unmarshallTableDisplay(n, settings); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("logger")) { + settings = unmarshallLogger(n, settings); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase(Settings.TABLE_CLIPBOARD_FORMAT_ELEMENT)) { + settings = this.unmarshallClipboardFormat(n, settings); + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase(Settings.ICONS_ELEMENT_NAME)) { + settings = this.unmarshallIcons(n, settings); + } + } + return settings; + } + + + private Settings unmarshallWindow(Node windowNode, Settings settings) { + Node n; + NodeList nodes = windowNode.getChildNodes(); + + for (int i = 0; i < nodes.getLength(); i++) { + n = nodes.item(i); + + if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("maximized")) { + settings.setWindowMaximized(unmarshallAttribute(n, "value", false)); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("size")) { + settings.setWindowSize(new Dimension(unmarshallAttribute(n, "y", 600), + unmarshallAttribute(n, "x", 800))); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("location")) { + // set default location in top left screen if no settings file found + settings.setWindowLocation(new Point(unmarshallAttribute(n, "x", 0), + unmarshallAttribute(n, "y", 0))); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("splitpane")) { + settings.setSplitPaneLocation(unmarshallAttribute(n, "location", 150)); + + } + } + return settings; + } + + private Settings unmarshallFiles(Node urlNode, Settings settings) { + Node n; + NodeList nodes = urlNode.getChildNodes(); + + for (int i = 0; i < nodes.getLength(); i++) { + n = nodes.item(i); + + if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("ecudefinitionfile")) { + settings.addEcuDefinitionFile(new File(unmarshallAttribute(n, "name", "ecu_defs.xml"))); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("image_dir")) { + settings.setLastImageDir(new File(unmarshallAttribute(n, "path", "ecu_defs.xml"))); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase(Settings.REPOSITORY_ELEMENT_NAME)) { + settings.setLastRepositoryDir(new File(unmarshallAttribute(n, Settings.REPOSITORY_ATTRIBUTE_NAME, "repositories"))); + + } + } + return settings; + } + + private Settings unmarshallOptions(Node optionNode, Settings settings) { + Node n; + NodeList nodes = optionNode.getChildNodes(); + + for (int i = 0; i < nodes.getLength(); i++) { + n = nodes.item(i); + + if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("obsoletewarning")) { + settings.setObsoleteWarning(Boolean.parseBoolean(unmarshallAttribute(n, "value", "true"))); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("debug")) { + settings.setDebug(Boolean.parseBoolean(unmarshallAttribute(n, "value", "true"))); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("calcconflictwarning")) { + settings.setCalcConflictWarning(Boolean.parseBoolean(unmarshallAttribute(n, "value", "true"))); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("userlevel")) { + settings.setUserLevel(unmarshallAttribute(n, "value", 1)); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("tableclickcount")) { + settings.setTableClickCount(unmarshallAttribute(n, "value", 2)); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("tableclickbehavior")) { + settings.setTableClickBehavior(unmarshallAttribute(n, "value", 0)); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("version")) { + settings.setRecentVersion(unmarshallAttribute(n, "value", "")); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("savedebugtables")) { + settings.setSaveDebugTables(Boolean.parseBoolean(unmarshallAttribute(n, "value", "false"))); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("displayhightables")) { + settings.setDisplayHighTables(Boolean.parseBoolean(unmarshallAttribute(n, "value", "false"))); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("valuelimitwarning")) { + settings.setValueLimitWarning(Boolean.parseBoolean(unmarshallAttribute(n, "value", "true"))); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("coloraxis")) { + settings.setColorAxis(Boolean.parseBoolean(unmarshallAttribute(n, "value", "false"))); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("showtabletoolbarborder")) { + settings.setShowTableToolbarBorder(Boolean.parseBoolean(unmarshallAttribute(n, "value", "false"))); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("openromexpanded")) { + settings.setOpenExpanded(Boolean.parseBoolean(unmarshallAttribute(n, "value", "true"))); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("alwaysopentableatzero")) { + settings.setAlwaysOpenTableAtZero(Boolean.parseBoolean(unmarshallAttribute(n, "value", "false"))); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("defaultscale")) { + settings.setDefaultScale(unmarshallAttribute(n, "value", "Metric")); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("scaleHeadersAndData")) { + settings.setScaleHeadersAndData(Boolean.parseBoolean(unmarshallAttribute(n, "value", "true"))); + + } + } + return settings; + } + + private Settings unmarshallTableDisplay(Node tableDisplayNode, Settings settings) { + Node n; + NodeList nodes = tableDisplayNode.getChildNodes(); + + for (int i = 0; i < nodes.getLength(); i++) { + n = nodes.item(i); + + if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("font")) { + settings.setTableFont(new Font(unmarshallAttribute(n, "face", "Arial"), + unmarshallAttribute(n, "decoration", BOLD), + unmarshallAttribute(n, "size", 12))); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("cellsize")) { + settings.setCellSize(new Dimension(unmarshallAttribute(n, "width", 42), + unmarshallAttribute(n, "height", 18))); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("colors")) { + settings = unmarshallColors(n, settings); + + } + } + return settings; + } + + private Settings unmarshallColors(Node colorNode, Settings settings) { + Node n; + NodeList nodes = colorNode.getChildNodes(); + + for (int i = 0; i < nodes.getLength(); i++) { + n = nodes.item(i); + + if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("max")) { + settings.setMaxColor(unmarshallColor(n)); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("min")) { + settings.setMinColor(unmarshallColor(n)); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("highlight")) { + settings.setHighlightColor(unmarshallColor(n)); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("select")) { + settings.setSelectColor(unmarshallColor(n)); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("increaseborder")) { + settings.setIncreaseBorder(unmarshallColor(n)); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("decreaseborder")) { + settings.setDecreaseBorder(unmarshallColor(n)); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("axis")) { + settings.setAxisColor(unmarshallColor(n)); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("warning")) { + settings.setWarningColor(unmarshallColor(n)); + + } + } + return settings; + } + + + private Settings unmarshallLogger(Node loggerNode, Settings settings) { + NodeList nodes = loggerNode.getChildNodes(); + if (loggerNode.getNodeType() == ELEMENT_NODE && loggerNode.getNodeName().equalsIgnoreCase("logger")) { + settings.setLocale(unmarshallAttribute(loggerNode, "locale", "system")); + } + + for (int i = 0; i < nodes.getLength(); i++) { + Node n = nodes.item(i); + + if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("serial")) { + settings.setLoggerPortDefault(unmarshallAttribute(n, "port", "")); + settings.setRefreshMode(unmarshallAttribute(n, "refresh", false)); + settings.setDestinationId((byte) unmarshallAttribute(n, "ecuid", (byte) 0x10)); + settings.setFastPoll(unmarshallAttribute(n, "fastpoll", true)); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("protocol")) { + settings.setLoggerProtocol(unmarshallAttribute(n, "name", "SSM")); + settings.setTransportProtocol(unmarshallAttribute(n, "transport", "ISO9141")); + settings.setJ2534Device(unmarshallAttribute(n, "library", null)); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("maximized")) { + settings.setLoggerWindowMaximized(unmarshallAttribute(n, "value", false)); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("size")) { + settings.setLoggerWindowSize(new Dimension(unmarshallAttribute(n, "y", 600), + unmarshallAttribute(n, "x", 1000))); + settings.setLoggerDividerLocation(unmarshallAttribute(n, "divider", 500)); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("location")) { + settings.setLoggerWindowLocation(new Point(unmarshallAttribute(n, "x", 150), + unmarshallAttribute(n, "y", 150))); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("tabs")) { + settings.setLoggerSelectedTabIndex(unmarshallAttribute(n, "selected", 0)); + settings.setLoggerParameterListState(unmarshallAttribute(n, "showlist", true)); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("definition")) { + settings.setLoggerDefinitionFilePath(unmarshallAttribute(n, "path", settings.getLoggerDefinitionFilePath())); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("profile")) { + settings.setLoggerProfileFilePath(unmarshallAttribute(n, "path", "")); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("filelogging")) { + settings.setLoggerOutputDirPath(unmarshallAttribute(n, "path", "")); + settings.setFileLoggingControllerSwitchId(unmarshallAttribute(n, "switchid", settings.getFileLoggingControllerSwitchId())); + settings.setFileLoggingControllerSwitchActive(unmarshallAttribute(n, "active", true)); + settings.setFileLoggingAbsoluteTimestamp(unmarshallAttribute(n, "absolutetimestamp", false)); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("debug")) { + settings.setLoggerDebuggingLevel(unmarshallAttribute(n, "level", "info")); + + } else if (n.getNodeType() == ELEMENT_NODE && n.getNodeName().equalsIgnoreCase("plugins")) { + Map pluginPorts = new HashMap(); + NodeList pluginNodes = n.getChildNodes(); + for (int j = 0; j < pluginNodes.getLength(); j++) { + Node pluginNode = pluginNodes.item(j); + if (pluginNode.getNodeType() == ELEMENT_NODE && pluginNode.getNodeName().equalsIgnoreCase("plugin")) { + String id = unmarshallAttribute(pluginNode, "id", null); + if (id == null || id.trim().length() == 0) continue; + String port = unmarshallAttribute(pluginNode, "port", null); + if (port == null || port.trim().length() == 0) continue; + pluginPorts.put(id.trim(), port.trim()); + } + else if (pluginNode.getNodeType() == ELEMENT_NODE && pluginNode.getNodeName().equalsIgnoreCase("phidgets")) { +// final Map phidgets = new HashMap(); +// NodeList sensorNodes = pluginNode.getChildNodes(); +// for (int k = 0; k < sensorNodes.getLength(); k++) { +// Node sensorNode = sensorNodes.item(k); +// if (sensorNode.getNodeType() == ELEMENT_NODE && sensorNode.getNodeName().equalsIgnoreCase("phidget")) { +// final String name = unmarshallAttribute(sensorNode, "name", null); +// final int number = unmarshallAttribute(sensorNode, "number", -1); +// final String units = unmarshallAttribute(sensorNode, "units", null); +// final String expression = unmarshallAttribute(sensorNode, "expression", null); +// final String format = unmarshallAttribute(sensorNode, "format", null); +// final float min = Float.parseFloat(unmarshallAttribute(sensorNode, "min", "-1.0")); +// final float max = Float.parseFloat(unmarshallAttribute(sensorNode, "max", "-1.0")); +// final float step = Float.parseFloat(unmarshallAttribute(sensorNode, "step", "-1.0")); +// if (name != null && number != -1) { +// final String inputName = name.replaceAll("Phidget IK Sensor ", ""); +// final IntfKitSensor sensor = new IntfKitSensor(); +// sensor.setInputNumber(number); +// sensor.setInputName(name); +// sensor.setUnits(units); +// sensor.setExpression(expression); +// sensor.setFormat(format); +// sensor.setMinValue(min); +// sensor.setMaxValue(max); +// sensor.setStepValue(step); +// phidgets.put(inputName, sensor); +// } +// } +// } +// settings.setPhidgetSensors(phidgets); + } + } + settings.setLoggerPluginPorts(pluginPorts); + } + } + return settings; + } + + private Color unmarshallColor(Node colorNode) { + return new Color(unmarshallAttribute(colorNode, "r", 155), + unmarshallAttribute(colorNode, "g", 155), + unmarshallAttribute(colorNode, "b", 155)); + } + + private Settings unmarshallClipboardFormat(Node formatNode, Settings settings) { + String tableClipboardFormat = unmarshallAttribute(formatNode, Settings.TABLE_CLIPBOARD_FORMAT_ATTRIBUTE, Settings.DEFAULT_CLIPBOARD_FORMAT); + if(tableClipboardFormat.equalsIgnoreCase(Settings.CUSTOM_CLIPBOARD_FORMAT)) { + settings.setTableClipboardFormat(Settings.CUSTOM_CLIPBOARD_FORMAT); + } else if (tableClipboardFormat.equalsIgnoreCase(Settings.AIRBOYS_CLIPBOARD_FORMAT)) { + settings.setAirboysFormat(); + return settings; + } else { + settings.setDefaultFormat(); + return settings; + } + + NodeList tableFormats = formatNode.getChildNodes(); + for( int i = 0; i < tableFormats.getLength(); i++) { + Node tableNode = tableFormats.item(i); + if(tableNode.getNodeType() == ELEMENT_NODE) { + if(tableNode.getNodeName().equalsIgnoreCase(Settings.TABLE_ELEMENT)) { + settings.setTableHeader(unmarshallAttribute(tableNode, Settings.TABLE_HEADER_ATTRIBUTE, Settings.DEFAULT_TABLE_HEADER)); + } else if(tableNode.getNodeName().equalsIgnoreCase(Settings.TABLE1D_ELEMENT)) { + settings.setTable1DHeader(unmarshallAttribute(tableNode, Settings.TABLE_HEADER_ATTRIBUTE, Settings.DEFAULT_TABLE1D_HEADER)); + } else if(tableNode.getNodeName().equalsIgnoreCase(Settings.TABLE2D_ELEMENT)) { + settings.setTable2DHeader(unmarshallAttribute(tableNode, Settings.TABLE_HEADER_ATTRIBUTE, Settings.DEFAULT_TABLE2D_HEADER)); + } else if(tableNode.getNodeName().equalsIgnoreCase(Settings.TABLE3D_ELEMENT)) { + settings.setTable3DHeader(unmarshallAttribute(tableNode, Settings.TABLE_HEADER_ATTRIBUTE, Settings.DEFAULT_TABLE3D_HEADER)); + } + } + } + return settings; + } + + private Settings unmarshallIcons(Node iconsNode, Settings settings) { + NodeList iconScales = iconsNode.getChildNodes(); + for(int i = 0; i < iconScales.getLength(); i++) { + Node scaleNode = iconScales.item(i); + if(scaleNode.getNodeType() == ELEMENT_NODE) { + if(scaleNode.getNodeName().equalsIgnoreCase(Settings.EDITOR_ICONS_ELEMENT_NAME)) { + try{ + settings.setEditorIconScale(unmarshallAttribute(scaleNode, Settings.EDITOR_ICONS_SCALE_ATTRIBUTE_NAME, Settings.DEFAULT_EDITOR_ICON_SCALE)); + } catch(NumberFormatException ex) { + settings.setEditorIconScale(Settings.DEFAULT_EDITOR_ICON_SCALE); + } + } else if(scaleNode.getNodeName().equalsIgnoreCase(Settings.TABLE_ICONS_ELEMENT_NAME)) { + try{ + settings.setTableIconScale(unmarshallAttribute(scaleNode, Settings.TABLE_ICONS_SCALE_ATTRIBUTE_NAME, Settings.DEFAULT_TABLE_ICON_SCALE)); + } catch(NumberFormatException ex) { + settings.setTableIconScale(Settings.DEFAULT_TABLE_ICON_SCALE); + } + } + } + } + return settings; + } +} diff --git a/java_console/romraider/src/com/romraider/xml/InvalidTableNameException.java b/java_console/romraider/src/com/romraider/xml/InvalidTableNameException.java new file mode 100644 index 0000000000..7ba33c51be --- /dev/null +++ b/java_console/romraider/src/com/romraider/xml/InvalidTableNameException.java @@ -0,0 +1,30 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.xml; + +public final class InvalidTableNameException extends Exception { + + private static final long serialVersionUID = 5189492974906792046L; + + @Override + public String getMessage() { + return "Invalid table name."; + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/xml/RomAttributeParser.java b/java_console/romraider/src/com/romraider/xml/RomAttributeParser.java new file mode 100644 index 0000000000..b914ebd60a --- /dev/null +++ b/java_console/romraider/src/com/romraider/xml/RomAttributeParser.java @@ -0,0 +1,202 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// Parses attributes from ROM XML + +package com.romraider.xml; + +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import com.romraider.Settings; + +public final class RomAttributeParser { + + private RomAttributeParser() { + } + + public static int parseEndian(String input) { + if (input.equalsIgnoreCase("big") || input.equalsIgnoreCase(String.valueOf(Settings.ENDIAN_BIG))) { + return Settings.ENDIAN_BIG; + } + else if (input.equalsIgnoreCase("little") || input.equalsIgnoreCase(String.valueOf(Settings.ENDIAN_LITTLE))) { + return Settings.ENDIAN_LITTLE; + } + else { + return Settings.ENDIAN_LITTLE; + } + } + + public static int parseHexString(String input) { + if (input.equals("0")) { + return 0; + } + else if (input.length() > 2 && input.substring(0, 2).equalsIgnoreCase("0x")) { + return Integer.parseInt(input.substring(2), 16); + } + else { + return Integer.parseInt(input, 16); + } + } + + public static int parseStorageType(String input) { + if (input.equalsIgnoreCase("float")) { + return Settings.STORAGE_TYPE_FLOAT; + } + else if (input.startsWith("uint")) { + return Integer.parseInt(input.substring(4)) / 8; + } + else if (input.startsWith("int")) { + return Integer.parseInt(input.substring(3)) / 8; + } + else { + return Integer.parseInt(input); + } + } + + public static boolean parseStorageDataSign(String input) { + if (input.startsWith("int")) { + return true; + } + else { + return false; + } + } + + public static int parseScaleType(String input) { + if (input.equalsIgnoreCase("inverse")) { + return Settings.INVERSE; + } + else { + return Settings.LINEAR; + } + } + + public static int parseTableType(String input) { + if (input.equalsIgnoreCase("3D") || input.equalsIgnoreCase(String.valueOf(Settings.TABLE_3D))) { + return Settings.TABLE_3D; + } + else if (input.equalsIgnoreCase("2D") || input.equalsIgnoreCase(String.valueOf(Settings.TABLE_2D))) { + return Settings.TABLE_2D; + } + else if (input.equalsIgnoreCase("X Axis") || input.equalsIgnoreCase("Static X Axis") || input.equalsIgnoreCase(String.valueOf(Settings.TABLE_X_AXIS))) { + return Settings.TABLE_X_AXIS; + } + else if (input.equalsIgnoreCase("Y Axis") || input.equalsIgnoreCase("Static Y Axis") || input.equalsIgnoreCase(String.valueOf(Settings.TABLE_Y_AXIS))) { + return Settings.TABLE_Y_AXIS; + } + else { + return Settings.TABLE_1D; + } + } + + public static long parseByteValue(byte[] input, int endian, int address, int length, boolean signed) throws ArrayIndexOutOfBoundsException, IndexOutOfBoundsException { + try { + long output = 0L; + ByteBuffer bb = ByteBuffer.wrap(input, address, length); + if (endian == Settings.ENDIAN_LITTLE) { + bb.order(ByteOrder.LITTLE_ENDIAN); + } + switch (length) { + case 1: + output = bb.get(); + break; + case 2: + output = bb.getShort(); + break; + case 4: + output = bb.getInt(); + break; + } + if (!signed) { + switch (length) { + case 1: + output = output & 0xff; + break; + case 2: + output = output & 0xffff; + break; + case 4: + output = output & 0xffffffffL; + break; + } + } + return output; + } catch (IndexOutOfBoundsException ex) { + throw new IndexOutOfBoundsException(); + } + } + + public static byte[] parseIntegerValue(int input, int endian, int length) { + try { + ByteBuffer bb = ByteBuffer.allocate(length); + if (endian == Settings.ENDIAN_LITTLE) { + bb.order(ByteOrder.LITTLE_ENDIAN); + } + switch (length) { + case 1: + bb.put((byte) input); + break; + case 2: + bb.putShort((short) input); + break; + case 4: + bb.putInt(input); + break; + } + return bb.array(); + } + catch (BufferOverflowException ex) { + throw new BufferOverflowException(); + } + } + + public static int parseFileSize(String input) throws NumberFormatException { + try { + return Integer.parseInt(input); + } catch (NumberFormatException ex) { + if (input.substring(input.length() - 2).equalsIgnoreCase("kb")) { + return Integer.parseInt(input.substring(0, input.length() - 2)) * 1024; + } + else if (input.substring(input.length() - 2).equalsIgnoreCase("mb")) { + return Integer.parseInt(input.substring(0, input.length() - 2)) * 1024 * 1024; + } + throw new NumberFormatException(); + } + } + + public static byte[] floatToByte(float input, int endian) { + byte[] output = new byte[4]; + ByteBuffer bb = ByteBuffer.wrap(output, 0, 4); + if (endian == Settings.ENDIAN_LITTLE) { + bb.order(ByteOrder.BIG_ENDIAN); + } + bb.putFloat(input); + return bb.array(); + } + + public static float byteToFloat(byte[] input, int endian) { + ByteBuffer bb = ByteBuffer.wrap(input, 0, 4); + if (endian == Settings.ENDIAN_LITTLE) { + bb.order(ByteOrder.BIG_ENDIAN); + } + return bb.getFloat(); + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/xml/RomNotFoundException.java b/java_console/romraider/src/com/romraider/xml/RomNotFoundException.java new file mode 100644 index 0000000000..e595821b94 --- /dev/null +++ b/java_console/romraider/src/com/romraider/xml/RomNotFoundException.java @@ -0,0 +1,28 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.xml; + +public final class RomNotFoundException extends Exception { + + private static final long serialVersionUID = -5434546006966986885L; + + public RomNotFoundException() { + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/xml/TableIsOmittedException.java b/java_console/romraider/src/com/romraider/xml/TableIsOmittedException.java new file mode 100644 index 0000000000..7b0e6c29d8 --- /dev/null +++ b/java_console/romraider/src/com/romraider/xml/TableIsOmittedException.java @@ -0,0 +1,32 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.xml; + +public final class TableIsOmittedException extends Exception { + + private static final long serialVersionUID = -2128531751395058602L; + + public TableIsOmittedException() { + } + + public String getMessage() { + return "Table omitted."; + } +} \ No newline at end of file diff --git a/java_console/romraider/src/com/romraider/xml/TableNotFoundException.java b/java_console/romraider/src/com/romraider/xml/TableNotFoundException.java new file mode 100644 index 0000000000..c1c6c32a28 --- /dev/null +++ b/java_console/romraider/src/com/romraider/xml/TableNotFoundException.java @@ -0,0 +1,29 @@ +/* + * RomRaider Open-Source Tuning, Logging and Reflashing + * Copyright (C) 2006-2012 RomRaider.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.romraider.xml; + +public final class TableNotFoundException extends Exception { + + private static final long serialVersionUID = -7492075561444288417L; + + public String getMessage() { + return "Table not found."; + } +} \ No newline at end of file