From cc52ae5bd7a05cbe4cecb6b7cf57e1ced418e299 Mon Sep 17 00:00:00 2001 From: Denny Sheirer Date: Sun, 26 Apr 2020 11:53:43 +0000 Subject: [PATCH] #5 Playlist Editor With Radio Reference Support (#760) * WIP - Adds support for aliasing P25 Phase 2 tones.Playlist editor - adds editor support for alias actions * Playlist editor implementation and integration into application is complete. Adds JavaFX window monitoring to persist window size/location across application runs. Co-authored-by: Denny --- .idea/dictionaries/denny.xml | 1 + .idea/misc.xml | 2 +- .idea/modules.xml | 10 + build.gradle | 10 +- sdr-trunk.ipr | 131 +- .../java/io/github/dsheirer/alias/Alias.java | 556 +++++- .../dsheirer/alias/AliasController.java | 381 ---- .../io/github/dsheirer/alias/AliasEditor.java | 157 -- .../github/dsheirer/alias/AliasFactory.java | 152 +- .../io/github/dsheirer/alias/AliasList.java | 335 +++- .../io/github/dsheirer/alias/AliasModel.java | 318 ++-- .../dsheirer/alias/AliasNameEditor.java | 333 ---- .../dsheirer/alias/ComponentEditor.java | 121 -- .../alias/ComponentValidationException.java | 14 - .../dsheirer/alias/MultipleAliasEditor.java | 466 ----- .../dsheirer/alias/action/AliasAction.java | 34 + .../alias/action/AliasActionEditor.java | 332 ---- .../alias/action/AliasActionManager.java | 36 +- .../alias/action/RecurringAction.java | 7 +- .../alias/action/beep/BeepAction.java | 73 +- .../alias/action/beep/BeepActionEditor.java | 185 -- .../alias/action/clip/ClipAction.java | 31 +- .../alias/action/clip/ClipActionEditor.java | 285 --- .../alias/action/script/ScriptAction.java | 27 +- .../action/script/ScriptActionEditor.java | 281 --- .../io/github/dsheirer/alias/id/AliasID.java | 95 +- .../github/dsheirer/alias/id/AliasIDType.java | 10 +- .../alias/id/AliasIdentifierEditor.java | 370 ---- .../github/dsheirer/alias/id/WildcardID.java | 120 -- .../alias/id/broadcast/BroadcastChannel.java | 49 +- .../id/broadcast/BroadcastChannelEditor.java | 129 -- .../dsheirer/alias/id/esn/ESNEditor.java | 113 -- .../io/github/dsheirer/alias/id/esn/Esn.java | 7 + .../id/legacy/fleetsync/FleetsyncID.java | 6 + .../legacy/fleetsync/FleetsyncIDEditor.java | 137 -- .../alias/id/legacy/mdc/MDC1200ID.java | 6 + .../alias/id/legacy/mdc/MDC1200IDEditor.java | 133 -- .../alias/id/legacy/mobileID/MINEditor.java | 127 -- .../alias/id/legacy/mobileID/Min.java | 6 + .../alias/id/legacy/mpt1327/MPT1327ID.java | 6 + .../id/legacy/mpt1327/MPT1327IDEditor.java | 135 -- .../legacy/nonrecordable/NonRecordable.java | 6 + .../nonrecordable/NonRecordableEditor.java | 78 - .../alias/id/legacy/siteID/SiteID.java | 6 + .../alias/id/legacy/siteID/SiteIDEditor.java | 116 -- .../legacy/talkgroup/LegacyTalkgroupID.java | 6 + .../talkgroup/LegacyTalkgroupIDEditor.java | 121 -- .../alias/id/legacy/uniqueID/UniqueID.java | 6 + .../id/legacy/uniqueID/UniqueIDEditor.java | 123 -- .../alias/id/lojack/LoJackFunctionAndID.java | 8 + .../alias/id/lojack/LoJackIDEditor.java | 160 -- .../dsheirer/alias/id/priority/Priority.java | 12 + .../alias/id/priority/PriorityEditor.java | 163 -- .../github/dsheirer/alias/id/radio/Radio.java | 29 +- .../dsheirer/alias/id/radio/RadioEditor.java | 235 --- .../dsheirer/alias/id/radio/RadioFormat.java | 4 + .../alias/id/radio/RadioFormatter.java | 27 +- .../dsheirer/alias/id/radio/RadioRange.java | 46 +- .../alias/id/radio/RadioRangeEditor.java | 273 --- .../dsheirer/alias/id/record/Record.java | 6 + .../alias/id/record/RecordEditor.java | 73 - .../dsheirer/alias/id/status/StatusID.java | 75 - .../alias/id/status/StatusIDEditor.java | 140 -- .../alias/id/status/UnitStatusID.java | 88 + .../alias/id/status/UserStatusID.java | 88 + .../alias/id/talkgroup/Talkgroup.java | 44 +- .../alias/id/talkgroup/TalkgroupEditor.java | 233 --- .../alias/id/talkgroup/TalkgroupFormat.java | 5 +- .../id/talkgroup/TalkgroupFormatter.java | 28 +- .../alias/id/talkgroup/TalkgroupRange.java | 45 +- .../id/talkgroup/TalkgroupRangeEditor.java | 271 --- .../dsheirer/alias/id/tone/TonesID.java | 150 ++ .../dsheirer/audio/AbstractAudioModule.java | 11 +- .../audio/broadcast/AudioBroadcaster.java | 24 +- .../broadcast/BroadcastConfiguration.java | 185 +- .../BroadcastConfigurationEditor.java | 126 -- .../audio/broadcast/BroadcastFactory.java | 51 - .../audio/broadcast/BroadcastModel.java | 233 ++- .../audio/broadcast/BroadcastPanel.java | 270 --- .../audio/broadcast/ConfiguredBroadcast.java | 142 ++ .../BroadcastifyConfiguration.java | 59 +- .../BroadcastifyConfigurationEditor.java | 408 ----- .../broadcastify/UserFeedSelectionDialog.java | 343 ---- .../IcecastHTTPConfigurationEditor.java | 383 ---- .../IcecastTCPConfigurationEditor.java | 367 ---- .../v1/ShoutcastV1ConfigurationEditor.java | 351 ---- .../v2/ShoutcastV2Configuration.java | 35 +- .../v2/ShoutcastV2ConfigurationEditor.java | 395 ---- .../audio/playback/AudioChannelPanel.java | 2 +- .../dsheirer/audio/playback/AudioPanel.java | 6 +- .../metadata/ChannelMetadataModel.java | 2 +- .../metadata/ChannelMetadataPanel.java | 8 +- .../channel/metadata/NowPlayingPanel.java | 13 +- .../dsheirer/controller/ControllerPanel.java | 78 +- .../dsheirer/controller/channel/Channel.java | 219 ++- .../controller/channel/ChannelController.java | 306 ---- .../controller/channel/ChannelEditor.java | 262 --- .../controller/channel/ChannelEvent.java | 16 + .../controller/channel/ChannelException.java | 36 + .../controller/channel/ChannelModel.java | 278 ++- .../channel/ChannelProcessingManager.java | 117 +- .../channel/ChannelSelectionManager.java | 5 +- .../channel/NameConfigurationEditor.java | 344 ---- .../controller/channel/map/ChannelMap.java | 109 +- .../channel/map/ChannelMapEditor.java | 322 ---- .../channel/map/ChannelMapManager.java | 128 -- .../channel/map/ChannelMapManagerFrame.java | 60 - .../channel/map/ChannelMapModel.java | 47 +- .../controller/channel/map/ChannelRange.java | 162 +- .../channel/map/ChannelRangeModel.java | 46 +- .../dsheirer/gui/JavaFxWindowManager.java | 380 +++- .../java/io/github/dsheirer/gui/SDRTrunk.java | 189 +- .../dsheirer/gui/control/HexFormatter.java | 139 ++ .../gui/control/IntegerFormatter.java | 108 ++ .../gui/control/IntegerTextField.java | 84 + .../dsheirer/gui/control/LtrFormatter.java | 230 +++ .../gui/control/MaxLengthUnaryOperator.java | 45 + .../gui/control/MultipleFrequencyEditor.java | 499 ----- .../gui/control/PrefixIdentFormatter.java | 216 +++ .../gui/editor/EditorValidationException.java | 25 - .../gui/editor/EmptyValidatingEditor.java | 36 - .../dsheirer/gui/editor/ValidatingEditor.java | 15 - .../gui/instrument/PlaybackController.java | 2 - .../github/dsheirer/gui/playlist/Editor.java | 70 + .../dsheirer/gui/playlist/PlaylistEditor.java | 276 +++ .../playlist/PlaylistEditorApplication.java | 90 + .../gui/playlist/PlaylistEditorRequest.java | 30 + .../gui/playlist/ViewPlaylistRequest.java | 32 + .../alias/AliasConfigurationEditor.java | 811 ++++++++ .../gui/playlist/alias/AliasEditor.java | 143 ++ .../gui/playlist/alias/AliasItemEditor.java | 1627 +++++++++++++++++ .../gui/playlist/alias/AliasTabRequest.java | 31 + .../alias/AliasViewByIdentifierEditor.java | 314 ++++ .../alias/AliasViewByRecordingEditor.java | 510 ++++++ .../gui/playlist/alias/ColorUtil.java | 97 + .../alias/ViewAliasIdentifierRequest.java | 44 + .../gui/playlist/alias/ViewAliasRequest.java | 44 + .../playlist/alias/action/ActionEditor.java | 31 + .../alias/action/ActionEditorFactory.java | 48 + .../gui/playlist/alias/action/BeepEditor.java | 313 ++++ .../gui/playlist/alias/action/ClipEditor.java | 416 +++++ .../alias/action/EmptyActionEditor.java} | 47 +- .../playlist/alias/action/ScriptEditor.java | 416 +++++ .../playlist/alias/action/TestMessage.java | 69 + .../action/UnrecognizedActionEditor.java | 47 + .../identifier/EmptyIdentifierEditor.java | 46 + .../playlist/alias/identifier/EsnEditor.java | 99 + .../alias/identifier/IdentifierEditor.java | 31 + .../identifier/IdentifierEditorFactory.java | 60 + .../alias/identifier/LojackEditor.java | 142 ++ .../alias/identifier/RadioIdEditor.java | 288 +++ .../alias/identifier/RadioIdRangeEditor.java | 350 ++++ .../alias/identifier/TalkgroupEditor.java | 300 +++ .../identifier/TalkgroupRangeEditor.java | 364 ++++ .../alias/identifier/TonesEditor.java | 383 ++++ .../alias/identifier/UnitStatusEditor.java | 140 ++ .../UnrecognizedIdentifierEditor.java | 49 + .../alias/identifier/UserStatusEditor.java | 133 ++ .../channel/AMConfigurationEditor.java | 277 +++ .../channel/ChannelConfigurationEditor.java | 687 +++++++ .../ChannelConfigurationEditorFactory.java | 76 + .../gui/playlist/channel/ChannelEditor.java | 715 ++++++++ .../playlist/channel/ChannelTabRequest.java | 31 + .../channel/LTRConfigurationEditor.java | 414 +++++ .../channel/LTRNetConfigurationEditor.java | 415 +++++ .../channel/MPT1327ConfigurationEditor.java | 448 +++++ .../channel/NBFMConfigurationEditor.java | 434 +++++ .../channel/P25P1ConfigurationEditor.java | 429 +++++ .../channel/P25P2ConfigurationEditor.java | 411 +++++ .../channel/PassportConfigurationEditor.java | 296 +++ .../channel/UnknownConfigurationEditor.java | 123 ++ .../playlist/channel/ViewChannelRequest.java | 44 + .../playlist/channelMap/ChannelMapEditor.java | 821 +++++++++ .../ChannelMapEditorViewRequest.java | 54 + .../AuxDecoderConfigurationEditor.java | 146 ++ .../eventlog/EventLogConfigurationEditor.java | 142 ++ .../manager/PlaylistManagerEditor.java | 539 ++++++ .../playlist/radioreference/AgencyEditor.java | 213 +++ .../radioreference/AgencyFrequencyEditor.java | 591 ++++++ .../FlashAliasListComboBoxRequest.java | 27 + .../radioreference/FrequencyEditor.java | 484 +++++ .../gui/playlist/radioreference/Level.java | 22 + .../playlist/radioreference/LoginDialog.java | 395 ++++ .../radioreference/ModeDecoderType.java | 93 + .../radioreference/RadioReferenceDecoder.java | 366 ++++ .../radioreference/RadioReferenceEditor.java | 779 ++++++++ .../RadioReferenceUnavailableAlert.java | 39 + .../playlist/radioreference/SiteEditor.java | 1210 ++++++++++++ .../playlist/radioreference/SystemEditor.java | 410 +++++ .../SystemSiteSelectionEditor.java | 250 +++ .../SystemTalkgroupSelectionEditor.java | 853 +++++++++ .../radioreference/TalkgroupEditor.java | 368 ++++ .../radioreference/TalkgroupEncryption.java | 61 + .../radioreference/TalkgroupMode.java | 70 + .../record/RecordConfigurationEditor.java | 142 ++ .../gui/playlist/source/FrequencyEditor.java | 479 +++++ .../gui/playlist/source/FrequencyField.java | 113 ++ .../source/SourceConfigurationEditor.java | 76 + .../streaming/AbstractStreamEditor.java | 343 ++++ .../streaming/BroadcastifyStreamEditor.java | 210 +++ .../streaming/IcecastHTTPStreamEditor.java | 43 + .../streaming/IcecastStreamEditor.java | 280 +++ .../streaming/IcecastTCPStreamEditor.java | 43 + .../streaming/ShoutcastV1StreamEditor.java | 238 +++ .../streaming/ShoutcastV2StreamEditor.java | 289 +++ .../streaming/StreamAliasSelectionEditor.java | 518 ++++++ .../streaming/StreamEditorFactory.java | 56 + .../playlist/streaming/StreamingEditor.java | 521 ++++++ .../streaming/UnknownStreamEditor.java | 142 ++ .../preference/PreferenceEditorFactory.java | 2 +- .../gui/preference/PreferenceEditorType.java | 2 +- .../TalkgroupFormatPreferenceEditor.java | 11 +- ...a => UserPreferenceEditorViewRequest.java} | 15 +- ...Editor.java => UserPreferencesEditor.java} | 124 +- .../tuner/TunerPreferenceEditor.java | 32 +- .../identifier/talkgroup/LTRTalkgroup.java | 26 +- .../talkgroup/UnknownTalkgroupIdentifier.java | 45 + .../dsheirer/identifier/tone/AmbeTone.java | 296 +++ .../identifier/tone/P25ToneIdentifier.java | 6 +- .../github/dsheirer/identifier/tone/Tone.java | 162 ++ .../identifier/tone/ToneIdentifier.java | 6 +- .../identifier/tone/ToneSequence.java | 179 ++ .../dsheirer/message/MessageDirection.java | 46 +- .../decode/AuxDecodeConfigurationEditor.java | 150 -- .../decode/DecodeConfigurationEditor.java | 139 -- .../module/decode/DecoderFactory.java | 56 +- .../dsheirer/module/decode/DecoderType.java | 4 +- .../module/decode/am/AMDecoderEditor.java | 98 - .../decode/config/AuxDecodeConfiguration.java | 101 +- .../module/decode/event/DecodeEventModel.java | 2 +- .../module/decode/event/DecodeEventPanel.java | 2 +- .../decode/ltrnet/LTRNetDecoderEditor.java | 113 -- .../decode/ltrnet/message/LtrNetMessage.java | 3 +- .../ltrstandard/DecodeConfigLTRStandard.java | 2 +- .../ltrstandard/LTRStandardDecoder.java | 2 +- .../ltrstandard/LTRStandardDecoderEditor.java | 112 -- .../ltrstandard/LTRStandardDecoderState.java | 12 +- .../ltrstandard/LTRStandardMessageFilter.java | 7 +- .../LTRStandardMessageProcessor.java | 12 +- .../ltrstandard/channel/LtrChannel.java | 2 +- .../decode/ltrstandard/message/Call.java | 2 +- .../decode/ltrstandard/message/CallEnd.java | 2 +- .../decode/ltrstandard/message/Idle.java | 2 +- ...TRStandardMessage.java => LTRMessage.java} | 8 +- .../ltrstandard/message/UnknownMessage.java | 2 +- .../decode/mpt1327/MPT1327DecoderEditor.java | 263 --- .../mpt1327/identifier/MPT1327Talkgroup.java | 25 +- .../module/decode/nbfm/NBFMDecoderEditor.java | 136 -- .../decode/p25/audio/P25P2AudioModule.java | 110 +- .../p25/phase1/DecodeConfigP25Phase1.java | 32 +- .../decode/p25/phase1/P25P1DecoderEditor.java | 221 --- .../decode/p25/phase2/P25P2DecoderEditor.java | 283 --- .../passport/PassportDecoderEditor.java | 93 - .../decode/passport/PassportDecoderState.java | 8 +- .../decode/passport/PassportMessage.java | 9 +- .../passport/identifier/PassportRadioId.java | 62 + .../identifier/PassportTalkgroup.java | 16 +- .../log/EventLogConfigurationEditor.java | 175 -- .../dsheirer/playlist/PlaylistManager.java | 334 +++- .../dsheirer/playlist/PlaylistUpdater.java | 10 +- .../github/dsheirer/playlist/PlaylistV2.java | 34 +- .../dsheirer/preference/PreferenceType.java | 3 +- .../dsheirer/preference/UserPreferences.java | 11 +- .../preference/identifier/IntegerFormat.java | 5 + .../identifier/TalkgroupFormatPreference.java | 74 +- .../talkgroup/APCO25TalkgroupFormatter.java | 16 +- ...ter.java => AbstractIntegerFormatter.java} | 7 +- .../FleetsyncTalkgroupFormatter.java | 9 +- .../talkgroup/LTRTalkgroupFormatter.java | 50 +- .../talkgroup/MDC1200TalkgroupFormatter.java | 17 +- .../talkgroup/MPT1327TalkgroupFormatter.java | 9 +- .../talkgroup/PassportTalkgroupFormatter.java | 62 +- .../talkgroup/UnknownTalkgroupFormatter.java | 31 + .../preference/javafx/JavaFxPreferences.java | 171 ++ .../playlist/PlaylistPreference.java | 151 +- .../RadioReferencePreference.java | 337 +++- .../io/github/dsheirer/protocol/Protocol.java | 6 +- .../record/RecordConfigurationEditor.java | 261 --- .../github/dsheirer/record/RecorderType.java | 5 +- .../CachingRadioReferenceService.java | 233 +++ .../radioreference/RadioReference.java | 231 +++ .../source/SourceConfigurationEditor.java | 145 -- .../source/mixer/MixerSourceEditor.java | 175 -- .../dsheirer/source/tuner/TunerModel.java | 18 + .../source/tuner/TunerSourceEditor.java | 190 -- .../TunerSourceMultipleFrequencyEditor.java | 184 -- .../tuner/TunerSpectralDisplayManager.java | 29 +- .../dsheirer/spectrum/OverlayPanel.java | 5 + .../spectrum/SpectralDisplayPanel.java | 10 +- .../dsheirer/spectrum/SpectrumFrame.java | 12 +- 290 files changed, 31212 insertions(+), 15267 deletions(-) create mode 100644 .idea/modules.xml delete mode 100644 src/main/java/io/github/dsheirer/alias/AliasController.java delete mode 100644 src/main/java/io/github/dsheirer/alias/AliasEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/AliasNameEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/ComponentEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/ComponentValidationException.java delete mode 100644 src/main/java/io/github/dsheirer/alias/MultipleAliasEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/action/AliasActionEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/action/beep/BeepActionEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/action/clip/ClipActionEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/action/script/ScriptActionEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/AliasIdentifierEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/WildcardID.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/broadcast/BroadcastChannelEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/esn/ESNEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/legacy/fleetsync/FleetsyncIDEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/legacy/mdc/MDC1200IDEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/legacy/mobileID/MINEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/legacy/mpt1327/MPT1327IDEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/legacy/nonrecordable/NonRecordableEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/legacy/siteID/SiteIDEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/legacy/talkgroup/LegacyTalkgroupIDEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/legacy/uniqueID/UniqueIDEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/lojack/LoJackIDEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/priority/PriorityEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/radio/RadioEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/radio/RadioRangeEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/record/RecordEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/status/StatusID.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/status/StatusIDEditor.java create mode 100644 src/main/java/io/github/dsheirer/alias/id/status/UnitStatusID.java create mode 100644 src/main/java/io/github/dsheirer/alias/id/status/UserStatusID.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupRangeEditor.java create mode 100644 src/main/java/io/github/dsheirer/alias/id/tone/TonesID.java delete mode 100644 src/main/java/io/github/dsheirer/audio/broadcast/BroadcastConfigurationEditor.java delete mode 100644 src/main/java/io/github/dsheirer/audio/broadcast/BroadcastPanel.java create mode 100644 src/main/java/io/github/dsheirer/audio/broadcast/ConfiguredBroadcast.java delete mode 100644 src/main/java/io/github/dsheirer/audio/broadcast/broadcastify/BroadcastifyConfigurationEditor.java delete mode 100644 src/main/java/io/github/dsheirer/audio/broadcast/broadcastify/UserFeedSelectionDialog.java delete mode 100644 src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastHTTPConfigurationEditor.java delete mode 100644 src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastTCPConfigurationEditor.java delete mode 100644 src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v1/ShoutcastV1ConfigurationEditor.java delete mode 100644 src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v2/ShoutcastV2ConfigurationEditor.java delete mode 100644 src/main/java/io/github/dsheirer/controller/channel/ChannelController.java delete mode 100644 src/main/java/io/github/dsheirer/controller/channel/ChannelEditor.java create mode 100644 src/main/java/io/github/dsheirer/controller/channel/ChannelException.java delete mode 100644 src/main/java/io/github/dsheirer/controller/channel/NameConfigurationEditor.java delete mode 100644 src/main/java/io/github/dsheirer/controller/channel/map/ChannelMapEditor.java delete mode 100644 src/main/java/io/github/dsheirer/controller/channel/map/ChannelMapManager.java delete mode 100644 src/main/java/io/github/dsheirer/controller/channel/map/ChannelMapManagerFrame.java create mode 100644 src/main/java/io/github/dsheirer/gui/control/HexFormatter.java create mode 100644 src/main/java/io/github/dsheirer/gui/control/IntegerFormatter.java create mode 100644 src/main/java/io/github/dsheirer/gui/control/IntegerTextField.java create mode 100644 src/main/java/io/github/dsheirer/gui/control/LtrFormatter.java create mode 100644 src/main/java/io/github/dsheirer/gui/control/MaxLengthUnaryOperator.java delete mode 100644 src/main/java/io/github/dsheirer/gui/control/MultipleFrequencyEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/control/PrefixIdentFormatter.java delete mode 100644 src/main/java/io/github/dsheirer/gui/editor/EditorValidationException.java delete mode 100644 src/main/java/io/github/dsheirer/gui/editor/EmptyValidatingEditor.java delete mode 100644 src/main/java/io/github/dsheirer/gui/editor/ValidatingEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/Editor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/PlaylistEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/PlaylistEditorApplication.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/PlaylistEditorRequest.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/ViewPlaylistRequest.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/AliasConfigurationEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/AliasEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/AliasItemEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/AliasTabRequest.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/AliasViewByIdentifierEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/AliasViewByRecordingEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/ColorUtil.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/ViewAliasIdentifierRequest.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/ViewAliasRequest.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/action/ActionEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/action/ActionEditorFactory.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/action/BeepEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/action/ClipEditor.java rename src/main/java/io/github/dsheirer/gui/{editor/DocumentListenerEditor.java => playlist/alias/action/EmptyActionEditor.java} (57%) create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/action/ScriptEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/action/TestMessage.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/action/UnrecognizedActionEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/EmptyIdentifierEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/EsnEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/IdentifierEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/IdentifierEditorFactory.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/LojackEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/RadioIdEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/RadioIdRangeEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/TalkgroupEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/TalkgroupRangeEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/TonesEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/UnitStatusEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/UnrecognizedIdentifierEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/UserStatusEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/channel/AMConfigurationEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelConfigurationEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelConfigurationEditorFactory.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelTabRequest.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/channel/LTRConfigurationEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/channel/LTRNetConfigurationEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/channel/MPT1327ConfigurationEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/channel/NBFMConfigurationEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/channel/P25P1ConfigurationEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/channel/P25P2ConfigurationEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/channel/PassportConfigurationEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/channel/UnknownConfigurationEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/channel/ViewChannelRequest.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/channelMap/ChannelMapEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/channelMap/ChannelMapEditorViewRequest.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/decoder/AuxDecoderConfigurationEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/eventlog/EventLogConfigurationEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/manager/PlaylistManagerEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/radioreference/AgencyEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/radioreference/AgencyFrequencyEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/radioreference/FlashAliasListComboBoxRequest.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/radioreference/FrequencyEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/radioreference/Level.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/radioreference/LoginDialog.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/radioreference/ModeDecoderType.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/radioreference/RadioReferenceDecoder.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/radioreference/RadioReferenceEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/radioreference/RadioReferenceUnavailableAlert.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/radioreference/SiteEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/radioreference/SystemEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/radioreference/SystemSiteSelectionEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/radioreference/SystemTalkgroupSelectionEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/radioreference/TalkgroupEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/radioreference/TalkgroupEncryption.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/radioreference/TalkgroupMode.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/record/RecordConfigurationEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/source/FrequencyEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/source/FrequencyField.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/source/SourceConfigurationEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/streaming/AbstractStreamEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/streaming/BroadcastifyStreamEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/streaming/IcecastHTTPStreamEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/streaming/IcecastStreamEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/streaming/IcecastTCPStreamEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/streaming/ShoutcastV1StreamEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/streaming/ShoutcastV2StreamEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/streaming/StreamAliasSelectionEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/streaming/StreamEditorFactory.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/streaming/StreamingEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/playlist/streaming/UnknownStreamEditor.java rename src/main/java/io/github/dsheirer/gui/preference/{PreferenceEditorViewRequest.java => UserPreferenceEditorViewRequest.java} (73%) rename src/main/java/io/github/dsheirer/gui/preference/{PreferencesEditor.java => UserPreferencesEditor.java} (79%) create mode 100644 src/main/java/io/github/dsheirer/identifier/talkgroup/UnknownTalkgroupIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/identifier/tone/AmbeTone.java create mode 100644 src/main/java/io/github/dsheirer/identifier/tone/Tone.java create mode 100644 src/main/java/io/github/dsheirer/identifier/tone/ToneSequence.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/AuxDecodeConfigurationEditor.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/DecodeConfigurationEditor.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/am/AMDecoderEditor.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderEditor.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoderEditor.java rename src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/{LTRStandardMessage.java => LTRMessage.java} (92%) delete mode 100644 src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderEditor.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/nbfm/NBFMDecoderEditor.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderEditor.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2DecoderEditor.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoderEditor.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/passport/identifier/PassportRadioId.java delete mode 100644 src/main/java/io/github/dsheirer/module/log/EventLogConfigurationEditor.java rename src/main/java/io/github/dsheirer/preference/identifier/talkgroup/{IntegerFormatter.java => AbstractIntegerFormatter.java} (89%) create mode 100644 src/main/java/io/github/dsheirer/preference/identifier/talkgroup/UnknownTalkgroupFormatter.java create mode 100644 src/main/java/io/github/dsheirer/preference/javafx/JavaFxPreferences.java delete mode 100644 src/main/java/io/github/dsheirer/record/RecordConfigurationEditor.java create mode 100644 src/main/java/io/github/dsheirer/service/radioreference/CachingRadioReferenceService.java create mode 100644 src/main/java/io/github/dsheirer/service/radioreference/RadioReference.java delete mode 100644 src/main/java/io/github/dsheirer/source/SourceConfigurationEditor.java delete mode 100644 src/main/java/io/github/dsheirer/source/mixer/MixerSourceEditor.java delete mode 100644 src/main/java/io/github/dsheirer/source/tuner/TunerSourceEditor.java delete mode 100644 src/main/java/io/github/dsheirer/source/tuner/TunerSourceMultipleFrequencyEditor.java diff --git a/.idea/dictionaries/denny.xml b/.idea/dictionaries/denny.xml index 3721dbce5..2c06cff85 100644 --- a/.idea/dictionaries/denny.xml +++ b/.idea/dictionaries/denny.xml @@ -23,6 +23,7 @@ shoutcast streamable talkgroup + talkgroups tgid ultravox wacn diff --git a/.idea/misc.xml b/.idea/misc.xml index d4b0813c4..6746d6276 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,7 +1,7 @@ - + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 000000000..b386659b9 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 3d4dd9f6c..a6d25f94f 100644 --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,7 @@ plugins { id 'java' id 'idea' id 'org.openjfx.javafxplugin' version '0.0.8' - id 'org.beryx.runtime' version '1.8.0' + id 'org.beryx.runtime' version '1.8.1' } repositories { @@ -33,7 +33,7 @@ repositories { maven { url "https://jitpack.io" } } -version = '0.4.0' +version = '0.5.0-alpha1' sourceCompatibility = '13' sourceSets { @@ -65,7 +65,7 @@ dependencies { implementation 'com.miglayout:miglayout-swing:5.2' implementation 'com.mpatric:mp3agic:0.9.1' implementation 'eu.hansolo:charts:1.0.5' - implementation 'io.github.dsheirer:radio-reference-api:15.1.2' + implementation 'io.github.dsheirer:radio-reference-api:15.1.4' implementation 'javax.usb:usb-api:1.0.2' implementation 'net.coderazzi:tablefilter-swing:5.4.0' implementation 'org.apache.commons:commons-lang3:3.8.1' @@ -124,7 +124,7 @@ jar { } /** - * Java Development Kit (JDK) locations. In order to build OS-specific images, these paths must point to the 'bin' + * Java Development Kit (JDK) locations. In order to build OS-specific images, these paths must point to the 'bin' * directory within a JDK for each of the specified architectures. These JDKs are used by the runtime and runtimeZip * tasks to produce platform-specific builds. If none of these paths exists, then an image will be created for the * host OS using the installed JDK. @@ -174,7 +174,7 @@ runtime { } options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages'] - modules = ['java.desktop', 'java.naming', 'jdk.unsupported', 'jdk.unsupported.desktop', 'java.net.http'] + modules = ['java.desktop', 'java.naming', 'jdk.unsupported', 'jdk.unsupported.desktop', 'java.net.http', 'java.sql'] imageZip = hasTargetJdk ? file("$buildDir/image/sdr-trunk.zip") : file("$buildDir/image/sdr-trunk-" + version + ".zip") } diff --git a/sdr-trunk.ipr b/sdr-trunk.ipr index 50ef3e983..aeef19113 100644 --- a/sdr-trunk.ipr +++ b/sdr-trunk.ipr @@ -34,6 +34,8 @@ - + + + + + + + + + + + + + @@ -309,6 +338,22 @@ + + + + + + + + + + + + + + + + @@ -353,6 +398,13 @@ + + + + + + + @@ -417,13 +469,13 @@ - + - + - + @@ -481,15 +533,15 @@ - + - + - + - + @@ -536,15 +588,15 @@ - + - + - + - + @@ -613,6 +665,17 @@ + + + + + + + + + + + @@ -668,6 +731,17 @@ + + + + + + + + + + + @@ -745,6 +819,28 @@ + + + + + + + + + + + + + + + + + + + + + + @@ -778,6 +874,17 @@ + + + + + + + + + + + diff --git a/src/main/java/io/github/dsheirer/alias/Alias.java b/src/main/java/io/github/dsheirer/alias/Alias.java index cd50e0db1..2b75a1e1f 100644 --- a/src/main/java/io/github/dsheirer/alias/Alias.java +++ b/src/main/java/io/github/dsheirer/alias/Alias.java @@ -1,21 +1,23 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer * - * 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 3 of the License, or - * (at your option) any later version. + * * ****************************************************************************** + * * Copyright (C) 2014-2020 Dennis Sheirer + * * + * * 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 3 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, see + * * ***************************************************************************** * - * 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, see - * ***************************************************************************** */ package io.github.dsheirer.alias; @@ -28,161 +30,465 @@ import io.github.dsheirer.alias.id.broadcast.BroadcastChannel; import io.github.dsheirer.alias.id.priority.Priority; import io.github.dsheirer.alias.id.record.Record; +import javafx.beans.Observable; +import javafx.beans.binding.Bindings; +import javafx.beans.binding.BooleanBinding; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import javafx.util.Callback; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.awt.Color; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.TreeSet; +/** + * Alias provides an aliasing (e.g. name, color, etc) container that is linked to multiple alias identifiers and + * provides a corresponding set of actions related to the alias. + */ @JacksonXmlRootElement(localName = "alias") public class Alias { - private String mList; - private String mGroup; - private String mName; - private int mColor; - private String mIconName; - private List mAliasIDs = new ArrayList(); - private List mAliasActions = new ArrayList(); + private final static Logger mLog = LoggerFactory.getLogger(Alias.class); + + private BooleanProperty mRecordable = new SimpleBooleanProperty(); + private BooleanProperty mStreamable = new SimpleBooleanProperty(); + private BooleanProperty mOverlap = new SimpleBooleanProperty(); + private IntegerProperty mColor = new SimpleIntegerProperty(); + private IntegerProperty mPriority = new SimpleIntegerProperty(Priority.DEFAULT_PRIORITY); + private IntegerProperty mNonAudioIdentifierCount = new SimpleIntegerProperty(); + private StringProperty mAliasListName = new SimpleStringProperty(); + private StringProperty mGroup = new SimpleStringProperty(); + private StringProperty mIconName = new SimpleStringProperty(); + private StringProperty mName = new SimpleStringProperty(); + private ObservableList mAliasIDs = FXCollections.observableArrayList(); + private ObservableList mAliasActions = FXCollections.observableArrayList(); + /** + * Constructs an instance and sets the specified name. + * @param name for the alias + */ + public Alias(String name) + { + mName.set(name); + + //Bind the non-audio identifier count property to the size of a filtered list of alias identifiers + mNonAudioIdentifierCount.bind(Bindings.size(new FilteredList<>(mAliasIDs, aliasID -> { + return aliasID != null && !aliasID.isAudioIdentifier(); + }))); + } + + /** + * Constructs an instance + */ public Alias() { + this(null); } - public Alias(String name) + /** + * Count of non-audio identifiers for this alias + */ + @JsonIgnore + public IntegerProperty nonAudioIdentifierCountProperty() + { + return mNonAudioIdentifierCount; + } + + /** + * Audio playback priority + */ + @JsonIgnore + public IntegerProperty priorityProperty() + { + return mPriority; + } + + /** + * Indicates if one or more of the identifiers for this alias overlap with identifiers from another alias + * within the same alias list. + */ + @JsonIgnore + public BooleanProperty overlapProperty() + { + return mOverlap; + } + + /** + * Recordable property + */ + @JsonIgnore + public BooleanProperty recordableProperty() + { + return mRecordable; + } + + /** + * Streamable property + */ + @JsonIgnore + public BooleanProperty streamableProperty() { - mName = name; + return mStreamable; } + /** + * Alias list name property + */ + @JsonIgnore + public StringProperty aliasListNameProperty() + { + return mAliasListName; + } + + /** + * Group property + */ + @JsonIgnore + public StringProperty groupProperty() + { + return mGroup; + } + + /** + * Alias name property + * @return + */ + @JsonIgnore + public StringProperty nameProperty() + { + return mName; + } + + /** + * Alias color value property + */ + @JsonIgnore + public IntegerProperty colorProperty() + { + return mColor; + } + + /** + * Icon name property + */ + @JsonIgnore + public StringProperty iconNameProperty() + { + return mIconName; + } + + /** + * Alias identifiers list property + */ + @JsonIgnore + public ObservableList aliasIds() + { + return mAliasIDs; + } + + /** + * Alias actions list property + */ + @JsonIgnore + public ObservableList aliasActions() + { + return mAliasActions; + } + + /** + * Uses name value for string representation + */ public String toString() { return getName(); } + /** + * Alias name + */ @JacksonXmlProperty(isAttribute = true, localName = "name") public String getName() { - return mName; + return mName.get(); } public void setName(String name) { - mName = name; + mName.set(name); } + /** + * Alias list name for the alias list that this alias belongs to + */ @JacksonXmlProperty(isAttribute = true, localName = "list") - public String getList() + public String getAliasListName() { - return mList; + return mAliasListName.get(); } - public void setList(String list) + public void setAliasListName(String aliasListName) { - mList = list; + mAliasListName.set(aliasListName); } + @JsonIgnore + public boolean matchesAliasList(String aliasList) + { + boolean isEmptyArgument = aliasList == null || aliasList.isEmpty() || aliasList.contentEquals(AliasModel.NO_ALIAS_LIST); + boolean isEmptyThis = getAliasListName() == null || getAliasListName().isEmpty(); + + if(isEmptyArgument && isEmptyThis) + { + return true; + } + else if(!isEmptyArgument && !isEmptyThis && aliasList.contentEquals(getAliasListName())) + { + return true; + } + + return false; + } + + /** + * Indicates if this alias has a specified alias list name + */ public boolean hasList() { - return mList != null; + return mAliasListName.get() != null; } + /** + * Grouping tag for this alias. + */ @JacksonXmlProperty(isAttribute = true, localName = "group") public String getGroup() { - return mGroup; + return mGroup.get(); } public void setGroup(String group) { - mGroup = group; + mGroup.set(group); } + /** + * Indicates if this alias has a grouping tag + */ public boolean hasGroup() { - return mGroup != null; + return mGroup.get() != null; } + /** + * Color (RGBA) value to use for this alias + */ @JacksonXmlProperty(isAttribute = true, localName = "color") public int getColor() { - return mColor; + return mColor.get(); + } + + @JsonIgnore + public String getColorHex() + { + return String.format("#%06X", (0xFFFFFF & mColor.get())); } public void setColor(int color) { - mColor = color; + mColor.set(color); } + /** + * Display color for this alias. + */ @JsonIgnore public Color getDisplayColor() { return new Color(getColor()); } + /** + * Icon name of the icon to use for this alias. + */ @JacksonXmlProperty(isAttribute = true, localName = "iconName") public String getIconName() { - return mIconName; + return mIconName.get(); } public void setIconName(String iconName) { - mIconName = iconName; + mIconName.set(iconName); } + /** + * List of alias identifiers for this alias. + */ @JacksonXmlProperty(isAttribute = false, localName = "id") - public List getId() + public List getAliasIdentifiers() { return mAliasIDs; } - public void setId(ArrayList id) + public void setAliasIdentifiers(List id) { - mAliasIDs = id; + mAliasIDs.clear(); + + for(AliasID aliasID : id) + { + addAliasID(aliasID); + } } + /** + * Adds an alias identifier to this alias and updates the recordable and streamable attributes. + */ public void addAliasID(AliasID id) { mAliasIDs.add(id); + validate(); + updateOverlapBinding(); } + /** + * Removes an alias identifier from this alias and updates the recordable and streamable attributes. + */ public void removeAliasID(AliasID id) { - mAliasIDs.remove(id); + if(id != null) + { + mAliasIDs.remove(id); + validate(); + updateOverlapBinding(); + } } + /** + * List of alias actions associated with this alias. + */ @JacksonXmlProperty(isAttribute = false, localName = "action") - public List getAction() + public List getAliasActions() { return mAliasActions; } - public void setAction(List actions) + public void setAliasActions(List actions) { - mAliasActions = actions; + mAliasActions.setAll(actions); } + /** + * Adds the specified alias action to this alias + */ public void addAliasAction(AliasAction action) { mAliasActions.add(action); } + /** + * Removes the specified alias action from this alias + */ public void removeAliasAction(AliasAction action) { mAliasActions.remove(action); } + /** + * Indicates if this alias has any associated + * @return + */ public boolean hasActions() { return !mAliasActions.isEmpty(); } + + /** + * Updates the playback priority this alias + */ + private void updatePriority() + { + for(AliasID aliasID: mAliasIDs) + { + if(aliasID instanceof Priority) + { + mPriority.set(((Priority)aliasID).getPriority()); + return; + } + } + + mPriority.set(Priority.DEFAULT_PRIORITY); + } + + private void updateOverlapBinding() + { + mOverlap.unbind(); + + BooleanBinding binding = new SimpleBooleanProperty(true).not(); + + for(AliasID aliasID: mAliasIDs) + { + binding = binding.or(aliasID.overlapProperty()); + } + + mOverlap.bind(binding); + } + + /** + * Updates the recordable status for this alias + */ + private void updateRecordable() + { + for(AliasID aliasID: mAliasIDs) + { + if(aliasID instanceof Record) + { + mRecordable.set(true); + return; + } + } + + mRecordable.set(false); + } + + /** + * Updates the streamable status for this alias + */ + private void updateStreamable() + { + for(AliasID aliasID: mAliasIDs) + { + if(aliasID instanceof BroadcastChannel) + { + mStreamable.set(true); + return; + } + } + + mStreamable.set(false); + } + /** * Perform any validation/cleanup actions on this alias. */ public void validate() { + updatePriority(); + updateRecordable(); + updateStreamable(); } /** @@ -220,22 +526,23 @@ public boolean hasCallPriority() */ public void setCallPriority(int priority) { - if(priority == Priority.DO_NOT_MONITOR || - (Priority.MIN_PRIORITY <= priority && priority <= Priority.MAX_PRIORITY)) + AliasID priorityID = null; + + for(AliasID aliasID: mAliasIDs) { - for(AliasID id : mAliasIDs) + if(aliasID instanceof Priority) { - if(id.getType() == AliasIDType.PRIORITY) - { - ((Priority)id).setPriority(priority); - return; - } + priorityID = aliasID; } + } + + removeAliasID(priorityID); - //If we don't find a priority id, create one - Priority p = new Priority(); - p.setPriority(priority); - addAliasID(p); + //Don't add a priority alias id if the priority is MAX_PRIORITY or DEFAULT_PRIORITY since the getCallPriority() + //will return that as the default value + if(priority == Priority.DO_NOT_MONITOR || (Priority.MIN_PRIORITY <= priority && priority < Priority.MAX_PRIORITY)) + { + addAliasID(new Priority(priority)); } } @@ -245,15 +552,7 @@ public void setCallPriority(int priority) @JsonIgnore public boolean isRecordable() { - for(AliasID id : getId()) - { - if(id.getType() == AliasIDType.RECORD) - { - return true; - } - } - - return false; + return mRecordable.get(); } /** @@ -261,35 +560,21 @@ public boolean isRecordable() */ public void setRecordable(boolean recordable) { - if(recordable) + AliasID recordID = null; + + for(AliasID aliasID: mAliasIDs) { - for(AliasID id : getId()) + if(aliasID instanceof Record) { - if(id.getType() == AliasIDType.RECORD) - { - return; - } + recordID = aliasID; } - - addAliasID(new Record()); } - else - { - AliasID toRemove = null; - for(AliasID id : getId()) - { - if(id.getType() == AliasIDType.RECORD) - { - toRemove = id; - break; - } - } + removeAliasID(recordID); - if(toRemove != null) - { - removeAliasID(toRemove); - } + if(recordable) + { + addAliasID(new Record()); } } @@ -299,15 +584,7 @@ public void setRecordable(boolean recordable) @JsonIgnore public boolean isStreamable() { - for(AliasID id : getId()) - { - if(id.getType() == AliasIDType.BROADCAST_CHANNEL) - { - return true; - } - } - - return false; + return mStreamable.get(); } /** @@ -318,7 +595,7 @@ public Set getBroadcastChannels() { Set broadcastChannels = new TreeSet<>(); - for(AliasID id : getId()) + for(AliasID id : getAliasIdentifiers()) { if(id.getType() == AliasIDType.BROADCAST_CHANNEL) { @@ -339,7 +616,7 @@ public boolean hasBroadcastChannel(String channel) return false; } - for(AliasID id : getId()) + for(AliasID id : getAliasIdentifiers()) { if(id.getType() == AliasIDType.BROADCAST_CHANNEL && ((BroadcastChannel)id).getChannelName().contentEquals(channel)) @@ -350,4 +627,85 @@ public boolean hasBroadcastChannel(String channel) return false; } + + /** + * Removes any broadcast channel(s) that match the argument + */ + public void removeBroadcastChannel(String channel) + { + if(channel == null || channel.isEmpty()) + { + return; + } + + List toRemove = new ArrayList<>(); + + for(AliasID aliasID: getAliasIdentifiers()) + { + if(aliasID instanceof BroadcastChannel && + ((BroadcastChannel)aliasID).getChannelName().contentEquals(channel)) + { + toRemove.add(aliasID); + } + } + + for(AliasID aliasID: toRemove) + { + removeAliasID(aliasID); + } + } + + /** + * Removes (clears) all broadcast channels from this alias + */ + public void removeAllBroadcastChannels() + { + List toRemove = new ArrayList<>(); + for(AliasID aliasID: mAliasIDs) + { + if(aliasID instanceof BroadcastChannel) + { + toRemove.add(aliasID); + } + } + + for(AliasID aliasID: toRemove) + { + removeAliasID(aliasID); + } + } + + /** + * Removes all non audio identifiers to prepare for replacing them from an editor. + */ + public void removeNonAudioIdentifiers() + { + Iterator it = mAliasIDs.iterator(); + + while(it.hasNext()) + { + if(!it.next().isAudioIdentifier()) + { + it.remove(); + } + } + } + + /** + * Removes all alias actions from this alias + */ + public void removeAllActions() + { + mAliasActions.clear(); + } + + /** + * Creates an observable property extractor for use with observable lists to detect changes internal to this object. + */ + public static Callback extractor() + { + return (Alias a) -> new Observable[] {a.recordableProperty(), a.streamableProperty(), a.colorProperty(), + a.aliasListNameProperty(), a.groupProperty(), a.iconNameProperty(), a.nameProperty(), a.aliasIds(), + a.aliasActions(), a.nonAudioIdentifierCountProperty(), a.overlapProperty()}; + } } diff --git a/src/main/java/io/github/dsheirer/alias/AliasController.java b/src/main/java/io/github/dsheirer/alias/AliasController.java deleted file mode 100644 index 4b321d913..000000000 --- a/src/main/java/io/github/dsheirer/alias/AliasController.java +++ /dev/null @@ -1,381 +0,0 @@ -/* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2019 Dennis Sheirer - * - * 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 3 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, see - * ***************************************************************************** - */ - -package io.github.dsheirer.alias; - -import com.jidesoft.swing.JideSplitPane; -import io.github.dsheirer.audio.broadcast.BroadcastModel; -import io.github.dsheirer.gui.editor.Editor; -import io.github.dsheirer.icon.IconManager; -import io.github.dsheirer.preference.UserPreferences; -import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor; -import net.coderazzi.filters.gui.AutoChoices; -import net.coderazzi.filters.gui.TableFilterHeader; -import net.miginfocom.swing.MigLayout; - -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.ListSelectionModel; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.table.DefaultTableCellRenderer; -import java.awt.Color; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Rectangle; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.ArrayList; -import java.util.List; - -public class AliasController extends JPanel implements ActionListener, ListSelectionListener -{ - private static final long serialVersionUID = 1L; - - private static final String TABLE_PREFERENCE_KEY = "alias.controller"; - private AliasModel mAliasModel; - private JTable mAliasTable; - private TableFilterHeader mTableFilterHeader; - private AliasEditor mAliasEditor; - private MultipleAliasEditor mMultipleAliasEditor; - private JideSplitPane mSplitPane; - private UserPreferences mUserPreferences; - - private static final String NEW_ALIAS = "New"; - private static final String COPY_ALIAS = "Copy"; - private static final String DELETE_ALIAS = "Delete"; - - private JButton mNewButton = new JButton(NEW_ALIAS); - private JButton mCopyButton = new JButton(COPY_ALIAS); - private JButton mDeleteButton = new JButton(DELETE_ALIAS); - - private IconCellRenderer mIconCellRenderer; - private JTableColumnWidthMonitor mColumnWidthMonitor; - - public AliasController(AliasModel aliasModel, BroadcastModel broadcastModel, IconManager iconManager, - UserPreferences userPreferences) - { - mAliasModel = aliasModel; - mAliasEditor = new AliasEditor(mAliasModel, broadcastModel, iconManager); - mMultipleAliasEditor = new MultipleAliasEditor(mAliasModel, broadcastModel, iconManager); - mIconCellRenderer = new IconCellRenderer(iconManager); - mUserPreferences = userPreferences; - - init(); - - mAliasModel.addListener(mAliasEditor); - } - - private void init() - { - setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]")); - - //System Configuration View and Editor - mAliasTable = new JTable(mAliasModel); - mAliasTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); - mAliasTable.getSelectionModel().addListSelectionListener(this); - mAliasTable.setAutoCreateRowSorter(true); - - mAliasTable.getColumnModel().getColumn(AliasModel.COLUMN_COLOR).setCellRenderer(new ColorCellRenderer()); - - mAliasTable.getColumnModel().getColumn(AliasModel.COLUMN_ICON).setCellRenderer(mIconCellRenderer); - - mColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mAliasTable, TABLE_PREFERENCE_KEY); - - mTableFilterHeader = new TableFilterHeader(mAliasTable, AutoChoices.ENABLED); - mTableFilterHeader.setFilterOnUpdates(true); - - JScrollPane tableScroller = new JScrollPane(mAliasTable); - - JPanel buttonsPanel = new JPanel(); - - buttonsPanel.setLayout(new MigLayout("insets 0 0 0 0", - "[grow,fill][grow,fill][grow,fill]", "[]")); - - mNewButton.addActionListener(this); - mNewButton.setToolTipText("Adds a new alias"); - buttonsPanel.add(mNewButton); - - mCopyButton.addActionListener(this); - mCopyButton.setEnabled(false); - mCopyButton.setToolTipText("Creates a copy of the currently selected alias and adds it"); - buttonsPanel.add(mCopyButton); - - mDeleteButton.addActionListener(this); - mDeleteButton.setEnabled(false); - mDeleteButton.setToolTipText("Deletes the currently selected alias"); - buttonsPanel.add(mDeleteButton); - - JPanel listAndButtonsPanel = new JPanel(); - - listAndButtonsPanel.setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", - "[grow,fill][]")); - - listAndButtonsPanel.add(tableScroller, "wrap"); - listAndButtonsPanel.add(buttonsPanel); - - mSplitPane = new JideSplitPane(JideSplitPane.HORIZONTAL_SPLIT); - mSplitPane.setDividerSize(5); - mSplitPane.setShowGripper(true); - mSplitPane.add(listAndButtonsPanel); - mSplitPane.add(mAliasEditor); - - add(mSplitPane); - } - - /** - * Sets the editor argument as the currently visible alias editor - */ - private void setEditor(Editor editor) - { - if(mSplitPane.getComponentCount() == 3) - { - Component component = mSplitPane.getComponent(2); - - if(component instanceof Editor && component != editor) - { - //Get current divider location so that we can reapply it after - //changing out the editors, so that the interface doesn't move - int location = mSplitPane.getDividerLocation(0); - - ((Editor) component).setItem(null); - - mSplitPane.remove(component); - editor.setPreferredSize(new Dimension(100, 100)); - mSplitPane.add(editor); - - mSplitPane.validate(); - mSplitPane.setDividerLocation(0, location); - mSplitPane.repaint(); - } - } - } - - private Alias getAlias(int selectedRow) - { - if(selectedRow >= 0) - { - int index = mAliasTable.convertRowIndexToModel(selectedRow); - - return mAliasModel.getAliasAtIndex(index); - } - - return null; - } - - @Override - public void valueChanged(ListSelectionEvent event) - { - //Limits event firing to only when selection is complete - if(!event.getValueIsAdjusting()) - { - int[] selectedRows = mAliasTable.getSelectedRows(); - - if(selectedRows.length == 0) - { - mAliasEditor.setItem(null); - mCopyButton.setEnabled(false); - mDeleteButton.setEnabled(false); - } - else if(selectedRows.length == 1) - { - setEditor(mAliasEditor); - - Alias selectedAlias = getAlias(selectedRows[0]); - - mAliasEditor.setItem(selectedAlias); - - if(selectedAlias != null) - { - mCopyButton.setEnabled(true); - mDeleteButton.setEnabled(true); - } - else - { - mCopyButton.setEnabled(false); - mDeleteButton.setEnabled(false); - } - } - else - { - setEditor(mMultipleAliasEditor); - - mCopyButton.setEnabled(true); - mDeleteButton.setEnabled(true); - - List selectedAliases = new ArrayList(); - - for(int selectedRow : selectedRows) - { - Alias alias = getAlias(selectedRow); - - selectedAliases.add(alias); - } - - mMultipleAliasEditor.setItem(selectedAliases); - } - } - } - - /** - * Adds the alias to the alias table/model and scrolls the view it - */ - private void addAlias(Alias alias) - { - //Remove any column headers so that the newly added alias shows correctly. - mTableFilterHeader.resetFilter(); - - int index = mAliasModel.addAlias(alias); - - if(index >= 0) - { - int translatedIndex = mAliasTable.convertRowIndexToView(index); - mAliasTable.setRowSelectionInterval(translatedIndex, translatedIndex); - mAliasTable.scrollRectToVisible( - new Rectangle(mAliasTable.getCellRect(translatedIndex, 0, true))); - } - } - - private List getSelectedAliases() - { - List selected = new ArrayList<>(); - - int[] rows = mAliasTable.getSelectedRows(); - - for(int row : rows) - { - if(row >= 0) - { - Alias alias = mAliasModel.getAliasAtIndex(mAliasTable.convertRowIndexToModel(row)); - - if(alias != null) - { - selected.add(alias); - } - } - } - - return selected; - } - - /** - * Responds to New, Copy and Delete Channel button invocations - */ - @Override - public void actionPerformed(ActionEvent event) - { - switch(event.getActionCommand()) - { - case NEW_ALIAS: - addAlias(new Alias("New Alias")); - break; - case COPY_ALIAS: - for(Alias alias : getSelectedAliases()) - { - addAlias(AliasFactory.copyOf(alias)); - } - break; - case DELETE_ALIAS: - List toDelete = getSelectedAliases(); - - if(toDelete != null && !toDelete.isEmpty()) - { - String title = toDelete.size() == 1 ? "Delete Alias?" : "Delete Aliases?"; - String prompt = toDelete.size() == 1 ? "Do you want to delete this alias?" : - "Do you want to delete these " + toDelete.size() + " aliases?"; - - int choice = JOptionPane.showConfirmDialog(AliasController.this, - prompt, title, JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); - - if(choice == JOptionPane.YES_OPTION) - { - for(Alias alias : toDelete) - { - mAliasModel.removeAlias(alias); - } - } - } - break; - default: - break; - } - } - - /** - * Colorizes the cell based on the cell's integer value - */ - public class ColorCellRenderer extends DefaultTableCellRenderer - { - private static final long serialVersionUID = 1L; - - @Override - public Component getTableCellRendererComponent(JTable table, - Object value, boolean isSelected, boolean hasFocus, int row, - int column) - { - JLabel label = (JLabel) super.getTableCellRendererComponent(table, - value, isSelected, hasFocus, row, column); - - label.setBackground(new Color((int) value)); - - label.setText(""); - - return label; - } - } - - /** - * Displays cell's icon and name - */ - public class IconCellRenderer extends DefaultTableCellRenderer - { - private static final long serialVersionUID = 1L; - - private IconManager mIconManager; - - public IconCellRenderer(IconManager iconManager) - { - mIconManager = iconManager; - } - - @Override - public Component getTableCellRendererComponent(JTable table, - Object value, boolean isSelected, boolean hasFocus, int row, - int column) - { - JLabel label = (JLabel) super.getTableCellRendererComponent(table, - value, isSelected, hasFocus, row, column); - - ImageIcon icon = mIconManager.getIcon(label.getText(), 12); - - if(icon != null) - { - label.setIcon(icon); - } - - return label; - } - } -} diff --git a/src/main/java/io/github/dsheirer/alias/AliasEditor.java b/src/main/java/io/github/dsheirer/alias/AliasEditor.java deleted file mode 100644 index 7becf5b51..000000000 --- a/src/main/java/io/github/dsheirer/alias/AliasEditor.java +++ /dev/null @@ -1,157 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * 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 3 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, see - ******************************************************************************/ -package io.github.dsheirer.alias; - -import com.jidesoft.swing.JideTabbedPane; -import io.github.dsheirer.alias.action.AliasActionEditor; -import io.github.dsheirer.alias.id.AliasIdentifierEditor; -import io.github.dsheirer.audio.broadcast.BroadcastModel; -import io.github.dsheirer.gui.editor.Editor; -import io.github.dsheirer.icon.IconManager; -import io.github.dsheirer.sample.Listener; -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -public class AliasEditor extends Editor implements ActionListener, Listener -{ - private static final long serialVersionUID = 1L; - - private static final String DEFAULT_NAME = "select an alias"; - - private AliasModel mAliasModel; - - private JLabel mAliasLabel; - private AliasNameEditor mAliasNameEditor; - private AliasIdentifierEditor mAliasIdentifierEditor; - private AliasActionEditor mAliasActionEditor; - - public AliasEditor(AliasModel aliasModel, BroadcastModel broadcastModel, IconManager iconManager ) - { - mAliasModel = aliasModel; - mAliasNameEditor = new AliasNameEditor( mAliasModel, iconManager ); - mAliasIdentifierEditor = new AliasIdentifierEditor( aliasModel, broadcastModel ); - mAliasActionEditor = new AliasActionEditor( aliasModel ); - - mAliasModel.addListener( this ); - - init(); - } - - - @Override - public void setItem( Alias alias ) - { - super.setItem( alias ); - - if( hasItem() ) - { - mAliasLabel.setText( getItem().getName() ); - } - else - { - mAliasLabel.setText( DEFAULT_NAME ); - } - - mAliasNameEditor.setItem( alias ); - mAliasIdentifierEditor.setItem( alias ); - mAliasActionEditor.setItem( alias ); - } - - private void init() - { - setLayout( new MigLayout( "fill,wrap 2", "[grow,fill][grow,fill]", - "[][][][][][grow,fill]" ) ); - - add( new JLabel( "Alias:" ), "align right" ); - mAliasLabel = new JLabel( DEFAULT_NAME ); - add( mAliasLabel, "align left" ); - - add( new JSeparator( JSeparator.HORIZONTAL ), "span,growx,push" ); - - JideTabbedPane tabs = new JideTabbedPane(); - tabs.setFont( this.getFont() ); - tabs.setForeground( Color.BLACK ); - - tabs.addTab( "Alias", mAliasNameEditor ); - tabs.addTab( "Audio / Identifier", mAliasIdentifierEditor ); - tabs.addTab( "Action", mAliasActionEditor ); - add( tabs, "span,grow" ); - - //Playlist management buttons - JButton btnSave = new JButton( "Save" ); - btnSave.setToolTipText( "Save alias changes to the playlist" ); - btnSave.addActionListener( AliasEditor.this ); - add( btnSave ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.setToolTipText( "Reset alias changes since last save" ); - btnReset.addActionListener( AliasEditor.this ); - add( btnReset, "wrap" ); - } - - @Override - public void receive( AliasEvent event ) - { - //If this is the currently displayed alias, reload it - if( hasItem() && getItem() == event.getAlias() ) - { - switch( event.getEvent() ) - { - case CHANGE: - //Alias changed - reset the editor with changed alias - setItem( event.getAlias() ); - break; - case DELETE: - setItem( null ); - break; - default: - break; - } - } - } - - @Override - public void actionPerformed( ActionEvent e ) - { - if( hasItem() ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) - { - save(); - } - else if( command.contentEquals( "Reset" ) ) - { - reset(); - } - } - } - - @Override - public void save() - { - mAliasNameEditor.save(); - mAliasIdentifierEditor.save(); - mAliasActionEditor.save(); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/AliasFactory.java b/src/main/java/io/github/dsheirer/alias/AliasFactory.java index 707e207d1..2618e47d1 100644 --- a/src/main/java/io/github/dsheirer/alias/AliasFactory.java +++ b/src/main/java/io/github/dsheirer/alias/AliasFactory.java @@ -1,7 +1,7 @@ /* * * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer + * * Copyright (C) 2014-2020 Dennis Sheirer * * * * 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 @@ -24,55 +24,37 @@ import io.github.dsheirer.alias.action.AliasAction; import io.github.dsheirer.alias.action.AliasActionType; import io.github.dsheirer.alias.action.beep.BeepAction; -import io.github.dsheirer.alias.action.beep.BeepActionEditor; import io.github.dsheirer.alias.action.clip.ClipAction; -import io.github.dsheirer.alias.action.clip.ClipActionEditor; import io.github.dsheirer.alias.action.script.ScriptAction; -import io.github.dsheirer.alias.action.script.ScriptActionEditor; import io.github.dsheirer.alias.id.AliasID; import io.github.dsheirer.alias.id.AliasIDType; import io.github.dsheirer.alias.id.broadcast.BroadcastChannel; -import io.github.dsheirer.alias.id.broadcast.BroadcastChannelEditor; -import io.github.dsheirer.alias.id.esn.ESNEditor; import io.github.dsheirer.alias.id.esn.Esn; import io.github.dsheirer.alias.id.legacy.fleetsync.FleetsyncID; -import io.github.dsheirer.alias.id.legacy.fleetsync.FleetsyncIDEditor; import io.github.dsheirer.alias.id.legacy.mdc.MDC1200ID; -import io.github.dsheirer.alias.id.legacy.mdc.MDC1200IDEditor; -import io.github.dsheirer.alias.id.legacy.mobileID.MINEditor; import io.github.dsheirer.alias.id.legacy.mobileID.Min; import io.github.dsheirer.alias.id.legacy.mpt1327.MPT1327ID; -import io.github.dsheirer.alias.id.legacy.mpt1327.MPT1327IDEditor; import io.github.dsheirer.alias.id.legacy.nonrecordable.NonRecordable; -import io.github.dsheirer.alias.id.legacy.nonrecordable.NonRecordableEditor; import io.github.dsheirer.alias.id.legacy.siteID.SiteID; -import io.github.dsheirer.alias.id.legacy.siteID.SiteIDEditor; import io.github.dsheirer.alias.id.legacy.talkgroup.LegacyTalkgroupID; -import io.github.dsheirer.alias.id.legacy.talkgroup.LegacyTalkgroupIDEditor; import io.github.dsheirer.alias.id.legacy.uniqueID.UniqueID; -import io.github.dsheirer.alias.id.legacy.uniqueID.UniqueIDEditor; import io.github.dsheirer.alias.id.lojack.LoJackFunctionAndID; -import io.github.dsheirer.alias.id.lojack.LoJackIDEditor; import io.github.dsheirer.alias.id.priority.Priority; -import io.github.dsheirer.alias.id.priority.PriorityEditor; import io.github.dsheirer.alias.id.radio.Radio; -import io.github.dsheirer.alias.id.radio.RadioEditor; import io.github.dsheirer.alias.id.radio.RadioRange; -import io.github.dsheirer.alias.id.radio.RadioRangeEditor; import io.github.dsheirer.alias.id.record.Record; -import io.github.dsheirer.alias.id.record.RecordEditor; -import io.github.dsheirer.alias.id.status.StatusID; -import io.github.dsheirer.alias.id.status.StatusIDEditor; +import io.github.dsheirer.alias.id.status.UnitStatusID; +import io.github.dsheirer.alias.id.status.UserStatusID; import io.github.dsheirer.alias.id.talkgroup.Talkgroup; -import io.github.dsheirer.alias.id.talkgroup.TalkgroupEditor; import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange; -import io.github.dsheirer.alias.id.talkgroup.TalkgroupRangeEditor; -import io.github.dsheirer.audio.broadcast.BroadcastModel; -import io.github.dsheirer.gui.editor.Editor; -import io.github.dsheirer.gui.editor.EmptyEditor; +import io.github.dsheirer.alias.id.tone.TonesID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class AliasFactory { + private static final Logger mLog = LoggerFactory.getLogger(AliasFactory.class); + public static AliasID copyOf(AliasID id) { switch(id.getType()) @@ -87,11 +69,6 @@ public static AliasID copyOf(AliasID id) Esn copyESN = new Esn(); copyESN.setEsn(originalESN.getEsn()); return copyESN; - case LTR_NET_UID: - UniqueID originalUniqueID = (UniqueID)id; - UniqueID copyUniqueID = new UniqueID(); - copyUniqueID.setUid(originalUniqueID.getUid()); - return copyUniqueID; case LOJACK: LoJackFunctionAndID originalLoJackFunctionAndID = (LoJackFunctionAndID)id; LoJackFunctionAndID copyLoJackFunctionAndID = new LoJackFunctionAndID(); @@ -111,11 +88,13 @@ public static AliasID copyOf(AliasID id) case RADIO_ID: Radio originalRadio = (Radio)id; Radio copyRadio = new Radio(originalRadio.getProtocol(), originalRadio.getValue()); + copyRadio.setOverlap(originalRadio.overlapProperty().get()); return copyRadio; case RADIO_ID_RANGE: RadioRange originalRadioRange = (RadioRange)id; RadioRange copyRadioRange = new RadioRange(originalRadioRange.getProtocol(), originalRadioRange.getMinRadio(), originalRadioRange.getMaxRadio()); + copyRadioRange.setOverlap(originalRadioRange.overlapProperty().get()); return copyRadioRange; case RECORD: return new Record(); @@ -125,27 +104,45 @@ public static AliasID copyOf(AliasID id) copySiteID.setSite(originalSiteID.getSite()); return copySiteID; case STATUS: - StatusID originalStatusID = (StatusID)id; - StatusID copyStatusID = new StatusID(); - copyStatusID.setStatus(originalStatusID.getStatus()); - return copyStatusID; + UserStatusID originalUserStatusID = (UserStatusID)id; + UserStatusID copyUserStatusID = new UserStatusID(); + copyUserStatusID.setStatus(originalUserStatusID.getStatus()); + copyUserStatusID.setOverlap(originalUserStatusID.overlapProperty().get()); + return copyUserStatusID; case TALKGROUP: Talkgroup originalTalkgroup = (Talkgroup)id; Talkgroup copyTalkgroup = new Talkgroup(originalTalkgroup.getProtocol(), originalTalkgroup.getValue()); + copyTalkgroup.setOverlap(originalTalkgroup.overlapProperty().get()); return copyTalkgroup; case TALKGROUP_RANGE: TalkgroupRange originalRange = (TalkgroupRange)id; TalkgroupRange copyRange = new TalkgroupRange(originalRange.getProtocol(), originalRange.getMinTalkgroup(), originalRange.getMaxTalkgroup()); + copyRange.setOverlap(originalRange.overlapProperty().get()); return copyRange; + case TONES: + TonesID originalTones = (TonesID)id; + TonesID copyTones = new TonesID(); + copyTones.setToneSequence(originalTones.getToneSequence().copyOf()); + return copyTones; + case UNIT_STATUS: + UnitStatusID originalUnitStatus = (UnitStatusID)id; + UnitStatusID copyUnitStatusID = new UnitStatusID(); + copyUnitStatusID.setStatus(originalUnitStatus.getStatus()); + copyUnitStatusID.setOverlap(originalUnitStatus.overlapProperty().get()); + return copyUnitStatusID; //Legacy identifiers ... not supported case FLEETSYNC: case LEGACY_TALKGROUP: + case LTR_NET_UID: case MDC1200: case MPT1327: case NON_RECORDABLE: + return null; default: + mLog.warn("Unrecognized Alias ID Type [" + id.getType() + "] cannot make copy of instance"); + break; } return null; @@ -155,7 +152,11 @@ public static AliasAction copyOf(AliasAction action) { if(action instanceof BeepAction) { - return new BeepAction(); + BeepAction original = (BeepAction)action; + BeepAction copyBeep = new BeepAction(); + copyBeep.setInterval(original.getInterval()); + copyBeep.setPeriod(original.getPeriod()); + return copyBeep; } else if(action instanceof ClipAction) { @@ -182,12 +183,12 @@ else if(action instanceof ScriptAction) public static Alias copyOf(Alias original) { Alias copy = new Alias(original.getName()); - copy.setList(original.getList()); + copy.setAliasListName(original.getAliasListName()); copy.setGroup(original.getGroup()); copy.setColor(original.getColor()); copy.setIconName(original.getIconName()); - for(AliasID id : original.getId()) + for(AliasID id : original.getAliasIdentifiers()) { AliasID copyID = copyOf(id); @@ -197,7 +198,7 @@ public static Alias copyOf(Alias original) } } - for(AliasAction action : original.getAction()) + for(AliasAction action : original.getAliasActions()) { AliasAction copyAction = copyOf(action); @@ -210,77 +211,6 @@ public static Alias copyOf(Alias original) return copy; } - public static Editor getEditor(AliasID aliasID, BroadcastModel broadcastModel) - { - if(aliasID != null) - { - switch(aliasID.getType()) - { - case BROADCAST_CHANNEL: - return new BroadcastChannelEditor(aliasID, broadcastModel); - case ESN: - return new ESNEditor(aliasID); - case LTR_NET_UID: - return new UniqueIDEditor(aliasID); - case LOJACK: - return new LoJackIDEditor(aliasID); - case MIN: - return new MINEditor(aliasID); - case PRIORITY: - return new PriorityEditor(aliasID); - case RADIO_ID: - return new RadioEditor(aliasID); - case RADIO_ID_RANGE: - return new RadioRangeEditor(aliasID); - case RECORD: - return new RecordEditor(aliasID); - case SITE: - return new SiteIDEditor(aliasID); - case STATUS: - return new StatusIDEditor(aliasID); - case TALKGROUP: - return new TalkgroupEditor(aliasID); - case TALKGROUP_RANGE: - return new TalkgroupRangeEditor(aliasID); - - case FLEETSYNC: - return new FleetsyncIDEditor(aliasID); - case LEGACY_TALKGROUP: - return new LegacyTalkgroupIDEditor(aliasID); - case MDC1200: - return new MDC1200IDEditor(aliasID); - case MPT1327: - return new MPT1327IDEditor(aliasID); - case NON_RECORDABLE: - return new NonRecordableEditor(aliasID); - default: - break; - } - } - - return new EmptyEditor(); - } - - public static Editor getEditor(AliasAction aliasAction) - { - if(aliasAction != null) - { - switch(aliasAction.getType()) - { - case BEEP: - return new BeepActionEditor(aliasAction); - case CLIP: - return new ClipActionEditor(aliasAction); - case SCRIPT: - return new ScriptActionEditor(aliasAction); - default: - break; - } - } - - return new EmptyEditor(); - } - public static AliasID getAliasID(AliasIDType type) { switch(type) @@ -318,7 +248,7 @@ public static AliasID getAliasID(AliasIDType type) case SITE: return new SiteID(); case STATUS: - return new StatusID(); + return new UserStatusID(); case LEGACY_TALKGROUP: return new LegacyTalkgroupID(); default: diff --git a/src/main/java/io/github/dsheirer/alias/AliasList.java b/src/main/java/io/github/dsheirer/alias/AliasList.java index 36e4b5d63..6c2293ea0 100644 --- a/src/main/java/io/github/dsheirer/alias/AliasList.java +++ b/src/main/java/io/github/dsheirer/alias/AliasList.java @@ -1,7 +1,7 @@ /* * * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer + * * Copyright (C) 2014-2020 Dennis Sheirer * * * * 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 @@ -28,9 +28,11 @@ import io.github.dsheirer.alias.id.priority.Priority; import io.github.dsheirer.alias.id.radio.Radio; import io.github.dsheirer.alias.id.radio.RadioRange; -import io.github.dsheirer.alias.id.status.StatusID; +import io.github.dsheirer.alias.id.status.UnitStatusID; +import io.github.dsheirer.alias.id.status.UserStatusID; import io.github.dsheirer.alias.id.talkgroup.Talkgroup; import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange; +import io.github.dsheirer.alias.id.tone.TonesID; import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.identifier.IdentifierCollection; import io.github.dsheirer.identifier.esn.ESNIdentifier; @@ -40,8 +42,12 @@ import io.github.dsheirer.identifier.status.UnitStatusIdentifier; import io.github.dsheirer.identifier.status.UserStatusIdentifier; import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; +import io.github.dsheirer.identifier.tone.ToneIdentifier; +import io.github.dsheirer.identifier.tone.ToneSequence; import io.github.dsheirer.protocol.Protocol; import io.github.dsheirer.sample.Listener; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,9 +63,12 @@ public class AliasList implements Listener private Map mTalkgroupProtocolMap = new EnumMap<>(Protocol.class); private Map mRadioProtocolMap = new EnumMap<>(Protocol.class); private Map mESNMap = new HashMap<>(); - private Map mStatusMap = new HashMap<>(); + private Map mUnitStatusMap = new HashMap<>(); + private Map mUserStatusMap = new HashMap<>(); + private Map mToneSequenceMap = new HashMap<>(); private boolean mHasAliasActions = false; private String mName; + private ObservableList mAliases = FXCollections.observableArrayList(Alias.extractor()); /** * List of aliases where all aliases share the same list name. Contains @@ -72,6 +81,14 @@ public AliasList(String name) mName = name; } + /** + * Observable list of aliases contained in this alias list + */ + public ObservableList aliases() + { + return mAliases; + } + /** * Adds the alias to this list */ @@ -79,7 +96,7 @@ public void addAlias(Alias alias) { if(alias != null) { - for(AliasID aliasID : alias.getId()) + for(AliasID aliasID : alias.getAliasIdentifiers()) { addAliasID(aliasID, alias); } @@ -89,6 +106,8 @@ public void addAlias(Alias alias) { mHasAliasActions = true; } + + mAliases.add(alias); } /** @@ -163,7 +182,67 @@ private void addAliasID(AliasID id, Alias alias) } break; case STATUS: - mStatusMap.put(((StatusID) id).getStatus(), alias); + int userStatus = ((UserStatusID)id).getStatus(); + + if(mUserStatusMap.containsKey(userStatus)) + { + id.setOverlap(true); + + Alias existing = mUserStatusMap.get(userStatus); + + for(AliasID aliasID: existing.getAliasIdentifiers()) + { + if(aliasID instanceof UserStatusID && ((UserStatusID)aliasID).getStatus() == userStatus) + { + aliasID.setOverlap(true); + } + } + } + mUserStatusMap.put(userStatus, alias); + break; + case UNIT_STATUS: + int unitStatus = ((UnitStatusID)id).getStatus(); + + if(mUnitStatusMap.containsKey(unitStatus)) + { + id.setOverlap(true); + + Alias existing = mUnitStatusMap.get(unitStatus); + + for(AliasID aliasID: existing.getAliasIdentifiers()) + { + if(aliasID instanceof UnitStatusID && ((UnitStatusID)aliasID).getStatus() == unitStatus) + { + aliasID.setOverlap(true); + } + } + } + mUnitStatusMap.put(unitStatus, alias); + break; + case TONES: + ToneSequence toneSequence = ((TonesID)id).getToneSequence(); + + if(toneSequence != null) + { + if(mToneSequenceMap.containsKey(toneSequence)) + { + id.setOverlap(true); + + Alias existing = mToneSequenceMap.get(toneSequence); + + for(AliasID aliasID: existing.getAliasIdentifiers()) + { + if(aliasID instanceof TonesID && ((TonesID)aliasID).equals(id)) + { + aliasID.setOverlap(true); + } + } + } + else + { + mToneSequenceMap.put(toneSequence, alias); + } + } break; } } @@ -174,18 +253,112 @@ private void addAliasID(AliasID id, Alias alias) } } + /** + * Removes the alias and alias identifier to the internal type mapping. + */ + private void removeAliasID(AliasID id, Alias alias) + { + try + { + switch(id.getType()) + { + case TALKGROUP: + Talkgroup talkgroup = (Talkgroup)id; + + TalkgroupAliasList talkgroupAliasList = mTalkgroupProtocolMap.get(talkgroup.getProtocol()); + + if(talkgroupAliasList != null) + { + talkgroupAliasList.remove(talkgroup, alias); + } + break; + case TALKGROUP_RANGE: + TalkgroupRange talkgroupRange = (TalkgroupRange)id; + + TalkgroupAliasList talkgroupRangeAliasList = mTalkgroupProtocolMap.get(talkgroupRange.getProtocol()); + + if(talkgroupRangeAliasList != null) + { + talkgroupRangeAliasList.remove(talkgroupRange, alias); + } + break; + case RADIO_ID: + Radio radio = (Radio)id; + + RadioAliasList radioAliasList = mRadioProtocolMap.get(radio.getProtocol()); + + if(radioAliasList != null) + { + radioAliasList.remove(radio, alias); + } + break; + case RADIO_ID_RANGE: + RadioRange radioRange = (RadioRange)id; + + RadioAliasList radioRangeAliasList = mRadioProtocolMap.get(radioRange.getProtocol()); + + if(radioRangeAliasList != null) + { + radioRangeAliasList.remove(radioRange, alias); + } + break; + case ESN: + String esn = ((Esn) id).getEsn(); + + if(esn != null && !esn.isEmpty()) + { + String key = esn.toLowerCase(); + + if(mESNMap.containsKey(key) && mESNMap.get(key) == alias) + { + mESNMap.remove(key); + } + } + break; + case STATUS: + int status = ((UserStatusID)id).getStatus(); + + if(mUserStatusMap.containsKey(status) && mUserStatusMap.get(status) == alias) + { + mUserStatusMap.remove(status); + } + break; + case UNIT_STATUS: + int unitStatus = ((UnitStatusID)id).getStatus(); + + if(mUnitStatusMap.containsKey(unitStatus) && mUnitStatusMap.get(unitStatus) == alias) + { + mUnitStatusMap.remove(unitStatus); + } + break; + case TONES: + ToneSequence toneSequence = ((TonesID)id).getToneSequence(); + + if(toneSequence != null && mToneSequenceMap.containsKey(toneSequence) && + mToneSequenceMap.get(toneSequence) == alias) + { + mToneSequenceMap.remove(toneSequence); + } + break; + } + } + catch(Exception e) + { + mLog.error("Couldn't remove alias ID " + id + " for alias " + alias, e); + } + } + /** * Removes the alias from this list */ public void removeAlias(Alias alias) { - for(TalkgroupAliasList talkgroupAliasList: mTalkgroupProtocolMap.values()) + for(AliasID aliasID: alias.getAliasIdentifiers()) { - talkgroupAliasList.remove(alias); + removeAliasID(aliasID, alias); } - remove(alias, mStatusMap); - remove(alias, mESNMap); + mAliases.remove(alias); } /** @@ -241,20 +414,13 @@ public void receive(AliasEvent event) switch(event.getEvent()) { case ADD: - if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) + if(alias.getAliasListName() != null && getName().equalsIgnoreCase(alias.getAliasListName())) { addAlias(alias); } break; - case CHANGE: - if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) - { - removeAlias(alias); - addAlias(alias); - } - break; case DELETE: - if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) + if(alias.getAliasListName() != null && getName().equalsIgnoreCase(alias.getAliasListName())) { removeAlias(alias); } @@ -336,14 +502,31 @@ public List getAliases(Identifier identifier) if(identifier instanceof UnitStatusIdentifier) { int status = ((UnitStatusIdentifier)identifier).getValue(); - return toList(mStatusMap.get(status)); + return toList(mUserStatusMap.get(status)); } break; case USER_STATUS: if(identifier instanceof UserStatusIdentifier) { int status = ((UserStatusIdentifier)identifier).getValue(); - return toList(mStatusMap.get(status)); + return toList(mUserStatusMap.get(status)); + } + break; + case TONE: + if(identifier instanceof ToneIdentifier) + { + ToneSequence toneSequence = ((ToneIdentifier)identifier).getValue(); + + if(toneSequence != null && toneSequence.hasTones()) + { + for(Map.Entry entry: mToneSequenceMap.entrySet()) + { + if(entry.getKey().isContainedIn(toneSequence)) + { + return toList(entry.getValue()); + } + } + } } break; } @@ -477,13 +660,32 @@ public List getBroadcastChannels(IdentifierCollection identifi } /** - * Removes the alias (as a value) from the specified map + * Identifies all aliases that have alias identifiers that match the alias ID and are in the same alias list. + * @param alias for the list to search + * @param aliasID to find matches + * @return list of one or more aliases that have matching alias identifiers */ - public static void remove(Alias alias, Map map) + public List getOverlappingAliases(Alias alias, AliasID aliasID) { - map.entrySet().removeIf(entry -> alias.equals(((Map.Entry)entry).getValue())); - } + List aliases = new ArrayList<>(); + for(Alias otherAlias: mAliases) + { + if(otherAlias != alias) + { + for(AliasID otherId: otherAlias.getAliasIdentifiers()) + { + if(otherId.matches(aliasID) || otherId.overlaps(aliasID)) + { + aliases.add(otherAlias); + continue; + } + } + } + } + + return aliases; + } /** * Listing of talkgroups and ranges for a specific protocol @@ -520,16 +722,22 @@ public Alias getAlias(TalkgroupIdentifier identifier) public void add(Talkgroup talkgroup, Alias alias) { - //Detect talkgroup collisions + //Detect talkgroup collisions and set overlap flag for both if(mTalkgroupAliasMap.containsKey(talkgroup.getValue())) { Alias existing = mTalkgroupAliasMap.get(talkgroup.getValue()); if(!existing.equals(alias)) { - mLog.warn("Alias [" + alias.getName() + "] talkgroup [" + talkgroup.getValue() + - "] has the same talkgroup value as alias [" + existing.getName() + - "] - alias [" + alias.getName() + "] will be used for alias list [" + getName() + "]"); + talkgroup.setOverlap(true); + + for(AliasID aliasID: existing.getAliasIdentifiers()) + { + if(aliasID instanceof Talkgroup && ((Talkgroup)aliasID).getValue() == talkgroup.getValue()) + { + aliasID.setOverlap(true); + } + } } } @@ -543,32 +751,29 @@ public void add(TalkgroupRange talkgroupRange, Alias alias) { if(talkgroupRange.overlaps(entry.getKey()) && !entry.getValue().equals(alias)) { - mLog.warn("Alias [" + alias.getName() + "] with talkgroup range [" + talkgroupRange.toString() + - "] overlaps with alias [" + entry.getValue().getName() + - "] with talkgroup range [" + entry.getKey().toString() + "] for alias list [" + getName() + "]"); + talkgroupRange.setOverlap(true); + entry.getKey().setOverlap(true); } } mTalkgroupRangeAliasMap.put(talkgroupRange, alias); } - public void remove(Talkgroup talkgroup) - { - mTalkgroupAliasMap.remove(talkgroup.getValue()); - } - - public void remove(TalkgroupRange talkgroupRange) + public void remove(Talkgroup talkgroup, Alias alias) { - mTalkgroupRangeAliasMap.remove(talkgroupRange); + if(mTalkgroupAliasMap.containsKey(talkgroup.getValue()) && mTalkgroupAliasMap.get(talkgroup.getValue()) == alias) + { + mTalkgroupAliasMap.remove(talkgroup.getValue()); + } } - /** - * Removes the alias from all internal maps - */ - public void remove(Alias alias) + public void remove(TalkgroupRange talkgroupRange, Alias alias) { - AliasList.remove(alias, mTalkgroupAliasMap); - AliasList.remove(alias, mTalkgroupRangeAliasMap); + //Only remove the entry if both the key and the value match + if(mTalkgroupRangeAliasMap.containsKey(talkgroupRange) && mTalkgroupRangeAliasMap.get(talkgroupRange) == alias) + { + mTalkgroupRangeAliasMap.remove(talkgroupRange); + } } } @@ -614,9 +819,15 @@ public void add(Radio radio, Alias alias) if(!existing.equals(alias)) { - mLog.warn("Alias [" + alias.getName() + "] radio ID [" + radio.getValue() + - "] has the same value as alias [" + existing.getName() + - "] - alias [" + alias.getName() + "] will be used for alias list [" + getName() + "]"); + radio.setOverlap(true); + + for(AliasID aliasID: existing.getAliasIdentifiers()) + { + if(aliasID instanceof Radio && ((Radio)aliasID).getValue() == radio.getValue()) + { + aliasID.setOverlap(true); + } + } } } @@ -630,32 +841,30 @@ public void add(RadioRange radioRange, Alias alias) { if(radioRange.overlaps(entry.getKey()) && !entry.getValue().equals(alias)) { - mLog.warn("Alias [" + alias.getName() + "] with radio ID range [" + radioRange.toString() + - "] overlaps with alias [" + entry.getValue().getName() + - "] with range [" + entry.getKey().toString() + "] for alias list [" + getName() + "]"); + radioRange.setOverlap(true); + entry.getKey().setOverlap(true); } } mRadioRangeAliasMap.put(radioRange, alias); } - public void remove(Radio radio) - { - mRadioAliasMap.remove(radio.getValue()); - } - - public void remove(RadioRange radioRange) + public void remove(Radio radio, Alias alias) { - mRadioRangeAliasMap.remove(radioRange); + //Only remove the entry if both the key and the value match + if(mRadioAliasMap.containsKey(radio.getValue()) && mRadioAliasMap.get(radio.getValue()) == alias) + { + mRadioAliasMap.remove(radio.getValue()); + } } - /** - * Removes the alias from all internal maps - */ - public void remove(Alias alias) + public void remove(RadioRange radioRange, Alias alias) { - AliasList.remove(alias, mRadioAliasMap); - AliasList.remove(alias, mRadioRangeAliasMap); + //Only remove the entry if both the key and the value match + if(mRadioRangeAliasMap.containsKey(radioRange) && mRadioRangeAliasMap.get(radioRange) == alias) + { + mRadioRangeAliasMap.remove(radioRange); + } } } } diff --git a/src/main/java/io/github/dsheirer/alias/AliasModel.java b/src/main/java/io/github/dsheirer/alias/AliasModel.java index 9b990173d..e14e64570 100644 --- a/src/main/java/io/github/dsheirer/alias/AliasModel.java +++ b/src/main/java/io/github/dsheirer/alias/AliasModel.java @@ -1,59 +1,73 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer * - * 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 3 of the License, or - * (at your option) any later version. + * * ****************************************************************************** + * * Copyright (C) 2014-2020 Dennis Sheirer + * * + * * 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 3 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, see + * * ***************************************************************************** * - * 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, see - * ***************************************************************************** */ package io.github.dsheirer.alias; +import io.github.dsheirer.alias.id.AliasID; +import io.github.dsheirer.alias.id.AliasIDType; import io.github.dsheirer.alias.id.broadcast.BroadcastChannel; import io.github.dsheirer.identifier.IdentifierCollection; import io.github.dsheirer.identifier.configuration.AliasListConfigurationIdentifier; import io.github.dsheirer.sample.Broadcaster; import io.github.dsheirer.sample.Listener; +import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import javax.swing.table.AbstractTableModel; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; /** * Alias Model contains all aliases and is responsible for creation and management of alias lists. Alias lists are a * set of aliases that all share a common alias list name and can be attached to a decoding channel for aliasing * identifiers produced by channel decoder(s). */ -public class AliasModel extends AbstractTableModel +public class AliasModel { - private static final long serialVersionUID = 1L; - - public static final int COLUMN_LIST = 0; - public static final int COLUMN_GROUP = 1; - public static final int COLUMN_NAME = 2; - public static final int COLUMN_ICON = 3; - public static final int COLUMN_COLOR = 4; - - private List mAliases = new CopyOnWriteArrayList<>(); + private final static Logger mLog = LoggerFactory.getLogger(AliasModel.class); + public static final String NO_ALIAS_LIST = "(No Alias List)"; + private ObservableList mAliases = FXCollections.observableArrayList(Alias.extractor()); + private ObservableList mAliasListNames = FXCollections.observableArrayList(); private Broadcaster mAliasEventBroadcaster = new Broadcaster<>(); private Map mAliasListMap = new HashMap<>(); public AliasModel() { + //Register a listener to detect alias changes and broadcast change events to cause playlist save requests + mAliases.addListener(new AliasListChangeListener()); + } + + public ObservableList aliasList() + { + return mAliases; + } + + public ObservableList aliasListNames() + { + return mAliasListNames; } /** @@ -64,14 +78,17 @@ public List getAliases() return Collections.unmodifiableList(mAliases); } - public Alias getAliasAtIndex(int row) + /** + * Removes all aliases from the list and broadcasts the alias delete event for each + */ + public void clear() { - if(mAliases.size() >= row) + List aliasToRemove = new ArrayList<>(mAliases); + + for(Alias alias: aliasToRemove) { - return mAliases.get(row); + removeAlias(alias); } - - return null; } /** @@ -126,7 +143,7 @@ public AliasList getAliasList(String name) for(Alias alias : mAliases) { - if(alias.hasList() && alias.getList().equalsIgnoreCase(name)) + if(alias.hasList() && alias.getAliasListName().equalsIgnoreCase(name)) { aliasList.addAlias(alias); } @@ -145,19 +162,7 @@ public AliasList getAliasList(String name) */ public List getListNames() { - List listNames = new ArrayList<>(); - - for(Alias alias : mAliases) - { - if(alias.hasList() && !listNames.contains(alias.getList())) - { - listNames.add(alias.getList()); - } - } - - Collections.sort(listNames); - - return listNames; + return mAliasListNames; } /** @@ -180,33 +185,6 @@ public List getGroupNames() return groupNames; } - /** - * Returns a list of alias group names for all aliases that have a matching - * list name value - */ - public List getGroupNames(String listName) - { - List groupNames = new ArrayList<>(); - - if(listName != null) - { - for(Alias alias : mAliases) - { - if(alias.hasList() && - alias.hasGroup() && - listName.equals(alias.getList()) && - !groupNames.contains(alias.getGroup())) - { - groupNames.add(alias.getGroup()); - } - } - } - - Collections.sort(groupNames); - - return groupNames; - } - /** * Bulk loading of aliases */ @@ -226,19 +204,33 @@ public int addAlias(Alias alias) if(alias != null) { mAliases.add(alias); - + addAliasList(alias.getAliasListName()); int index = mAliases.size() - 1; - - fireTableRowsInserted(index, index); - broadcast(new AliasEvent(alias, AliasEvent.Event.ADD)); - return index; } return -1; } + public void addAliasList(String aliasListName) + { + if(aliasListName != null && !aliasListName.isEmpty()) + { + if(!mAliasListNames.contains(aliasListName)) + { + mAliasListNames.add(aliasListName); + FXCollections.sort(mAliasListNames); + } + } + else if(!mAliasListNames.contains(NO_ALIAS_LIST)) + { + //This list allows users to view unassigned aliases so that they can move them to a valid alias list, but + // it is not assignable to a channel + mAliasListNames.add(NO_ALIAS_LIST); + } + } + /** * Removes the channel from the model and broadcasts a channel remove event */ @@ -246,119 +238,94 @@ public void removeAlias(Alias alias) { if(alias != null) { - int index = mAliases.indexOf(alias); - mAliases.remove(alias); - - fireTableRowsDeleted(index, index); - broadcast(new AliasEvent(alias, AliasEvent.Event.DELETE)); } } /** - * Renames any broadcast channels that have the previous name. - * - * @param previousName to rename - * @param newName to assign to the broadcast channel + * Retrieves all aliases that match the alias list and have at least one alias ID of the specified type + * @param aliasListName to search + * @param type to find + * @return list of aliases */ - public void renameBroadcastChannel(String previousName, String newName) + public List getAliases(String aliasListName, AliasIDType type) { - if(previousName == null || previousName.isEmpty() || newName == null || newName.isEmpty()) - { - return; - } + List aliases = new ArrayList<>(); for(Alias alias : mAliases) { - if(alias.hasBroadcastChannel(previousName)) + if(alias.hasList() && alias.getAliasListName().equalsIgnoreCase(aliasListName)) { - for(BroadcastChannel broadcastChannel : alias.getBroadcastChannels()) + for(AliasID aliasID: alias.getAliasIdentifiers()) { - if(broadcastChannel.getChannelName().contentEquals(previousName)) + if(aliasID.getType() == type) { - broadcastChannel.setChannelName(newName); + aliases.add(alias); + break; } } } } - } - - @Override - public int getRowCount() - { - return mAliases.size(); - } - @Override - public int getColumnCount() - { - return 5; + return aliases; } - @Override - public String getColumnName(int columnIndex) + /** + * Indicates that one or more of the aliases managed by this model are configured to stream to the specified + * broadcast channel argument. + * @param broadcastChannel to check + * @return true if the broadcast channel is non-null, non-empty and at least one alias is configured to stream to + * the specified stream name. + */ + public boolean hasAliasesWithBroadcastChannel(String broadcastChannel) { - switch(columnIndex) + if(broadcastChannel == null || broadcastChannel.isEmpty()) { - case COLUMN_LIST: - return "List"; - case COLUMN_GROUP: - return "Group"; - case COLUMN_NAME: - return "Name"; - case COLUMN_ICON: - return "Icon"; - case COLUMN_COLOR: - return "Color"; + return false; } - return null; - } - - @Override - public Class getColumnClass(int columnIndex) - { - if(columnIndex == COLUMN_COLOR) + for(Alias alias: mAliases) { - return Integer.class; + if(alias.hasBroadcastChannel(broadcastChannel)) + { + return true; + } } - return String.class; - } - - @Override - public boolean isCellEditable(int rowIndex, int columnIndex) - { return false; } - @Override - public Object getValueAt(int rowIndex, int columnIndex) + /** + * Updates all aliases configured to stream to the previousStreamName with the updatedStreamName + * @param previousStreamName to be removed + * @param updatedStreamName to be added + */ + public void updateBroadcastChannel(String previousStreamName, String updatedStreamName) { - Alias alias = mAliases.get(rowIndex); - - switch(columnIndex) + if(previousStreamName == null || previousStreamName.isEmpty() || updatedStreamName == null || updatedStreamName.isEmpty()) { - case COLUMN_LIST: - return alias.getList(); - case COLUMN_GROUP: - return alias.getGroup(); - case COLUMN_NAME: - return alias.getName(); - case COLUMN_ICON: - return alias.getIconName(); - case COLUMN_COLOR: - return alias.getColor(); + return; } - return null; - } + for(Alias alias: mAliases) + { + if(alias.hasBroadcastChannel(previousStreamName)) + { + for(BroadcastChannel broadcastChannel: alias.getBroadcastChannels()) + { + if(broadcastChannel.getChannelName().contentEquals(previousStreamName)) + { + alias.removeAliasID(broadcastChannel); - @Override - public void setValueAt(Object aValue, int rowIndex, int columnIndex) - { - throw new IllegalArgumentException("Not yet implemented"); + if(!alias.hasBroadcastChannel(updatedStreamName)) + { + alias.addAliasID(new BroadcastChannel(updatedStreamName)); + } + } + } + } + } } public void broadcast(AliasEvent event) @@ -371,13 +338,6 @@ public void broadcast(AliasEvent event) alias.validate(); } - if(event.getEvent() == AliasEvent.Event.CHANGE) - { - int index = mAliases.indexOf(event.getAlias()); - - fireTableRowsUpdated(index, index); - } - mAliasEventBroadcaster.broadcast(event); } @@ -390,4 +350,40 @@ public void removeListener(Listener listener) { mAliasEventBroadcaster.removeListener(listener); } + + + /** + * Observable list change listener for both channels and traffic channels lists + */ + public class AliasListChangeListener implements ListChangeListener + { + @Override + public void onChanged(ListChangeListener.Change change) + { + while(change.next()) + { + if(change.wasAdded()) + { + for(Alias alias: change.getAddedSubList()) + { + mAliasEventBroadcaster.broadcast(new AliasEvent(alias, AliasEvent.Event.ADD)); + } + } + else if(change.wasRemoved()) + { + for(Alias alias: change.getRemoved()) + { + mAliasEventBroadcaster.broadcast(new AliasEvent(alias, AliasEvent.Event.DELETE)); + } + } + else if(change.wasUpdated()) + { + for(int x = change.getFrom(); x < change.getTo(); x++) + { + mAliasEventBroadcaster.broadcast(new AliasEvent(change.getList().get(x), AliasEvent.Event.CHANGE)); + } + } + } + } + } } diff --git a/src/main/java/io/github/dsheirer/alias/AliasNameEditor.java b/src/main/java/io/github/dsheirer/alias/AliasNameEditor.java deleted file mode 100644 index c0c2f74d8..000000000 --- a/src/main/java/io/github/dsheirer/alias/AliasNameEditor.java +++ /dev/null @@ -1,333 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer - * - * 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 3 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, see - * - ******************************************************************************/ -package io.github.dsheirer.alias; - -import io.github.dsheirer.gui.editor.Editor; -import io.github.dsheirer.icon.Icon; -import io.github.dsheirer.icon.IconListCellRenderer; -import io.github.dsheirer.icon.IconManager; -import io.github.dsheirer.icon.IconTableModel; -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.event.TableModelEvent; -import javax.swing.event.TableModelListener; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.List; - -public class AliasNameEditor extends Editor -{ - private static final long serialVersionUID = 1L; - - private static ComboBoxModel EMPTY_LIST_MODEL = new DefaultComboBoxModel<>(); - private static ComboBoxModel EMPTY_GROUP_MODEL = new DefaultComboBoxModel<>(); - - private JComboBox mListCombo = new JComboBox<>(EMPTY_LIST_MODEL); - private JComboBox mGroupCombo = new JComboBox<>(EMPTY_GROUP_MODEL); - private JComboBox mIconCombo; - private JTextField mName; - private JButton mButtonColor; - private JButton mBtnIconManager; - - private AliasModel mAliasModel; - private IconManager mIconManager; - - public AliasNameEditor(AliasModel aliasModel, IconManager iconManager) - { - mAliasModel = aliasModel; - mIconManager = iconManager; - - mIconManager.getModel().addTableModelListener(new TableModelListener() - { - @Override - public void tableChanged(TableModelEvent e) - { - refreshIcons(); - } - }); - - init(); - } - - private void init() - { - setLayout(new MigLayout("fill,wrap 2", "[right][grow,fill]", "[][][][][][][grow]")); - - add(new JLabel("Name:")); - mName = new JTextField(); - mName.getDocument().addDocumentListener(new DocumentListener() - { - @Override - public void removeUpdate(DocumentEvent e) - { - setModified(true); - } - - @Override - public void insertUpdate(DocumentEvent e) - { - setModified(true); - } - - @Override - public void changedUpdate(DocumentEvent e) - { - } - }); - add(mName, "wrap"); - - add(new JLabel("List:")); - mListCombo.setEditable(true); - mListCombo.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - if(mListCombo.getSelectedItem() != null) - { - List groups = mAliasModel - .getGroupNames((String) mListCombo.getSelectedItem()); - - if(groups.isEmpty()) - { - mGroupCombo.setModel(EMPTY_GROUP_MODEL); - } - else - { - mGroupCombo.setModel(new DefaultComboBoxModel( - groups.toArray(new String[0]))); - ; - } - } - - setModified(true); - } - }); - add(mListCombo, "wrap"); - - add(new JLabel("Group:")); - mGroupCombo.setEditable(true); - mGroupCombo.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - setModified(true); - } - }); - add(mGroupCombo, "wrap"); - - add(new JLabel("Color:")); - - mButtonColor = new JButton("Select ..."); - mButtonColor.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - Color newColor = JColorChooser.showDialog( - AliasNameEditor.this, - "Choose color for this alias", - (hasItem() ? getItem().getDisplayColor() : null)); - - if(newColor != null) - { - mButtonColor.setForeground(newColor); - mButtonColor.setBackground(newColor); - - setModified(true); - } - } - }); - add(mButtonColor, "wrap"); - - add(new JLabel("Icon:")); - - mIconCombo = new JComboBox(mIconManager.getIcons()); - - IconListCellRenderer renderer = new IconListCellRenderer(mIconManager); - renderer.setPreferredSize(new Dimension(200, 30)); - mIconCombo.setRenderer(renderer); - mIconCombo.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - setModified(true); - } - }); - - add(mIconCombo, "wrap"); - - //Dummy place holder - add(new JLabel()); - - mBtnIconManager = new JButton("Icon Manager"); - mBtnIconManager.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent arg0) - { - mIconManager.showEditor(AliasNameEditor.this); - } - }); - - add(mBtnIconManager, "span 2,wrap"); - - setModified(false); - } - - private void refreshIcons() - { - if(mIconCombo != null) - { - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - mIconCombo.setModel(new DefaultComboBoxModel<>(mIconManager.getIcons())); - - if(hasItem()) - { - String iconName = getItem().getIconName(); - Icon aliasIcon = mIconManager.getModel().getIcon(iconName); - - if(aliasIcon != null) - { - mIconCombo.setSelectedItem(aliasIcon); - } - } - } - }); - } - } - - @Override - public void setItem(Alias alias) - { - super.setItem(alias); - - if(hasItem()) - { - mName.setText(alias.getName()); - - mIconCombo.setModel(new DefaultComboBoxModel<>(mIconManager.getIcons())); - - List listNames = mAliasModel.getListNames(); - - if(listNames.isEmpty()) - { - mListCombo.setModel(EMPTY_LIST_MODEL); - } - else - { - mListCombo.setModel(new DefaultComboBoxModel( - listNames.toArray(new String[0]))); - ; - } - - mListCombo.setSelectedItem(alias.getList()); - - List groupNames = mAliasModel.getGroupNames(alias.getList()); - - if(groupNames.isEmpty()) - { - mGroupCombo.setModel(EMPTY_GROUP_MODEL); - } - else - { - mGroupCombo.setModel(new DefaultComboBoxModel( - groupNames.toArray(new String[0]))); - ; - } - - mGroupCombo.setSelectedItem(alias.getGroup()); - - Color color = alias.getDisplayColor(); - - mButtonColor.setBackground(color); - mButtonColor.setForeground(color); - - String iconName = alias.getIconName(); - - if(iconName == null) - { - iconName = IconTableModel.DEFAULT_ICON; - } - - Icon savedIcon = mIconManager.getModel().getIcon(iconName); - - if(savedIcon != null) - { - mIconCombo.setSelectedItem(savedIcon); - } - } - else - { - mListCombo.setModel(EMPTY_LIST_MODEL); - mGroupCombo.setModel(EMPTY_GROUP_MODEL); - mName.setText(null); - - mButtonColor.setBackground(getBackground()); - mButtonColor.setForeground(getForeground()); - } - - repaint(); - - setModified(false); - } - - @Override - public void save() - { - if(hasItem() && isModified()) - { - Alias alias = getItem(); - - if(mListCombo.getSelectedItem() != null) - { - alias.setList((String) mListCombo.getSelectedItem()); - } - - if(mGroupCombo.getSelectedItem() != null) - { - alias.setGroup((String) mGroupCombo.getSelectedItem()); - } - - alias.setName(mName.getText()); - - alias.setColor(mButtonColor.getBackground().getRGB()); - - if(mIconCombo.getSelectedItem() != null) - { - alias.setIconName(((Icon) mIconCombo.getSelectedItem()).getName()); - } - - setModified(false); - - //Broadcast an alias change event to save the updates - mAliasModel.broadcast(new AliasEvent(getItem(), AliasEvent.Event.CHANGE)); - } - } -} diff --git a/src/main/java/io/github/dsheirer/alias/ComponentEditor.java b/src/main/java/io/github/dsheirer/alias/ComponentEditor.java deleted file mode 100644 index 7bae5fcff..000000000 --- a/src/main/java/io/github/dsheirer/alias/ComponentEditor.java +++ /dev/null @@ -1,121 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * 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 3 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, see - ******************************************************************************/ -package io.github.dsheirer.alias; - -import javax.swing.*; -import javax.swing.event.DocumentListener; - -public abstract class ComponentEditor extends JPanel implements DocumentListener -{ - private static final long serialVersionUID = 1L; - private boolean mModified; - private ComponentModificationListener mListener; - protected T mComponent; - - /** - * Abstract class for editor with notifications when contents are modified - * and methods for saving or resetting the component values with the - * contents of the editor. - */ - public ComponentEditor( T component ) - { - mComponent = component; - } - - public ComponentEditor() - { - } - - public void dispose() - { - mComponent = null; - mListener = null; - } - - public T getComponent() - { - return mComponent; - } - - protected void setModified( boolean modified ) - { - mModified = modified; - - if( modified && mListener != null ) - { - mListener.componentModified(); - } - } - - /** - * Indicates if the contents of the editor have been modified - */ - public boolean isModified() - { - return mModified; - } - - public void setComponentModificationListener( ComponentModificationListener listener ) - { - mListener = listener; - } - - /** - * Sets or resets the editable component - */ - public abstract void setComponent( T t ); - - /** - * Saves changed editor values to the component - */ - public abstract void save(); - - /** - * Reloads the editor contents from the source component and resets the - * modified flag - */ - public void reset() - { - setComponent( getComponent() ); - } - - /** - * Validate contents of other editors against this component editor before - * saving. Override this method to allow controller to pass other editors - * to this editor for content inspection/validation before indicating that - * this editor passes validation. This allows for validating editor contents - * that are contingent on settings in other editors. - * - * @throws ComponentValidationException if the contents of the editor - * argument cannot be validated, with the exception containing a detailed - * description of the issue. - */ - public void validate( ComponentEditor editor ) throws ComponentValidationException - { - } - - /** - * Listener interface to receive notification that the contents of the - * editor have been modified from the original component value and either - * need to be saved or reset - */ - public interface ComponentModificationListener - { - public void componentModified(); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/ComponentValidationException.java b/src/main/java/io/github/dsheirer/alias/ComponentValidationException.java deleted file mode 100644 index 9bf0445a5..000000000 --- a/src/main/java/io/github/dsheirer/alias/ComponentValidationException.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.dsheirer.alias; - -public class ComponentValidationException extends Exception -{ - private static final long serialVersionUID = 1L; - - /** - * Component editor contents validation error - */ - public ComponentValidationException( String message ) - { - super( message ); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/MultipleAliasEditor.java b/src/main/java/io/github/dsheirer/alias/MultipleAliasEditor.java deleted file mode 100644 index d2d9c6552..000000000 --- a/src/main/java/io/github/dsheirer/alias/MultipleAliasEditor.java +++ /dev/null @@ -1,466 +0,0 @@ -package io.github.dsheirer.alias; - -import io.github.dsheirer.alias.id.AliasIDType; -import io.github.dsheirer.alias.id.broadcast.BroadcastChannel; -import io.github.dsheirer.alias.id.priority.Priority; -import io.github.dsheirer.audio.broadcast.BroadcastModel; -import io.github.dsheirer.gui.editor.Editor; -import io.github.dsheirer.icon.Icon; -import io.github.dsheirer.icon.IconListCellRenderer; -import io.github.dsheirer.icon.IconManager; -import io.github.dsheirer.sample.Listener; -import net.miginfocom.swing.MigLayout; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.event.TableModelEvent; -import javax.swing.event.TableModelListener; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.List; - -public class MultipleAliasEditor extends Editor> - implements Listener -{ - private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger(MultipleAliasEditor.class); - - private static String HELP_TEXT = - "Select attributes below to change for all selected aliases"; - - private JLabel mAliasCountLabel; - private JLabel mAliasCount; - - private JCheckBox mListCheckBox = new JCheckBox("List"); - private JComboBox mListCombo = new JComboBox<>(); - private JCheckBox mGroupCheckBox = new JCheckBox("Group"); - private JComboBox mGroupCombo = new JComboBox<>(); - private JCheckBox mIconCheckBox = new JCheckBox("Icon"); - private JComboBox mIconCombo; - private JCheckBox mColorCheckBox = new JCheckBox("Color"); - private JButton mButtonColor; - private JButton mBtnIconManager; - private JCheckBox mRecordCheckBox = new JCheckBox("Record"); - private JComboBox mRecordActionCombo = new JComboBox<>(Record.values()); - private JCheckBox mPriorityCheckBox = new JCheckBox("Priority"); - private JSlider mPrioritySlider; - private JLabel mPrioritySliderLabel; - private JCheckBox mStreamCheckBox = new JCheckBox("Stream"); - private JComboBox mStreamCombo; - - private AliasModel mAliasModel; - private BroadcastModel mBroadcastModel; - private IconManager mIconManager; - - public MultipleAliasEditor(AliasModel aliasModel, BroadcastModel broadcastModel, IconManager iconManager) - { - mAliasModel = aliasModel; - mBroadcastModel = broadcastModel; - mIconManager = iconManager; - - mAliasModel.addListener(this); - mIconManager.getModel().addTableModelListener(new TableModelListener() - { - @Override - public void tableChanged(TableModelEvent e) - { - refreshIcons(); - } - }); - - init(); - } - - public List getAliases() - { - if (hasItem()) - { - return (List) getItem(); - } - - return null; - } - - public void init() - { - setLayout(new MigLayout("fill,wrap 2", "[grow,fill][grow,fill]", - "[][][][][][][][][][][][][][grow,fill]")); - - mAliasCountLabel = new JLabel("Alias:"); - add(mAliasCountLabel); - - mAliasCount = new JLabel("Multiple"); - add(mAliasCount, "span"); - - add(new JSeparator(), "span"); - - add(new JLabel(HELP_TEXT), "span"); - - add(mListCheckBox); - - mListCombo.setEditable(true); - add(mListCombo, "wrap"); - - add(mGroupCheckBox); - - mGroupCombo.setEditable(true); - add(mGroupCombo, "wrap"); - - add(mColorCheckBox); - - mButtonColor = new JButton("Select ..."); - mButtonColor.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - Color newColor = JColorChooser.showDialog( - MultipleAliasEditor.this, - "Choose color for this alias", null); - - if (newColor != null) - { - mButtonColor.setForeground(newColor); - mButtonColor.setBackground(newColor); - } - } - }); - add(mButtonColor, "wrap"); - - add(mIconCheckBox); - - mIconCombo = new JComboBox(mIconManager.getIcons()); - - IconListCellRenderer renderer = new IconListCellRenderer(mIconManager); - renderer.setPreferredSize(new Dimension(200, 30)); - mIconCombo.setRenderer(renderer); - add(mIconCombo, "wrap"); - - //Dummy place holder - add(new JLabel()); - - mBtnIconManager = new JButton("Icon Manager"); - mBtnIconManager.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent arg0) - { - mIconManager.showEditor(MultipleAliasEditor.this); - } - }); - - add(mBtnIconManager, "span,wrap"); - - add(mRecordCheckBox); - add(mRecordActionCombo); - - //Placeholder - add(new JLabel(" ")); - mPrioritySliderLabel = new JLabel("Audio Priority: " + Priority.MIN_PRIORITY); - add(mPrioritySliderLabel); - - add(mPriorityCheckBox); - - mPrioritySlider = new JSlider(JSlider.HORIZONTAL, - Priority.MIN_PRIORITY, - Priority.MAX_PRIORITY + 1, - Priority.MIN_PRIORITY); - - mPrioritySlider.setMajorTickSpacing(20); - mPrioritySlider.setMinorTickSpacing(5); - mPrioritySlider.setPaintTicks(true); - mPrioritySlider.setLabelTable(mPrioritySlider.createStandardLabels(25, 25)); - mPrioritySlider.setPaintLabels(true); - mPrioritySlider.addChangeListener(new ChangeListener() - { - @Override - public void stateChanged(ChangeEvent e) - { - int priority = mPrioritySlider.getValue(); - - if (priority > Priority.MAX_PRIORITY) - { - mPrioritySliderLabel.setText("Audio Priority: Do Not Monitor"); - } - else - { - mPrioritySliderLabel.setText("Audio Priority: " + mPrioritySlider.getValue()); - } - } - }); - mPrioritySlider.setToolTipText(HELP_TEXT); - - add(mPrioritySlider); - - add(mStreamCheckBox); - - mStreamCombo = new JComboBox(); - mStreamCombo.setEditable(true); - add(mStreamCombo, "wrap"); - - JButton saveButton = new JButton("Save"); - saveButton.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - save(); - } - }); - add(saveButton); - - JButton resetButton = new JButton("Reset"); - resetButton.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - reset(); - } - }); - add(resetButton); - - setModified(false); - } - - private void refreshIcons() - { - if(mIconCombo != null) - { - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - mIconCombo.setModel(new DefaultComboBoxModel<>(mIconManager.getIcons())); - } - }); - } - } - - @Override - public void setItem(List item) - { - super.setItem(item); - - StringBuilder sb = new StringBuilder(); - sb.append("Multiple Selected [ "); - - if (hasItem()) - { - sb.append(String.valueOf(getAliases().size())); - } - else - { - sb.append("0"); - } - - sb.append(" ]"); - - mAliasCount.setText(sb.toString()); - - List listNames = mAliasModel.getListNames(); - listNames.add(0, ""); - mListCombo.setModel(new DefaultComboBoxModel( - listNames.toArray(new String[0]))); - - List groupNames = mAliasModel.getGroupNames(); - groupNames.add(0, ""); - mGroupCombo.setModel(new DefaultComboBoxModel( - groupNames.toArray(new String[0]))); - - List streamNames = mBroadcastModel.getBroadcastConfigurationNames(); - mStreamCombo.setModel(new DefaultComboBoxModel(streamNames.toArray(new String[0]))); - - mIconCombo.setModel(new DefaultComboBoxModel<>(mIconManager.getIcons())); - - setCheckBoxesUnselected(); - - setModified(false); - } - - private void setCheckBoxesUnselected() - { - mListCheckBox.setSelected(false); - mGroupCheckBox.setSelected(false); - mColorCheckBox.setSelected(false); - mIconCheckBox.setSelected(false); - mRecordCheckBox.setSelected(false); - mPriorityCheckBox.setSelected(false); - mStreamCheckBox.setSelected(false); - } - - @Override - public void reset() - { - List listNames = mAliasModel.getListNames(); - listNames.add(0, ""); - mListCombo.setModel(new DefaultComboBoxModel( - listNames.toArray(new String[0]))); - - List groupNames = mAliasModel.getGroupNames(); - groupNames.add(0, ""); - mGroupCombo.setModel(new DefaultComboBoxModel( - groupNames.toArray(new String[0]))); - - mButtonColor.setForeground(getForeground()); - mButtonColor.setBackground(getBackground()); - - super.reset(); - } - - private boolean hasRequestedChanges() - { - return mListCheckBox.isSelected() || - mGroupCheckBox.isSelected() || - mColorCheckBox.isSelected() || - mIconCheckBox.isSelected() || - mRecordCheckBox.isSelected() || - mPriorityCheckBox.isSelected() || - mStreamCheckBox.isSelected(); - } - - @Override - public void save() - { - if (hasRequestedChanges() && hasItem()) - { - List aliases = getAliases(); - - for (Alias alias : aliases) - { - if (mListCheckBox.isSelected()) - { - String list = null; - - if (mListCombo.getSelectedItem() != null) - { - list = (String) mListCombo.getSelectedItem(); - } - - alias.setList(list); - } - - if (mGroupCheckBox.isSelected()) - { - String group = null; - - if (mGroupCombo.getSelectedItem() != null) - { - group = (String) mGroupCombo.getSelectedItem(); - } - - alias.setGroup(group); - } - - if (mColorCheckBox.isSelected()) - { - alias.setColor(mButtonColor.getForeground().getRGB()); - } - - if (mIconCheckBox.isSelected()) - { - if (mIconCombo.getSelectedItem() != null) - { - alias.setIconName(((Icon)mIconCombo.getSelectedItem()).getName()); - } - else - { - alias.setIconName(null); - } - } - - if (mRecordCheckBox.isSelected()) - { - Record action = - (Record) mRecordActionCombo.getSelectedItem(); - - alias.setRecordable(action == Record.RECORDABLE); - } - - if (mPriorityCheckBox.isSelected()) - { - int priority = mPrioritySlider.getValue(); - - //This is a work-around -- we use max priority + 1 in the - //gui to indicate do not monitor, and change the value here - if (priority == Priority.MAX_PRIORITY + 1) - { - priority = Priority.DO_NOT_MONITOR; - } - - alias.setCallPriority(priority); - } - - if(mStreamCheckBox.isSelected()) - { - if(mStreamCombo.getSelectedItem() != null) - { - String streamName = (String)mStreamCombo.getSelectedItem(); - - boolean hasChannel = false; - - //Ensure the alias doesn't already have the channel - for(BroadcastChannel channel: alias.getBroadcastChannels()) - { - if(channel.getChannelName().equals(streamName)) - { - hasChannel = true; - continue; - } - } - - if(!hasChannel) - { - BroadcastChannel channel = (BroadcastChannel)AliasFactory - .getAliasID(AliasIDType.BROADCAST_CHANNEL); - - channel.setChannelName(streamName); - alias.addAliasID(channel); - } - } - } - } - - for (Alias alias : aliases) - { - //Broadcast an alias change event to save the updates - mAliasModel.broadcast(new AliasEvent(alias, AliasEvent.Event.CHANGE)); - } - } - } - - @Override - public void receive(AliasEvent event) - { - if (event.getEvent() == AliasEvent.Event.DELETE && - hasItem() && - getAliases().contains(event.getAlias())) - { - getAliases().remove(event.getAlias()); - setItem(getAliases()); - } - } - - public enum Record - { - NON_RECORDABLE("Non-Recordable"), - RECORDABLE("Recordable"); - - private String mLabel; - - private Record(String label) - { - mLabel = label; - } - - public String toString() - { - return mLabel; - } - } -} diff --git a/src/main/java/io/github/dsheirer/alias/action/AliasAction.java b/src/main/java/io/github/dsheirer/alias/action/AliasAction.java index 6018019da..8c009de39 100644 --- a/src/main/java/io/github/dsheirer/alias/action/AliasAction.java +++ b/src/main/java/io/github/dsheirer/alias/action/AliasAction.java @@ -27,6 +27,9 @@ import io.github.dsheirer.alias.action.clip.ClipAction; import io.github.dsheirer.alias.action.script.ScriptAction; import io.github.dsheirer.message.IMessage; +import javafx.beans.Observable; +import javafx.beans.property.SimpleStringProperty; +import javafx.util.Callback; /** * Alias action defines an action to execute when an alias is detected active. @@ -41,10 +44,33 @@ @JacksonXmlRootElement(localName = "action") public abstract class AliasAction { + private SimpleStringProperty mValueProperty = new SimpleStringProperty(); + public AliasAction() { } + /** + * String property representation of this alias ID + */ + @JsonIgnore + public SimpleStringProperty valueProperty() + { + return mValueProperty; + } + + /** + * Updates the value property for this alias ID. Note: this method is intended to be invoked by all subclasses + * each time that any of the subclass member variable values change. + */ + public void updateValueProperty() + { + valueProperty().set(toString()); + + //Hack: harmless, but the list extractor does not consistently update unless we do this. + valueProperty().get(); + } + @JsonIgnore public abstract AliasActionType getType(); @@ -60,4 +86,12 @@ public AliasAction() * Dismiss a persistent alias action */ public abstract void dismiss(boolean reset); + + /** + * Creates an observable property extractor for use with observable lists to detect changes internal to this object. + */ + public static Callback extractor() + { + return (AliasAction aliasAction) -> new Observable[] {aliasAction.valueProperty()}; + } } diff --git a/src/main/java/io/github/dsheirer/alias/action/AliasActionEditor.java b/src/main/java/io/github/dsheirer/alias/action/AliasActionEditor.java deleted file mode 100644 index b6cce86aa..000000000 --- a/src/main/java/io/github/dsheirer/alias/action/AliasActionEditor.java +++ /dev/null @@ -1,332 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * 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 3 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, see - ******************************************************************************/ -package io.github.dsheirer.alias.action; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasEvent; -import io.github.dsheirer.alias.AliasEvent.Event; -import io.github.dsheirer.alias.AliasFactory; -import io.github.dsheirer.alias.AliasModel; -import io.github.dsheirer.gui.editor.Editor; -import io.github.dsheirer.gui.editor.EmptyEditor; -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -public class AliasActionEditor extends Editor -{ - private static final long serialVersionUID = 1L; - - private static ListModel EMPTY_MODEL = new DefaultListModel<>(); - private JList mAliasActionList = new JList<>( EMPTY_MODEL ); - private JButton mNewActionButton; - private JButton mCloneActionButton; - private JButton mDeleteActionButton; - private EditorContainer mEditorContainer = new EditorContainer(); - - private AliasModel mAliasModel; - - public AliasActionEditor( AliasModel model ) - { - mAliasModel = model; - init(); - } - - private void init() - { - setLayout( new MigLayout( "fill,wrap 3", - "[grow,fill][grow,fill][grow,fill]", "[][grow,fill][]" ) ); - - mAliasActionList.setVisibleRowCount( 6 ); - mAliasActionList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); - mAliasActionList.setLayoutOrientation( JList.VERTICAL ); - mAliasActionList.addListSelectionListener( new ListSelectionListener() - { - @Override - public void valueChanged( ListSelectionEvent event ) - { - if( !event.getValueIsAdjusting() ) - { - JList list = (JList)event.getSource(); - - Object selectedItem = list.getSelectedValue(); - - if( selectedItem != null && selectedItem instanceof AliasAction ) - { - AliasAction selected = (AliasAction)selectedItem; - - mEditorContainer.setAliasAction( selected ); - - mCloneActionButton.setEnabled( true ); - mDeleteActionButton.setEnabled( true ); - } - else - { - mCloneActionButton.setEnabled( false ); - mDeleteActionButton.setEnabled( false ); - } - } - } - } ); - - JScrollPane scroller = new JScrollPane( mAliasActionList ); - add( scroller, "span,grow" ); - - add( mEditorContainer, "span,grow" ); - - mNewActionButton = new JButton( "New ..." ); - mNewActionButton.setToolTipText( "Create a new Alias Action" ); - mNewActionButton.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( MouseEvent e ) - { - JPopupMenu menu = new JPopupMenu(); - - menu.add( new AddAliasActionItem( AliasActionType.BEEP ) ); - menu.add( new AddAliasActionItem( AliasActionType.CLIP ) ); - menu.add( new AddAliasActionItem( AliasActionType.SCRIPT ) ); - - menu.show( e.getComponent(), e.getX(), e.getY() ); - } - } ); - - add( mNewActionButton ); - - mCloneActionButton = new JButton( "Clone" ); - mCloneActionButton.setToolTipText( "Create a copy of the currently selected action" ); - mCloneActionButton.setEnabled( false ); - mCloneActionButton.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - AliasAction selected = mAliasActionList.getSelectedValue(); - - if( selected != null ) - { - AliasAction clone = AliasFactory.copyOf( selected ); - - if( clone != null ) - { - addAliasActionToList( clone ); - } - } - } - } ); - add( mCloneActionButton ); - - mDeleteActionButton = new JButton( "Delete" ); - mDeleteActionButton.setToolTipText( "Delete the currently selected action" ); - mDeleteActionButton.setEnabled( false ); - mDeleteActionButton.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - final AliasAction selected = mAliasActionList.getSelectedValue(); - - if( selected != null ) - { - int choice = JOptionPane.showConfirmDialog( mDeleteActionButton, - "Do you want to delete [" + selected.toString() + "]", - "Are you sure?", JOptionPane.YES_NO_OPTION ); - - if( choice == JOptionPane.YES_OPTION && hasItem() ) - { - Alias alias = getItem(); - - alias.removeAliasAction( selected ); - - mAliasModel.broadcast( new AliasEvent( alias, Event.CHANGE ) ); - - setItem( alias ); - } - } - } - } ); - add( mDeleteActionButton, "wrap" ); - } - - @Override - public void setItem( Alias alias ) - { - super.setItem( alias ); - - if( alias == null || alias.getAction().isEmpty() ) - { - mAliasActionList.setModel( EMPTY_MODEL ); - } - else - { - DefaultListModel model = new DefaultListModel<>(); - - List actions = alias.getAction(); - - actions.sort(Comparator.comparing(Object::toString)); - - for( AliasAction action: actions ) - { - model.addElement( action ); - } - - mAliasActionList.setModel( model ); - } - - mEditorContainer.setAliasAction( null ); - } - - @Override - public void save() - { - if( isModified() || mEditorContainer.isModified() ) - { - if( mEditorContainer.isModified() ) - { - mEditorContainer.save(); - } - - setModified( false ); - - if( hasItem() ) - { - mAliasModel.broadcast( new AliasEvent( getItem(), Event.CHANGE ) ); - } - } - } - - public class EditorContainer extends JPanel - { - private static final long serialVersionUID = 1L; - - private Editor mEditor = new EmptyEditor<>(); - - public EditorContainer() - { - setLayout( new MigLayout( "","[grow,fill]", "[grow,fill]" ) ); - - add( mEditor ); - } - - public boolean isModified() - { - if( mEditor != null ) - { - return mEditor.isModified(); - } - - return false; - } - - public void save() - { - if( mEditor != null ) - { - mEditor.save(); - } - } - - public void setAliasAction( AliasAction aliasAction ) - { - if( mEditor != null ) - { - if( mEditor.isModified() ) - { - int option = JOptionPane.showConfirmDialog( - EditorContainer.this, - "Action settings have changed. Do you want to save these changes?", - "Save Changes?", - JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE ); - - if( option == JOptionPane.YES_OPTION ) - { - mEditor.save(); - } - } - } - removeAll(); - - //This will always give us an editor - mEditor = AliasFactory.getEditor( aliasAction ); - - add( mEditor ); - - revalidate(); - } - } - - private void addAliasActionToList( final AliasAction action ) - { - if( action != null && hasItem() ) - { - final Alias alias = getItem(); - - alias.addAliasAction( action ); - - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - setItem( alias ); - - mAliasActionList.setSelectedValue( action, true ); - - AliasActionEditor.this.setModified( true ); - } - } ); - } - } - - public class AddAliasActionItem extends JMenuItem - { - private static final long serialVersionUID = 1L; - - private AliasActionType mAliasActionType; - - public AddAliasActionItem( AliasActionType type ) - { - super( type.toString() ); - - mAliasActionType = type; - - addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - final AliasAction action = - AliasFactory.getAliasAction( mAliasActionType ); - - addAliasActionToList( action ); - } - } ); - } - } -} diff --git a/src/main/java/io/github/dsheirer/alias/action/AliasActionManager.java b/src/main/java/io/github/dsheirer/alias/action/AliasActionManager.java index d69885853..39a13842f 100644 --- a/src/main/java/io/github/dsheirer/alias/action/AliasActionManager.java +++ b/src/main/java/io/github/dsheirer/alias/action/AliasActionManager.java @@ -1,21 +1,23 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer * - * 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 3 of the License, or - * (at your option) any later version. + * * ****************************************************************************** + * * Copyright (C) 2014-2020 Dennis Sheirer + * * + * * 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 3 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, see + * * ***************************************************************************** * - * 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, see - * ***************************************************************************** */ package io.github.dsheirer.alias.action; @@ -63,7 +65,7 @@ public void receive(IMessage message) { if(alias != null && alias.hasActions()) { - for(AliasAction action: alias.getAction()) + for(AliasAction action: alias.getAliasActions()) { action.execute(alias, message); } @@ -76,13 +78,13 @@ public void receive(IMessage message) @Override public void reset() { - //No actions neeeded + //No actions needed } @Override public void start() { - //No actions neeeded + //No actions needed } @Override diff --git a/src/main/java/io/github/dsheirer/alias/action/RecurringAction.java b/src/main/java/io/github/dsheirer/alias/action/RecurringAction.java index 5df291080..31535c648 100644 --- a/src/main/java/io/github/dsheirer/alias/action/RecurringAction.java +++ b/src/main/java/io/github/dsheirer/alias/action/RecurringAction.java @@ -61,7 +61,7 @@ public void execute(Alias alias, IMessage message) break; case UNTIL_DISMISSED: mPerpetualAction = ThreadPool.SCHEDULED.scheduleAtFixedRate( - new PerformActionTask(alias, message), mPeriod, mPeriod, TimeUnit.SECONDS); + new PerformActionTask(alias, message), 0, mPeriod, TimeUnit.SECONDS); StringBuilder sb = new StringBuilder(); sb.append("
Alias ["); @@ -120,6 +120,7 @@ public int getPeriod() public void setPeriod(int period) { mPeriod = period; + updateValueProperty(); } @JacksonXmlProperty(isAttribute = true, localName = "interval") @@ -131,14 +132,14 @@ public Interval getInterval() public void setInterval(Interval interval) { mInterval = interval; - mRunning.set(false); + updateValueProperty(); } public enum Interval { ONCE("Once"), - DELAYED_RESET("Once, Reset After Delay"), + DELAYED_RESET("Once - Reset After Delay"), UNTIL_DISMISSED("Until Dismissed"); private String mLabel; diff --git a/src/main/java/io/github/dsheirer/alias/action/beep/BeepAction.java b/src/main/java/io/github/dsheirer/alias/action/beep/BeepAction.java index 644b655da..d2e993ca4 100644 --- a/src/main/java/io/github/dsheirer/alias/action/beep/BeepAction.java +++ b/src/main/java/io/github/dsheirer/alias/action/beep/BeepAction.java @@ -24,10 +24,43 @@ import io.github.dsheirer.alias.Alias; import io.github.dsheirer.alias.action.AliasActionType; import io.github.dsheirer.alias.action.RecurringAction; +import io.github.dsheirer.audio.AudioFormats; +import io.github.dsheirer.gui.preference.playback.ToneFrequency; +import io.github.dsheirer.gui.preference.playback.ToneUtil; +import io.github.dsheirer.gui.preference.playback.ToneVolume; import io.github.dsheirer.message.IMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.Clip; +import javax.sound.sampled.DataLine; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.ShortBuffer; public class BeepAction extends RecurringAction { + private static final Logger mLog = LoggerFactory.getLogger(BeepAction.class); + private byte[] mToneBytes; + + public BeepAction() + { + setInterval(Interval.ONCE); + float[] tone = ToneUtil.getTone(ToneFrequency.F700, ToneVolume.V10, 1000); + + /* Little-endian byte buffer */ + ByteBuffer buffer = ByteBuffer.allocate(tone.length * 2).order(ByteOrder.LITTLE_ENDIAN); + + ShortBuffer shortBuffer = buffer.asShortBuffer(); + + for(float sample : tone) + { + shortBuffer.put((short) (sample * Short.MAX_VALUE)); + } + + mToneBytes = buffer.array(); + } @JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") @Override @@ -39,12 +72,48 @@ public AliasActionType getType() @Override public void performAction(Alias alias, IMessage message ) { - System.out.println( "\007" ); + DataLine.Info info = new DataLine.Info(Clip.class, AudioFormats.PCM_SIGNED_8KHZ_16BITS_MONO); + + if(!AudioSystem.isLineSupported(info)) + { + mLog.error("Audio clip playback is not supported on this system"); + return; + } + + try + { + Clip clip = (Clip)AudioSystem.getLine(info); + clip.open(AudioFormats.PCM_SIGNED_8KHZ_16BITS_MONO, mToneBytes, 0, mToneBytes.length); + clip.start(); + } + catch(Exception e) + { + mLog.error("Error attempting to play audio test tone"); + } } @Override public String toString() { - return "Beep"; + StringBuilder sb = new StringBuilder(); + sb.append("Beep"); + + if(getInterval() != null) + { + switch(getInterval()) + { + case ONCE: + sb.append(" Once"); + break; + case DELAYED_RESET: + sb.append(" Once, Reset After ").append(getPeriod()).append(" Seconds"); + break; + case UNTIL_DISMISSED: + sb.append(" Every ").append(getPeriod()).append(" Seconds Until Dismissed"); + break; + } + } + + return sb.toString(); } } diff --git a/src/main/java/io/github/dsheirer/alias/action/beep/BeepActionEditor.java b/src/main/java/io/github/dsheirer/alias/action/beep/BeepActionEditor.java deleted file mode 100644 index 109eea708..000000000 --- a/src/main/java/io/github/dsheirer/alias/action/beep/BeepActionEditor.java +++ /dev/null @@ -1,185 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * 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 3 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, see - ******************************************************************************/ -package io.github.dsheirer.alias.action.beep; - -import io.github.dsheirer.alias.action.AliasAction; -import io.github.dsheirer.alias.action.RecurringAction; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -public class BeepActionEditor extends DocumentListenerEditor -{ - private static final long serialVersionUID = 1L; - - private static final String HELP_TEXT = - "

Beep Action

" - + "This action will sound a beep according to the interval
" - + "that you select:

" - + "Once: Beep the first time the alias is active and
" - + "never again

" - + "Once, Reset After Delay: Beep once and suppress
" - + "subsequent beeps for the specified period in seconds.
" - + "After the reset period, beep again when the alias is active.

" - + "Until Dismissed: Beep every period seconds until you
" - + "click OK on the dialog that appears. Alerting is suppressed
" - + "for 15 seconds after you click OK.
" - + ""; - - private JComboBox mComboInterval; - private JSlider mPeriodSlider; - private JLabel mPeriodSliderLabel; - - public BeepActionEditor( AliasAction aliasAction ) - { - init(); - - setItem( aliasAction ); - } - - public BeepAction getBeepAction() - { - if( getItem() instanceof BeepAction ) - { - return (BeepAction)getItem(); - } - - return null; - } - - private void init() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][]" ) ); - - add( new JLabel( "Beep Interval:" ) ); - - mComboInterval = new JComboBox( RecurringAction.Interval.values() ); - mComboInterval.setToolTipText( HELP_TEXT ); - mComboInterval.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - RecurringAction.Interval selected = mComboInterval - .getItemAt( mComboInterval.getSelectedIndex() ); - - if( selected != null ) - { - /* Enable/disable period spinner based on selection */ - boolean enabled = selected != RecurringAction.Interval.ONCE; - - mPeriodSlider.setEnabled( enabled ); - mPeriodSliderLabel.setEnabled( enabled ); - } - - setModified( true ); - } - }); - add( mComboInterval, "wrap" ); - - final SpinnerModel model = new SpinnerNumberModel( 1, 1, 30, 1 ); - model.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - setModified( true ); - } - } ); - - mPeriodSlider = new JSlider( JSlider.HORIZONTAL, 1, 60, 1 ); - mPeriodSlider.setMajorTickSpacing( 10 ); - mPeriodSlider.setMinorTickSpacing( 2 ); - mPeriodSlider.setPaintTicks( true ); - mPeriodSlider.setLabelTable( mPeriodSlider.createStandardLabels( 10, 10 ) ); - mPeriodSlider.setPaintLabels( true ); - mPeriodSlider.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - mPeriodSliderLabel.setText( "Period: " + mPeriodSlider.getValue() ); - setModified( true ); - } - } ); - mPeriodSlider.setToolTipText( HELP_TEXT ); - - mPeriodSliderLabel = new JLabel( "Period: " + mPeriodSlider.getValue() + " " ); - add( mPeriodSliderLabel ); - add( mPeriodSlider, "wrap,grow" ); - - JLabel help = new JLabel( "Help ..." ); - help.setForeground( Color.BLUE.brighter() ); - help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - help.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( MouseEvent e ) - { - JOptionPane.showMessageDialog( BeepActionEditor.this, - HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( help, "align left" ); - } - - @Override - public void setItem( AliasAction item ) - { - super.setItem( item ); - - if( hasItem() ) - { - BeepAction beep = getBeepAction(); - - RecurringAction.Interval interval = beep.getInterval(); - - mComboInterval.setSelectedItem( interval ); - - boolean enabled = interval != RecurringAction.Interval.ONCE; - - mPeriodSliderLabel.setEnabled( enabled ); - mPeriodSlider.setEnabled( enabled ); - mPeriodSlider.setValue( beep.getPeriod() ); - } - - setModified( false ); - } - - @Override - public void save() - { - if( hasItem() && isModified() ) - { - BeepAction beep = getBeepAction(); - - beep.setInterval( (RecurringAction.Interval)mComboInterval.getSelectedItem() ); - beep.setPeriod( mPeriodSlider.getValue() ); - } - - setModified( false ); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/action/clip/ClipAction.java b/src/main/java/io/github/dsheirer/alias/action/clip/ClipAction.java index 9bea39334..439ccc701 100644 --- a/src/main/java/io/github/dsheirer/alias/action/clip/ClipAction.java +++ b/src/main/java/io/github/dsheirer/alias/action/clip/ClipAction.java @@ -45,6 +45,7 @@ public class ClipAction extends RecurringAction public ClipAction() { + updateValueProperty(); } @JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") @@ -62,6 +63,7 @@ public String getPath() public void setPath(String path) { mFilePath = path; + updateValueProperty(); } @Override @@ -87,8 +89,7 @@ public void play() throws Exception { mClip = AudioSystem.getClip(); - AudioInputStream ais = - AudioSystem.getAudioInputStream(new File(mFilePath)); + AudioInputStream ais = AudioSystem.getAudioInputStream(new File(mFilePath)); mClip.open(ais); } @@ -113,17 +114,33 @@ public void play() throws Exception } } - @Override public String toString() { - if(mFilePath == null) + StringBuilder sb = new StringBuilder(); + sb.append("Play Audio Clip"); + + if(getInterval() != null) { - return "Play Clip"; + switch(getInterval()) + { + case ONCE: + sb.append(" Once"); + break; + case DELAYED_RESET: + sb.append(" Once, Reset After ").append(getPeriod()).append(" Seconds"); + break; + case UNTIL_DISMISSED: + sb.append(" Every ").append(getPeriod()).append(" Seconds Until Dismissed"); + break; + } } - else + + if(getPath() == null) { - return "Play Clip: " + mFilePath; + sb.append(" - (audio file empty)"); } + + return sb.toString(); } } diff --git a/src/main/java/io/github/dsheirer/alias/action/clip/ClipActionEditor.java b/src/main/java/io/github/dsheirer/alias/action/clip/ClipActionEditor.java deleted file mode 100644 index b8d31a0aa..000000000 --- a/src/main/java/io/github/dsheirer/alias/action/clip/ClipActionEditor.java +++ /dev/null @@ -1,285 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * 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 3 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, see - ******************************************************************************/ -package io.github.dsheirer.alias.action.clip; - -import io.github.dsheirer.alias.action.AliasAction; -import io.github.dsheirer.alias.action.RecurringAction; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.File; - -public class ClipActionEditor extends DocumentListenerEditor -{ - private static final long serialVersionUID = 1L; - - private static final String PLEASE_SELECT_A_FILE = "Please select a file"; - private static final String HELP_TEXT = - "

Clip Action

" - + "This action will play an audio clip according to the interval
" - + "that you select:

" - + "Once: play the first time the alias is active and
" - + "never again

" - + "Once, Reset After Delay: play once and suppress
" - + "subsequent runs for the specified period in seconds.
" - + "After the reset period, play again when the alias is active.

" - + "Until Dismissed: play every period seconds until you
" - + "click OK on the dialog that appears. Alerting is suppressed
" - + "for 15 seconds after you click OK.
" - + ""; - - private JComboBox mComboInterval; - private JSlider mPeriodSlider; - private JLabel mPeriodSliderLabel; - private JTextField mTextFilePath; - - public ClipActionEditor( AliasAction aliasAction ) - { - init(); - - setItem( aliasAction ); - } - - public ClipAction getClipAction() - { - if( getItem() instanceof ClipAction ) - { - return (ClipAction)getItem(); - } - - return null; - } - - private void init() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][]" ) ); - - add( new JLabel( "Play Interval:" ) ); - - mComboInterval = new JComboBox( RecurringAction.Interval.values() ); - mComboInterval.setToolTipText( HELP_TEXT ); - mComboInterval.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - RecurringAction.Interval selected = mComboInterval - .getItemAt( mComboInterval.getSelectedIndex() ); - - if( selected != null ) - { - /* Enable/disable period slider based on selection */ - boolean enabled = selected != RecurringAction.Interval.ONCE; - - mPeriodSlider.setEnabled( enabled ); - mPeriodSliderLabel.setEnabled( enabled ); - } - - setModified( true ); - } - }); - add( mComboInterval ); - - final SpinnerModel model = new SpinnerNumberModel( 1, 1, 30, 1 ); - model.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - setModified( true ); - } - } ); - - mPeriodSlider = new JSlider( JSlider.HORIZONTAL, 1, 60, 1 ); - mPeriodSlider.setMajorTickSpacing( 10 ); - mPeriodSlider.setMinorTickSpacing( 2 ); - mPeriodSlider.setPaintTicks( true ); - mPeriodSlider.setLabelTable( mPeriodSlider.createStandardLabels( 10, 10 ) ); - mPeriodSlider.setPaintLabels( true ); - mPeriodSlider.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - mPeriodSliderLabel.setText( "Period: " + mPeriodSlider.getValue() ); - setModified( true ); - } - } ); - mPeriodSlider.setToolTipText( HELP_TEXT ); - - mPeriodSliderLabel = new JLabel( "Period: " + mPeriodSlider.getValue() + " " ); - add( mPeriodSliderLabel ); - add( mPeriodSlider, "wrap,grow" ); - - mTextFilePath = new JTextField( PLEASE_SELECT_A_FILE ); - add( mTextFilePath, "growx,span" ); - - JButton fileButton = new JButton( "File" ); - - fileButton.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - final JFileChooser chooser = new JFileChooser(); - - int value = chooser.showOpenDialog( ClipActionEditor.this ); - - if( value == JFileChooser.APPROVE_OPTION ) - { - File file = chooser.getSelectedFile(); - - mTextFilePath.setText( file.getAbsolutePath() ); - - setModified( true ); - } - } - } ); - - add( fileButton, "grow" ); - - JButton testButton = new JButton( "Test" ); - - testButton.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - if( hasItem() ) - { - String path = mTextFilePath.getText(); - - if( path == null || path.trim().isEmpty() ) - { - JOptionPane.showMessageDialog( ClipActionEditor.this, - "Please select an audio file", PLEASE_SELECT_A_FILE, - JOptionPane.ERROR_MESSAGE ); - } - else - { - if( isModified() ) - { - int option = JOptionPane.showConfirmDialog( - ClipActionEditor.this, - "Settings have changed. Do you want to save these changes?", - "Save Changes?", - JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE ); - - if( option == JOptionPane.YES_OPTION ) - { - save(); - } - } - - new Thread( new Runnable() - { - @Override - public void run() - { - try - { - getClipAction().play(); - } - catch( Exception e1 ) - { - JOptionPane.showMessageDialog( ClipActionEditor.this, - "Couldn't play audio clip [" + e1.getMessage() + "]", - "Error", JOptionPane.ERROR_MESSAGE ); - } - } - }).start(); - } - } - } - } ); - - add( testButton, "grow,wrap" ); - - JLabel help = new JLabel( "Help ..." ); - help.setForeground( Color.BLUE.brighter() ); - help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - help.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( MouseEvent e ) - { - JOptionPane.showMessageDialog( ClipActionEditor.this, - HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( help, "align left" ); - } - - @Override - public void setItem( AliasAction item ) - { - super.setItem( item ); - - if( hasItem() ) - { - ClipAction clip = getClipAction(); - - RecurringAction.Interval interval = clip.getInterval(); - - mComboInterval.setSelectedItem( interval ); - - boolean enabled = interval != RecurringAction.Interval.ONCE; - - mPeriodSliderLabel.setEnabled( enabled ); - mPeriodSlider.setEnabled( enabled ); - mPeriodSlider.setValue( clip.getPeriod() ); - - String filepath = clip.getPath(); - - if( filepath != null && !filepath.isEmpty() ) - { - mTextFilePath.setText( filepath ); - } - else - { - mTextFilePath.setText( PLEASE_SELECT_A_FILE ); - } - } - - setModified( false ); - } - - @Override - public void save() - { - if( hasItem() && isModified() ) - { - ClipAction script = getClipAction(); - - script.setInterval( (RecurringAction.Interval)mComboInterval.getSelectedItem() ); - script.setPeriod( mPeriodSlider.getValue() ); - script.setPath( mTextFilePath.getText() ); - } - - setModified( false ); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/action/script/ScriptAction.java b/src/main/java/io/github/dsheirer/alias/action/script/ScriptAction.java index e837010d2..7a771ed0a 100644 --- a/src/main/java/io/github/dsheirer/alias/action/script/ScriptAction.java +++ b/src/main/java/io/github/dsheirer/alias/action/script/ScriptAction.java @@ -39,6 +39,7 @@ public class ScriptAction extends RecurringAction public ScriptAction() { + updateValueProperty(); } @JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") @@ -56,6 +57,7 @@ public String getScript() public void setScript(String script) { mScript = script; + updateValueProperty(); } @Override @@ -104,13 +106,30 @@ public void play() throws Exception @Override public String toString() { - if(mScript == null) + StringBuilder sb = new StringBuilder(); + sb.append("Run Script"); + + if(getInterval() != null) { - return "Run Script"; + switch(getInterval()) + { + case ONCE: + sb.append(" Once"); + break; + case DELAYED_RESET: + sb.append(" Once, Reset After ").append(getPeriod()).append(" Seconds"); + break; + case UNTIL_DISMISSED: + sb.append(" Every ").append(getPeriod()).append(" Seconds Until Dismissed"); + break; + } } - else + + if(getScript() == null) { - return "Run Script: " + mScript; + sb.append(" - (script file empty)"); } + + return sb.toString(); } } diff --git a/src/main/java/io/github/dsheirer/alias/action/script/ScriptActionEditor.java b/src/main/java/io/github/dsheirer/alias/action/script/ScriptActionEditor.java deleted file mode 100644 index 533f19419..000000000 --- a/src/main/java/io/github/dsheirer/alias/action/script/ScriptActionEditor.java +++ /dev/null @@ -1,281 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * 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 3 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, see - ******************************************************************************/ -package io.github.dsheirer.alias.action.script; - -import io.github.dsheirer.alias.action.AliasAction; -import io.github.dsheirer.alias.action.RecurringAction; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.File; - -public class ScriptActionEditor extends DocumentListenerEditor -{ - private static final long serialVersionUID = 1L; - - private static final String PLEASE_SELECT_A_FILE = "Please select a file"; - private static final String HELP_TEXT = - "

Script Action

" - + "This action will run a script according to the interval
" - + "that you select:

" - + "Once: run the first time the alias is active and
" - + "never again

" - + "Once, Reset After Delay: run once and suppress
" - + "subsequent runs for the specified period in seconds.
" - + "After the reset period, run again when the alias is active.

" - + "Until Dismissed: run every period seconds until you
" - + "click OK on the dialog that appears. Alerting is suppressed
" - + "for 15 seconds after you click OK.
" - + ""; - - private JComboBox mComboInterval; - private JSlider mPeriodSlider; - private JLabel mPeriodSliderLabel; - private JTextField mTextFilePath; - - public ScriptActionEditor( AliasAction aliasAction ) - { - init(); - - setItem( aliasAction ); - } - - public ScriptAction getScriptAction() - { - if( getItem() instanceof ScriptAction ) - { - return (ScriptAction)getItem(); - } - - return null; - } - - private void init() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][]" ) ); - - add( new JLabel( "Run Interval:" ) ); - - mComboInterval = new JComboBox( RecurringAction.Interval.values() ); - mComboInterval.setToolTipText( HELP_TEXT ); - mComboInterval.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - RecurringAction.Interval selected = mComboInterval - .getItemAt( mComboInterval.getSelectedIndex() ); - - if( selected != null ) - { - /* Enable/disable period slider based on selection */ - boolean enabled = selected != RecurringAction.Interval.ONCE; - - mPeriodSlider.setEnabled( enabled ); - mPeriodSliderLabel.setEnabled( enabled ); - } - - setModified( true ); - } - }); - add( mComboInterval ); - - final SpinnerModel model = new SpinnerNumberModel( 1, 1, 30, 1 ); - model.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - setModified( true ); - } - } ); - - mPeriodSlider = new JSlider( JSlider.HORIZONTAL, 1, 60, 1 ); - mPeriodSlider.setMajorTickSpacing( 10 ); - mPeriodSlider.setMinorTickSpacing( 2 ); - mPeriodSlider.setPaintTicks( true ); - mPeriodSlider.setLabelTable( mPeriodSlider.createStandardLabels( 10, 10 ) ); - mPeriodSlider.setPaintLabels( true ); - mPeriodSlider.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - mPeriodSliderLabel.setText( "Period: " + mPeriodSlider.getValue() ); - setModified( true ); - } - } ); - mPeriodSlider.setToolTipText( HELP_TEXT ); - - mPeriodSliderLabel = new JLabel( "Period: " + mPeriodSlider.getValue() + " " ); - add( mPeriodSliderLabel ); - add( mPeriodSlider, "wrap,grow" ); - - mTextFilePath = new JTextField( PLEASE_SELECT_A_FILE ); - add( mTextFilePath, "growx,span" ); - - JButton fileButton = new JButton( "File" ); - - fileButton.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - final JFileChooser chooser = new JFileChooser(); - - int value = chooser.showOpenDialog( ScriptActionEditor.this ); - - if( value == JFileChooser.APPROVE_OPTION ) - { - File file = chooser.getSelectedFile(); - - mTextFilePath.setText( file.getAbsolutePath() ); - - setModified( true ); - } - } - } ); - - add( fileButton, "grow" ); - - JButton testButton = new JButton( "Test" ); - testButton.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent actionEvent ) - { - String script = mTextFilePath.getText(); - - if( script == null || script.trim().isEmpty() ) - { - JOptionPane.showMessageDialog( ScriptActionEditor.this, - "Please select a script file", PLEASE_SELECT_A_FILE, - JOptionPane.ERROR_MESSAGE ); - } - else - { - if( isModified() ) - { - int option = JOptionPane.showConfirmDialog( - ScriptActionEditor.this, - "Settings have changed. Do you want to save these changes?", - "Save Changes?", - JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE ); - - if( option == JOptionPane.YES_OPTION ) - { - save(); - } - } - - new Thread( new Runnable() - { - @Override - public void run() - { - try - { - getScriptAction().play(); - } - catch( Exception e1 ) - { - JOptionPane.showMessageDialog( ScriptActionEditor.this, - e1.getMessage(), "Error", JOptionPane.ERROR_MESSAGE ); - } - } - }).start(); - } - - } - } ); - - add( testButton, "grow,wrap" ); - - JLabel help = new JLabel( "Help ..." ); - help.setForeground( Color.BLUE.brighter() ); - help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - help.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( MouseEvent e ) - { - JOptionPane.showMessageDialog( ScriptActionEditor.this, - HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( help, "align left" ); - } - - @Override - public void setItem( AliasAction item ) - { - super.setItem( item ); - - if( hasItem() ) - { - ScriptAction script = getScriptAction(); - - RecurringAction.Interval interval = script.getInterval(); - - mComboInterval.setSelectedItem( interval ); - - boolean enabled = interval != RecurringAction.Interval.ONCE; - - mPeriodSliderLabel.setEnabled( enabled ); - mPeriodSlider.setEnabled( enabled ); - mPeriodSlider.setValue( script.getPeriod() ); - - String filepath = script.getScript(); - - if( filepath != null && !filepath.isEmpty() ) - { - mTextFilePath.setText( filepath ); - } - else - { - mTextFilePath.setText( PLEASE_SELECT_A_FILE ); - } - } - - setModified( false ); - } - - @Override - public void save() - { - if( hasItem() && isModified() ) - { - ScriptAction script = getScriptAction(); - - script.setInterval( (RecurringAction.Interval)mComboInterval.getSelectedItem() ); - script.setPeriod( mPeriodSlider.getValue() ); - script.setScript( mTextFilePath.getText() ); - } - - setModified( false ); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/AliasID.java b/src/main/java/io/github/dsheirer/alias/id/AliasID.java index 86b5c93b2..f8b09ea72 100644 --- a/src/main/java/io/github/dsheirer/alias/id/AliasID.java +++ b/src/main/java/io/github/dsheirer/alias/id/AliasID.java @@ -40,43 +40,126 @@ import io.github.dsheirer.alias.id.radio.Radio; import io.github.dsheirer.alias.id.radio.RadioRange; import io.github.dsheirer.alias.id.record.Record; -import io.github.dsheirer.alias.id.status.StatusID; +import io.github.dsheirer.alias.id.status.UnitStatusID; +import io.github.dsheirer.alias.id.status.UserStatusID; import io.github.dsheirer.alias.id.talkgroup.Talkgroup; import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange; +import io.github.dsheirer.alias.id.tone.TonesID; +import javafx.beans.Observable; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.util.Callback; @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") @JsonSubTypes({ @JsonSubTypes.Type(value = BroadcastChannel.class, name = "broadcastChannel"), @JsonSubTypes.Type(value = Esn.class, name = "esn"), @JsonSubTypes.Type(value = FleetsyncID.class, name = "fleetsyncID"), + @JsonSubTypes.Type(value = LegacyTalkgroupID.class, name = "talkgroupID"), @JsonSubTypes.Type(value = LoJackFunctionAndID.class, name = "loJackFunctionAndID"), @JsonSubTypes.Type(value = MDC1200ID.class, name = "mdc1200ID"), @JsonSubTypes.Type(value = Min.class, name = "min"), @JsonSubTypes.Type(value = MPT1327ID.class, name = "mpt1327ID"), @JsonSubTypes.Type(value = NonRecordable.class, name = "nonRecordable"), + @JsonSubTypes.Type(value = Priority.class, name = "priority"), @JsonSubTypes.Type(value = Radio.class, name = "radio"), @JsonSubTypes.Type(value = RadioRange.class, name = "radioRange"), - @JsonSubTypes.Type(value = Talkgroup.class, name = "talkgroup"), - @JsonSubTypes.Type(value = TalkgroupRange.class, name = "talkgroupRange"), - @JsonSubTypes.Type(value = Priority.class, name = "priority"), @JsonSubTypes.Type(value = Record.class, name = "record"), @JsonSubTypes.Type(value = SiteID.class, name = "siteID"), - @JsonSubTypes.Type(value = StatusID.class, name = "statusID"), - @JsonSubTypes.Type(value = LegacyTalkgroupID.class, name = "talkgroupID"), + @JsonSubTypes.Type(value = Talkgroup.class, name = "talkgroup"), + @JsonSubTypes.Type(value = TalkgroupRange.class, name = "talkgroupRange"), + @JsonSubTypes.Type(value = TonesID.class, name = "tones"), + @JsonSubTypes.Type(value = UserStatusID.class, name = "statusID"), + @JsonSubTypes.Type(value = UnitStatusID.class, name = "unitStatusID"), @JsonSubTypes.Type(value = UniqueID.class, name = "uniqueID") }) @JacksonXmlRootElement(localName = "id") public abstract class AliasID { + private SimpleStringProperty mValueProperty = new SimpleStringProperty(); + private BooleanProperty mOverlapProperty = new SimpleBooleanProperty(); + public AliasID() { } + /** + * String property representation of this alias ID + */ + @JsonIgnore + public SimpleStringProperty valueProperty() + { + return mValueProperty; + } + + /** + * Indicates if this alias identifier overlaps with any other alias identifier in the same alias list + */ + @JsonIgnore + public BooleanProperty overlapProperty() + { + return mOverlapProperty; + } + + /** + * Sets the overlap flag for this alias id. Overlap indicates that this identifier collides with another + * identifier within the same alias list. + */ + public void setOverlap(boolean overlap) + { + mOverlapProperty.set(overlap); + updateValueProperty(); + } + + /** + * Updates the value property for this alias ID. Note: this method is intended to be invoked by all subclasses + * each time that any of the subclass member variable values change. + */ + public void updateValueProperty() + { + if(overlapProperty().get()) + { + valueProperty().set(toString() + " - Error: Overlap"); + } + else + { + valueProperty().set(toString()); + } + + //Hack: harmless, but the list extractor does not consistently update unless we do this. + valueProperty().get(); + } + @JsonIgnore public abstract AliasIDType getType(); public abstract boolean matches(AliasID id); + /** + * Indicates if this alias ID overlaps with another alias ID. + * Note: this method is intended to be overridden by certain subclass implementations. + */ + @JsonIgnore + public boolean overlaps(AliasID id) + { + return false; + } + @JsonIgnore public abstract boolean isValid(); + + /** + * Indicates if the identifier is an audio identifier (monitor, record, stream) + */ + @JsonIgnore + public abstract boolean isAudioIdentifier(); + + /** + * Creates an observable property extractor for use with observable lists to detect changes internal to this object. + */ + public static Callback extractor() + { + return (AliasID aid) -> new Observable[] {aid.valueProperty(), aid.overlapProperty()}; + } } diff --git a/src/main/java/io/github/dsheirer/alias/id/AliasIDType.java b/src/main/java/io/github/dsheirer/alias/id/AliasIDType.java index 1ff9f356a..77a913378 100644 --- a/src/main/java/io/github/dsheirer/alias/id/AliasIDType.java +++ b/src/main/java/io/github/dsheirer/alias/id/AliasIDType.java @@ -21,6 +21,8 @@ */ package io.github.dsheirer.alias.id; +import java.util.EnumSet; + public enum AliasIDType { BROADCAST_CHANNEL("Audio Broadcast Channel"), @@ -34,7 +36,9 @@ public enum AliasIDType RADIO_ID_RANGE("Radio ID Range"), RECORD("Record"), SITE("Site"), - STATUS("Status"), + STATUS("User Status"), + TONES("Tone Sequence"), + UNIT_STATUS("Unit Status"), TALKGROUP("Talkgroup"), TALKGROUP_RANGE("Talkgroup Range"), @@ -52,6 +56,10 @@ public enum AliasIDType mLabel = label; } + //Values used by the View-By alias editor + public static EnumSet VIEW_BY_VALUES = EnumSet.of(TALKGROUP, TALKGROUP_RANGE, RADIO_ID, RADIO_ID_RANGE, + UNIT_STATUS, STATUS, TONES); + public String toString() { return mLabel; diff --git a/src/main/java/io/github/dsheirer/alias/id/AliasIdentifierEditor.java b/src/main/java/io/github/dsheirer/alias/id/AliasIdentifierEditor.java deleted file mode 100644 index 226871452..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/AliasIdentifierEditor.java +++ /dev/null @@ -1,370 +0,0 @@ -/* - * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * 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 3 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, see - * * ***************************************************************************** - * - * - */ -package io.github.dsheirer.alias.id; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasEvent; -import io.github.dsheirer.alias.AliasFactory; -import io.github.dsheirer.alias.AliasModel; -import io.github.dsheirer.audio.broadcast.BroadcastModel; -import io.github.dsheirer.gui.editor.Editor; -import io.github.dsheirer.gui.editor.EmptyEditor; -import net.miginfocom.swing.MigLayout; - -import javax.swing.DefaultListModel; -import javax.swing.JButton; -import javax.swing.JList; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JScrollPane; -import javax.swing.ListModel; -import javax.swing.ListSelectionModel; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import java.awt.EventQueue; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -public class AliasIdentifierEditor extends Editor -{ - private static final long serialVersionUID = 1L; - - private static ListModel EMPTY_MODEL = new DefaultListModel<>(); - private JList mAliasIDList = new JList<>(EMPTY_MODEL); - private JButton mNewIDButton; - private JButton mCloneIDButton; - private JButton mDeleteIDButton; - private EditorContainer mEditorContainer = new EditorContainer(); - - private AliasModel mAliasModel; - private BroadcastModel mBroadcastModel; - - public AliasIdentifierEditor(AliasModel aliasModel, BroadcastModel broadcastModel) - { - mAliasModel = aliasModel; - mBroadcastModel = broadcastModel; - - init(); - } - - private void init() - { - setLayout(new MigLayout("fill,wrap 3", - "[grow,fill][grow,fill][grow,fill]", "[][grow,fill][]")); - - mAliasIDList.setVisibleRowCount(6); - mAliasIDList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - mAliasIDList.setLayoutOrientation(JList.VERTICAL); - mAliasIDList.addListSelectionListener(new ListSelectionListener() - { - @Override - public void valueChanged(ListSelectionEvent event) - { - if(!event.getValueIsAdjusting()) - { - JList list = (JList)event.getSource(); - - Object selectedItem = list.getSelectedValue(); - - if(selectedItem != null && selectedItem instanceof AliasID) - { - AliasID selected = (AliasID)selectedItem; - - mEditorContainer.setAliasID(selected); - - mCloneIDButton.setEnabled(true); - mDeleteIDButton.setEnabled(true); - } - else - { - mCloneIDButton.setEnabled(false); - mDeleteIDButton.setEnabled(false); - } - } - } - }); - - JScrollPane scroller = new JScrollPane(mAliasIDList); - add(scroller, "span,grow"); - - add(mEditorContainer, "span,grow"); - - mNewIDButton = new JButton("New ..."); - mNewIDButton.setToolTipText("Create a new Alias Identifier"); - mNewIDButton.addMouseListener(new MouseAdapter() - { - @Override - public void mouseClicked(MouseEvent e) - { - JPopupMenu menu = new JPopupMenu(); - - menu.add(new AddAliasIdentifierItem(AliasIDType.TALKGROUP)); - menu.add(new AddAliasIdentifierItem(AliasIDType.TALKGROUP_RANGE)); - menu.add(new AddAliasIdentifierItem(AliasIDType.RADIO_ID)); - menu.add(new AddAliasIdentifierItem(AliasIDType.RADIO_ID_RANGE)); - menu.add(new AddAliasIdentifierItem(AliasIDType.ESN)); -// menu.add(new AddAliasIdentifierItem(AliasIDType.LOJACK)); - menu.add(new AddAliasIdentifierItem(AliasIDType.STATUS)); - - menu.addSeparator(); - - menu.add(new AddAliasIdentifierItem(AliasIDType.BROADCAST_CHANNEL)); - - if(hasItem()) - { - Alias alias = getItem(); - - if(!alias.isRecordable()) - { - menu.add(new AddAliasIdentifierItem(AliasIDType.RECORD)); - } - - if(!alias.hasCallPriority()) - { - menu.add(new AddAliasIdentifierItem(AliasIDType.PRIORITY)); - } - } - - menu.show(e.getComponent(), e.getX(), e.getY()); - } - }); - - add(mNewIDButton); - - mCloneIDButton = new JButton("Clone"); - mCloneIDButton.setToolTipText("Create a copy of the currently selected identifier"); - mCloneIDButton.setEnabled(false); - mCloneIDButton.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - AliasID selected = mAliasIDList.getSelectedValue(); - - if(selected != null) - { - AliasID clone = AliasFactory.copyOf(selected); - - if(clone != null) - { - addAliasIDToList(clone); - } - } - } - }); - add(mCloneIDButton); - - mDeleteIDButton = new JButton("Delete"); - mDeleteIDButton.setToolTipText("Delete the currently selected identifier"); - mDeleteIDButton.setEnabled(false); - mDeleteIDButton.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - final AliasID selected = mAliasIDList.getSelectedValue(); - - if(selected != null) - { - int choice = JOptionPane.showConfirmDialog(mDeleteIDButton, - "Do you want to delete [" + selected.toString() + "]", - "Are you sure?", JOptionPane.YES_NO_OPTION); - - if(choice == JOptionPane.YES_OPTION && hasItem()) - { - Alias alias = getItem(); - - alias.removeAliasID(selected); - - mAliasModel.broadcast(new AliasEvent(alias, AliasEvent.Event.CHANGE)); - - setItem(alias); - } - } - } - }); - add(mDeleteIDButton, "wrap"); - } - - @Override - public void setItem(Alias alias) - { - super.setItem(alias); - - if(alias == null || alias.getId().isEmpty()) - { - mAliasIDList.setModel(EMPTY_MODEL); - } - else - { - DefaultListModel model = new DefaultListModel(); - - List ids = alias.getId(); - - ids.sort(Comparator.comparing(Object::toString)); - - for(AliasID id : ids) - { - model.addElement(id); - } - - mAliasIDList.setModel(model); - } - - mEditorContainer.setAliasID(null); - } - - @Override - public void save() - { - if(isModified() || mEditorContainer.isModified()) - { - if(mEditorContainer.isModified()) - { - mEditorContainer.save(); - } - - setModified(false); - - if(hasItem()) - { - mAliasModel.broadcast(new AliasEvent(getItem(), AliasEvent.Event.CHANGE)); - } - } - } - - public class EditorContainer extends JPanel - { - private static final long serialVersionUID = 1L; - - private Editor mEditor = new EmptyEditor<>(); - - public EditorContainer() - { - setLayout(new MigLayout("", "[grow,fill]", "[grow,fill]")); - - add(mEditor); - } - - public boolean isModified() - { - if(mEditor != null) - { - return mEditor.isModified(); - } - - return false; - } - - public void save() - { - if(mEditor != null) - { - mEditor.save(); - } - } - - public void setAliasID(AliasID aliasID) - { - if(mEditor != null) - { - if(mEditor.isModified()) - { - int option = JOptionPane.showConfirmDialog( - EditorContainer.this, - "Identifier settings have changed. Do you want to save these changes?", - "Save Changes?", - JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE); - - if(option == JOptionPane.YES_OPTION) - { - mEditor.save(); - } - } - } - removeAll(); - - //This will always give us an editor - mEditor = AliasFactory.getEditor(aliasID, mBroadcastModel); - - add(mEditor); - - revalidate(); - } - } - - private void addAliasIDToList(final AliasID id) - { - if(id != null && hasItem()) - { - final Alias alias = getItem(); - - alias.addAliasID(id); - - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - setItem(alias); - - mAliasIDList.setSelectedValue(id, true); - - AliasIdentifierEditor.this.setModified(true); - } - }); - } - } - - public class AddAliasIdentifierItem extends JMenuItem - { - private static final long serialVersionUID = 1L; - - private AliasIDType mAliasIDType; - - public AddAliasIdentifierItem(AliasIDType type) - { - super(type.toString()); - - mAliasIDType = type; - - addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - final AliasID id = AliasFactory.getAliasID(mAliasIDType); - - addAliasIDToList(id); - } - }); - } - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/WildcardID.java b/src/main/java/io/github/dsheirer/alias/id/WildcardID.java deleted file mode 100644 index 2b9b0afd6..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/WildcardID.java +++ /dev/null @@ -1,120 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * 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 3 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, see - * - ******************************************************************************/ -package io.github.dsheirer.alias.id; - -import org.apache.commons.lang3.Validate; -import org.apache.commons.math3.util.FastMath; - -import java.util.regex.Pattern; - -public class WildcardID implements Comparable -{ - public static final String WILDCARD = "*"; - public static final String REGEX_WILDCARD = "."; - - private String mValue; - private Pattern mPattern; - private int mWeight; - - /** - * Wildcard identifier for matching to string identifiers containing single-character (*) wildcard values. - * - * Supports ordering from most-specific to least-specific using the build-in weighting calculation where identifier - * patterns containing fewer wildcards closer to the least significant digit will be weighted more heavily than - * identifier patterns containing more wildcards, or wildcard characters in the most significant digits. - * - * @param value is a string value containing one or more asterisk (*) single character wildcard with a maximum - * length of 10 characters. - * - * @throws AssertionError if the value is null, longer than 10 characters, or doesn't contain at least 1 asterisk - * @throws IllegalArgumentException if the value cannot be compiled as a regular expression after converting the - * asterisk characters to the regex single-character wildcard value (.) - */ - public WildcardID(String value) - { - Validate.isTrue(value != null && value.contains(WILDCARD) && value.length() < 10); - - mValue = value; - - try - { - mPattern = Pattern.compile(value.replace(WILDCARD, REGEX_WILDCARD)); - } - catch(Exception e) - { - throw new IllegalArgumentException("Invalid regex pattern for alias ID value [" + value + "]", e); - } - - mWeight = calculateWeight(); - } - - public String value() - { - return mValue; - } - - /** - * Indicates if the id matches the internal regex pattern for this wildcard alias ID - * @param id - * @return - */ - public boolean matches(String id) - { - return id != null && mPattern.matcher(id).matches(); - } - - public int weight() - { - return mWeight; - } - - /** - * Calculates a weighting value for wildcard character quantity and significant digit location - */ - private int calculateWeight() - { - int weight = 0; - int characterCount = -1; - - for( int x = 0; x < mValue.length(); x++) - { - if(mValue.substring(x, x + 1).equals(WILDCARD)) - { - weight += (int)(FastMath.pow(2, mValue.length() - x - 1)); //Position weight - characterCount++; - } - } - - weight += (int)(FastMath.pow(2, characterCount)) * 1000; //Character count weight - - return weight; - } - - @Override - public int compareTo(WildcardID otherWildcardID) - { - return Integer.compare(this.weight(), otherWildcardID.weight()); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof WildcardID)) return false; - return compareTo((WildcardID) o) == 0; - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/broadcast/BroadcastChannel.java b/src/main/java/io/github/dsheirer/alias/id/broadcast/BroadcastChannel.java index d3aec77c8..5f4911162 100644 --- a/src/main/java/io/github/dsheirer/alias/id/broadcast/BroadcastChannel.java +++ b/src/main/java/io/github/dsheirer/alias/id/broadcast/BroadcastChannel.java @@ -1,21 +1,24 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2016 Dennis Sheirer +/* * - * 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 3 of the License, or - * (at your option) any later version. + * * ****************************************************************************** + * * Copyright (C) 2014-2020 Dennis Sheirer + * * + * * 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 3 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, see + * * ***************************************************************************** * - * 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, see - * - ******************************************************************************/ + */ package io.github.dsheirer.alias.id.broadcast; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; @@ -31,6 +34,12 @@ public BroadcastChannel() //JAXB Constructor } + @Override + public boolean isAudioIdentifier() + { + return true; + } + @Override public int compareTo(BroadcastChannel other) { @@ -94,6 +103,7 @@ public String getChannelName() public void setChannelName(String channel) { mChannelName = channel; + updateValueProperty(); } @JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") @@ -118,13 +128,6 @@ public boolean matches(AliasID id) @Override public String toString() { - if(isValid()) - { - return "Broadcast Channel: " + mChannelName; - } - else - { - return "Broadcast Channel: None Selected"; - } + return isValid() ? mChannelName : "(invalid)"; } } diff --git a/src/main/java/io/github/dsheirer/alias/id/broadcast/BroadcastChannelEditor.java b/src/main/java/io/github/dsheirer/alias/id/broadcast/BroadcastChannelEditor.java deleted file mode 100644 index cd4ebc3d1..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/broadcast/BroadcastChannelEditor.java +++ /dev/null @@ -1,129 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * 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 3 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, see - * - ******************************************************************************/ -package io.github.dsheirer.alias.id.broadcast; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.audio.broadcast.BroadcastConfiguration; -import io.github.dsheirer.audio.broadcast.BroadcastModel; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.List; - -public class BroadcastChannelEditor extends DocumentListenerEditor -{ - private static final long serialVersionUID = 1L; - - private static final String HELP_TEXT = "" - + "

Audio Broadcast Channel

" - + "Name of a audio streaming broadcast configuration
" - + "to be used for streaming audio to an external
" - + "streaming server"; - - private JComboBox mBroadcastConfigurations; - private BroadcastModel mBroadcastModel; - - public BroadcastChannelEditor(AliasID aliasID, BroadcastModel broadcastModel) - { - mBroadcastModel = broadcastModel; - - initGUI(); - setItem( aliasID ); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 1", "[]", "[][]" ) ); - - add( new JLabel( "Broadcast Channel" ), "grow" ); - - List channelNames = mBroadcastModel.getBroadcastConfigurationNames(); - - mBroadcastConfigurations = new JComboBox(channelNames.toArray(new String[0])); - mBroadcastConfigurations.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - setModified(true); - } - }); - - add(mBroadcastConfigurations, "grow"); - - JLabel help = new JLabel( "Help ..." ); - help.setForeground( Color.BLUE.brighter() ); - help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - help.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( MouseEvent e ) - { - JOptionPane.showMessageDialog( BroadcastChannelEditor.this, - HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - - add( help, "align left" ); - } - - @Override - public void setItem(AliasID item) - { - super.setItem(item); - - if(item instanceof BroadcastChannel) - { - BroadcastChannel channel = (BroadcastChannel)item; - - if(channel.isValid()) - { - mBroadcastConfigurations.setSelectedItem(channel.getChannelName()); - } - } - - setModified(false); - } - - @Override - public void save() - { - String channel = (String)mBroadcastConfigurations.getSelectedItem(); - - BroadcastConfiguration selectedConfiguration = mBroadcastModel.getBroadcastConfiguration(channel); - - - if(selectedConfiguration != null) - { - ((BroadcastChannel)getItem()).setChannelName(selectedConfiguration.getName()); - } - else - { - ((BroadcastChannel)getItem()).setChannelName(null); - } - } - - -} diff --git a/src/main/java/io/github/dsheirer/alias/id/esn/ESNEditor.java b/src/main/java/io/github/dsheirer/alias/id/esn/ESNEditor.java deleted file mode 100644 index 06675ffcc..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/esn/ESNEditor.java +++ /dev/null @@ -1,113 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * 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 3 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, see - ******************************************************************************/ -package io.github.dsheirer.alias.id.esn; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -public class ESNEditor extends DocumentListenerEditor -{ - private static final long serialVersionUID = 1L; - - private static final String HELP_TEXT = - "

Electronic Serial Number (ESN)

" - + "ESN: hexadecimal 0-9, A-F (e.g. ABCD1234 )
" - + "Wildcard: use an asterisk (*) to wildcard individual
" - + "digits (e.g. ABCD123* or AB**1**4)" - + ""; - - private JTextField mTextField; - - public ESNEditor( AliasID aliasID ) - { - initGUI(); - - setItem( aliasID ); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); - - add( new JLabel( "ESN:" ) ); - mTextField = new JTextField(); - mTextField.getDocument().addDocumentListener( this ); - mTextField.setToolTipText( HELP_TEXT ); - add( mTextField, "growx,push" ); - - JLabel help = new JLabel( "Help ..." ); - help.setForeground( Color.BLUE.brighter() ); - help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - help.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( MouseEvent e ) - { - JOptionPane.showMessageDialog( ESNEditor.this, - HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( help, "align left" ); - } - - public Esn getEsn() - { - if( getItem() instanceof Esn ) - { - return (Esn)getItem(); - } - - return null; - } - - @Override - public void setItem( AliasID aliasID ) - { - super.setItem( aliasID ); - - Esn esn = getEsn(); - - if( esn != null ) - { - mTextField.setText( esn.getEsn() ); - } - - setModified( false ); - - repaint(); - } - - @Override - public void save() - { - Esn esn = getEsn(); - - if( esn != null ) - { - esn.setEsn( mTextField.getText() ); - } - - setModified( false ); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/esn/Esn.java b/src/main/java/io/github/dsheirer/alias/id/esn/Esn.java index a2b660225..a3f73c77f 100644 --- a/src/main/java/io/github/dsheirer/alias/id/esn/Esn.java +++ b/src/main/java/io/github/dsheirer/alias/id/esn/Esn.java @@ -29,6 +29,12 @@ public Esn() { } + @Override + public boolean isAudioIdentifier() + { + return false; + } + @JacksonXmlProperty(isAttribute = true, localName = "esn") public String getEsn() { @@ -44,6 +50,7 @@ public boolean isValid() public void setEsn(String esn ) { mEsn = esn; + updateValueProperty(); } public String toString() diff --git a/src/main/java/io/github/dsheirer/alias/id/legacy/fleetsync/FleetsyncID.java b/src/main/java/io/github/dsheirer/alias/id/legacy/fleetsync/FleetsyncID.java index 3076d3b72..970d96814 100644 --- a/src/main/java/io/github/dsheirer/alias/id/legacy/fleetsync/FleetsyncID.java +++ b/src/main/java/io/github/dsheirer/alias/id/legacy/fleetsync/FleetsyncID.java @@ -31,6 +31,12 @@ public FleetsyncID() { } + @Override + public boolean isAudioIdentifier() + { + return false; + } + @JacksonXmlProperty(isAttribute = true, localName = "ident") public String getIdent() { diff --git a/src/main/java/io/github/dsheirer/alias/id/legacy/fleetsync/FleetsyncIDEditor.java b/src/main/java/io/github/dsheirer/alias/id/legacy/fleetsync/FleetsyncIDEditor.java deleted file mode 100644 index 85f7a2ba9..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/legacy/fleetsync/FleetsyncIDEditor.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer - * - * 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 3 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, see - * ***************************************************************************** - */ -package io.github.dsheirer.alias.id.legacy.fleetsync; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import net.miginfocom.swing.MigLayout; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.JFormattedTextField; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JTextField; -import javax.swing.text.MaskFormatter; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -public class FleetsyncIDEditor extends DocumentListenerEditor -{ - private final static Logger mLog = LoggerFactory.getLogger( FleetsyncIDEditor.class ); - - private static final long serialVersionUID = 1L; - - private static final String HELP_TEXT = "" - + "

Fleetsync Identifier

" - + "Fleetsync: ggg-uuuu where g=Group and u=Unit (e.g. 001-0001)
" - + "Wildcard: use an asterisk (*) for each digit (e.g. 001-****)" - + ""; - - private JTextField mTextField; - - public FleetsyncIDEditor( AliasID aliasID ) - { - initGUI(); - - setItem( aliasID ); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); - - add( new JLabel( "Fleetsync ID:" ) ); - - MaskFormatter formatter = null; - - try - { - //Mask: 3 digits - 4 digits - formatter = new MaskFormatter( "###-####" ); - } - catch( Exception e ) - { - //Do nothing, the mask was invalid - } - - mTextField = new JFormattedTextField( formatter ); - mTextField.getDocument().addDocumentListener( this ); - mTextField.setToolTipText( HELP_TEXT ); - - add( mTextField, "growx,push" ); - - JLabel help = new JLabel( "Help ..." ); - help.setForeground( Color.BLUE.brighter() ); - help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - help.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( MouseEvent e ) - { - JOptionPane.showMessageDialog( FleetsyncIDEditor.this, - HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( help, "align left" ); - } - - public FleetsyncID getFleetsyncID() - { - if( getItem() instanceof FleetsyncID ) - { - return (FleetsyncID)getItem(); - } - - return null; - } - - @Override - public void setItem( AliasID aliasID ) - { - super.setItem( aliasID ); - - FleetsyncID fleetsync = getFleetsyncID(); - - if( fleetsync != null ) - { - mTextField.setText( fleetsync.getIdent() ); - } - - setModified( false ); - - repaint(); - } - - @Override - public void save() - { - FleetsyncID fleetsync = getFleetsyncID(); - - if( fleetsync != null ) - { - fleetsync.setIdent( mTextField.getText() ); - } - - setModified( false ); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/legacy/mdc/MDC1200ID.java b/src/main/java/io/github/dsheirer/alias/id/legacy/mdc/MDC1200ID.java index 85dc60d9c..b1a09062c 100644 --- a/src/main/java/io/github/dsheirer/alias/id/legacy/mdc/MDC1200ID.java +++ b/src/main/java/io/github/dsheirer/alias/id/legacy/mdc/MDC1200ID.java @@ -32,6 +32,12 @@ public MDC1200ID() { } + @Override + public boolean isAudioIdentifier() + { + return false; + } + @JacksonXmlProperty(isAttribute = true, localName = "ident") public String getIdent() { diff --git a/src/main/java/io/github/dsheirer/alias/id/legacy/mdc/MDC1200IDEditor.java b/src/main/java/io/github/dsheirer/alias/id/legacy/mdc/MDC1200IDEditor.java deleted file mode 100644 index f472e6d54..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/legacy/mdc/MDC1200IDEditor.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer - * - * 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 3 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, see - * ***************************************************************************** - */ -package io.github.dsheirer.alias.id.legacy.mdc; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import net.miginfocom.swing.MigLayout; - -import javax.swing.JFormattedTextField; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JTextField; -import javax.swing.text.MaskFormatter; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -public class MDC1200IDEditor extends DocumentListenerEditor -{ - private static final long serialVersionUID = 1L; - - private static final String HELP_TEXT = "" - + "

MDC-1200 Identifier

" - + "MDC-1200: four digit [0-9] value (e.g. 1234)
" - + "Wildcard: use an asterisk (*) to wildcard individual
" - + "digits (e.g. 123* or **34)" - + ""; - - private JTextField mTextField; - - public MDC1200IDEditor(AliasID aliasID) - { - initGUI(); - - setItem(aliasID); - } - - private void initGUI() - { - setLayout(new MigLayout("fill,wrap 2", "[right][left]", "[][]")); - - add(new JLabel("MDC-1200 ID:")); - - MaskFormatter formatter = null; - - try - { - //Mask: 4 digits - formatter = new MaskFormatter("####"); - } - catch(Exception e) - { - //Do nothing, the mask was invalid - } - - mTextField = new JFormattedTextField(formatter); - mTextField.getDocument().addDocumentListener(this); - mTextField.setToolTipText(HELP_TEXT); - add(mTextField, "growx,push"); - - JLabel help = new JLabel("Help ..."); - help.setForeground(Color.BLUE.brighter()); - help.setCursor(new Cursor(Cursor.HAND_CURSOR)); - help.addMouseListener(new MouseAdapter() - { - @Override - public void mouseClicked(MouseEvent e) - { - JOptionPane.showMessageDialog(MDC1200IDEditor.this, - HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE); - } - }); - add(help, "align left"); - } - - public MDC1200ID getMDC1200ID() - { - if(getItem() instanceof MDC1200ID) - { - return (MDC1200ID)getItem(); - } - - return null; - } - - @Override - public void setItem(AliasID aliasID) - { - super.setItem(aliasID); - - MDC1200ID mdc = getMDC1200ID(); - - if(mdc != null) - { - mTextField.setText(mdc.getIdent()); - } - - setModified(false); - - repaint(); - } - - @Override - public void save() - { - MDC1200ID mdc = getMDC1200ID(); - - if(mdc != null) - { - mdc.setIdent(mTextField.getText()); - } - - setModified(false); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/legacy/mobileID/MINEditor.java b/src/main/java/io/github/dsheirer/alias/id/legacy/mobileID/MINEditor.java deleted file mode 100644 index f50b831a7..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/legacy/mobileID/MINEditor.java +++ /dev/null @@ -1,127 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * 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 3 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, see - ******************************************************************************/ -package io.github.dsheirer.alias.id.legacy.mobileID; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; -import javax.swing.text.MaskFormatter; -import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -public class MINEditor extends DocumentListenerEditor -{ - private static final long serialVersionUID = 1L; - - private static final String HELP_TEXT = "" - + "

Passport Mobile ID (MIN)

" - + "MIN: six character hex value (e.g. AB12CD)
" - + "Wildcard: use an asterisk (*) to wildcard individual
" - + "digits (e.g. AB**CD or AB12**)" - + ""; - - private JTextField mTextField; - - public MINEditor( AliasID aliasID ) - { - initGUI(); - - setItem( aliasID ); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); - - add( new JLabel( "MIN:" ) ); - - MaskFormatter formatter = null; - - try - { - //Mask: 6 hex characters - formatter = new MaskFormatter( "HHHHHH" ); - } - catch( Exception e ) - { - //Do nothing, the mask was invalid - } - - mTextField = new JFormattedTextField( formatter ); - mTextField.getDocument().addDocumentListener( this ); - mTextField.setToolTipText( HELP_TEXT ); - add( mTextField, "growx,push" ); - - JLabel help = new JLabel( "Help ..." ); - help.setForeground( Color.BLUE.brighter() ); - help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - help.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( MouseEvent e ) - { - JOptionPane.showMessageDialog( MINEditor.this, - HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( help, "align left" ); - } - - public Min getMin() - { - if( getItem() instanceof Min ) - { - return (Min)getItem(); - } - - return null; - } - - @Override - public void setItem( AliasID aliasID ) - { - super.setItem( aliasID ); - - Min min = getMin(); - - if( min != null ) - { - mTextField.setText( min.getMin() ); - } - - setModified( false ); - - repaint(); - } - - @Override - public void save() - { - Min min = getMin(); - - if( min != null ) - { - min.setMin( mTextField.getText() ); - } - - setModified( false ); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/legacy/mobileID/Min.java b/src/main/java/io/github/dsheirer/alias/id/legacy/mobileID/Min.java index 4cd2e203a..c20fea890 100644 --- a/src/main/java/io/github/dsheirer/alias/id/legacy/mobileID/Min.java +++ b/src/main/java/io/github/dsheirer/alias/id/legacy/mobileID/Min.java @@ -33,6 +33,12 @@ public Min() { } + @Override + public boolean isAudioIdentifier() + { + return false; + } + @JacksonXmlProperty(isAttribute = true, localName = "min") public String getMin() { diff --git a/src/main/java/io/github/dsheirer/alias/id/legacy/mpt1327/MPT1327ID.java b/src/main/java/io/github/dsheirer/alias/id/legacy/mpt1327/MPT1327ID.java index b27ad6ee4..e10bf3322 100644 --- a/src/main/java/io/github/dsheirer/alias/id/legacy/mpt1327/MPT1327ID.java +++ b/src/main/java/io/github/dsheirer/alias/id/legacy/mpt1327/MPT1327ID.java @@ -32,6 +32,12 @@ public MPT1327ID() { } + @Override + public boolean isAudioIdentifier() + { + return false; + } + @JacksonXmlProperty(isAttribute = true, localName = "ident") public String getIdent() { diff --git a/src/main/java/io/github/dsheirer/alias/id/legacy/mpt1327/MPT1327IDEditor.java b/src/main/java/io/github/dsheirer/alias/id/legacy/mpt1327/MPT1327IDEditor.java deleted file mode 100644 index 36c38ecc9..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/legacy/mpt1327/MPT1327IDEditor.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer - * - * 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 3 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, see - * ***************************************************************************** - */ -package io.github.dsheirer.alias.id.legacy.mpt1327; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import net.miginfocom.swing.MigLayout; - -import javax.swing.JFormattedTextField; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JTextField; -import javax.swing.text.MaskFormatter; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -public class MPT1327IDEditor extends DocumentListenerEditor -{ - private static final long serialVersionUID = 1L; - - private static final String HELP_TEXT = "" - + "

MPT-1327 Identifier

" - + "MPT-1327: decimal (0-9) format ppp-iiii where
" - + "p=Prefix and i=Ident (e.g. 123-0001)
" - + "Wildcard: use an asterisk (*) to wildcard individual
" - + "digits (e.g. ABCD123* or AB**1**4)" - + ""; - - private JTextField mTextField; - - public MPT1327IDEditor( AliasID aliasID ) - { - initGUI(); - - setItem( aliasID ); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); - - add( new JLabel( "MPT-1327 ID:" ) ); - - MaskFormatter formatter = null; - - try - { - //Mask: 3 digits - 4 digits - formatter = new MaskFormatter( "***-****" ); - } - catch( Exception e ) - { - //Do nothing, the mask was invalid - } - - mTextField = new JFormattedTextField( formatter ); - mTextField.getDocument().addDocumentListener( this ); - mTextField.setToolTipText( HELP_TEXT ); - - add( mTextField, "growx,push" ); - - JLabel help = new JLabel( "Help ..." ); - help.setForeground( Color.BLUE.brighter() ); - help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - help.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( MouseEvent e ) - { - JOptionPane.showMessageDialog( MPT1327IDEditor.this, - HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( help, "align left" ); - } - - public MPT1327ID getMPT1327ID() - { - if( getItem() instanceof MPT1327ID ) - { - return (MPT1327ID)getItem(); - } - - return null; - } - - @Override - public void setItem( AliasID aliasID ) - { - super.setItem( aliasID ); - - MPT1327ID mpt = getMPT1327ID(); - - if( mpt != null ) - { - mTextField.setText( mpt.getIdent() ); - } - - setModified( false ); - - repaint(); - } - - @Override - public void save() - { - MPT1327ID mpt = getMPT1327ID(); - - if( mpt != null ) - { - mpt.setIdent( mTextField.getText() ); - } - - setModified( false ); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/legacy/nonrecordable/NonRecordable.java b/src/main/java/io/github/dsheirer/alias/id/legacy/nonrecordable/NonRecordable.java index b9d10706f..5d87682cc 100644 --- a/src/main/java/io/github/dsheirer/alias/id/legacy/nonrecordable/NonRecordable.java +++ b/src/main/java/io/github/dsheirer/alias/id/legacy/nonrecordable/NonRecordable.java @@ -33,6 +33,12 @@ public AliasIDType getType() return AliasIDType.NON_RECORDABLE; } + @Override + public boolean isAudioIdentifier() + { + return true; + } + @Override public boolean isValid() { diff --git a/src/main/java/io/github/dsheirer/alias/id/legacy/nonrecordable/NonRecordableEditor.java b/src/main/java/io/github/dsheirer/alias/id/legacy/nonrecordable/NonRecordableEditor.java deleted file mode 100644 index 51a3438a3..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/legacy/nonrecordable/NonRecordableEditor.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer - * - * 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 3 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, see - * ***************************************************************************** - */ -package io.github.dsheirer.alias.id.legacy.nonrecordable; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import net.miginfocom.swing.MigLayout; - -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -public class NonRecordableEditor extends DocumentListenerEditor -{ - private static final long serialVersionUID = 1L; - - private static final String HELP_TEXT = "" - + "

Audio Non-Recordable

" - + "This identifies an alias as non-recordable.

" - + "The alias can still be monitored but associated audio will
" - + "not be recorded.

" - + "The default behavior when audio recording is enabled for a
" - + "channel is to record all aliases except any alias with a
" - + "non-recordable identifier"; - - public NonRecordableEditor(AliasID aliasID) - { - initGUI(); - - setItem(aliasID); - } - - private void initGUI() - { - setLayout(new MigLayout("fill,wrap 1", "[]", "[][]")); - - add(new JLabel("Non-Recordable"), "wrap"); - - JLabel help = new JLabel("Help ..."); - help.setForeground(Color.BLUE.brighter()); - help.setCursor(new Cursor(Cursor.HAND_CURSOR)); - help.addMouseListener(new MouseAdapter() - { - @Override - public void mouseClicked(MouseEvent e) - { - JOptionPane.showMessageDialog(NonRecordableEditor.this, - HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE); - } - }); - add(help, "align left"); - } - - @Override - public void save() - { - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/legacy/siteID/SiteID.java b/src/main/java/io/github/dsheirer/alias/id/legacy/siteID/SiteID.java index a7616e7eb..fddaa6d97 100644 --- a/src/main/java/io/github/dsheirer/alias/id/legacy/siteID/SiteID.java +++ b/src/main/java/io/github/dsheirer/alias/id/legacy/siteID/SiteID.java @@ -29,6 +29,12 @@ public SiteID() { } + @Override + public boolean isAudioIdentifier() + { + return false; + } + @JacksonXmlProperty(isAttribute = true, localName = "site") public String getSite() { diff --git a/src/main/java/io/github/dsheirer/alias/id/legacy/siteID/SiteIDEditor.java b/src/main/java/io/github/dsheirer/alias/id/legacy/siteID/SiteIDEditor.java deleted file mode 100644 index e7184ba5f..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/legacy/siteID/SiteIDEditor.java +++ /dev/null @@ -1,116 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * 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 3 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, see - ******************************************************************************/ -package io.github.dsheirer.alias.id.legacy.siteID; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -public class SiteIDEditor extends DocumentListenerEditor -{ - private static final long serialVersionUID = 1L; - - private static final String HELP_TEXT = "" - + "

Site Identifier

" - + "LTR-Net: decimal (0-9) (e.g. 019)
" - + "MPT-1327: 5 digits (0-9) (e.g. 23619)
" - + "Passport: 3 digits (0-9) (e.g. 019)
" - + "P25: hex (0-9, A-F) format RR-SS where RR = RF Subsystem
" - + "and SS = Site Number (e.g. RFSS 1 Site 1F: 01-1F)" - + ""; - - private JTextField mTextField; - - public SiteIDEditor( AliasID aliasID ) - { - initGUI(); - - setItem( aliasID ); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); - - add( new JLabel( "Site ID:" ) ); - mTextField = new JTextField(); - mTextField.getDocument().addDocumentListener( this ); - mTextField.setToolTipText( HELP_TEXT ); - add( mTextField, "growx,push" ); - - JLabel help = new JLabel( "Help ..." ); - help.setForeground( Color.BLUE.brighter() ); - help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - help.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( MouseEvent e ) - { - JOptionPane.showMessageDialog( SiteIDEditor.this, - HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - - add( help, "align left" ); - } - - public SiteID getSiteID() - { - if( getItem() instanceof SiteID ) - { - return (SiteID)getItem(); - } - - return null; - } - - @Override - public void setItem( AliasID aliasID ) - { - super.setItem( aliasID ); - - SiteID siteID = getSiteID(); - - if( siteID != null ) - { - mTextField.setText( siteID.getSite() ); - } - - setModified( false ); - - repaint(); - } - - @Override - public void save() - { - SiteID siteID = getSiteID(); - - if( siteID != null ) - { - siteID.setSite( mTextField.getText() ); - } - - setModified( false ); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/legacy/talkgroup/LegacyTalkgroupID.java b/src/main/java/io/github/dsheirer/alias/id/legacy/talkgroup/LegacyTalkgroupID.java index 8eb9553fa..296cd1bbc 100644 --- a/src/main/java/io/github/dsheirer/alias/id/legacy/talkgroup/LegacyTalkgroupID.java +++ b/src/main/java/io/github/dsheirer/alias/id/legacy/talkgroup/LegacyTalkgroupID.java @@ -38,6 +38,12 @@ public LegacyTalkgroupID(String talkgroup) mTalkgroup = talkgroup; } + @Override + public boolean isAudioIdentifier() + { + return false; + } + @JacksonXmlProperty(isAttribute = true, localName = "talkgroup") public String getTalkgroup() { diff --git a/src/main/java/io/github/dsheirer/alias/id/legacy/talkgroup/LegacyTalkgroupIDEditor.java b/src/main/java/io/github/dsheirer/alias/id/legacy/talkgroup/LegacyTalkgroupIDEditor.java deleted file mode 100644 index 7d88b5876..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/legacy/talkgroup/LegacyTalkgroupIDEditor.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer - * - * 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 3 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, see - * ***************************************************************************** - */ -package io.github.dsheirer.alias.id.legacy.talkgroup; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import net.miginfocom.swing.MigLayout; - -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JTextField; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -public class LegacyTalkgroupIDEditor extends DocumentListenerEditor -{ - private static final long serialVersionUID = 1L; - - private static final String HELP_TEXT = - "

Talkgroup Identifier

" - + "P25: 4 or 6 hex characters (e.g. AB12 or ABC123)
" - + "LTR: A-HH-TTT where A=Area H=Home T=Talkgroup (0-01-128)
" - + "Passport: 5-digit number (12345 or 00023)
" - + "
" - + "Wildcard: use an asterisk (*) in place of each talkgroup digit (e.g. 0*1*5)" - + ""; - - private JTextField mTextField; - - public LegacyTalkgroupIDEditor(AliasID aliasID) - { - initGUI(); - - setItem(aliasID); - } - - private void initGUI() - { - setLayout(new MigLayout("fill,wrap 2", "[right][left]", "[][]")); - - add(new JLabel("Talkgroup:")); - - mTextField = new JTextField(); - mTextField.getDocument().addDocumentListener(this); - mTextField.setToolTipText(HELP_TEXT); - add(mTextField, "growx,push"); - - JLabel help = new JLabel("Help ..."); - help.setForeground(Color.BLUE.brighter()); - help.setCursor(new Cursor(Cursor.HAND_CURSOR)); - help.addMouseListener(new MouseAdapter() - { - @Override - public void mouseClicked(MouseEvent e) - { - JOptionPane.showMessageDialog(LegacyTalkgroupIDEditor.this, - HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE); - } - }); - add(help, "align left"); - } - - public LegacyTalkgroupID getTalkgroupID() - { - if(getItem() instanceof LegacyTalkgroupID) - { - return (LegacyTalkgroupID)getItem(); - } - - return null; - } - - @Override - public void setItem(AliasID aliasID) - { - super.setItem(aliasID); - - LegacyTalkgroupID talkgroup = getTalkgroupID(); - - if(talkgroup != null) - { - mTextField.setText(talkgroup.getTalkgroup()); - } - - setModified(false); - - repaint(); - } - - @Override - public void save() - { - LegacyTalkgroupID talkgroup = getTalkgroupID(); - - if(talkgroup != null) - { - talkgroup.setTalkgroup(mTextField.getText()); - } - - setModified(false); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/legacy/uniqueID/UniqueID.java b/src/main/java/io/github/dsheirer/alias/id/legacy/uniqueID/UniqueID.java index 249a66eef..a1a41f821 100644 --- a/src/main/java/io/github/dsheirer/alias/id/legacy/uniqueID/UniqueID.java +++ b/src/main/java/io/github/dsheirer/alias/id/legacy/uniqueID/UniqueID.java @@ -29,6 +29,12 @@ public UniqueID() { } + @Override + public boolean isAudioIdentifier() + { + return false; + } + @JacksonXmlProperty(isAttribute = true, localName = "uid") public int getUid() { diff --git a/src/main/java/io/github/dsheirer/alias/id/legacy/uniqueID/UniqueIDEditor.java b/src/main/java/io/github/dsheirer/alias/id/legacy/uniqueID/UniqueIDEditor.java deleted file mode 100644 index 41dcb6fa6..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/legacy/uniqueID/UniqueIDEditor.java +++ /dev/null @@ -1,123 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * 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 3 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, see - ******************************************************************************/ -package io.github.dsheirer.alias.id.legacy.uniqueID; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -public class UniqueIDEditor extends DocumentListenerEditor -{ - private static final long serialVersionUID = 1L; - - private static final String HELP_TEXT = "" - + "

LTR-Net Unique ID (UID)

" - + "UID: identifier assigned to each radio in
" - + "the range 1 - 2097152" - + ""; - - private JTextField mTextField; - - public UniqueIDEditor( AliasID aliasID ) - { - initGUI(); - - setItem( aliasID ); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); - - add( new JLabel( "Unique ID:" ) ); - mTextField = new JTextField(); - mTextField.getDocument().addDocumentListener( this ); - mTextField.setToolTipText( HELP_TEXT ); - add( mTextField, "growx,push" ); - - JLabel help = new JLabel( "Help ..." ); - help.setForeground( Color.BLUE.brighter() ); - help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - help.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( MouseEvent e ) - { - JOptionPane.showMessageDialog( UniqueIDEditor.this, - HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( help, "align left" ); - } - - public UniqueID getUniqueID() - { - if( getItem() instanceof UniqueID ) - { - return (UniqueID)getItem(); - } - - return null; - } - - @Override - public void setItem( AliasID aliasID ) - { - super.setItem( aliasID ); - - UniqueID uid = getUniqueID(); - - if( uid != null ) - { - mTextField.setText( String.valueOf( uid.getUid() ) ); - } - - setModified( false ); - - repaint(); - } - - @Override - public void save() - { - UniqueID uid = getUniqueID(); - - if( uid != null ) - { - int id = 0; - - try - { - id = Integer.parseInt( mTextField.getText() ); - } - catch( Exception e ) - { - //Do nothing, we couldn't parse the value - } - - uid.setUid( id ); - } - - setModified( false ); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/lojack/LoJackFunctionAndID.java b/src/main/java/io/github/dsheirer/alias/id/lojack/LoJackFunctionAndID.java index 8ec2802ea..efd44446a 100644 --- a/src/main/java/io/github/dsheirer/alias/id/lojack/LoJackFunctionAndID.java +++ b/src/main/java/io/github/dsheirer/alias/id/lojack/LoJackFunctionAndID.java @@ -32,6 +32,12 @@ public LoJackFunctionAndID() { } + @Override + public boolean isAudioIdentifier() + { + return false; + } + @JacksonXmlProperty(isAttribute = true, localName = "id") public String getID() { @@ -41,6 +47,7 @@ public String getID() public void setID(String id) { mID = id; + updateValueProperty(); } @JacksonXmlProperty(isAttribute = true, localName = "function") @@ -52,6 +59,7 @@ public LJ1200Message.Function getFunction() public void setFunction(LJ1200Message.Function function) { mFunction = function; + updateValueProperty(); } @Override diff --git a/src/main/java/io/github/dsheirer/alias/id/lojack/LoJackIDEditor.java b/src/main/java/io/github/dsheirer/alias/id/lojack/LoJackIDEditor.java deleted file mode 100644 index 85e9fc8e9..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/lojack/LoJackIDEditor.java +++ /dev/null @@ -1,160 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * 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 3 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, see - ******************************************************************************/ -package io.github.dsheirer.alias.id.lojack; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import io.github.dsheirer.module.decode.lj1200.LJ1200Message; -import io.github.dsheirer.module.decode.lj1200.LJ1200Message.Function; -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; -import javax.swing.text.MaskFormatter; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -public class LoJackIDEditor extends DocumentListenerEditor -{ - private static final long serialVersionUID = 1L; - - private JTextField mTextField; - - private JComboBox mFunctionCombo; - - private static final String HELP_TEXT = "" - + "

LoJack Function and Identifier

" - + "Function Code: 1Y-SITE ID
" - + "ID: 5 numbers or characters (e.g. 1BN47)
" - + "
" - + "Wildcard: use an asterisk (*) to wildcard ID
" - + "characters (e.g. AB*CD or ***12 or *****)

" - + "The middle character in a reply ID code identifies the
" - + "entity. Valid ID middle characters are:
" - + "
  • Tower: X,Y
  • " - + "
  • Transponder: 0-9,A,C-H,J-N,P-W
  • " - + "
  • Not Used: B,I,O,Z
  • " - + ""; - - public LoJackIDEditor( AliasID aliasID ) - { - initGUI(); - - setItem( aliasID ); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][]" ) ); - - add( new JLabel( "Function:" ) ); - mFunctionCombo = new JComboBox( Function.values() ); - mFunctionCombo.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - setModified( true ); - } - } ); - mFunctionCombo.setToolTipText( HELP_TEXT ); - add( mFunctionCombo, "growx, push" ); - - add( new JLabel( "ID:" ) ); - - MaskFormatter formatter = null; - - try - { - //Mask: any character or number, 5 places - formatter = new MaskFormatter( "AAAAA" ); - } - catch( Exception e ) - { - //Do nothing, the mask was invalid - } - - mTextField = new JFormattedTextField( formatter ); - mTextField.getDocument().addDocumentListener( this ); - mTextField.setToolTipText( HELP_TEXT ); - add( mTextField, "growx,push" ); - - JLabel help = new JLabel( "Help ..." ); - help.setForeground( Color.BLUE.brighter() ); - help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - help.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( MouseEvent e ) - { - JOptionPane.showMessageDialog( LoJackIDEditor.this, - HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( help, "align left" ); - } - - public LoJackFunctionAndID getLoJackID() - { - if( getItem() instanceof LoJackFunctionAndID ) - { - return (LoJackFunctionAndID)getItem(); - } - - return null; - } - - @Override - public void setItem( AliasID aliasID ) - { - super.setItem( aliasID ); - - LoJackFunctionAndID lojack = getLoJackID(); - - if( lojack != null ) - { - mFunctionCombo.setSelectedItem( lojack.getFunction() ); - mTextField.setText( lojack.getID() ); - } - else - { - mFunctionCombo.setSelectedItem( null ); - mTextField.setText( null ); - } - - setModified( false ); - - repaint(); - } - - @Override - public void save() - { - LoJackFunctionAndID lojack = getLoJackID(); - - if( lojack != null ) - { - lojack.setID( mTextField.getText() ); - lojack.setFunction( (Function)mFunctionCombo.getSelectedItem() ); - } - - setModified( false ); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/priority/Priority.java b/src/main/java/io/github/dsheirer/alias/id/priority/Priority.java index 6184c8ffe..3dff5c5a1 100644 --- a/src/main/java/io/github/dsheirer/alias/id/priority/Priority.java +++ b/src/main/java/io/github/dsheirer/alias/id/priority/Priority.java @@ -39,6 +39,17 @@ public Priority() { } + public Priority(int priority) + { + mPriority = priority; + } + + @Override + public boolean isAudioIdentifier() + { + return true; + } + @Override public boolean isValid() { @@ -63,6 +74,7 @@ public int getPriority() public void setPriority(int priority) { mPriority = priority; + updateValueProperty(); } public String toString() diff --git a/src/main/java/io/github/dsheirer/alias/id/priority/PriorityEditor.java b/src/main/java/io/github/dsheirer/alias/id/priority/PriorityEditor.java deleted file mode 100644 index 767625c5d..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/priority/PriorityEditor.java +++ /dev/null @@ -1,163 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * 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 3 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, see - ******************************************************************************/ -package io.github.dsheirer.alias.id.priority; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -public class PriorityEditor extends DocumentListenerEditor -{ - private static final long serialVersionUID = 1L; - - public static final String DO_NOT_MONITOR = "Do Not Monitor"; - - private static final String HELP_TEXT = "" - + "

    Call Audio Priority

    " - + "Priority determines which calls have priority for playback
    " - + "over your computer speakers, or designates an alias for
    " - + "no-monitoring if you don't want to hear calls from an alias.

    " - + "Lower values indicate higher priority levels.

    " - + "Do Not Monitor: slide priority all the way to the right" - + ""; - - private JSlider mPrioritySlider; - private JLabel mPrioritySliderLabel; - - public PriorityEditor( AliasID aliasID ) - { - initGUI(); - - setItem( aliasID ); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); - - mPrioritySlider = new JSlider( JSlider.HORIZONTAL, - Priority.MIN_PRIORITY, - Priority.MAX_PRIORITY + 1, - Priority.MIN_PRIORITY ); - - mPrioritySlider.setMajorTickSpacing( 20 ); - mPrioritySlider.setMinorTickSpacing( 5 ); - mPrioritySlider.setPaintTicks( true ); - mPrioritySlider.setLabelTable( mPrioritySlider.createStandardLabels( 20, 20 ) ); - mPrioritySlider.setPaintLabels( true ); - mPrioritySlider.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - int priority = mPrioritySlider.getValue(); - - if( priority == Priority.MAX_PRIORITY + 1 ) - { - mPrioritySliderLabel.setText( "Priority: Do Not Monitor" ); - } - else - { - mPrioritySliderLabel.setText( "Priority: " + priority ); - } - - setModified( true ); - } - } ); - mPrioritySlider.setToolTipText( HELP_TEXT ); - - mPrioritySliderLabel = new JLabel( "Priority: " + mPrioritySlider.getValue() + " " ); - add( mPrioritySliderLabel, "span,align center" ); - add( mPrioritySlider, "span,grow" ); - - JLabel help = new JLabel( "Help ..." ); - help.setForeground( Color.BLUE.brighter() ); - help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - help.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( MouseEvent e ) - { - JOptionPane.showMessageDialog( PriorityEditor.this, - HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( help, "align left" ); - } - - public Priority getPriority() - { - if( getItem() instanceof Priority ) - { - return (Priority)getItem(); - } - - return null; - } - - @Override - public void setItem( AliasID aliasID ) - { - super.setItem( aliasID ); - - Priority priority = getPriority(); - - if( priority != null ) - { - int value = priority.getPriority(); - - if( value == Priority.DO_NOT_MONITOR ) - { - value = Priority.MAX_PRIORITY + 1; - } - - mPrioritySlider.setValue( value ); - } - - setModified( false ); - - repaint(); - } - - @Override - public void save() - { - Priority priority = getPriority(); - - if( priority != null ) - { - int value = mPrioritySlider.getValue(); - - if( value == Priority.MAX_PRIORITY + 1 ) - { - value = Priority.DO_NOT_MONITOR; - } - - priority.setPriority( value ); - } - - setModified( false ); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/radio/Radio.java b/src/main/java/io/github/dsheirer/alias/id/radio/Radio.java index f266faa30..ef29788fc 100644 --- a/src/main/java/io/github/dsheirer/alias/id/radio/Radio.java +++ b/src/main/java/io/github/dsheirer/alias/id/radio/Radio.java @@ -25,14 +25,13 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import io.github.dsheirer.alias.id.AliasID; import io.github.dsheirer.alias.id.AliasIDType; -import io.github.dsheirer.alias.id.talkgroup.TalkgroupFormat; import io.github.dsheirer.alias.id.talkgroup.TalkgroupFormatter; import io.github.dsheirer.protocol.Protocol; /** * Integer radio identifier with protocol. */ -public class Radio extends AliasID +public class Radio extends AliasID implements Comparable { private Protocol mProtocol = Protocol.UNKNOWN; private int mValue; @@ -48,6 +47,12 @@ public Radio(Protocol protocol, int value) mValue = value; } + @Override + public boolean isAudioIdentifier() + { + return false; + } + @JacksonXmlProperty(isAttribute = true, localName = "value") public int getValue() { @@ -57,6 +62,7 @@ public int getValue() public void setValue(int value) { mValue = value; + updateValueProperty(); } @JacksonXmlProperty(isAttribute = true, localName = "protocol") @@ -68,6 +74,7 @@ public Protocol getProtocol() public void setProtocol(Protocol protocol) { mProtocol = protocol; + updateValueProperty(); } @Override @@ -115,4 +122,22 @@ public AliasIDType getType() { return AliasIDType.RADIO_ID; } + + @Override + public int compareTo(Radio other) + { + if(other == null) + { + return -1; + } + + if(getProtocol().equals(other.getProtocol())) + { + return Integer.compare(getValue(), other.getValue()); + } + else + { + return getProtocol().compareTo(other.getProtocol()); + } + } } diff --git a/src/main/java/io/github/dsheirer/alias/id/radio/RadioEditor.java b/src/main/java/io/github/dsheirer/alias/id/radio/RadioEditor.java deleted file mode 100644 index ebf29ada5..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/radio/RadioEditor.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * 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 3 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, see - * * ***************************************************************************** - * - * - */ -package io.github.dsheirer.alias.id.radio; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import io.github.dsheirer.protocol.Protocol; -import net.miginfocom.swing.MigLayout; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.DefaultComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.JFormattedTextField; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.text.MaskFormatter; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.text.ParseException; - -public class RadioEditor extends DocumentListenerEditor -{ - private final static Logger mLog = LoggerFactory.getLogger(RadioEditor.class); - private static final long serialVersionUID = 1L; - - private static final String VALID_CHARACTERS_FOR_ASTERISK_MASKS = "0123456789 "; - private MaskFormatter mMaskFormatter = new MaskFormatter(); - private JComboBox mComboProtocol; - private JFormattedTextField mRadioIdField; - - - public RadioEditor(AliasID aliasID) - { - initGUI(); - setItem(aliasID); - } - - private void initGUI() - { - setLayout(new MigLayout("fill,wrap 2", "[right][left]", "[][]")); - - add(new JLabel("Protocol:")); - - mComboProtocol = new JComboBox<>(); - - DefaultComboBoxModel model = new DefaultComboBoxModel<>(); - - for(Protocol protocol : Protocol.RADIO_ID_PROTOCOLS) - { - model.addElement(protocol); - } - - mComboProtocol.setModel(model); - mComboProtocol.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - Protocol protocol = (Protocol)mComboProtocol.getSelectedItem(); - updateEditor(protocol); - setModified(true); - } - }); - - add(mComboProtocol); - - add(new JLabel("Value:")); - - mRadioIdField = new JFormattedTextField(mMaskFormatter); - mRadioIdField.getDocument().addDocumentListener(this); - add(mRadioIdField, "growx,push"); - - JLabel help = new JLabel("Help ..."); - help.setForeground(Color.BLUE.brighter()); - help.setCursor(new Cursor(Cursor.HAND_CURSOR)); - help.addMouseListener(new MouseAdapter() - { - @Override - public void mouseClicked(MouseEvent e) - { - RadioFormat radioFormat = RadioFormat.get(getCurrentProtocol()); - JOptionPane.showMessageDialog(RadioEditor.this, - radioFormat.getValidRangeHelpText(), "Help", JOptionPane.INFORMATION_MESSAGE); - } - }); - add(help, "align left"); - } - - private void updateEditor(Protocol protocol) - { - RadioFormat mask = RadioFormat.get(protocol); - int currentValue = 0; - Radio radio = getRadio(); - - if(radio != null) - { - currentValue = radio.getValue(); - } - - try - { - mRadioIdField.setValue(null); - mRadioIdField.setToolTipText(mask.getValidRangeHelpText()); - mMaskFormatter.setMask(mask.getMask()); - if(mask.getMask().contains("*")) - { - mMaskFormatter.setValidCharacters(VALID_CHARACTERS_FOR_ASTERISK_MASKS); - } - mRadioIdField.setValue(RadioFormatter.format(protocol, currentValue)); - } - catch(ParseException pe) - { - mLog.error("Error applying talkgroup editor mask to mask formatter [" + mask.getMask() + "]"); - } - } - - private Protocol getCurrentProtocol() - { - return mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex()); - } - - /** - * Current talkgroup alias id - */ - public Radio getRadio() - { - if(getItem() instanceof Radio) - { - return (Radio)getItem(); - } - - return null; - } - - @Override - public void setItem(AliasID aliasID) - { - super.setItem(aliasID); - - Radio radio = getRadio(); - - if(radio != null) - { - Protocol currentProtocol = getCurrentProtocol(); - if(currentProtocol == radio.getProtocol()) - { - updateEditor(radio.getProtocol()); - } - mComboProtocol.getModel().setSelectedItem(radio.getProtocol()); - - String formatted = RadioFormatter.format(radio.getProtocol(), radio.getValue()); - mRadioIdField.setValue(formatted); - } - else - { - mComboProtocol.getModel().setSelectedItem(Protocol.UNKNOWN); - mRadioIdField.setValue(RadioFormatter.format(getCurrentProtocol(), 0)); - } - - setModified(false); - - repaint(); - } - - @Override - public void save() - { - Radio radio = getRadio(); - - if(radio != null) - { - Protocol protocol = mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex()); - - if(protocol == null) - { - protocol = Protocol.UNKNOWN; - } - - int value = -1; - - try - { - value = RadioFormatter.parse(protocol, mRadioIdField.getText()); - } - catch(ParseException pe) - { - //ignore ... value is still -1 and outside valid value range - } - - RadioFormat mask = RadioFormat.get(protocol); - - //Check for valid value within range ... notify user but allow value to persist - if(value < mask.getMinimumValidValue() || value > mask.getMaximumValidValue()) - { - String message = "Invalid value [" + mRadioIdField.getText() + "]. " + protocol.name() + - " valid range is [" + mask.getValidRangeDescription() + "]"; - - JOptionPane.showMessageDialog(RadioEditor.this, message, "Invalid Radio ID Value", - JOptionPane.ERROR_MESSAGE); - } - else - { - radio.setValue(value); - } - - radio.setProtocol(protocol); - } - - setModified(false); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/radio/RadioFormat.java b/src/main/java/io/github/dsheirer/alias/id/radio/RadioFormat.java index af347f96e..54233f64f 100644 --- a/src/main/java/io/github/dsheirer/alias/id/radio/RadioFormat.java +++ b/src/main/java/io/github/dsheirer/alias/id/radio/RadioFormat.java @@ -36,6 +36,8 @@ public enum RadioFormat { APCO25("********", 0, 0xFFFFFF, "0 to 16,777,215", "APCO25 valid range is 0 to 16,777,215"), + PASSPORT("********", 0, 0x7FFFFF, "0 to 8,388,607", + "PASSPORT valid range is 0 to 8,388,607"), UNKNOWN("********", 1, 0xFFFFFF, "1 to 16,777,215", "Unknown protocol valid value range is 1-16,777,215"); @@ -110,6 +112,8 @@ public static RadioFormat get(Protocol protocol) { case APCO25: return APCO25; + case PASSPORT: + return PASSPORT; default: return UNKNOWN; } diff --git a/src/main/java/io/github/dsheirer/alias/id/radio/RadioFormatter.java b/src/main/java/io/github/dsheirer/alias/id/radio/RadioFormatter.java index 1d542e9e0..3dc8f3338 100644 --- a/src/main/java/io/github/dsheirer/alias/id/radio/RadioFormatter.java +++ b/src/main/java/io/github/dsheirer/alias/id/radio/RadioFormatter.java @@ -22,8 +22,10 @@ package io.github.dsheirer.alias.id.radio; +import io.github.dsheirer.preference.identifier.IntegerFormat; import io.github.dsheirer.preference.identifier.talkgroup.APCO25TalkgroupFormatter; -import io.github.dsheirer.preference.identifier.talkgroup.IntegerFormatter; +import io.github.dsheirer.preference.identifier.talkgroup.AbstractIntegerFormatter; +import io.github.dsheirer.preference.identifier.talkgroup.UnknownTalkgroupFormatter; import io.github.dsheirer.protocol.Protocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,12 +41,12 @@ public class RadioFormatter { private final static Logger mLog = LoggerFactory.getLogger(RadioFormatter.class); - private static Map mFormatterMap = new EnumMap<>(Protocol.class); + private static Map mFormatterMap = new EnumMap<>(Protocol.class); static { mFormatterMap.put(Protocol.APCO25, new APCO25TalkgroupFormatter()); - mFormatterMap.put(Protocol.UNKNOWN, new IntegerFormatter()); + mFormatterMap.put(Protocol.UNKNOWN, new UnknownTalkgroupFormatter()); } public RadioFormatter() @@ -56,7 +58,7 @@ public RadioFormatter() */ public static int parse(Protocol protocol, String value) throws ParseException { - IntegerFormatter formatter = mFormatterMap.get(protocol); + AbstractIntegerFormatter formatter = mFormatterMap.get(protocol); if(formatter == null) { @@ -71,7 +73,7 @@ public static int parse(Protocol protocol, String value) throws ParseException */ public static String format(Protocol protocol, int value) { - IntegerFormatter formatter = mFormatterMap.get(protocol); + AbstractIntegerFormatter formatter = mFormatterMap.get(protocol); if(formatter == null) { @@ -80,4 +82,19 @@ public static String format(Protocol protocol, int value) return formatter.format(value); } + + /** + * Formats the integer value to the specified integer format using the protocol specific formatter + */ + public static String format(Protocol protocol, int value, IntegerFormat format) + { + AbstractIntegerFormatter formatter = mFormatterMap.get(protocol); + + if(formatter == null) + { + formatter = mFormatterMap.get(Protocol.UNKNOWN); + } + + return formatter.format(value, format); + } } diff --git a/src/main/java/io/github/dsheirer/alias/id/radio/RadioRange.java b/src/main/java/io/github/dsheirer/alias/id/radio/RadioRange.java index 207ce6657..44724fdba 100644 --- a/src/main/java/io/github/dsheirer/alias/id/radio/RadioRange.java +++ b/src/main/java/io/github/dsheirer/alias/id/radio/RadioRange.java @@ -30,7 +30,7 @@ /** * Integer radio identifier range of values with protocol. */ -public class RadioRange extends AliasID +public class RadioRange extends AliasID implements Comparable { private Protocol mProtocol = Protocol.UNKNOWN; private int mMinRadio; @@ -41,6 +41,12 @@ public RadioRange() //No arg JAXB constructor } + @Override + public boolean isAudioIdentifier() + { + return false; + } + /** * Creates a radio range of from - to radio values (inclusive) for the specified protocol * @param protocol for the radio range @@ -63,6 +69,7 @@ public int getMinRadio() public void setMinRadio(int minRadio) { mMinRadio = minRadio; + updateValueProperty(); } @JacksonXmlProperty(isAttribute = true, localName = "max") @@ -74,6 +81,7 @@ public int getMaxRadio() public void setMaxRadio(int maxRadio) { mMaxRadio = maxRadio; + updateValueProperty(); } @JacksonXmlProperty(isAttribute = true, localName = "protocol") @@ -99,7 +107,8 @@ public boolean isValid() return radioFormat.getMinimumValidValue() <= mMinRadio && mMinRadio <= radioFormat.getMaximumValidValue() && radioFormat.getMinimumValidValue() <= mMaxRadio && - mMaxRadio <= radioFormat.getMaximumValidValue(); + mMaxRadio <= radioFormat.getMaximumValidValue() && + mMinRadio < mMaxRadio; } public String toString() @@ -142,6 +151,13 @@ public boolean contains(int radioValue) return getMinRadio() <= radioValue && radioValue <= getMaxRadio(); } + @Override + public boolean overlaps(AliasID other) + { + return other instanceof RadioRange && overlaps((RadioRange)other); + } + + /** * Indicates if this talkgroup range overlaps the talkgroup range argument. * @param radioRange to check for overlap @@ -163,4 +179,30 @@ public AliasIDType getType() { return AliasIDType.RADIO_ID_RANGE; } + + + @Override + public int compareTo(RadioRange other) + { + if(other == null) + { + return -1; + } + + if(getProtocol().equals(other.getProtocol())) + { + if(getMinRadio() == other.getMinRadio()) + { + return Integer.compare(getMaxRadio(), other.getMaxRadio()); + } + else + { + return Integer.compare(getMinRadio(), other.getMinRadio()); + } + } + else + { + return getProtocol().compareTo(other.getProtocol()); + } + } } diff --git a/src/main/java/io/github/dsheirer/alias/id/radio/RadioRangeEditor.java b/src/main/java/io/github/dsheirer/alias/id/radio/RadioRangeEditor.java deleted file mode 100644 index 37c2b7773..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/radio/RadioRangeEditor.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * 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 3 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, see - * * ***************************************************************************** - * - * - */ -package io.github.dsheirer.alias.id.radio; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import io.github.dsheirer.protocol.Protocol; -import net.miginfocom.swing.MigLayout; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.DefaultComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.JFormattedTextField; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.text.MaskFormatter; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.text.ParseException; - -public class RadioRangeEditor extends DocumentListenerEditor -{ - private final static Logger mLog = LoggerFactory.getLogger(RadioRangeEditor.class); - private static final long serialVersionUID = 1L; - - private static final String VALID_CHARACTERS_FOR_ASTERISK_MASKS = "0123456789 "; - private MaskFormatter mMaskFormatter = new MaskFormatter(); - private JComboBox mComboProtocol; - private JFormattedTextField mMinRadioField; - private JFormattedTextField mMaxRadioField; - - public RadioRangeEditor(AliasID aliasID) - { - initGUI(); - - setItem(aliasID); - } - - private void initGUI() - { - setLayout(new MigLayout("fill,wrap 2", "[right][left]", "[][]")); - - add(new JLabel("Protocol:")); - - mComboProtocol = new JComboBox(); - - DefaultComboBoxModel model = new DefaultComboBoxModel<>(); - - for(Protocol protocol : Protocol.RADIO_ID_PROTOCOLS) - { - model.addElement(protocol); - } - - mComboProtocol.setModel(model); - mComboProtocol.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - Protocol protocol = (Protocol)mComboProtocol.getSelectedItem(); - updateEditor(protocol); - setModified(true); - } - }); - - add(mComboProtocol); - - add(new JLabel("Min:")); - - mMinRadioField = new JFormattedTextField(mMaskFormatter); - mMinRadioField.getDocument().addDocumentListener(this); - add(mMinRadioField, "growx,push"); - - add(new JLabel("Max:")); - - mMaxRadioField = new JFormattedTextField(mMaskFormatter); - mMaxRadioField.getDocument().addDocumentListener(this); - add(mMaxRadioField, "growx,push"); - - JLabel help = new JLabel("Help ..."); - help.setForeground(Color.BLUE.brighter()); - help.setCursor(new Cursor(Cursor.HAND_CURSOR)); - help.addMouseListener(new MouseAdapter() - { - @Override - public void mouseClicked(MouseEvent e) - { - RadioFormat radioFormat = RadioFormat.get(getCurrentProtocol()); - JOptionPane.showMessageDialog(RadioRangeEditor.this, - radioFormat.getValidRangeHelpText(), "Help", JOptionPane.INFORMATION_MESSAGE); - } - }); - add(help, "align left"); - } - - private void updateEditor(Protocol protocol) - { - RadioFormat mask = RadioFormat.get(protocol); - int currentMinValue = 0; - int currentMaxValue = 0; - RadioRange radioRange = getRadioRange(); - - if(radioRange != null) - { - currentMinValue = radioRange.getMinRadio(); - currentMaxValue = radioRange.getMaxRadio(); - } - - try - { - mMinRadioField.setValue(null); - mMaxRadioField.setValue(null); - - mMinRadioField.setToolTipText(mask.getValidRangeHelpText()); - mMaxRadioField.setToolTipText(mask.getValidRangeHelpText()); - mMaskFormatter.setMask(mask.getMask()); - if(mask.getMask().contains("*")) - { - mMaskFormatter.setValidCharacters(VALID_CHARACTERS_FOR_ASTERISK_MASKS); - } - - mMinRadioField.setValue(RadioFormatter.format(protocol, currentMinValue)); - mMaxRadioField.setValue(RadioFormatter.format(protocol, currentMaxValue)); - } - catch(ParseException pe) - { - mLog.error("Error applying radio editor mask to mask formatter [" + mask.getMask() + "]"); - } - } - - private Protocol getCurrentProtocol() - { - return mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex()); - } - - - - public RadioRange getRadioRange() - { - if(getItem() instanceof RadioRange) - { - return (RadioRange)getItem(); - } - - return null; - } - - @Override - public void setItem(AliasID aliasID) - { - super.setItem(aliasID); - - RadioRange radioRange = getRadioRange(); - - updateEditor(radioRange.getProtocol()); - - if(radioRange != null) - { - mComboProtocol.getModel().setSelectedItem(radioRange.getProtocol()); - String minFormatted = RadioFormatter.format(radioRange.getProtocol(), radioRange.getMinRadio()); - mMinRadioField.setValue(minFormatted); - String maxFormatted = RadioFormatter.format(radioRange.getProtocol(), radioRange.getMaxRadio()); - mMaxRadioField.setValue(maxFormatted); - } - else - { - mComboProtocol.getModel().setSelectedItem(Protocol.UNKNOWN); - mMinRadioField.setValue(RadioFormatter.format(getCurrentProtocol(), 0)); - mMaxRadioField.setValue(RadioFormatter.format(getCurrentProtocol(), 0)); - } - - setModified(false); - - repaint(); - } - - @Override - public void save() - { - RadioRange radioRange = getRadioRange(); - - if(radioRange != null) - { - Protocol protocol = mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex()); - - if(protocol == null) - { - protocol = Protocol.UNKNOWN; - } - - radioRange.setProtocol(protocol); - - int minValue = -1; - int maxValue = -1; - - try - { - minValue = RadioFormatter.parse(protocol, mMinRadioField.getText()); - } - catch(ParseException pe) - { - //ignore ... value is still -1 and outside valid value range - } - - try - { - maxValue = RadioFormatter.parse(protocol, mMaxRadioField.getText()); - } - catch(ParseException pe) - { - //ignore ... value is still -1 and outside valid value range - } - - RadioFormat mask = RadioFormat.get(protocol); - - //Check for valid value within range ... notify user but allow value to persist - if(minValue < mask.getMinimumValidValue() || minValue > mask.getMaximumValidValue()) - { - String message = "Invalid minimum value [" + mMinRadioField.getText() + "]. " + protocol.name() + - " valid range is [" + mask.getValidRangeDescription() + "]"; - - JOptionPane.showMessageDialog(RadioRangeEditor.this, message, - "Invalid Radio Value", JOptionPane.ERROR_MESSAGE); - } - else - { - radioRange.setMinRadio(minValue); - } - - //Check for valid value within range ... notify user but allow value to persist - if(maxValue < mask.getMinimumValidValue() || maxValue > mask.getMaximumValidValue()) - { - String message = "Invalid maximum value [" + mMaxRadioField.getText() + "]. " + protocol.name() + - " valid range is [" + mask.getValidRangeDescription() + "]"; - - JOptionPane.showMessageDialog(RadioRangeEditor.this, message, - "Invalid Radio Value", JOptionPane.ERROR_MESSAGE); - } - else - { - radioRange.setMaxRadio(maxValue); - } - - radioRange.setProtocol(protocol); - } - - setModified(false); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/record/Record.java b/src/main/java/io/github/dsheirer/alias/id/record/Record.java index 85ab55ca2..201f7d90e 100644 --- a/src/main/java/io/github/dsheirer/alias/id/record/Record.java +++ b/src/main/java/io/github/dsheirer/alias/id/record/Record.java @@ -36,6 +36,12 @@ public AliasIDType getType() return AliasIDType.RECORD; } + @Override + public boolean isAudioIdentifier() + { + return true; + } + @Override public boolean isValid() { diff --git a/src/main/java/io/github/dsheirer/alias/id/record/RecordEditor.java b/src/main/java/io/github/dsheirer/alias/id/record/RecordEditor.java deleted file mode 100644 index 91b8c8090..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/record/RecordEditor.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer - * - * 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 3 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, see - * ***************************************************************************** - */ -package io.github.dsheirer.alias.id.record; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import net.miginfocom.swing.MigLayout; - -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -public class RecordEditor extends DocumentListenerEditor -{ - private static final long serialVersionUID = 1L; - - private static final String HELP_TEXT = "" - + "

    Record Audio

    " - + "Any audio associated with this alias will be recorded.

    "; - - public RecordEditor(AliasID aliasID ) - { - initGUI(); - - setItem( aliasID ); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 1", "[]", "[][]" ) ); - - add( new JLabel( "Record Audio" ), "wrap" ); - - JLabel help = new JLabel( "Help ..." ); - help.setForeground( Color.BLUE.brighter() ); - help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - help.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( MouseEvent e ) - { - JOptionPane.showMessageDialog( RecordEditor.this, - HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( help, "align left" ); - } - - @Override - public void save() - { - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/status/StatusID.java b/src/main/java/io/github/dsheirer/alias/id/status/StatusID.java deleted file mode 100644 index 24050b1a7..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/status/StatusID.java +++ /dev/null @@ -1,75 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * 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 3 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, see - ******************************************************************************/ -package io.github.dsheirer.alias.id.status; - -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.alias.id.AliasIDType; - -public class StatusID extends AliasID -{ - private int mStatus; - - public StatusID() - { - } - - @JacksonXmlProperty(isAttribute = true, localName = "status") - public int getStatus() - { - return mStatus; - } - - public void setStatus(int status) - { - this.mStatus = status; - } - - @Override - public boolean isValid() - { - return true; - } - - public String toString() - { - return "Status: " + String.format("%03d", mStatus); - } - - @Override - public boolean matches(AliasID id) - { - boolean retVal = false; - - if(id instanceof StatusID) - { - StatusID statusId = (StatusID)id; - - retVal = (mStatus == statusId.getStatus()); - } - - return retVal; - } - - @JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") - @Override - public AliasIDType getType() - { - return AliasIDType.STATUS; - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/status/StatusIDEditor.java b/src/main/java/io/github/dsheirer/alias/id/status/StatusIDEditor.java deleted file mode 100644 index 69a4b9966..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/status/StatusIDEditor.java +++ /dev/null @@ -1,140 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * 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 3 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, see - ******************************************************************************/ -package io.github.dsheirer.alias.id.status; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; -import javax.swing.text.MaskFormatter; -import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -public class StatusIDEditor extends DocumentListenerEditor -{ - private static final long serialVersionUID = 1L; - - private static final String HELP_TEXT = "" - + "

    Status Identifier

    " - + "Status numbers are used in some protocols like Fleetsync.

    " - + "The status number is assigned a meaning in the radio. You
    " - + "can assign a 3 digit status code (use leading zeros) to an
    " - + "alias where the alias contains the status meaning.

    " - + "Status: 001 engine start" - + ""; - - private JTextField mTextField; - - public StatusIDEditor( AliasID aliasID ) - { - initGUI(); - - setItem( aliasID ); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); - - add( new JLabel( "Status:" ) ); - - MaskFormatter formatter = null; - - try - { - //Mask: 3 digits - formatter = new MaskFormatter( "###" ); - } - catch( Exception e ) - { - //Do nothing, the mask was invalid - } - - mTextField = new JFormattedTextField( formatter ); - mTextField.getDocument().addDocumentListener( this ); - mTextField.setToolTipText( HELP_TEXT ); - add( mTextField, "growx,push" ); - - JLabel help = new JLabel( "Help ..." ); - help.setForeground( Color.BLUE.brighter() ); - help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - help.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( MouseEvent e ) - { - JOptionPane.showMessageDialog( StatusIDEditor.this, - HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( help, "align left" ); - } - - public StatusID getStatusID() - { - if( getItem() instanceof StatusID ) - { - return (StatusID)getItem(); - } - - return null; - } - - @Override - public void setItem( AliasID aliasID ) - { - super.setItem( aliasID ); - - StatusID statusID = getStatusID(); - - if( statusID != null ) - { - mTextField.setText( String.format( "%03d", statusID.getStatus() ) ); - } - - setModified( false ); - - repaint(); - } - - @Override - public void save() - { - StatusID statusID = getStatusID(); - - if( statusID != null ) - { - int status = 0; - - try - { - status = Integer.valueOf( mTextField.getText() ); - } - catch( Exception e ) - { - //Do nothing, we couldn't parse the value - } - - statusID.setStatus( status ); - } - - setModified( false ); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/status/UnitStatusID.java b/src/main/java/io/github/dsheirer/alias/id/status/UnitStatusID.java new file mode 100644 index 000000000..78f5f4c55 --- /dev/null +++ b/src/main/java/io/github/dsheirer/alias/id/status/UnitStatusID.java @@ -0,0 +1,88 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2020 Dennis Sheirer + * + * 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 3 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, see + * **************************************************************************** + */ +package io.github.dsheirer.alias.id.status; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import io.github.dsheirer.alias.id.AliasID; +import io.github.dsheirer.alias.id.AliasIDType; + +/** + * Unit Status + */ +public class UnitStatusID extends AliasID +{ + private int mStatus; + + public UnitStatusID() + { + } + + @Override + public boolean isAudioIdentifier() + { + return false; + } + + @JacksonXmlProperty(isAttribute = true, localName = "status") + public int getStatus() + { + return mStatus; + } + + public void setStatus(int status) + { + mStatus = status; + updateValueProperty(); + } + + @Override + public boolean isValid() + { + return true; + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("Unit Status: ").append(String.format("%03d", mStatus)); + return sb.toString(); + } + + @Override + public boolean matches(AliasID id) + { + boolean retVal = false; + + if(id instanceof UnitStatusID) + { + UnitStatusID userStatusId = (UnitStatusID)id; + + retVal = (mStatus == userStatusId.getStatus()); + } + + return retVal; + } + + @JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") + @Override + public AliasIDType getType() + { + return AliasIDType.UNIT_STATUS; + } +} diff --git a/src/main/java/io/github/dsheirer/alias/id/status/UserStatusID.java b/src/main/java/io/github/dsheirer/alias/id/status/UserStatusID.java new file mode 100644 index 000000000..3794969e8 --- /dev/null +++ b/src/main/java/io/github/dsheirer/alias/id/status/UserStatusID.java @@ -0,0 +1,88 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2020 Dennis Sheirer + * + * 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 3 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, see + * **************************************************************************** + */ +package io.github.dsheirer.alias.id.status; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import io.github.dsheirer.alias.id.AliasID; +import io.github.dsheirer.alias.id.AliasIDType; + +/** + * User Status + */ +public class UserStatusID extends AliasID +{ + private int mStatus; + + public UserStatusID() + { + } + + @Override + public boolean isAudioIdentifier() + { + return false; + } + + @JacksonXmlProperty(isAttribute = true, localName = "status") + public int getStatus() + { + return mStatus; + } + + public void setStatus(int status) + { + mStatus = status; + updateValueProperty(); + } + + @Override + public boolean isValid() + { + return true; + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("User Status: ").append(String.format("%03d", mStatus)); + return sb.toString(); + } + + @Override + public boolean matches(AliasID id) + { + boolean retVal = false; + + if(id instanceof UserStatusID) + { + UserStatusID userStatusId = (UserStatusID)id; + + retVal = (mStatus == userStatusId.getStatus()); + } + + return retVal; + } + + @JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") + @Override + public AliasIDType getType() + { + return AliasIDType.STATUS; + } +} diff --git a/src/main/java/io/github/dsheirer/alias/id/talkgroup/Talkgroup.java b/src/main/java/io/github/dsheirer/alias/id/talkgroup/Talkgroup.java index a10a713c8..b38e67051 100644 --- a/src/main/java/io/github/dsheirer/alias/id/talkgroup/Talkgroup.java +++ b/src/main/java/io/github/dsheirer/alias/id/talkgroup/Talkgroup.java @@ -24,12 +24,15 @@ import io.github.dsheirer.alias.id.AliasID; import io.github.dsheirer.alias.id.AliasIDType; import io.github.dsheirer.protocol.Protocol; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Integer talkgroup identifier with protocol. */ -public class Talkgroup extends AliasID +public class Talkgroup extends AliasID implements Comparable { + private static final Logger mLog = LoggerFactory.getLogger(Talkgroup.class); private Protocol mProtocol = Protocol.UNKNOWN; private int mValue; @@ -44,6 +47,12 @@ public Talkgroup(Protocol protocol, int value) mValue = value; } + @Override + public boolean isAudioIdentifier() + { + return false; + } + @JacksonXmlProperty(isAttribute = true, localName = "value") public int getValue() { @@ -53,6 +62,7 @@ public int getValue() public void setValue(int value) { mValue = value; + updateValueProperty(); } @JacksonXmlProperty(isAttribute = true, localName = "protocol") @@ -64,6 +74,7 @@ public Protocol getProtocol() public void setProtocol(Protocol protocol) { mProtocol = protocol; + updateValueProperty(); } @Override @@ -82,7 +93,18 @@ public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("Talkgroup:").append(TalkgroupFormatter.format(mProtocol, mValue)); + String talkgroup = null; + + try + { + talkgroup = TalkgroupFormatter.format(mProtocol, mValue); + } + catch(Exception e) + { + mLog.error("Error formatting Talkgroup Protocol [" + mProtocol + "] value [" + mValue + "]", e); + } + + sb.append("Talkgroup:").append(talkgroup != null ? talkgroup : "error"); sb.append(" Protocol:").append((mProtocol)); if(!isValid()) @@ -111,4 +133,22 @@ public AliasIDType getType() { return AliasIDType.TALKGROUP; } + + @Override + public int compareTo(Talkgroup other) + { + if(other == null) + { + return -1; + } + + if(getProtocol().equals(other.getProtocol())) + { + return Integer.compare(getValue(), other.getValue()); + } + else + { + return getProtocol().compareTo(other.getProtocol()); + } + } } diff --git a/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupEditor.java b/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupEditor.java deleted file mode 100644 index 9d0a37c35..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupEditor.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2019 Dennis Sheirer - * - * 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 3 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, see - * ***************************************************************************** - */ -package io.github.dsheirer.alias.id.talkgroup; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import io.github.dsheirer.protocol.Protocol; -import net.miginfocom.swing.MigLayout; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.DefaultComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.JFormattedTextField; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.text.MaskFormatter; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.text.ParseException; - -public class TalkgroupEditor extends DocumentListenerEditor -{ - private final static Logger mLog = LoggerFactory.getLogger(TalkgroupEditor.class); - private static final long serialVersionUID = 1L; - - private static final String VALID_CHARACTERS_FOR_ASTERISK_MASKS = "0123456789 "; - private MaskFormatter mMaskFormatter = new MaskFormatter(); - private JComboBox mComboProtocol; - private JFormattedTextField mTalkgroupField; - - - public TalkgroupEditor(AliasID aliasID) - { - initGUI(); - setItem(aliasID); - } - - private void initGUI() - { - setLayout(new MigLayout("fill,wrap 2", "[right][left]", "[][]")); - - add(new JLabel("Protocol:")); - - mComboProtocol = new JComboBox<>(); - - DefaultComboBoxModel model = new DefaultComboBoxModel<>(); - - for(Protocol protocol : Protocol.TALKGROUP_PROTOCOLS) - { - model.addElement(protocol); - } - - mComboProtocol.setModel(model); - mComboProtocol.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - Protocol protocol = (Protocol)mComboProtocol.getSelectedItem(); - updateEditor(protocol); - setModified(true); - } - }); - - add(mComboProtocol); - - add(new JLabel("Value:")); - - mTalkgroupField = new JFormattedTextField(mMaskFormatter); - mTalkgroupField.getDocument().addDocumentListener(this); - add(mTalkgroupField, "growx,push"); - - JLabel help = new JLabel("Help ..."); - help.setForeground(Color.BLUE.brighter()); - help.setCursor(new Cursor(Cursor.HAND_CURSOR)); - help.addMouseListener(new MouseAdapter() - { - @Override - public void mouseClicked(MouseEvent e) - { - TalkgroupFormat talkgroupFormat = TalkgroupFormat.get(getCurrentProtocol()); - JOptionPane.showMessageDialog(TalkgroupEditor.this, - talkgroupFormat.getValidRangeHelpText(), "Help", JOptionPane.INFORMATION_MESSAGE); - } - }); - add(help, "align left"); - } - - private void updateEditor(Protocol protocol) - { - TalkgroupFormat mask = TalkgroupFormat.get(protocol); - int currentValue = 0; - Talkgroup talkgroup = getTalkgroup(); - - if(talkgroup != null) - { - currentValue = talkgroup.getValue(); - } - - try - { - mTalkgroupField.setValue(null); - mTalkgroupField.setToolTipText(mask.getValidRangeHelpText()); - mMaskFormatter.setMask(mask.getMask()); - if(mask.getMask().contains("*")) - { - mMaskFormatter.setValidCharacters(VALID_CHARACTERS_FOR_ASTERISK_MASKS); - } - mTalkgroupField.setValue(TalkgroupFormatter.format(protocol, currentValue)); - } - catch(ParseException pe) - { - mLog.error("Error applying talkgroup editor mask to mask formatter [" + mask.getMask() + "]"); - } - } - - private Protocol getCurrentProtocol() - { - return mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex()); - } - - /** - * Current talkgroup alias id - */ - public Talkgroup getTalkgroup() - { - if(getItem() instanceof Talkgroup) - { - return (Talkgroup)getItem(); - } - - return null; - } - - @Override - public void setItem(AliasID aliasID) - { - super.setItem(aliasID); - - Talkgroup talkgroup = getTalkgroup(); - - if(talkgroup != null) - { - Protocol currentProtocol = getCurrentProtocol(); - if(currentProtocol == talkgroup.getProtocol()) - { - updateEditor(talkgroup.getProtocol()); - } - mComboProtocol.getModel().setSelectedItem(talkgroup.getProtocol()); - - String formatted = TalkgroupFormatter.format(talkgroup.getProtocol(), talkgroup.getValue()); - mTalkgroupField.setValue(formatted); - } - else - { - mComboProtocol.getModel().setSelectedItem(Protocol.UNKNOWN); - mTalkgroupField.setValue(TalkgroupFormatter.format(getCurrentProtocol(), 0)); - } - - setModified(false); - - repaint(); - } - - @Override - public void save() - { - Talkgroup talkgroup = getTalkgroup(); - - if(talkgroup != null) - { - Protocol protocol = mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex()); - - if(protocol == null) - { - protocol = Protocol.UNKNOWN; - } - - int value = -1; - - try - { - value = TalkgroupFormatter.parse(protocol, mTalkgroupField.getText()); - } - catch(ParseException pe) - { - //ignore ... value is still -1 and outside valid value range - } - - TalkgroupFormat mask = TalkgroupFormat.get(protocol); - - //Check for valid value within range ... notify user but allow value to persist - if(value < mask.getMinimumValidValue() || value > mask.getMaximumValidValue()) - { - String message = "Invalid value [" + mTalkgroupField.getText() + "]. " + protocol.name() + - " valid range is [" + mask.getValidRangeDescription() + "]"; - - JOptionPane.showMessageDialog(TalkgroupEditor.this, message, "Invalid Talkgroup Value", - JOptionPane.ERROR_MESSAGE); - } - else - { - talkgroup.setValue(value); - } - - talkgroup.setProtocol(protocol); - } - - setModified(false); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupFormat.java b/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupFormat.java index 236a35e30..3ecc3adf5 100644 --- a/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupFormat.java +++ b/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupFormat.java @@ -36,9 +36,9 @@ public enum TalkgroupFormat { APCO25("********", 0, 0xFFFFFF, "0 to 65,535", "APCO25 talkgroup valid range is 0 to 65,535"), - FLEETSYNC("###-####", 1, 0x7FFFFF, "001-0001 to 127-8192", + FLEETSYNC("###-####", 0, 0x7FFFFF, "001-0001 to 127-8192", "Fleetsync valid ranges are 1-127(prefix)
    and 1-8192(ident) (ie. 001-0001 to 127-8192)"), - LTR("##-###", 257, 5375, "01-001 to 20-255", + LTR("##-###", 0x101, 0x3FFF, "01-001 to 20-255", "LTR valid ranges are 1-20(repeater) and 1-255(talkgroup) (ie. 01-001 to 20-255)"), MDC1200("*****", 1, 0xFFFF, "1 to 65,535", "MDC-1200 valid value range is 1-65,535"), @@ -124,7 +124,6 @@ public static TalkgroupFormat get(Protocol protocol) return FLEETSYNC; case LTR: case LTR_NET: - case LTR_STANDARD: return LTR; case MDC1200: return MDC1200; diff --git a/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupFormatter.java b/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupFormatter.java index 46c964e55..7e1b94d2d 100644 --- a/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupFormatter.java +++ b/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupFormatter.java @@ -20,13 +20,15 @@ package io.github.dsheirer.alias.id.talkgroup; +import io.github.dsheirer.preference.identifier.IntegerFormat; import io.github.dsheirer.preference.identifier.talkgroup.APCO25TalkgroupFormatter; +import io.github.dsheirer.preference.identifier.talkgroup.AbstractIntegerFormatter; import io.github.dsheirer.preference.identifier.talkgroup.FleetsyncTalkgroupFormatter; -import io.github.dsheirer.preference.identifier.talkgroup.IntegerFormatter; import io.github.dsheirer.preference.identifier.talkgroup.LTRTalkgroupFormatter; import io.github.dsheirer.preference.identifier.talkgroup.MDC1200TalkgroupFormatter; import io.github.dsheirer.preference.identifier.talkgroup.MPT1327TalkgroupFormatter; import io.github.dsheirer.preference.identifier.talkgroup.PassportTalkgroupFormatter; +import io.github.dsheirer.preference.identifier.talkgroup.UnknownTalkgroupFormatter; import io.github.dsheirer.protocol.Protocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,7 +44,7 @@ public class TalkgroupFormatter { private final static Logger mLog = LoggerFactory.getLogger(TalkgroupFormatter.class); - private static Map mFormatterMap = new EnumMap<>(Protocol.class); + private static Map mFormatterMap = new EnumMap<>(Protocol.class); static { @@ -51,11 +53,10 @@ public class TalkgroupFormatter LTRTalkgroupFormatter ltr = new LTRTalkgroupFormatter(); mFormatterMap.put(Protocol.LTR, ltr); mFormatterMap.put(Protocol.LTR_NET, ltr); - mFormatterMap.put(Protocol.LTR_STANDARD, ltr); mFormatterMap.put(Protocol.MDC1200, new MDC1200TalkgroupFormatter()); mFormatterMap.put(Protocol.MPT1327, new MPT1327TalkgroupFormatter()); mFormatterMap.put(Protocol.PASSPORT, new PassportTalkgroupFormatter()); - mFormatterMap.put(Protocol.UNKNOWN, new IntegerFormatter()); + mFormatterMap.put(Protocol.UNKNOWN, new UnknownTalkgroupFormatter()); } public TalkgroupFormatter() @@ -67,7 +68,7 @@ public TalkgroupFormatter() */ public static int parse(Protocol protocol, String value) throws ParseException { - IntegerFormatter formatter = mFormatterMap.get(protocol); + AbstractIntegerFormatter formatter = mFormatterMap.get(protocol); if(formatter == null) { @@ -82,7 +83,7 @@ public static int parse(Protocol protocol, String value) throws ParseException */ public static String format(Protocol protocol, int value) { - IntegerFormatter formatter = mFormatterMap.get(protocol); + AbstractIntegerFormatter formatter = mFormatterMap.get(protocol); if(formatter == null) { @@ -91,4 +92,19 @@ public static String format(Protocol protocol, int value) return formatter.format(value); } + + /** + * Formats the integer value to the specified integer format using the protocol specific formatter + */ + public static String format(Protocol protocol, int value, IntegerFormat format) + { + AbstractIntegerFormatter formatter = mFormatterMap.get(protocol); + + if(formatter == null) + { + formatter = mFormatterMap.get(Protocol.UNKNOWN); + } + + return formatter.format(value, format); + } } diff --git a/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupRange.java b/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupRange.java index a0df981c0..01cf6c93b 100644 --- a/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupRange.java +++ b/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupRange.java @@ -28,7 +28,7 @@ /** * Integer talkgroup identifier range of values with protocol. */ -public class TalkgroupRange extends AliasID +public class TalkgroupRange extends AliasID implements Comparable { private Protocol mProtocol = Protocol.UNKNOWN; private int mMinTalkgroup; @@ -52,6 +52,12 @@ public TalkgroupRange(Protocol protocol, int minTalkgroup, int maxTalkgroup) mMaxTalkgroup = maxTalkgroup; } + @Override + public boolean isAudioIdentifier() + { + return false; + } + @JacksonXmlProperty(isAttribute = true, localName = "min") public int getMinTalkgroup() { @@ -61,6 +67,7 @@ public int getMinTalkgroup() public void setMinTalkgroup(int minTalkgroup) { mMinTalkgroup = minTalkgroup; + updateValueProperty(); } @JacksonXmlProperty(isAttribute = true, localName = "max") @@ -72,6 +79,7 @@ public int getMaxTalkgroup() public void setMaxTalkgroup(int maxTalkgroup) { mMaxTalkgroup = maxTalkgroup; + updateValueProperty(); } @JacksonXmlProperty(isAttribute = true, localName = "protocol") @@ -83,6 +91,7 @@ public Protocol getProtocol() public void setProtocol(Protocol protocol) { mProtocol = protocol; + updateValueProperty(); } @Override @@ -97,7 +106,8 @@ public boolean isValid() return talkgroupFormat.getMinimumValidValue() <= mMinTalkgroup && mMinTalkgroup <= talkgroupFormat.getMaximumValidValue() && talkgroupFormat.getMinimumValidValue() <= mMaxTalkgroup && - mMaxTalkgroup <= talkgroupFormat.getMaximumValidValue(); + mMaxTalkgroup <= talkgroupFormat.getMaximumValidValue() && + mMinTalkgroup < mMaxTalkgroup; } public String toString() @@ -140,6 +150,12 @@ public boolean contains(int talkgroupValue) return getMinTalkgroup() <= talkgroupValue && talkgroupValue <= getMaxTalkgroup(); } + @Override + public boolean overlaps(AliasID other) + { + return other instanceof TalkgroupRange && overlaps((TalkgroupRange)other); + } + /** * Indicates if this talkgroup range overlaps the talkgroup range argument. * @param talkgroupRange to check for overlap @@ -161,4 +177,29 @@ public AliasIDType getType() { return AliasIDType.TALKGROUP_RANGE; } + + @Override + public int compareTo(TalkgroupRange other) + { + if(other == null) + { + return -1; + } + + if(getProtocol().equals(other.getProtocol())) + { + if(getMinTalkgroup() == other.getMinTalkgroup()) + { + return Integer.compare(getMaxTalkgroup(), other.getMaxTalkgroup()); + } + else + { + return Integer.compare(getMinTalkgroup(), other.getMinTalkgroup()); + } + } + else + { + return getProtocol().compareTo(other.getProtocol()); + } + } } diff --git a/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupRangeEditor.java b/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupRangeEditor.java deleted file mode 100644 index dca54176a..000000000 --- a/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupRangeEditor.java +++ /dev/null @@ -1,271 +0,0 @@ -/* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer - * - * 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 3 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, see - * ***************************************************************************** - */ -package io.github.dsheirer.alias.id.talkgroup; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import io.github.dsheirer.protocol.Protocol; -import net.miginfocom.swing.MigLayout; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.DefaultComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.JFormattedTextField; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.text.MaskFormatter; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.text.ParseException; - -public class TalkgroupRangeEditor extends DocumentListenerEditor -{ - private final static Logger mLog = LoggerFactory.getLogger(TalkgroupRangeEditor.class); - private static final long serialVersionUID = 1L; - - private static final String VALID_CHARACTERS_FOR_ASTERISK_MASKS = "0123456789 "; - private MaskFormatter mMaskFormatter = new MaskFormatter(); - private JComboBox mComboProtocol; - private JFormattedTextField mMinTalkgroupField; - private JFormattedTextField mMaxTalkgroupField; - - public TalkgroupRangeEditor(AliasID aliasID) - { - initGUI(); - - setItem(aliasID); - } - - private void initGUI() - { - setLayout(new MigLayout("fill,wrap 2", "[right][left]", "[][]")); - - add(new JLabel("Protocol:")); - - mComboProtocol = new JComboBox(); - - DefaultComboBoxModel model = new DefaultComboBoxModel<>(); - - for(Protocol protocol : Protocol.TALKGROUP_PROTOCOLS) - { - model.addElement(protocol); - } - - mComboProtocol.setModel(model); - mComboProtocol.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - Protocol protocol = (Protocol)mComboProtocol.getSelectedItem(); - updateEditor(protocol); - setModified(true); - } - }); - - add(mComboProtocol); - - add(new JLabel("Min:")); - - mMinTalkgroupField = new JFormattedTextField(mMaskFormatter); - mMinTalkgroupField.getDocument().addDocumentListener(this); - add(mMinTalkgroupField, "growx,push"); - - add(new JLabel("Max:")); - - mMaxTalkgroupField = new JFormattedTextField(mMaskFormatter); - mMaxTalkgroupField.getDocument().addDocumentListener(this); - add(mMaxTalkgroupField, "growx,push"); - - JLabel help = new JLabel("Help ..."); - help.setForeground(Color.BLUE.brighter()); - help.setCursor(new Cursor(Cursor.HAND_CURSOR)); - help.addMouseListener(new MouseAdapter() - { - @Override - public void mouseClicked(MouseEvent e) - { - TalkgroupFormat talkgroupFormat = TalkgroupFormat.get(getCurrentProtocol()); - JOptionPane.showMessageDialog(TalkgroupRangeEditor.this, - talkgroupFormat.getValidRangeHelpText(), "Help", JOptionPane.INFORMATION_MESSAGE); - } - }); - add(help, "align left"); - } - - private void updateEditor(Protocol protocol) - { - TalkgroupFormat mask = TalkgroupFormat.get(protocol); - int currentMinValue = 0; - int currentMaxValue = 0; - TalkgroupRange talkgroupRange = getTalkgroupRange(); - - if(talkgroupRange != null) - { - currentMinValue = talkgroupRange.getMinTalkgroup(); - currentMaxValue = talkgroupRange.getMaxTalkgroup(); - } - - try - { - mMinTalkgroupField.setValue(null); - mMaxTalkgroupField.setValue(null); - - mMinTalkgroupField.setToolTipText(mask.getValidRangeHelpText()); - mMaxTalkgroupField.setToolTipText(mask.getValidRangeHelpText()); - mMaskFormatter.setMask(mask.getMask()); - if(mask.getMask().contains("*")) - { - mMaskFormatter.setValidCharacters(VALID_CHARACTERS_FOR_ASTERISK_MASKS); - } - - mMinTalkgroupField.setValue(TalkgroupFormatter.format(protocol, currentMinValue)); - mMaxTalkgroupField.setValue(TalkgroupFormatter.format(protocol, currentMaxValue)); - } - catch(ParseException pe) - { - mLog.error("Error applying talkgroup editor mask to mask formatter [" + mask.getMask() + "]"); - } - } - - private Protocol getCurrentProtocol() - { - return mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex()); - } - - - - public TalkgroupRange getTalkgroupRange() - { - if(getItem() instanceof TalkgroupRange) - { - return (TalkgroupRange)getItem(); - } - - return null; - } - - @Override - public void setItem(AliasID aliasID) - { - super.setItem(aliasID); - - TalkgroupRange talkgroupRange = getTalkgroupRange(); - - updateEditor(talkgroupRange.getProtocol()); - - if(talkgroupRange != null) - { - mComboProtocol.getModel().setSelectedItem(talkgroupRange.getProtocol()); - String minFormatted = TalkgroupFormatter.format(talkgroupRange.getProtocol(), talkgroupRange.getMinTalkgroup()); - mMinTalkgroupField.setValue(minFormatted); - String maxFormatted = TalkgroupFormatter.format(talkgroupRange.getProtocol(), talkgroupRange.getMaxTalkgroup()); - mMaxTalkgroupField.setValue(maxFormatted); - } - else - { - mComboProtocol.getModel().setSelectedItem(Protocol.UNKNOWN); - mMinTalkgroupField.setValue(TalkgroupFormatter.format(getCurrentProtocol(), 0)); - mMaxTalkgroupField.setValue(TalkgroupFormatter.format(getCurrentProtocol(), 0)); - } - - setModified(false); - - repaint(); - } - - @Override - public void save() - { - TalkgroupRange talkgroupRange = getTalkgroupRange(); - - if(talkgroupRange != null) - { - Protocol protocol = mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex()); - - if(protocol == null) - { - protocol = Protocol.UNKNOWN; - } - - talkgroupRange.setProtocol(protocol); - - int minValue = -1; - int maxValue = -1; - - try - { - minValue = TalkgroupFormatter.parse(protocol, mMinTalkgroupField.getText()); - } - catch(ParseException pe) - { - //ignore ... value is still -1 and outside valid value range - } - - try - { - maxValue = TalkgroupFormatter.parse(protocol, mMaxTalkgroupField.getText()); - } - catch(ParseException pe) - { - //ignore ... value is still -1 and outside valid value range - } - - TalkgroupFormat mask = TalkgroupFormat.get(protocol); - - //Check for valid value within range ... notify user but allow value to persist - if(minValue < mask.getMinimumValidValue() || minValue > mask.getMaximumValidValue()) - { - String message = "Invalid minimum value [" + mMinTalkgroupField.getText() + "]. " + protocol.name() + - " valid range is [" + mask.getValidRangeDescription() + "]"; - - JOptionPane.showMessageDialog(TalkgroupRangeEditor.this, message, - "Invalid Talkgroup Value", JOptionPane.ERROR_MESSAGE); - } - else - { - talkgroupRange.setMinTalkgroup(minValue); - } - - //Check for valid value within range ... notify user but allow value to persist - if(maxValue < mask.getMinimumValidValue() || maxValue > mask.getMaximumValidValue()) - { - String message = "Invalid maximum value [" + mMaxTalkgroupField.getText() + "]. " + protocol.name() + - " valid range is [" + mask.getValidRangeDescription() + "]"; - - JOptionPane.showMessageDialog(TalkgroupRangeEditor.this, message, - "Invalid Talkgroup Value", JOptionPane.ERROR_MESSAGE); - } - else - { - talkgroupRange.setMaxTalkgroup(maxValue); - } - - talkgroupRange.setProtocol(protocol); - } - - setModified(false); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/tone/TonesID.java b/src/main/java/io/github/dsheirer/alias/id/tone/TonesID.java new file mode 100644 index 000000000..4d1937997 --- /dev/null +++ b/src/main/java/io/github/dsheirer/alias/id/tone/TonesID.java @@ -0,0 +1,150 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2020 Dennis Sheirer + * + * 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 3 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, see + * **************************************************************************** + */ + +package io.github.dsheirer.alias.id.tone; + +import io.github.dsheirer.alias.id.AliasID; +import io.github.dsheirer.alias.id.AliasIDType; +import io.github.dsheirer.identifier.tone.Tone; +import io.github.dsheirer.identifier.tone.ToneSequence; +import javafx.collections.ListChangeListener; + +import java.util.List; + +/** + * Tone sequence alias identifier. This is used for matching tone sequences produced by the AMBE audio CODEC. Some + * examples include DTMF dialed numbers, KNOX tones, 2-Tone, Tone-outs, etc. + */ +public class TonesID extends AliasID implements ListChangeListener +{ + private ToneSequence mToneSequence; + + public TonesID() + { + setToneSequence(new ToneSequence()); + //Empty serialization constructor + } + + /** + * Constructs an instance with the specified tone sequence + */ + public TonesID(ToneSequence toneSequence) + { + setToneSequence(toneSequence); + } + + @Override + public AliasIDType getType() + { + return AliasIDType.TONES; + } + + /** + * List of tones that define this identifier + */ + + public ToneSequence getToneSequence() + { + return mToneSequence; + } + + /** + * Sets the tone sequence(s) for this ID + */ + public void setToneSequence(ToneSequence toneSequence) + { + if(mToneSequence != null) + { + mToneSequence.tonesProperty().removeListener(this); + } + + mToneSequence = toneSequence; + mToneSequence.tonesProperty().addListener(this); + } + + /** + * Indicates if the other id is a tones identifier and has the exact same sequence of tones. However, it does not + * compare each of the tone duration value. + * @param id to check for match + * @return true if the identifiers match + */ + @Override + public boolean matches(AliasID id) + { + boolean match = true; + + if(mToneSequence.getTones().isEmpty() || !(id instanceof TonesID)) + { + match = false; + } + else + { + List otherTones = ((TonesID)id).getToneSequence().getTones(); + List thisTones = mToneSequence.getTones(); + + if(thisTones.size() == otherTones.size()) + { + for(int x = 0; x < thisTones.size(); x++) + { + if(thisTones.get(x).getAmbeTone() != otherTones.get(x).getAmbeTone()) + { + match = false; + continue; + } + } + } + else + { + match = false; + } + } + + return match; + } + + @Override + public boolean isValid() + { + return getToneSequence() != null && getToneSequence().hasTones(); + } + + @Override + public boolean isAudioIdentifier() + { + //This is not an audio identifier in the context that this method is used for. + return false; + } + + @Override + public String toString() + { + if(getToneSequence() != null) + { + return "Tones: " + getToneSequence().toString(); + } + + return "Tones: (empty)"; + } + + @Override + public void onChanged(Change c) + { + updateValueProperty(); + } +} diff --git a/src/main/java/io/github/dsheirer/audio/AbstractAudioModule.java b/src/main/java/io/github/dsheirer/audio/AbstractAudioModule.java index a253ff17e..5165a5917 100644 --- a/src/main/java/io/github/dsheirer/audio/AbstractAudioModule.java +++ b/src/main/java/io/github/dsheirer/audio/AbstractAudioModule.java @@ -125,8 +125,15 @@ protected void addAudio(float[] audioBuffer) audioSegment.linkTo(previous); } - audioSegment.addAudio(audioBuffer); - mAudioSampleCount += audioBuffer.length; + try + { + audioSegment.addAudio(audioBuffer); + mAudioSampleCount += audioBuffer.length; + } + catch(Exception e) + { + closeAudioSegment(); + } } /** diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/AudioBroadcaster.java b/src/main/java/io/github/dsheirer/audio/broadcast/AudioBroadcaster.java index 59770cb52..341871a81 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/AudioBroadcaster.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/AudioBroadcaster.java @@ -25,6 +25,8 @@ import io.github.dsheirer.identifier.IdentifierCollection; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.util.ThreadPool; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; import org.apache.commons.math3.util.FastMath; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,7 +54,7 @@ public abstract class AudioBroadcaster implements Listener private ISilenceGenerator mSilenceGenerator; private Listener mBroadcastEventListener; - private BroadcastState mBroadcastState = BroadcastState.READY; + private ObjectProperty mBroadcastState = new SimpleObjectProperty<>(BroadcastState.READY); private int mStreamedAudioCount = 0; private int mAgedOffAudioCount = 0; @@ -90,6 +92,18 @@ public AudioBroadcaster(BroadcastConfiguration broadcastConfiguration) mSilenceGenerator = BroadcastFactory.getSilenceGenerator(broadcastConfiguration.getBroadcastFormat()); } + /** + * Observable broadcast state property + */ + public ObjectProperty broadcastStateProperty() + { + return mBroadcastState; + } + + public void dispose() + { + } + /** * Broadcast binary audio data frames or sequences. */ @@ -251,18 +265,18 @@ public void broadcast(BroadcastEvent event) */ protected void setBroadcastState(BroadcastState state) { - if(mBroadcastState != state) + if(mBroadcastState.get() != state) { if(state == BroadcastState.CONNECTED || state == BroadcastState.DISCONNECTED) { mLog.info("[" + getStreamName() + "] status: " + state); } - mBroadcastState = state; + mBroadcastState.setValue(state); broadcast(new BroadcastEvent(this, BroadcastEvent.Event.BROADCASTER_STATE_CHANGE)); - if(mBroadcastState.isErrorState()) + if(mBroadcastState.get().isErrorState()) { stop(); } @@ -291,7 +305,7 @@ protected void setBroadcastState(BroadcastState state) */ public BroadcastState getBroadcastState() { - return mBroadcastState; + return mBroadcastState.get(); } /** diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastConfiguration.java b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastConfiguration.java index 220416e57..33e463a6a 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastConfiguration.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastConfiguration.java @@ -1,21 +1,24 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2016 Dennis Sheirer +/* * - * 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 3 of the License, or - * (at your option) any later version. + * * ****************************************************************************** + * * Copyright (C) 2014-2020 Dennis Sheirer + * * + * * 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 3 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, see + * * ***************************************************************************** * - * 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, see - * - ******************************************************************************/ + */ package io.github.dsheirer.audio.broadcast; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -26,6 +29,17 @@ import io.github.dsheirer.audio.broadcast.icecast.IcecastConfiguration; import io.github.dsheirer.audio.broadcast.shoutcast.v1.ShoutcastV1Configuration; import io.github.dsheirer.audio.broadcast.shoutcast.v2.ShoutcastV2Configuration; +import javafx.beans.Observable; +import javafx.beans.binding.Bindings; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.LongProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleLongProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.util.Callback; import java.net.InetSocketAddress; import java.net.SocketAddress; @@ -39,18 +53,32 @@ @JacksonXmlRootElement(localName = "stream") public abstract class BroadcastConfiguration { + // Static unique channel identifier tracking + private static int UNIQUE_ID = 0; + private BroadcastFormat mBroadcastFormat = BroadcastFormat.MP3; - private String mName; - private String mHost; - private int mPort; - private String mPassword; - private long mDelay; - private long mMaximumRecordingAge = 10 * 60 * 1000; //10 minutes default - private boolean mEnabled = true; + private StringProperty mName = new SimpleStringProperty(); + private StringProperty mHost = new SimpleStringProperty(); + private IntegerProperty mPort = new SimpleIntegerProperty(80); + private StringProperty mPassword = new SimpleStringProperty(); + private LongProperty mDelay = new SimpleLongProperty(); + private LongProperty mMaximumRecordingAge = new SimpleLongProperty(10 * 60 * 1000); //10 minutes default + private BooleanProperty mEnabled = new SimpleBooleanProperty(false); + private BooleanProperty mValid; + private int mId = ++UNIQUE_ID; public BroadcastConfiguration() { - //No-arg constructor required for JAXB + } + + /** + * Unique identifier for this configuration. + * Note: unique ids are generated at runtime and have no persistent context across sessions. + */ + @JsonIgnore + public int getId() + { + return mId; } /** @@ -66,6 +94,76 @@ public BroadcastConfiguration(BroadcastFormat format) mBroadcastFormat = format; } + /** + * Stream name + */ + public StringProperty nameProperty() + { + return mName; + } + + /** + * Server host name + */ + public StringProperty hostProperty() + { + return mHost; + } + + /** + * Feed port + */ + public IntegerProperty portProperty() + { + return mPort; + } + + /** + * Feed password + */ + public StringProperty passwordProperty() + { + return mPassword; + } + + /** + * Delay introduced for each recording before streaming + */ + public LongProperty delayProperty() + { + return mDelay; + } + + /** + * Maximum allowable age of a recording to stream. Recordings that exceed this threshold will be aged off. + */ + public LongProperty maximumRecordingAgeProperty() + { + return mMaximumRecordingAge; + } + + /** + * Stream enabled property + */ + public BooleanProperty enabledProperty() + { + return mEnabled; + } + + /** + * Configuration valid property + */ + public BooleanProperty validProperty() + { + if(mValid == null) + { + mValid = new SimpleBooleanProperty(); + mValid.bind(Bindings.and(Bindings.isNotNull(mHost), Bindings.greaterThan(mPort, 0))); + } + + return mValid; + } + /** * Broadcast server type */ @@ -83,7 +181,7 @@ private void setBroadcastServerType(BroadcastServerType type) @JacksonXmlProperty(isAttribute = true, localName = "name") public String getName() { - return mName; + return mName.get(); } /** @@ -93,7 +191,7 @@ public String getName() */ public void setName(String name) { - mName = name; + mName.set(name); } /** @@ -110,7 +208,7 @@ public boolean hasName() @JacksonXmlProperty(isAttribute = true, localName = "host") public String getHost() { - return mHost; + return mHost.get(); } /** @@ -120,7 +218,7 @@ public String getHost() */ public void setHost(String host) { - mHost = host; + mHost.set(host); } /** @@ -137,7 +235,7 @@ public boolean hasHost() @JacksonXmlProperty(isAttribute = true, localName = "port") public int getPort() { - return mPort; + return mPort.get(); } /** @@ -147,7 +245,7 @@ public int getPort() */ public void setPort(int port) { - mPort = port; + mPort.set(port); } /** @@ -155,7 +253,7 @@ public void setPort(int port) */ public boolean hasPort() { - return mPort > 0; + return mPort.get() > 0; } @JsonIgnore @@ -170,7 +268,7 @@ public SocketAddress getAddress() @JacksonXmlProperty(isAttribute = true, localName = "password") public String getPassword() { - return mPassword; + return mPassword.get(); } /** @@ -180,7 +278,7 @@ public String getPassword() */ public void setPassword(String password) { - mPassword = password; + mPassword.set(password); } /** @@ -211,7 +309,7 @@ public void setBroadcastFormat(BroadcastFormat format) @JacksonXmlProperty(isAttribute = true, localName = "delay") public long getDelay() { - return mDelay; + return mDelay.get(); } /** @@ -220,7 +318,7 @@ public long getDelay() */ public void setDelay(long delay) { - mDelay = delay; + mDelay.set(delay); } /** @@ -231,7 +329,7 @@ public void setDelay(long delay) @JacksonXmlProperty(isAttribute = true, localName = "maximum_recording_age") public long getMaximumRecordingAge() { - return mMaximumRecordingAge; + return mMaximumRecordingAge.get(); } /** @@ -241,7 +339,7 @@ public long getMaximumRecordingAge() */ public void setMaximumRecordingAge(long age) { - mMaximumRecordingAge = age; + mMaximumRecordingAge.set(age); } /** @@ -250,12 +348,12 @@ public void setMaximumRecordingAge(long age) @JacksonXmlProperty(isAttribute = true, localName = "enabled") public boolean isEnabled() { - return mEnabled; + return mEnabled.get(); } public void setEnabled(boolean enabled) { - mEnabled = enabled; + mEnabled.set(enabled); } @Override @@ -302,6 +400,15 @@ public int hashCode() @JsonIgnore public boolean isValid() { - return mHost != null && mPort > 0; + return validProperty().get(); + } + + /** + * Creates an observable property extractor for use with observable lists to detect changes internal to this object. + */ + public static Callback extractor() + { + return (BroadcastConfiguration b) -> new Observable[] {b.nameProperty(), b.hostProperty(), b.portProperty(), + b.passwordProperty(), b.maximumRecordingAgeProperty(), b.delayProperty(), b.enabledProperty()}; } } diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastConfigurationEditor.java b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastConfigurationEditor.java deleted file mode 100644 index bb5b9a182..000000000 --- a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastConfigurationEditor.java +++ /dev/null @@ -1,126 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * 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 3 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, see - * - ******************************************************************************/ -package io.github.dsheirer.audio.broadcast; - -import io.github.dsheirer.alias.AliasModel; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import io.github.dsheirer.icon.IconManager; - -import javax.swing.*; - -public abstract class BroadcastConfigurationEditor extends DocumentListenerEditor -{ - protected IconManager mIconManager; - protected BroadcastModel mBroadcastModel; - protected AliasModel mAliasModel; - - public BroadcastConfigurationEditor(BroadcastModel broadcastModel, AliasModel aliasModel, IconManager iconManager) - { - mBroadcastModel = broadcastModel; - mAliasModel = aliasModel; - mIconManager = iconManager; - } - - /** - * Updates the configuration with the new name and prompts the user to update any aliases that have a broadcast - * channel alias id with the old name to update to the new name. - * - * @param broadcastConfiguration to assign a new channel name - * @param newName to assign to the broadcast configuration. Note: assumes that validateConfiguration() has been - * invoked to verify that newName is non-null and non-empty - */ - protected void updateConfigurationName(BroadcastConfiguration broadcastConfiguration, String newName) - { - String previousName = broadcastConfiguration.getName(); - - if(previousName == null || previousName.isEmpty()) - { - broadcastConfiguration.setName(newName); - return; - } - - if(!previousName.equals(newName)) - { - mAliasModel.renameBroadcastChannel(previousName, newName); - broadcastConfiguration.setName(newName); - } - } - - /** - * Validates a text field control for a non-null, non-empty value - * @param field to validate - * @param title to use for error dialog - * @param message to use for error dialog - * @return true if field contains a non-null, non-empty value - */ - protected boolean validateTextField(JTextField field, String title, String message) - { - String text = field.getText(); - - if(text == null || text.isEmpty()) - { - JOptionPane.showMessageDialog(BroadcastConfigurationEditor.this, message, title, - JOptionPane.ERROR_MESSAGE); - - field.requestFocus(); - return false; - } - - return true; - } - - /** - * Validates the text field control that contains an integer value for non-null, non-empty and within the - * specified min/max valid range. - * @param field to validate - * @param title to use for error dialog - * @param message to use for error dialog - * @param minValid value - * @param maxValid value - * @return true if field contains a non-null, non-empty value within the valid min/max range - */ - protected boolean validateIntegerTextField(JTextField field, String title, String message, int minValid, int maxValid) - { - if(validateTextField(field, title, message)) - { - String text = field.getText(); - - try - { - int value = Integer.parseInt(text); - - if(minValid <= value && value <= maxValid) - { - return true; - } - } - catch(Exception e) - { - //Do nothing, we couldn't parse the number value - } - - JOptionPane.showMessageDialog(BroadcastConfigurationEditor.this, message, title, - JOptionPane.ERROR_MESSAGE); - - field.requestFocus(); - } - - return false; - } -} diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastFactory.java b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastFactory.java index d41484820..85a55e90c 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastFactory.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastFactory.java @@ -23,27 +23,18 @@ import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyConfiguration; -import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyConfigurationEditor; import io.github.dsheirer.audio.broadcast.icecast.IcecastHTTPAudioBroadcaster; import io.github.dsheirer.audio.broadcast.icecast.IcecastHTTPConfiguration; -import io.github.dsheirer.audio.broadcast.icecast.IcecastHTTPConfigurationEditor; import io.github.dsheirer.audio.broadcast.icecast.IcecastTCPAudioBroadcaster; import io.github.dsheirer.audio.broadcast.icecast.IcecastTCPConfiguration; -import io.github.dsheirer.audio.broadcast.icecast.IcecastTCPConfigurationEditor; import io.github.dsheirer.audio.broadcast.shoutcast.v1.ShoutcastV1AudioBroadcaster; import io.github.dsheirer.audio.broadcast.shoutcast.v1.ShoutcastV1Configuration; -import io.github.dsheirer.audio.broadcast.shoutcast.v1.ShoutcastV1ConfigurationEditor; import io.github.dsheirer.audio.broadcast.shoutcast.v2.ShoutcastV2AudioBroadcaster; import io.github.dsheirer.audio.broadcast.shoutcast.v2.ShoutcastV2Configuration; -import io.github.dsheirer.audio.broadcast.shoutcast.v2.ShoutcastV2ConfigurationEditor; import io.github.dsheirer.audio.convert.IAudioConverter; import io.github.dsheirer.audio.convert.ISilenceGenerator; import io.github.dsheirer.audio.convert.MP3AudioConverter; import io.github.dsheirer.audio.convert.MP3SilenceGenerator; -import io.github.dsheirer.gui.editor.Editor; -import io.github.dsheirer.gui.editor.EmptyEditor; -import io.github.dsheirer.icon.IconManager; -import io.github.dsheirer.preference.UserPreferences; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -140,48 +131,6 @@ public static BroadcastConfiguration getConfiguration(BroadcastServerType server return null; } - /** - * Constructs an editor for the specified broadcastAudio configuration - * - * @param configuration to modify or view - * @param broadcastModel model for broadcastAudio configurations - * @return an editor for the specified broadcastAudio configuration - */ - public static Editor getEditor(UserPreferences userPreferences, - BroadcastConfiguration configuration, - BroadcastModel broadcastModel, - AliasModel aliasModel, - IconManager iconManager) - { - Editor editor; - - switch(configuration.getBroadcastServerType()) - { - case BROADCASTIFY: - editor = new BroadcastifyConfigurationEditor(userPreferences, broadcastModel, aliasModel, iconManager); - break; - case ICECAST_TCP: - editor = new IcecastTCPConfigurationEditor(broadcastModel, aliasModel, iconManager); - break; - case ICECAST_HTTP: - editor = new IcecastHTTPConfigurationEditor(broadcastModel, aliasModel, iconManager); - break; - case SHOUTCAST_V1: - editor = new ShoutcastV1ConfigurationEditor(broadcastModel, aliasModel, iconManager); - break; - case SHOUTCAST_V2: - editor = new ShoutcastV2ConfigurationEditor(broadcastModel, aliasModel, iconManager); - break; - default: - editor = new EmptyEditor(); - break; - } - - editor.setItem(configuration); - - return editor; - } - public static ISilenceGenerator getSilenceGenerator(BroadcastFormat format) { switch(format) diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastModel.java b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastModel.java index 0f9d11739..50be596b6 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastModel.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastModel.java @@ -1,7 +1,7 @@ /* * * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer + * * Copyright (C) 2014-2020 Dennis Sheirer * * * * 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 @@ -29,6 +29,9 @@ import io.github.dsheirer.sample.Broadcaster; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.util.ThreadPool; +import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -68,11 +71,12 @@ public class BroadcastModel extends AbstractTableModel implements Listener