Compare commits

...

386 Commits

Author SHA1 Message Date
Denny
64ccacf04a Releasing Version 0.3.0 (final). 2017-09-22 05:56:27 -04:00
Denny Sheirer
f00b9555fe Merge pull request #271 from DSheirer/269-viterbi-3-4-rate
269 Viterbi decoder for DMR 3/4-rate packet messages
2017-09-15 11:50:19 -04:00
Denny
85442a9648 Resolves #269 - creates a generic Viterbi decoder and a Digital Mobile Radio (DMR) 3/4 Rate Viterbi decoder implementation for decoding Trellis Coded Modulation (TCM) encoded DMR Data packets. 2017-09-15 11:40:23 -04:00
Denny Sheirer
c67a0b329d Merge pull request #267 from DSheirer/266-licence-directory
266 Update build script to use gitlab license file
2017-09-10 04:55:57 -04:00
Denny
8035d64fcd Resolves #266 - updates build script to use new gitlab license file and
removes previous license file.
2017-09-10 04:54:26 -04:00
Denny Sheirer
df2a061a08 Create LICENSE 2017-08-28 19:51:13 -04:00
Denny
e430319937 Updates build to 0.3.0 Beta 14 2017-08-19 07:15:57 -04:00
Denny Sheirer
724f769771 Merge pull request #262 from DSheirer/260-icecast-stream-doesn't-reconnect
260 - Icecast and Shoutcast streaming doesn't auto-reconnect
2017-08-19 07:14:55 -04:00
Denny
c196cfebed Resolves issue #260 - applies changes to Shoutcast V1 and v2 that were
applied to the Icecast streamer for issue releated to reconnecting.
2017-08-19 07:13:08 -04:00
Denny
e80b5866d4 Resolves issue #260 where audio streaming was not re-connecting after a
disconnect.
2017-08-18 20:07:53 -04:00
Denny
c8994d5593 Updates build to version 0.3.0 Beta 13 2017-07-22 05:45:36 -04:00
Denny Sheirer
99ef57c90f Merge pull request #255 from DSheirer/249-overflow-teardown-lockup
249 Channel TEARDOWN/OVERFLOW state lockup
2017-07-22 05:44:12 -04:00
Denny
be62ae0459 Resolves #249 - removes the state monitor runnable from the channel
state and replaces it with a processing chain heartbeat that is generated
by the sample source.

Previously, the state monitor was running on a separate thread and this
was causing multi-threading issues.  With this update, all channel state
operations (and all module operations) occur on the source's buffer
processing thread.
2017-07-22 05:42:13 -04:00
Denny Sheirer
b9be6f43e0 Merge pull request #253 from DSheirer/252-usb-transfer-processor-bad-logging
252 USB transfer processor erroneous logging and performance enhancements
2017-07-09 12:29:21 +00:00
Denny
d5ad3f807d Resolves issue where the tuner's buffer processor is logging that an overflow state has been cleared, caused by a clearing of the buffer when the tuner is being shutdown because there are no more channel consumers registered on the tuner.
Tweaked buffer size limits and buffer handling to attempt to avoid overflow situations in the first place.

Resolved an issue where the tuner processor was not deregistering correctly from the LIBUSB tuner processor during tuner shutdown.
2017-07-09 08:21:07 -04:00
Denny Sheirer
d85ebd018d Merge pull request #251 from DSheirer/250-state-monitor-throwable
StateMonitor - try/catch for Throwables
2017-07-01 11:39:21 +00:00
Denny
ab4e31ae00 Ensures that any leftover cached LDU1 message is reset when the call ends. 2017-07-01 07:36:55 -04:00
Denny
5efd89632e Ensures that any leftover cached LDU1 message is reset when the call ends. 2017-05-28 07:30:05 -04:00
Denny
1dc3863489 Updates build to 0.3.0 Beta 12 2017-05-28 07:22:43 -04:00
Denny Sheirer
441286ba13 Merge pull request #247 from DSheirer/244-ldu1-encryption-flag
244 Prevent P25 Encrypted Audio Decoding/Playback
2017-05-28 11:20:20 +00:00
Denny
e4323df347 Resolves issue #244 - updates the P25 IMBE audio module to correctly
detect encrypted audio state so that encrypted audio is not (incorrectly)
decoded and played back to the user.

Processes HDU and LDU2 messages to identify encrypted call state.  Once
established, all subsequent LDU1/2 messages are decoded only if the call
is unencrypted.  If an LDU1 message is received out-of-sequence, meaning
that the initial HDU message was missed, then the LDU1 is cached until
we receive an LDU2 message and correctly determine the call encryption
state.
2017-05-28 07:17:09 -04:00
Denny
a872e0d664 Updates version to Beta 0.3.0 Beta 11 2017-05-20 07:24:27 -04:00
Denny Sheirer
fbc92cfe31 Merge pull request #246 from DSheirer/245-sound-card-source-issue
Using sound card or mixer as a source throws an error
2017-05-20 10:55:23 +00:00
Denny
1a432197ac Resolves #245 issue with NPE for frequency change event listener when using the sound card/mixer as a source. 2017-05-20 06:51:53 -04:00
Denny
fbc6e508bd Increments build to 0.3.0-beta10 2017-04-14 07:46:18 -04:00
Denny Sheirer
f9b6e67b00 Merge pull request #240 from DSheirer/234-long-alias-names-cause-audio-label-to-wrap
Long alias names cause audio label to wrap
2017-03-31 01:13:17 +00:00
Denny
a628800a71 Resolves #234 - updates the miglayout layout manager to allow jlabel text strings to be automatically truncated (with ellipses) when the alias name is too long. 2017-03-30 21:12:06 -04:00
Denny
25f71a76c4 Resolves #234 - hard codes alias name truncation when name exceeds 33 characters in length. 2017-03-30 20:03:48 -04:00
Denny
5fe8ccd478 Resolves #234 - change the order of assigning an icon and alias text to the audio panel in an attempt to prevent the audio channel panel from wrapping to the next line when the text is too long to display. 2017-03-30 19:52:35 -04:00
Denny Sheirer
069f951a86 Merge pull request #239 from DSheirer/228-traffic-channel-allocation-for-do-not-monitor
Traffic channel manager now allocates traffic channels for do not monitor aliases
2017-03-30 23:27:38 +00:00
Denny
dab6df7836 Resolves #228 - traffic channel manager now allocates traffic channels even when the alias is marked as do not monitor 2017-03-30 19:26:35 -04:00
Denny Sheirer
89da46b63c Merge pull request #238 from DSheirer/235-lojack-decoder-error-on-construction
Resolves error on constructing a LoJack decoder state.
2017-03-30 23:19:29 +00:00
Denny
5f2ce2a9f2 Resolves #235 - fixes error on constructing a LoJack decoder state. 2017-03-30 19:18:39 -04:00
Denny Sheirer
7a0b377971 Merge pull request #232 from DSheirer/229-p25-phase2-event-duplicates
Resolves #229 - duplicate P25 TDMA call events.
2017-03-23 21:52:47 +00:00
Denny
5dbe0ecce3 Resolves #229 - duplicate P25 TDMA call events. 2017-03-23 21:48:52 +00:00
Denny Sheirer
6647c581a6 Merge pull request #231 from DSheirer/230-p25-tdma-frequency-bands
P25 Phase II TDMA frequency bands are now displayed in details summary
2017-03-23 21:01:58 +00:00
Denny
a2ab5bfa95 Resolves #230 - P25 Phase II TDMA frequency bands are now displayed in the channel details summary. 2017-03-23 20:55:42 +00:00
Denny
ed486440b8 Merge remote-tracking branch 'origin/master' 2017-03-12 11:33:17 -04:00
Denny
13464e01fc Updates build to 0.3.0 Beta 9 2017-03-12 11:33:04 -04:00
Denny Sheirer
ea46a8b0e8 Merge pull request #225 from DSheirer/222-spectral-display-of-frequency-correction-offset
Frequency change event handling in the processing chain
2017-03-11 17:03:34 +00:00
Denny
d510c34356 Adds frequency change event listener handler to LSM decoder. 2017-03-11 12:01:45 -05:00
Denny
ae8a44caed Removes cic cleanup filter from processing chain. 2017-03-11 11:53:12 -05:00
Denny
1aa85a86ca Resolves issue with channel frequency correction events not being broadcast within the processing chain and rendering in the spectral display. 2017-03-11 10:16:08 -05:00
Denny Sheirer
17539d4d55 Merge pull request #224 from DSheirer/223-now-playing-frequency-format
Now Playing frequency display formatting
2017-03-11 11:14:58 +00:00
Denny
1db1d28390 Resolves issue with frequency display formating in the Now Playing window. 2017-03-11 06:13:59 -05:00
Denny Sheirer
37a941e904 Merge pull request #219 from DSheirer/218-audio-priority-
Resolves issue with audio playback allowing higher priority audio to preempt lower priority audio
2017-03-05 12:34:09 +00:00
Denny
57f6655824 Resolves issue where higher priority audio was not preempting lower priority audio. 2017-03-05 07:32:42 -05:00
Denny Sheirer
4cd8f8209f Merge pull request #216 from DSheirer/215-audio-recording-handles-part-2
215 audio recording handles part 2
2017-03-03 15:49:24 +00:00
Denny
61bfef5798 Incrementing build to version 0.3.0 beta8 2017-03-03 10:44:50 -05:00
Denny
ef172a04c6 Cleanup - removed synchronized blocks since the buffer processor thread is the only thread managing recorders. 2017-03-03 10:04:21 -05:00
Denny
a877afcceb Updates buffer processing in recorder manager so that we're not blocking any of the decoding channel producer threads that are trying to push audio packets to the recorder manager. Adds an overflowable packet queue and services the queue once a second followed by an idle recorder check (and disposal). 2017-03-03 10:01:32 -05:00
Denny
980ad72d4d Incrementing build to 0.3.0 beta7 2017-03-03 06:01:09 -05:00
Denny Sheirer
9b746cc032 Merge pull request #215 from DSheirer/198-audio-recorder-handles-left-open
Resolves issue audio recorders left open
2017-03-03 10:58:08 +00:00
Denny
f51ae0b5da Resolves issue with periodic instances of audio recorders not being shutdown correctly. Adds recorder monitor as a fail-safe to automatically shutdown recorders that don't receive an end-call indication. 2017-03-03 05:56:32 -05:00
Denny Sheirer
561078e9bf Merge pull request #214 from DSheirer/210-playlist-saving-all-times
Resolves issue with the playlist saving all of the time
2017-03-03 00:34:25 +00:00
Denny
e052b01f9b Resolves issue with the playlist saving all of the time due to audio streaming broadcast events. 2017-03-02 19:33:10 -05:00
Denny
09cf4fcfd7 Resolves issue with empty string list models in channel editor 2017-02-26 10:29:14 -05:00
Denny
5e731d0e9f Resolves issue with alias list and group combo box value editing. 2017-02-26 06:51:58 -05:00
Denny Sheirer
670a02b0ba Merge pull request #209 from DSheirer/153-cursor-frequency-artifacts
Resolves issue with spectral display rendering artifacts
2017-02-26 10:48:59 +00:00
Denny
9ef156b061 Resolves issue with spectral display rendering artifacts from cursor frequency readout when accessing menu items directly above the spectral display. 2017-02-26 05:47:26 -05:00
Denny
d9cf1b19f3 Removes assert() usage throughout project and replaces with Validate from apache commons. 2017-02-25 16:03:35 -05:00
Denny
a4a339b505 Resolves issue with FCD2 tuner frequency. 2017-02-25 14:58:51 -05:00
Denny
119370c46e Resolves issue with FCD2 tuner frequency. 2017-02-25 14:57:25 -05:00
Denny
4eb66e6045 Updated readme file with screen shot and link to new Version 0.3.0 user's manual. 2017-02-25 06:18:59 -05:00
Denny Sheirer
8827ecd0e9 Merge pull request #207 from DSheirer/206-alias-list-npe-null-list-name
206 alias list npe null list name
2017-02-24 10:25:29 +00:00
Denny
8660002f93 Incrementing build to beta 6 2017-02-24 05:24:38 -05:00
Denny
2058fcbc1d Resolves issue #206 - alias list throws NPE on aliases that don't have a list name specified. 2017-02-24 05:23:48 -05:00
Denny
9515dfa380 Incrementing build for 0.3.0 beta #5 2017-02-20 20:25:43 -05:00
Denny
9e9a604c6a Merge remote-tracking branch 'origin/master' 2017-02-20 20:20:11 -05:00
Denny
234e313b7a Resolves issue with Null Pointer exception in alias list 2017-02-20 20:19:47 -05:00
Denny Sheirer
e1e7169029 Merge pull request #203 from DSheirer/199-utf8-encoding-of-metadata
Resolves issue with UTF-8 encoding of Shoutcast 2 metadata updates.
2017-02-17 23:09:10 +00:00
Denny
f3605db0ed Resolves issue where metadata is UTF-8 URL encoded versus UTF-8 String encoding. 2017-02-17 18:08:01 -05:00
Denny Sheirer
c0a6d578c7 Merge pull request #202 from DSheirer/201-audio-stream-failed-reconnect
Resolves issue with audio broadcasters attempting to reconnect if a c…
2017-02-17 22:54:58 +00:00
Denny
79b4b30ec7 Resolves issue with audio broadcasters attempting to reconnect if a connection attempt is not successful. 2017-02-17 17:53:42 -05:00
Denny
17c7466c6b Resolves logic issue in Icecast error handling during resets. 2017-02-13 20:51:10 -05:00
Denny Sheirer
8162d6ffd6 Merge pull request #196 from DSheirer/164-patched-group-streaming-duplicates
Resolves #173 - duplicate streaming calls for patch groups.
2017-02-12 15:59:46 +00:00
Denny
cd60251337 Resolves #173 - duplicate streaming calls for patch groups. 2017-02-12 10:59:06 -05:00
Denny Sheirer
5647879b37 Merge pull request #195 from DSheirer/173-p25-tdma-channel-frequencies-incorrect
Resolves #173 - logging/management of  P25 TDMA channels.
2017-02-12 15:21:50 +00:00
Denny
6916d95cdf Resolves #173 - logging/management of P25 TDMA channels. There is no Phase II decoder support yet. 2017-02-12 10:20:45 -05:00
Denny Sheirer
a0c32f6fe0 Merge pull request #194 from DSheirer/193-icecast-tcp-write-timeout-exception
Resolves #193 - adds handling for MINA WriteTimeoutException
2017-02-12 13:05:11 +00:00
Denny
c3d60dc174 Resolves #193 - adds handling for MINA WriteTimeoutException error. Resets connection when this occurs. 2017-02-12 08:04:27 -05:00
Denny Sheirer
e55a2226a2 Merge pull request #192 from DSheirer/189-shoutcast-ultravox-UTF8-encoding
Resolves #189 - add UTF-8 encoding to shoutcast v2
2017-02-12 12:45:10 +00:00
Denny
9f01678b00 Resolves #189 - add UTF-8 encoding to shoutcast v2 (ultravox) XML metadata update values. 2017-02-12 07:43:51 -05:00
Denny Sheirer
46425e9cae Merge pull request #191 from DSheirer/188-p25-activity-summary-cleanup
188 p25 activity summary cleanup
2017-02-12 12:31:45 +00:00
Denny
3726401563 Resolves #188 - cleanup of the P25 and MPT activity summaries. Resolves an issue in the SNDCP downlink/uplink channel frequencies. 2017-02-12 07:30:32 -05:00
Denny
a3e50acfe3 Incrementing build to 0.3.0-beta4 2017-02-11 09:02:35 -05:00
Denny
cf0018e6ba Incrementing build to 0.3.0-beta4 2017-02-11 07:22:54 -05:00
Denny Sheirer
e8055dcab0 Merge pull request #187 from DSheirer/186-p25-c4fm-decoder-no-worky
Resolves #186 - p25 C4FM decoder wasn't working (at all).
2017-02-11 12:17:58 +00:00
Denny
fce3d5c4f3 Resolves #186 - p25 C4FM decoder wasn't working (at all). 2017-02-11 07:17:23 -05:00
Denny Sheirer
9cc0d367a8 Merge pull request #185 from DSheirer/183-alias-list-throws-npe-on-empty-name
Resolves #183 - alias list throws NPE when created with a null or emp…
2017-02-11 00:35:16 +00:00
Denny
2ca9e5d5ce Resolves #183 - alias list throws NPE when created with a null or empty name and receives alias updates. 2017-02-10 19:34:50 -05:00
Denny Sheirer
861203c3a0 Merge pull request #184 from DSheirer/181-patch-group-audio-priority
Resolves #181 - patch group audio priority will assume the highest (l…
2017-02-11 00:31:42 +00:00
Denny
7c930deefe Resolves #181 - patch group audio priority will assume the highest (listenable) audio priority and override a 'Do Not Monitor' setting if any of the other aliases have an audio priority defined. 2017-02-10 19:31:09 -05:00
Denny
0e0af932eb Incrementing build to 0.3.0-beta3 2017-02-10 17:09:54 -05:00
Denny Sheirer
6ba5e91fcb Merge pull request #182 from DSheirer/176-streaming-handle-additional-io-exceptions
Resolves #176 - resolves 2 streaming related issues
2017-02-10 22:06:32 +00:00
Denny
34dd2161ef Resolves #176 - 2 issues:
A) streaming connection errors - detects errors and if currently connected, resets the connection.

B)duplicate calls being streamed - issue with comparison of broadcast channels allowed the same broadcast channel to be queued two or more times for the list of channels to stream a recording.
2017-02-10 17:05:28 -05:00
Denny Sheirer
2543072e08 Merge pull request #180 from DSheirer/166-traffic_channel_manager_cleanup
Resolves #166 - removes traffic channel timeout from traffic channel manager
2017-02-10 15:31:24 +00:00
Denny
281be147f8 Resolves #166 - removes traffic channel timeout from traffic channel manager 2017-02-10 10:30:35 -05:00
Denny Sheirer
12ecca4555 Merge pull request #179 from DSheirer/169-aarch64-usb-support-for-ARM64
Resolves #165 - adds ARM64 (aarch64) linux libusb support
2017-02-10 15:26:16 +00:00
Denny
4c27d04120 Resolves #165 - adds ARM64 (aarch64) linux libusb support 2017-02-10 10:08:52 -05:00
Denny Sheirer
ece202315f Merge pull request #178 from DSheirer/165-overflowable-buffer-issue-causes-channels-decoding-stop
165 overflowable buffer issue causes channels decoding stop
2017-02-10 14:50:30 +00:00
Denny Sheirer
6afe5dcec9 Merge pull request #177 from DSheirer/master
165 - P25 Channel stops decoding
2017-02-10 14:47:33 +00:00
Denny
8db47c25bc #165 - cleanup of logging and comments in TunerChannelSource 2017-02-10 09:45:32 -05:00
Denny
6a340050c6 #165 - cleanup of logging and comments in TunerChannelSource 2017-02-10 06:41:45 -05:00
Denny
183eb2f7c7 Resolves #165 - P25 decoding channel stops arbitrarily and never recovers.
Issue identified with Patch Group management and circular dependencies that can develop because of stale patch groups.  Refactored all patch group management into a new PatchGroupManager.  Patch groups are now automatically deleted if we don't receive any update messages within 5 seconds.  The PatchGroupAlias was also updated to not roll-up attributes from subordinate patched group aliases when those aliases are also patch group aliases -- this eliminates the circular dependency chaining.

Added IOverflowListener interface and updated Sources to support overflow listeners.  Updated ChannelState to register as an overflow listener so that it can visually depict to the user when the channel enters/exits source overflow state.  Added new color pattern for OVERFLOW with yellow text on red.
2017-02-10 06:36:57 -05:00
Denny Sheirer
6bc92e8b94 Merge pull request #174 from RachelOlivero/gui_accessibility_enhancements
Add a Text Label to the Mute Image Button
2017-02-04 20:21:44 +00:00
Denny
767b302ed7 Adds additional unresolved address exception handler to metadata updater 2017-02-04 07:32:22 -05:00
Rachel Olivero
952d930b5b Adds a screen reader accessible label to the mute button. Label toggles between mute and unmute to match the action that will be taken when the button is next pressed. 2017-02-03 10:19:52 -05:00
Denny
26087c465f Adds additional unresolved address exception handler to metadata updater 2017-01-29 12:37:09 -05:00
Denny
74c15d530b Resolves #164 - USB time out while processing buffer transfers. Decreases periodicity of processing the LibUsb timeout handler from 9 milliseconds to 5 milliseconds to reduce chance that buffers are not being serviced in time. 2017-01-29 11:02:12 -05:00
Denny
8621d1c8e2 Bumping build to 0.3.0-beta2 2017-01-29 09:11:11 -05:00
Denny Sheirer
1cd45a1b79 Merge pull request #162 from DSheirer/161-e4000-tuner-error
Resolves #161 - E4000 tuner error.
2017-01-29 09:08:41 -05:00
Denny
8215b78a43 Resolves #161 - E4000 tuner error. Tweaks settings in the USB transfer processor to improve latency in IQ buffer handling. 2017-01-29 09:07:48 -05:00
Denny
f53519ad59 Updated CHANGELOG 2017-01-29 06:56:21 -05:00
Denny
e6ab615931 Updates the overflowable queue to reduce thread contention on the queue size monitor 2017-01-29 06:26:12 -05:00
Denny Sheirer
0c84984fcc Merge pull request #160 from DSheirer/159-icecast-unresolved-host
159 icecast unresolved host
2017-01-29 06:17:44 -05:00
Denny
b3e493989e Updates javadoc 2017-01-29 06:16:46 -05:00
Denny
50e838749d Resovles #159 - adds UnresolvedHostException error in Icecast metadata updater. 2017-01-29 04:31:31 -05:00
Denny Sheirer
c097e536e0 Merge pull request #158 from DSheirer/154-channel-metadata-context-menu
Resolves #154 - add right-click contextual channel menus for Now Play…
2017-01-28 20:42:58 -05:00
Denny
a228f29a40 Resolves #154 - add right-click contextual channel menus for Now Playing table.
Also disables right-click channel activation from the spectral display as a clean-up action.
2017-01-28 20:41:46 -05:00
Denny
74aab2cc72 Expanded error message for when drivers are not installed on user machine for RTL2832. 2017-01-28 20:04:20 -05:00
Denny
3543712fd3 Updating build to 0.3.0.beta1 2017-01-28 20:02:38 -05:00
Denny Sheirer
4780f00c44 Merge pull request #157 from DSheirer/155-libusb-pipe-error-on-resubmit
Resolves #155 - adds usb endpoint clear halt when attempting to resubmit transfer buffer
2017-01-28 19:35:38 -05:00
Denny
7e4acc568c Resolves #155 - adds usb endpoint clear halt when attempting to resubmit a transferred buffer that causes a usb pipe error. 2017-01-28 19:33:52 -05:00
Denny Sheirer
a1757686e9 Merge pull request #156 from DSheirer/155-libusb-pipe-error
Resolves #155 - LIBUSB_PIPE_ERROR when trying to start R820T tuner
2017-01-28 17:35:01 -05:00
Denny
960bf975d9 Resolves #155 - LIBUSB_PIPE_ERROR when trying to start R820T tuner 2017-01-28 17:33:59 -05:00
Denny Sheirer
57a52a662a Merge pull request #152 from DSheirer/151-mpt1327-call-timeout-no-persist
Resolves #151 - mpt1327 decoder configuration persistence
2017-01-28 17:01:10 -05:00
Denny
f7c144ca16 Resolves #151 - mpt1327 decoder configuration was not setting editor to modified on changes to traffic channel pool size or traffic channel timeout 2017-01-28 16:59:58 -05:00
Denny Sheirer
f63b05c1e4 Merge pull request #149 from DSheirer/123-add-channel-details-tab
Resolves #123 - adds channel details tab
2017-01-28 16:50:24 -05:00
Denny
78a4bfe915 Resolves #123 - adds channel details tab to display: System, Site and Channel name, and the complete Activity Summary 2017-01-28 16:47:44 -05:00
Denny Sheirer
3b2ff7144b Merge pull request #148 from DSheirer/111-broadcaster-queue-management
Resolves #111 - adds 'Maximum Age Limit' setting to streaming queues
2017-01-28 16:08:39 -05:00
Denny
cd9bb5a84d Resolves #111 - adds 'Maximum Age Limit' setting to each streaming configuration. Calls that sit in the streaming queue for too long will be aged off if their start time exceeds the delay time and maximum recording age limit. A new column is added to the streaming status table to reflect the number of calls that have been aged off. This would be an indicator to the user that they have too many talkgroups tagged to stream for a given channel and may want to change their settings to avoid age off. This mainly prevents excessive streaming queue build-up that might never get streamed and could potentially impact application memory usage. 2017-01-28 16:06:43 -05:00
Denny Sheirer
37d447066d Merge pull request #147 from DSheirer/81-p25-call-priority-and-encrypted-state
Resolves #81 - adds priority, encrypted channel, session mode and cal…
2017-01-28 14:47:12 -05:00
Denny
2ec882a87f Resolves #81 - adds priority, encrypted channel, session mode and call timer to P25 call events in the details column. 2017-01-28 14:46:38 -05:00
Denny Sheirer
7b9004af08 Merge pull request #146 from DSheirer/140-icecast-metadata-updater-url-encoding
Resolves #140 issue with updating metadata on Icecast2 servers
2017-01-28 11:17:16 -05:00
Denny
eba9367674 Resolves #140 issue with updating metadata on Icecast 2 servers where mountpoint was not URL encoded. 2017-01-28 11:15:27 -05:00
Denny Sheirer
8ffe91911b Merge pull request #145 from DSheirer/114-CSV-logging-issue
Resolves #114 issue with event log entries missing single quotes around date and time values.
2017-01-28 10:56:55 -05:00
Denny
a0e0a5ddc0 Resolves #114 issue with event log entries missing single quotes around date and time values. 2017-01-28 10:55:46 -05:00
Denny Sheirer
d500346ba2 Merge pull request #144 from DSheirer/112-playlist-v2-loading-issue
Resolves #112 issue with loading/discovering playlist V2 filename
2017-01-28 10:44:11 -05:00
Denny
3283b66bdc Resolves #112 issue with loading/discovering playlist V2 filename from the system properties. V2 playlist name is now hard-coded until a future enhancement that supports multiple user playlists. 2017-01-28 10:43:18 -05:00
Denny Sheirer
c16416ab14 Merge pull request #143 from DSheirer/141-streaming-temp-recordings-when-disabled
#141 - resolves issue with temporary streaming recordings being gener…
2017-01-28 10:36:07 -05:00
Denny
5e46e22721 #141 - resolves issue with temporary streaming recordings being generated for aliases that contained broadcast stream channel identifiers, but when no streaming channels were enabled. Resolved second issue where a broadcast channel that is enabled and streaming on application startup then gets disabled by the user and subsequently (persistently) auto restarts and connects. 2017-01-28 10:35:12 -05:00
Denny Sheirer
97e2ac31cd Merge pull request #142 from DSheirer/126-tuner-buffer-processor-out-of-memory
126 - Tuner processing refactor and significant improvement in processor and memory utilization.  R…
2017-01-28 09:09:47 -05:00
Denny
a302d098de 126 - Significant improvement in processor and memory utilization. Refactors all USB tuner sample processing with a new USBTransferProcessor class. LibUSB transfer timeout processing is now consolidated under the TunerManager and tuners register with this singleton to auto-start or auto-stop USB bulk transfer processing. Resolves long-standing issue impacting multi-tuner use where the second and subsequent tuners may have been routinely starved for LibUSB processing time under heavy-loading. All tuner buffer threaded processing is now handled as runnables in the scheduled thread pool, eliminating tuner-dedicated processing threads. Added tuner buffer size management to prevent memory blow-out when down-stream processing is either lagging or impacted by java garbage collection operations. Both tuners and individual DDC channels now have bounded sample buffer queues that automatically enter and exit overflow mode when predefined max/reset thresholds are met. When any queue goes into overflow mode, sample buffers are simply dropped in favor of preserving program/memory stability. 2017-01-28 09:08:11 -05:00
Denny Sheirer
b9e1efa92f Merge pull request #138 from DSheirer/135-audio_recording_issue
135 audio recording issue
2017-01-26 19:19:34 -05:00
Denny
07933831d2 135 WIP - resolves issue with recording. 2017-01-25 21:04:28 -05:00
Denny
e2e00a98d5 135 WIP - updates to resolve issue with all audio being recorded, even when user has not elected to record the audio. 2017-01-24 21:14:16 -05:00
Denny
ea0e1b152f Merge branch 'master' into 135-audio_recording_issue 2017-01-24 20:52:40 -05:00
Denny
afa451a070 #130 MPT-1327 Decoder Filter issue resolved. Converts the post-demodulation filter from a band pass filter to a low-pass filter with 60 dB attenuation and a cutoff of 3200 Hz. This is then followed by a high-pass filter at the decimated channel rate, internal to the decoder that performs DC removal and removes any CTCSS tones prior to the FSK decoder. 2017-01-24 20:49:12 -05:00
Denny Sheirer
eaeedc53fc Merge pull request #136 from DSheirer/130-mpt1327-decoder-filtering
#130 MPT-1327 Decoder Filter issue resolved.
2017-01-24 20:27:56 -05:00
Denny
1786ffac47 #130 MPT-1327 Decoder Filter issue resolved. Converts the post-demodulation filter from a band pass filter to a low-pass filter with 60 dB attenuation and a cutoff of 3200 Hz. This is then followed by a high-pass filter at the decimated channel rate, internal to the decoder that performs DC removal and removes any CTCSS tones prior to the FSK decoder. 2017-01-24 20:26:57 -05:00
Denny Sheirer
8b403e6d87 Merge pull request #134 from DSheirer/132-streaming_error_socket_timeout
#132 - adds enhanced streaming connection error handling.  Adds speci…
2017-01-24 19:31:00 -05:00
Denny
8a8fbc4936 #132 - adds enhanced streaming connection error handling. Adds specific handler for connection timeout to reset the connection. 2017-01-24 19:26:18 -05:00
Denny Sheirer
3c9c7794c9 Merge pull request #129 from DSheirer/128-mpt1327-editor-no-wildcards
#128-resolves issue with MPT1327 alias identifier editor preventing u…
2017-01-22 09:53:58 -05:00
Denny
48d3d7a8c1 #128-resolves issue with MPT1327 alias identifier editor preventing use of * wildcard characters. 2017-01-22 09:53:25 -05:00
Denny Sheirer
d81ef8db2a Merge pull request #127 from DSheirer/#108-thread-pool-cleanup
#108 thread pool cleanup
2017-01-22 09:38:44 -05:00
Denny
3d644c2460 #108 - cleanup 2017-01-22 09:34:55 -05:00
Denny
6e94d9ce51 #108 [WIP] Created new ThreadPool class with a static ScheduledThreadExecutor that is now shared by all components. Removed ThreadPoolManager from all classes. Consolidating into 1 pool seems to have reduced CPU usage by ~15-20%. Overall memory usage is also reduced. 2017-01-22 09:33:47 -05:00
Denny
523ac0314e Merge remote-tracking branch 'origin/master' 2017-01-22 07:29:33 -05:00
Denny Sheirer
b252159250 Merge pull request #124 from DSheirer/111-audio_broadcaster_queue_management
#111 resolves issue with probable thread contention on temporary stre…
2017-01-22 07:29:11 -05:00
Denny
2168ed112e #111 - import cleanup 2017-01-22 07:28:37 -05:00
Denny
2faaa93785 #111 resolves issue with probable thread contention on temporary streamed recording queue management that was causing processing thread backups and memory issues. Audio broadcaster was originally in this configuration but was changed to account for a potential issue where recordings could be streamed out of sequence if they occurred at the same time. Reverted queue type back to a more efficient FIFO linked transfer queue to minimize queue contention. 2017-01-22 07:20:24 -05:00
Denny Sheirer
7dc32954bf Merge pull request #122 from DSheirer/#90-channel_processing_list_and_gui_reformat
#90 channel processing list and gui reformat
2017-01-22 06:51:30 -05:00
Denny
a57183c4cc #90 - updated metadata and attributes to carry channel system/site/name values to support audio recording name generation. Updated always unsquelched decoder state to reset the PRIMARY_TO address to the channel name since the decoder will never push a value to this attribute and it's needed to correctly display audio content. 2017-01-22 06:44:07 -05:00
Denny
8fb2a66411 #90 [WIP] Logging cleanup 2017-01-21 11:06:11 -05:00
Denny
26837b8a68 #90 [WIP] Resolves issue with MPT-1327 decoder which has apparently been broken for a long time. 2017-01-21 11:05:30 -05:00
Denny
6b67a74055 #90 [WIP] Adds ability to show streaming status on main application window using menu (View > Show Streaming Status). Resolves a number of issues with the SystemProperties class. 2017-01-21 08:13:18 -05:00
Denny
2d9c3c0aa4 #90 [WIP] Adds support for attribute monitors to all of the decoder states. Updates color scheme in 'Now Playing' view for channel states. Wires selection listener for selected channel metadata to the Events and Messages views. Finalizes format for horizontal audio panels, font and coloration. 2017-01-20 21:12:57 -05:00
Denny
ce47d2545d #90 [WIP] GUI is functioning again and the new channels table is showing calls and the audio panel is now horizontal. Lots of gui tweaks still to do. 2017-01-19 22:10:49 -05:00
Denny
5f179c4863 #90 [WIP] adds AttributeMonitor and AliasedStringAttributeMonitor for managing the metadata attributes and issuing attribute change requests, so that we can remove attribute management code out of the decoder states. Adds heuristic tracking with the monitor so that we can better track the talkgroups and other attributes and smooth out the rapidly changing values when we're decoding a weak signal. This will eventually close out issue #101 2017-01-18 20:34:37 -05:00
Denny
acabab35ff Merge branch 'master' into #90-channel_processing_list_and_gui_reformat
# Conflicts:
#	src/alias/AliasList.java
2017-01-18 19:07:39 -05:00
Denny Sheirer
929fbdb21e Merge pull request #116 from DSheirer/#115_patch_group_alias_no_wildcard_alias
#115 patch group alias - resolves issue where wildcard aliases were b…
2017-01-17 20:45:32 -05:00
Denny
95a6deaae1 #115 patch group alias - resolves issue where wildcard aliases were being matched against a temporary patch group id and removed from the alias list for the lifespan of the patch group. General cleanup of the rollup methods for the patch group alias. Adds a new method to alias list that allows alias lookup from a tgid while excluding wildcard talkgroups. 2017-01-17 20:43:49 -05:00
Denny
bc8873de01 #90 [WIP] merged metadata management for channel state, decoder states and audio metadata into a new metadata class. The new metadata class is maintained for each decoding audio channel, or in the future, a decoding TDMA channel. Audio playback, recording and streaming all use the same metadata object for the channel. The list of channel processing gui panels have been deleted and channel processing status will now reside in a table where each table row is a metadata instance from the processing channel. The channel processing manager is responsible for adding/removing the channel's metadata instance to/from the metadata table. This allows the channel processing table to be the controller for the messages, events and channel spectrum views, keyed to the selected table row metadata.
I still have to clean up the gui components and wire in the table selection listeners.
2017-01-17 19:24:54 -05:00
Denny
16b69d4aef #90 [WIP] on redesigning the channel processing gui components and backing model. Simplifies channel state by introducing a new Metadata class that tracks all metadata for all decoders. 2017-01-15 08:42:13 -05:00
Denny
7115b4feb1 #90 [WIP] on redesigning the channel processing gui components and backing model. Simplifies channel state by introducing a new Metadata class that tracks all metadata for all decoders. 2017-01-14 11:21:53 -05:00
Denny Sheirer
6a9e522d77 Merge pull request #109 from DSheirer/#100-patch_group_aliases
#100 patch group alias enhancement
2017-01-13 15:48:56 -05:00
Denny
c39c49b049 #100 enhances patch group alias to include any existing alias for the patch talkgroup ID in the rolled-up alias. Streaming channels for the alias now includes any channels designated for the existing alias as well as the highest audio priority. Alias name for the patch group alias is either the existing alias name, or the label "Patch:" and the patch group ID. 2017-01-13 15:47:55 -05:00
Denny Sheirer
471685b576 Merge pull request #105 from DSheirer/#102-tooltip_typo
#102 resolves typo in tooltip for R820T Tuner Editor
2017-01-13 06:03:51 -05:00
Denny
2268879cec #102 resolves typo in tooltip for R820T Tuner Editor 2017-01-13 06:03:20 -05:00
Denny Sheirer
86e4ce817d Merge pull request #103 from DSheirer/#82_icon_manager_model
#82 icon manager model
2017-01-11 20:45:23 -05:00
Denny
75cb6c27d4 #82 - resolves issue with icon management and list updating after additions/deletions of icons. Updates the single and multi alias list editors to refresh their view any time that changes are made to the list of icons. 2017-01-11 20:44:07 -05:00
Denny
1352ef9092 Merge branch 'master' into #82_icon_manager_model 2017-01-09 18:19:28 -05:00
Denny Sheirer
8a150451c4 Merge pull request #99 from DSheirer/98_jmbe_version_check_error
#98 - updated logging to reflect the required version of JMBE (0.3.3).
2017-01-09 18:16:38 -05:00
Denny
5ff3f4169a #98 - updated logging to reflect the required version of JMBE (0.3.3). 2017-01-09 18:15:41 -05:00
Denny
b6498d048a #82 WIP - converted all classes to get icons from IconManager and removed icon management from the SettingsManager 2017-01-08 21:06:02 -05:00
Denny
488dc9da4c #82 WIP - reorganizing icon management and extracting icon model from the settings manager. 2017-01-08 15:01:53 -05:00
Denny Sheirer
4481560c41 Merge pull request #97 from DSheirer/#78_p25_patch_group_management
#78 p25 patch group management
2017-01-08 08:54:05 -05:00
Denny
1dbdede5a2 #78 updated change log 2017-01-08 07:57:22 -05:00
Denny
6bfe98dae6 #78 patch group alias and call event management. Adds new PatchGroupAlias which is a temporary alias group that is added to the alias list by the decoder state. The P25 decoder state maintains a mapping of patch group identifier to patched talkgroups and creates the patch group alias. This allows audio recording and audio streaming of patched talkgroups, according to the aliases corresponding to the group of patched aliases contained in the patch group alias.
Event logging is now pared down to only logging events when the internal patch group mapping is updated.
2017-01-08 07:54:48 -05:00
Denny Sheirer
de9c51c228 Merge pull request #94 from DSheirer/#66_remove_loggers_and_recorders_between_channel_restarts
#66 remove loggers and recorders between channel restarts
2017-01-08 05:28:16 -05:00
Denny
7865e813de #66 resolves issue with baseband recorders and loggers not being removed from channel processing chains when processing stops. This is now handled in the channel processing manager where baseband recorders and message/event loggers are added at processing start and removed at processing stop. 2017-01-08 05:21:42 -05:00
Denny
a55296eb45 Merge branch 'master' into #66_remove_loggers_and_recorders_between_channel_restarts 2017-01-08 04:44:53 -05:00
Denny
2c558ce929 #93 resolves issue with NPE when an exception is handled in the audio broadcaster exception handler 2017-01-08 04:44:13 -05:00
Denny
49a1ea04f8 #66 wip 2017-01-08 04:38:10 -05:00
Denny Sheirer
2226306de3 Merge pull request #89 from DSheirer/#65_audio_recorder_exception_on_startup
#65 resolves issue where on audio recorder startup an IO exception is…
2017-01-07 10:59:57 -05:00
Denny
0cf26da94e #65 resolves issue where on audio recorder startup an IO exception is thrown. Traps the exception and shuts down the recorder. 2017-01-07 10:56:57 -05:00
Denny Sheirer
59671738a2 Merge pull request #88 from DSheirer/#86_playlist_file_corruption_on_save
#86 - updates playlist configuration file save operations to 1)create…
2017-01-07 10:26:41 -05:00
Denny
c6f4d6da05 #86 - updates playlist configuration file save operations to 1)create a backup copy, 2)create a lock file, 3)store the playlist, and 4)remove the lock file. On startup, if it detects a residual lock file, the playlist manager will delete the existing playlist (assumes it is incomplete or corrupted), restore the previous playlist from the backup file, and remove the lock file. 2017-01-07 10:24:04 -05:00
Denny Sheirer
2d07734cc5 Merge pull request #85 from DSheirer/30-audio_streaming_support
30 audio streaming support
2017-01-07 08:25:19 -05:00
Denny
ad377f4006 #30 - updates changelog with description of audio broadcaster feature. 2017-01-07 08:19:18 -05:00
Denny
648b12fd16 #30 - resolves logic issue that was preventing deletion of temporary stream recordings. Changes the streamable recording queue in the AudioBroadcaster to find the recording with the earliest start time to prevent situations where a long recording might close out and enter the broadcast queue after a shorter recording that started later, but would have been replayed first. 2017-01-07 07:31:47 -05:00
Denny
bc430b255f #30 - changes internal MP3 temporary recording stream management so that the broadcast model manages the recordings and sends the completed recordings to the broadcasters to avoid 2 or more streams recording MP3 files for the same audio call.
Note: there is a bug where temporary recordings are not being consistently deleted that I'm still working on.
2017-01-03 09:27:17 -05:00
Denny
4578e45f60 #30 - adds streaming status color indicators to the streaming panel table.
Resolves issue with temporary streaming file name collisions by adding a 1-up number to each generated temporary recording file.
2017-01-03 06:41:22 -05:00
Denny
42c18341b6 #30 - adds support for Shoutcast V2 (aka Ultravox 2.1) audio streams.
Resolves issue with MP3 framing where the remote server was disconnecting us due to bad MP3 sync.  The LAME encoder was sending partial 20 byte MP3 frames initiall, then following with a 144 byte array which contained the remainder of the first frame and the header of the next frame.  Adjusted the MP3SilenceGenerator to only emit complete MP3 frames.

This should be the final commit for this feature, pending merge back into master branch.
2017-01-02 16:05:00 -05:00
Denny
e71a1dbeda #30 - changes audio broadcaster to send full/complete MP3 frames instead of partial frames in order to reduce remote server disconnnects due to MP3 frame sync loss on remote server. 2016-12-29 08:20:10 -05:00
Denny
a4e3c53ba8 #30 - adds support for Shoutcast V1 streaming. Resolves issue where temporary MP3 recording files were not being flushed at the end, causing partial frames to be streamed resulting in repeated remote server connection resets.
Note: Shoutcast V1 streaming to a Shoutcast V2.5.x server seems to be problematic.  The newer server tracks MP3 boundaries in order to insert advertising and this appears to have problems keeping sync with the MP3 stream.  I spent significant time eliminating MP3 framing errors in my code.  You can see these 'Bad frame' errors in the sc_serv.log and the shoutcast server will eventually reset the connection after a period of time or after a number of framing errors occur.  I anticipate that streaming to a Shoutcast V2 server using the ultravox (ie V2.x) protocol may resolve this issue.
2016-12-29 05:59:14 -05:00
Denny
0cebe7e5d9 #30 - cleanup 2016-12-26 13:48:24 -05:00
Denny
5611f594c7 #30 - improves silence frame handling. Appends silence to end of MP3 recordings to round them out to whole second intervals and adds an MP3 silence generator to generate 1 second silence frames each interval when there is no audio to broadcast. Previous approach used static silence frame, but we can't generate an exactly 1 second frame, so the remote server was resetting the connection when it detected that we were sending data at over or under rate. 2016-12-26 11:52:25 -05:00
Denny
524a76e583 #30 WIP extracted the IcecastMetadataUpdater and merged some of the icecast configuration method into a new superclass. 2016-12-26 09:08:34 -05:00
Denny
c216f56c4f #30 WIP extracted the IcecastMetadataUpdater and merged some of the icecast configuration method into a new superclass. 2016-12-24 05:41:04 -05:00
Denny
5e4cbf2a4e #30 WIP on using the new apache mina library 2016-12-18 10:17:09 -05:00
Denny
1d7354b4a8 #30 WIP on using the new apache mina library 2016-12-18 09:09:47 -05:00
Denny
d085ecc0c9 #30 WIP on using the new apache mina library 2016-12-17 16:29:52 -05:00
Denny
1c8362c1fc #30 WIP - cleanup 2016-12-11 10:38:23 -05:00
Denny
b65ff17ede #30 WIP - adds support for Icecast 2 (v2.4+) audio streams. Adds additional fields (genre, description, public) to Icecast 1 configuration editors. Adds audio broadcast delay to Broadcastify and Icecast editors. 2016-12-11 06:55:42 -05:00
Denny
ebca330730 #30 WIP - icecast 2 broadcaster cleanup 2016-12-10 10:42:59 -05:00
Denny
ef373daeef #30 WIP - icecast 2 broadcaster cleanup 2016-12-10 10:36:38 -05:00
Denny
4290d8e403 #30 WIP - icecast 2 broadcaster cleanup 2016-12-10 10:21:08 -05:00
Denny
293dec8a1c #30 WIP - adds radio reference web service lookup of broadcastify feed settings dialog. 2016-12-09 19:50:14 -05:00
Denny
f44892c62b #30 WIP - broadcastify and icecast TCP streams working and editors are complete. Adds icons to editor and table views. 2016-12-04 07:28:03 -05:00
Denny
b71415b82a #30 WIP - broadcastify multi-channel streaming is functional. 2016-11-27 11:58:34 -05:00
Denny
ecfac83eb4 #30 WIP - adds stream manager to receive multiple audio stream's packets and convert the audio and persist to disk. Broadcaster will now receive converted audio files to stream out to the server. 2016-11-13 04:33:45 -05:00
Denny
357979a5dd #30 - WIP - adds Icecast TCP(v2.3) and HTTP(v2.4) server support. Adds streaming tab and ability to add/delete/change streaming configurations. Adds streaming channel alias id so that aliases can be tagged to stream to one or more stream channels. Adds initial support for broadcastify streaming. 2016-11-06 09:33:49 -05:00
Denny
893cbba277 #30 - WIP on the shoutcast v2 protocol 2016-10-23 06:03:07 -04:00
Denny
ac23aea602 #30 - WIP on the shoutcast v2 protocol 2016-10-21 05:30:36 -04:00
Denny
a43ae9ce53 #30 - WIP on the shoutcast v2 protocol 2016-09-30 05:59:14 -04:00
Denny
02f470cf91 #30 - WIP on the shoutcast v2 protocol 2016-09-29 20:50:00 -04:00
Denny
6138838d60 #30 - WIP on the shoutcast v2 protocol 2016-09-14 06:07:21 -04:00
Denny
2537a71375 #30 - WIP Streaming audio support. Adds Shoutcast V1 handler. 2016-09-12 04:28:46 -04:00
Denny
9031fd78c9 #30 - WIP Streaming audio support 2016-08-21 11:41:42 -04:00
Denny Sheirer
e75290c42c Merge pull request #79 from DSheirer/53_audio_sample_rate_to_8kHz
53 audio sample rate to 8k hz
2016-08-21 07:27:29 -04:00
Denny
d466ec9f5e Changes audio module decimation filter from lowpass to bandpass so that we can remove 0-250 Hz and any LTR signalling from the audio output. 2016-08-21 07:21:38 -04:00
Denny
3af4b953f4 Changes internal audio sample rate from 48 kHz down to 8 kHz. Updates the P25 audio module to output 8 kHz audio. Updates the audio recorder to record at 8 kHz rate.
Note: required updated JMBE 0.3.3 library which supports 8 kHz audio output sample rate.
2016-08-19 21:09:39 -04:00
Denny
cc65d44237 Updates change log with efforts to day for 0.3.0 2016-08-04 20:01:45 -04:00
Denny
ae6ccc5d51 Updates change log with efforts to day for 0.3.0 2016-08-04 20:00:37 -04:00
Denny
77cdcf1862 #72 Enhances management of wildcard values for aliases. Calculates a relative weighting for each wildcard alias id and then orders (smallest to largest) the wildcard values so that when attempting to lookup an id value, it first attempts to look for a direct match and then defaults to iterating the list of wildcard values until it finds the most specific or best matching wildcard value. 2016-08-04 19:54:43 -04:00
Denny
bccd4f0e32 #72 Resolves issue with alias identifier wildcard matching 2016-08-01 05:04:01 -04:00
Denny
a76268b32c Cleanup 2016-07-31 11:00:34 -04:00
Denny
ba31015423 #74 - added specific call event types for patch group processing. 2016-07-31 10:18:15 -04:00
Denny
c635dc5d79 Updated call event details message for traffic channel rejects (no tuner, unknown frequency) to include the original call event details in addition to the traffic channel reject message. 2016-07-31 10:08:13 -04:00
Denny
d57c485450 Resolves issue with correct parsing of channel number for motorola patch group channel grants. 2016-07-31 09:46:09 -04:00
Denny
6e1f28917e Resolves null pointer exception when updating identifier from values. 2016-07-31 09:20:00 -04:00
Denny
6af24ffa38 Closes #72 - updates alias list to maintain a map of alias id to alias and separate lists for wildcard alias id values. Alias lookup logic is changed to first look for in the alias map for a concrete match to the alias identifier. If an alias is not found, then the list of wildcard alias identifier values is iterated looking for a matching wildcard. If the wildcard is found, then the matching alias is returned from the map. 2016-07-31 09:18:25 -04:00
Denny
b19bfb400e Merge remote-tracking branch 'origin/master' 2016-07-31 07:52:43 -04:00
Denny
a56935f92c Closes #74 - adds support for vendor-specific (Motorola) TSBK patch group management. Processes patch group channel grants and updates and allocates voice channel processing. Logs call events for patch group add (register) and patch group delete (deregister). Also adds call event logging when there is a planned control channel shutdown--logged only once. 2016-07-31 07:52:01 -04:00
Denny
b33c0359ef Resolves issue with 10 MHz sample rate discovery in Airspy R0/R2 2016-07-12 13:44:17 +00:00
Denny
078a6fddce Closes #71 - frequency controller throws exception when exceeding min/max tuning limits of a tuner.
Updated code to show dialog to user whenever the tuning limit has been exceeded.  Added new InvalidFrequencyException that includes the requested invalid frequency and the valid frequency limit that was exceeded
2016-06-04 08:21:28 -04:00
Denny
bf5ded53e2 Code cleanup 2016-06-01 04:15:16 -04:00
Denny
3c53f864c4 Closes #70 - airspy mini sample rates.
Change code to no longer assume that 10 MHz is an available default sample rate.  The mini is only capable of 6 and 3 MHz.

Note: only the 6 MHz sample rate option should be used at this time.  6 MHz can be decimated to 48 kHz using an integer decimator.  sdrtrunk doesn't support the 3 MHz option, because it can't decimate that rate to 48 kHz until the new channelizer is developed.
2016-05-30 08:58:00 -04:00
Denny
61d9698ca5 Code cleanup for waterfall and overlay panels 2016-05-30 07:30:17 -04:00
Denny
8fec7c7314 IntelliJ and configuration updates 2016-05-30 07:29:43 -04:00
Denny
90324404b2 Resolves issue #69 with double audio recordings 2016-05-30 07:25:50 -04:00
Denny
d520f43265 Adds support for Intelli-J IDE and adds missing CSS file for filter viewer 2016-05-30 07:03:31 -04:00
Denny
192d39294b Merge branch 'master' of https://github.com/DSheirer/sdrtrunk.git 2016-05-30 05:33:20 -04:00
Denny
129dba869f Adds Remez filter designer for low, high and band-pass filters. Support
for Hilbert and Differentiator FIR filter designers may be added in the
future.

Adds developer filter viewer tool written in JavaFX to visualize the
frequency and impulse response of FIR filters.  This application is not
intended to be a user application and is specifically for the developer
to analyze filters and filter specifications that will be used in the
code.

NOTE: Java 8 JDK is now required to build this project for version 0.3.x
and forward.  The JavaFX visualizer application and future user
interfaces will use the Java 8 version of JavaFX
2016-05-29 08:54:07 -04:00
Denny Sheirer
72390cc62a Update README.md
Updated download link
2016-05-02 04:28:22 -04:00
Denny
5cd51c3199 Revert "wip: adds filter viewer JavaFX utility application for visualizing filters designed through the filter factory."
This reverts commit 097884337b.
2016-04-18 18:44:43 -04:00
denny
097884337b wip: adds filter viewer JavaFX utility application for visualizing
filters designed through the filter factory.
2016-04-11 19:22:56 -04:00
denny
3231e9828b Resolves #64 Persist last tuned frequency to tuner configurations
Task-Url: http://github.com/DSheirer/sdrtrunk/issues/issue/64
2016-04-05 19:30:09 -04:00
Denny Sheirer
5672182609 Fixed type in R820T tuner editor 2016-04-02 08:15:09 -04:00
denny
631ce112bd Resolves issue with MessageActivityModel and includes missing files from
previous commit.
2016-04-01 08:27:50 -04:00
denny
c3d52ff86e Merges #34 Major redesign of Tuner, Channel and Alias management.
Changes to model, view and controller components and updates all gui
editors.  Removes the configuration panel and tree and replaces with 3
new panels.  Updates playlist.xml to a new playlist_v2.xml format.
2016-04-01 08:23:49 -04:00
denny
03e641525c Updating version to 0.2.0 (final). 2016-03-29 20:11:41 -04:00
denny
64fee2c718 wip #34 - testing and validating changes and testing decoding. 2016-03-29 20:00:23 -04:00
denny
55443b42f3 wip #34 - resolved issue with airspy correctly loading stored tuner
configuration values.  Updated remaining tuner editors to set loading
flag when loading tuner configuration and turn off after loading is
complete.
2016-03-27 07:25:17 -04:00
denny
3caadf0c90 Moves audio panel into the channel state list panel so that we don't
have to use a split pane.
2016-03-26 07:29:10 -04:00
denny
58160d6c6b wip #34 - resolved various issues with alias controller and editors.
Added synchronized method to channel processing manager to ensure that
we don't have contention during channel changes.
2016-03-26 07:17:43 -04:00
denny
112083a22f wip #34 - finished updating all channel configuration editors and
completed preliminary functional testing.
2016-03-25 14:26:08 -04:00
denny
f2b189ee2b Adds support to TunerManager for the RAD1O (HackRF compatible) tuner. 2016-03-21 18:39:59 -04:00
denny
3f0581dad1 Adds support for Munich hackerspace Rad1o tuner - a HackRF compatible
tuner.
2016-03-21 18:33:56 -04:00
denny
e89e7bd361 wip #34 - updating the decoder editors to the new editor format 2016-03-21 18:30:19 -04:00
denny
d93f497b33 wip #34 - reformatting the channel configuration editors. 2016-03-20 19:35:45 -04:00
denny
f32c520cab wip #34 - updates FCD Pro tuner and completes work on tuners, tuner
configurations and tuner editors.
2016-03-20 08:13:28 -04:00
denny
13617e8fe2 wip #34 updates funcube dongle pro plus tuner editor 2016-03-20 07:22:18 -04:00
denny
4adffbdca7 wip #34 adds sort ordering to tuner table 2016-03-20 06:40:25 -04:00
denny
7a5dc5ad58 wip: #34 Updates the HackRF tuner editor and a few other tuner
config/editor items
2016-03-20 06:27:53 -04:00
denny
d3e8816bb4 wip: #34 Adds the E4000 tuner editor and updates a few other tuner
editor pieces.
2016-03-20 05:30:16 -04:00
denny
6bb417f69a wip: #34 - finishes the airspy tuner editor panel and adds tuner info
button to the r820t tuner editor to view serial number and misc info.
2016-03-19 14:49:55 -04:00
denny
a4d69ce0aa wip #34 - final edits to R820T tuner configuration editor and introduced
frequency correction to the airspy tuner configuration.
2016-03-18 10:59:36 -04:00
denny
45f0e09d5a wip #34 - updated tuner configurations to include unique id and assigned
flag, so that we can get rid of tuner configuration assignment objects.
2016-03-17 22:01:31 -04:00
denny
f518c15a06 wip #34
34: Redesign Channel Management 

Task-Url: http://github.com/DSheirer/sdrtrunk/issues/issue/34
2016-03-17 21:16:21 -04:00
denny
6eade2b7ea wip: #34 2016-03-13 20:06:26 -04:00
denny
86816228c2 wip #34 - adds enable/disable to tuner editor components and frequency
control
2016-03-13 08:43:55 -04:00
denny
0605760fcd wip #34 - removed some wrapper methods from tuner since the tuner
controller is now exposed (frequency, sample rate, channel count, etc)
2016-03-13 07:38:29 -04:00
denny
415f95ec89 wip #34: refactored tuners to take a tuner controller as an argument.
Removed frequency change listener framework from tuner so that the tuner
controller handles all frequency change interaction.

Updated tuner view to allow selecting tuners for main spectral display
and/or launching a new spectral display by clicking in the tuner table
cell.
2016-03-13 07:09:26 -04:00
denny
1078bdc4fd wip: #34 - adds tuner spectral display manager to handle requests to
show a tuner in the main spectral display or to show the tuner in a new
spectral display
2016-03-12 13:11:06 -05:00
denny
218eeb1848 WIP #34 - more work on tuner model and view. Includes new tuner
configuration and tuner configuration assignment editors.  Beginning of
tuner editor panel rework.   Removes most of the remaining JTree
configuration pieces.  Updates the settings manager to load and save
tuners and refactors all of the tuner configuration management out into
the tuner configuration model.
2016-03-12 12:54:48 -05:00
denny
9d4afb8c1f WIP #34 - updates tuners and tuner model to provide details on
frequency, sample rate, sourced channels, and spectral display in the
tuner view tab.
2016-03-11 07:16:29 -05:00
denny
6fa3089bc9 WIP #34 - adds tuner model and view tab, preparing to remove the
configuration panel from the controller view.
2016-03-07 20:43:43 -05:00
denny
9273f679f4 Resolves issue with loading of channel map ranges from playlist_v2.xml 2016-03-07 19:05:34 -05:00
denny
b6e4c4e7aa WIP #34 - new channel map manager/editor is complete. Channel map
manager is now a separate window launched from the decoder tab,
specifically the MPT1327 decoder tab.
2016-03-06 19:22:54 -05:00
denny
2394ac2fa9 WIP #34: added channel map manager and new editor window. Removed a
number of unused classes.  Removed playlist manager from a number of
classes and replaced with the alias, channel and channel map models.
Updated playlist manager to no longer retain a playlist except for when
loading to models or when storing from models.  Extracted version 1 to
version 2 playlist conversion and changed playlist manager to keep the
original version 1 playlist and create a new version 2 (playlist_v2.xml)
file.  On startup, try to load a v2, and if not, look for a v1 to
convert.  Otherwise, don't create a playlist and one will be created
once the user adds items to the models.
2016-03-06 07:48:14 -05:00
denny
8aebdd1c26 Changes default channel source to be a tuner. 2016-03-05 08:25:22 -05:00
denny
8b11fabf3d wip: adds call priority and non-recordable editors to the alias
multi-select editor.  

Changes example hyperlink to 'Help'.
Ensures editor only allows 1 instance of call priority or non-recordable
can be added to any alias.
2016-03-05 08:23:08 -05:00
denny
4ad16296d8 wip: alias multi-editor feature complete 2016-03-05 07:33:04 -05:00
denny
e8fd4f363a wip: alias multi-select editor feature 2016-03-05 04:30:21 -05:00
denny
00d2b43c2a wip: finished alias, alias ID and alias action editing framework. 2016-03-04 09:33:46 -05:00
denny
2c3dbe1b22 Work in progress - more updates to the Alias Identifier editor 2016-03-01 19:54:59 -05:00
denny
6d1e7a78cf Work in progress - added ability to add new alias identifiers and to
auto save alias/identifier changes.
2016-02-29 20:14:52 -05:00
denny
d1ea8656c4 Resolved error when clicking clear on an empty message model. 2016-02-29 19:16:41 -05:00
denny
e31b6adf52 Updated help text display for each of the alias identifier editors. 2016-02-28 10:06:28 -05:00
denny
63cea60c42 Merges master CF2C0CA, EEAD22F, 5F2D8C8, 22D3871, and 6F2A7E8 - updates
libusb transfer processing and shutdown/cancel management of transfers
in progress.
2016-02-27 09:45:47 -05:00
denny
ece555dead Merges master 1550c62 - channel state panel null checking 2016-02-27 09:04:13 -05:00
denny
15716d1270 merges master 455e452 - removes logging from dft processor about buffer
overflows
2016-02-27 09:02:17 -05:00
denny
8d10b502ab Merges master ca8406b - DFT processor queue backing up 2016-02-27 09:01:07 -05:00
denny
62851351a4 Merges master 399d164 - default tuner configurations issue #57 2016-02-27 08:58:47 -05:00
denny
daf2b58cf2 Updates build to Version 0.2.0 Beta 8 2016-02-27 06:21:51 -05:00
denny
cf2c0ca07d Resolves significant memory usage issue - updates Airspy and HackRF with
the recent changes applied to RTL2832 Tuner controller for processing of
libusb transfer buffers.
2016-02-27 06:19:35 -05:00
denny
eead22ffad Issue #59 - more changes to improve handling of transfer buffers between
tuner start/stop iterations.
2016-02-26 14:52:31 -05:00
denny
1550c62782 Adds null checking to channel state panel. 2016-02-26 14:37:12 -05:00
denny
5f2d8c8fe6 Issue #59 - reorganizes management of the libusb transfers queue to
better handle processing and shutdowns.

-Reuses transfers between tuner start/stop invocations

-Moves libusb transfer status direct byte buffer allocation out of
buffer processing thread and reuses it, saving a lot of memory
allocations over time.
2016-02-26 14:35:34 -05:00
denny
22d3871d0b Restructures transfer buffer handling to better handle shutdown and
canceling of transfers across multiple threads.
2016-02-26 13:28:59 -05:00
denny
455e45269c Issue #58 - DFT processor queue - removed logging that indicates the
queue is full and samples are being purged.
2016-02-26 10:08:52 -05:00
denny
ca8406bdc0 Resolves #58 - DFT processor sample queue may fill up over time causing
the spectral display to be slow to reflect changes to tuner settings.

Changed the max size of the sample queue based on the highest rate tuner
sample rate 10 MHz (airspy) and the lowest available spectral update
frame rate (12).  Sized the buffer queue based on the maximum number of
buffers that should be available per DFT processing iteration.  If the
queue backs up, new buffers will be discarded until the DFT processor
consumes the queue and catches up.
2016-02-26 06:35:03 -05:00
denny
399d164bd0 Resolves #57 - default tuner configuration per tuner type was being
shared across multiple tuners of the same type, unless the user created
a new tuner configuration for each tuner. 

Updated code to create a default tuner configuration based on the tuner
type, but name the configuration as 'Default-UNIQUETUNERID', so that any
changes to that uniquely identified tuner will persist across sessions.

Note: this does not avoid the situation where multiple tuners of the
same type are all using the same default serial number (e.g. 0000001).
In this case, they will all still share the same configuraiton.  User
must change the serial number of the dongle in order for it to be
uniquely identifiable, so that we can correctly lookup the configuration
for the specific tuner.
2016-02-26 06:07:21 -05:00
denny
6f2a7e89a9 Resolves issue with RTL2832 Tuner cancelling transfer buffers that
haven't yet been initialized.  This occurs when there is a channel
allocated from the tuner that is the first/only listener and then is
immediately released.  Some of the buffers are in motion and the others
simply haven't yet been initialized.
2016-02-22 20:49:20 -05:00
denny
a807ff5c07 Resolves issue with RTL2832 tuner where we attempt to cancel transfers
that have not yet been initialized.
2016-02-22 20:47:40 -05:00
denny
e5004f9fa6 Work in progress - updates to alias and alias ID editors. 2016-02-21 20:02:27 -05:00
denny
bd09911e68 Alias updates to allow JAXB persistence 2016-02-21 05:45:54 -05:00
denny
ca2b12b868 Merges master 88d3088 2016-02-20 17:56:28 -05:00
denny
7ff9e9a7ce Merges master 894ebeb - patch to audio playback where audio output
channels were locking up after a period of time
2016-02-20 17:54:35 -05:00
denny
7da541ed06 Merges master a7da60c - TDU message text 2016-02-20 17:46:38 -05:00
denny
d90684dbdc Merges master be7ab87 patch to audio metadata 2016-02-20 17:45:18 -05:00
denny
9286fdf42d Merges cdcc487 from master 2016-02-20 17:42:07 -05:00
denny
b821588e54 Removes debug message from trafficchannelmanager 2016-02-20 17:20:15 -05:00
denny
62aca27e66 Resolves issue with resetting audio metadata between traffic channel
reuse.
2016-02-20 10:26:50 -05:00
denny
cdcc487095 Resolves potential threading issue in tuner channel source. 2016-02-20 10:21:47 -05:00
denny
d5b6589e36 Updates build to 0.2.0 beta 7 2016-02-20 10:05:22 -05:00
denny
be7ab871d1 Resolves issue with audio metadata updates that was causing the audio
playback panel to not detect changes in FROM/TO values for the current
audio playback.
2016-02-20 09:55:39 -05:00
denny
294a830aaf Updates TDU message decoded message text. 2016-02-20 08:54:02 -05:00
denny
a7da60cd27 Resolves issue with audio playback where audio playback stops working.
Potential existed for the audio output class to become unsynchronized
with the underlying source data line and attempt to perform a blocking
write to the data line when the data line doesn't have the capacity to
accept the full buffer, and the data line is not started.  Thus,
playback could potentially stay in a persitent blocked state.

Refactored Mono and Stereo audio output implementations to push the
audio buffer queue processing into the parent abstract class.

AudioManager was updated to remove the last time active timestamp out 
of the audio manager and down into the audio output to remove duplicate
timestamp accounting.
2016-02-20 08:53:09 -05:00
denny
894ebeb040 Removes logging of C4FM filter coefficients. 2016-02-19 21:03:34 -05:00
denny
682fe0c779 Updates linux launch script to allow clean exit on program exit. 2016-02-15 21:06:20 -05:00
denny
62c5265bce Updates launch script to allow clean exit on program termination. 2016-02-15 21:05:08 -05:00
denny
1687ca2c4b Updated data state default color setting 2016-02-15 20:29:46 -05:00
denny
5ee4a48ab3 Resolved issue with duplicate properties saves 2016-02-15 20:23:01 -05:00
denny
c8328b3eff Channel editor and sub editors updates. 2016-02-15 20:12:23 -05:00
denny
691e4c3383 merges master commit 94d85f6 to persist FFT size. 2016-02-15 20:11:53 -05:00
denny
5573b9a9fb Changes audio timeout from 2 seconds to 1 2016-02-15 16:01:06 -05:00
denny
f7f6c2fc3e Merges changes from master branch to resolve issue with CPU and memory
and constraints on scheduled thread pool saturation.  Adds a new
scheduled thread pool for each processing chain that the components in
the chain can share, versus the single shared pool exposed by the
threadpoolmanager class.
2016-02-15 15:47:14 -05:00
denny
88d3088ebc Resolves issue with concurrent access of channel panels in channel list 2016-02-15 14:58:07 -05:00
denny
dc3390460a Updates build to beta.6 2016-02-15 10:01:11 -05:00
denny
8db226cd7b Adds broadcasting of unknown P25 message types. If a P25 message passes
the NID crc check but uses an unrecognized data unit id, it will now be
broadcast as NID:UNKN, but allows for detection of new DUID types when
we have a large number of occurances of unknonwn duid types.
2016-02-15 10:00:29 -05:00
denny
b7a54598b1 Changes audio panel call timeout from 2 seconds to 1 second in order to
increase audio output cycle time.
2016-02-15 09:59:04 -05:00
denny
dd22397f75 Resolves issue with application memory growth and thread pool
saturation.

Each tunerchannelsource was sharing a thread pool and as the processing
load increased, the thread pool was not expanding and the processing
tasks were backing up, causing the buffers to grow with unprocessed
data.

Changed so that each tuner channel source is handed a thread pool to use
for processing incoming sample data.  Processing chain maintains the
thread pool and starts the channel source.  This allows traffic channels
to reuse the thread pool across multiple iterations.
2016-02-15 09:46:33 -05:00
denny
e2a0451bfb Resolves issue with scheduled thread pool overtasking. Removes tuner
channel source decimation processing over to its own thread pool.
2016-02-15 08:42:29 -05:00
denny
e7d3713791 Removes message broadcaster from Decoder abstract class -- processing
chain has a message broadcaster built in, so the decoder's broadcaster
is redundant.  Changes Decoder(s) to only have a single registered
message listener (ie processing chain)
2016-02-15 07:32:26 -05:00
denny
2c6e35c739 Work in progress.
-Removed channel collection panel since panels will only have 1 channel
now
-Resolved issue with channel list and channel selection events
-Fixed some of the classes from the rebase to master branch
2016-02-15 07:10:41 -05:00
denny
1f654b5b9c Rebase channel/alias model redesign branch on current master
(v0.2.0.beta5)
2016-02-14 11:45:43 -05:00
denny
1d7f255784 Revert "Merges master onto development branch"
This reverts commit 2420bc901c63ad9d41e7c99ac95a426f826182ff.
2016-02-13 15:07:53 -05:00
denny
3f674c0b8f Revert "Updates."
This reverts commit 0e01381935f0630fa8e1755d7c9c7fbeed7ae746.
2016-02-13 15:07:52 -05:00
denny
5071205e73 Updates. 2016-02-13 15:07:02 -05:00
denny
37a93c8884 Merges master onto development branch 2016-02-13 15:05:45 -05:00
denny
12ff5513b7 Adds preliminary support for legacy P25 CAI VSELP voice frames. VSELP 1
and 2 messages simply capture the message at this point -- don't yet
know the format for the voice frames and embedded signalling.
2016-02-13 15:04:10 -05:00
denny
bca39e74ce Work in progress - more updates to the alias controller and alias editor
panels.
2016-02-13 15:04:10 -05:00
denny
75714072b2 Adds AliasModel, AliasController tab with new AliasEditor 2016-02-13 15:04:10 -05:00
denny
15edcdebe6 Adds AliasModel and AliasEvent and support for conversion from legacy
playlist format.

Renames PlaylistEditor to ChannelController
2016-02-13 15:04:09 -05:00
denny
da924e8b3a Updates playlist editor with Add, Copy/Clone and Delete channel
functionality
2016-02-13 14:58:51 -05:00
denny
61f68c08e8 Adds support to channel model and processing manager to auto-start
channels upon loading the playlist.  

Processing manager now detects channel configuration changes and
restarts the channel if it is currently processing.

Channel editor enable button now shows green if it is processing, or
default when not processing.
2016-02-13 14:58:50 -05:00
denny
ec10bb7e62 Work in progress. 2016-02-13 14:57:13 -05:00
denny
7d1ec344de Work in progress - updates channel component editors and changes
functionality so that the editor starts up without a channel displayed
and as each channel row is selected in the channels table, the editor is
updated with the channel configuration.

Likewise, the channel editor is now a channel event listener on the
channel model and will maintain channel state if the user changes
channel configuration in any of the external channel controllers like
the spectral display or the channel list.
2016-02-13 14:57:13 -05:00
denny
4be63d4b95 Work in progress.
-Started work on new channel editor channel viewer/controller
-Removed a number of unused buffer package classes
-Moved automatic frequency control to be a module and removed special
handling
-Added IUnfilteredRealBufferListener & Provider interfaces
-Completely removed the ResourceManager class
2016-02-13 14:57:12 -05:00
denny
c405ddad96 Work in progress - program compiles and runs 2016-02-13 14:37:36 -05:00
denny
d8a20b5adb Updating build to beta 5 2016-02-13 09:10:06 -05:00
denny
c670f1d855 Resolves issue with audio playback state becoming disconnected with
AudioManager connection class monitoring of the playback state.  Adds a
line listener to monitor when the line is started/stopped and responds
accordingly.

Removes squelch control from mono and stereo audio playback components
which was a holdover -- squelch state is now managed within the 
audio component for each processing chain.
2016-02-13 09:07:37 -05:00
denny
1dc0125ef4 Resolves issue with LTR standard calls - ending state was not monitoring
the correct channel number on call ends.
2016-02-12 20:54:14 -05:00
denny
225e747d6e Updated README 2016-02-07 10:04:23 -05:00
denny
36f9953156 Updated README 2016-02-07 09:40:25 -05:00
denny
47c452e7e6 README update 2016-02-07 09:28:09 -05:00
denny
06c8bba97e Updated README with version 0.2.0 wiki updates. 2016-02-07 09:26:25 -05:00
denny
a25bb83dea Talkgroup ID Editor - updates help text in editor window 2016-02-06 09:34:13 -05:00
denny
8e11cb4554 Removes audio inversion from talkgroup id and editor. The audio
processing architecture will be updated in the next version and will
handle identifying and processing audio inversion on a per-alias basis.
2016-02-06 09:30:52 -05:00
denny
2892e4609e Removes the 'No Tuner' color change sub-menu from the decoding channels
list.  This is no longer a valid channel state since any decoding
channels that fail to get a tuner source will not be displayed.
2016-02-06 08:24:46 -05:00
denny
d6e161f3e9 Updating build to 0.2.0.beta4 2016-02-05 09:11:37 -05:00
denny
ba952af721 Added support for parsing additional Packet Data Unit unrecognized types
and removed logging of unrecognized PDUC messages.
2016-02-05 09:10:23 -05:00
denny
d9be346d7f Added new message for Packet Data Unit Confirmed messages with unknown
PDUType.
2016-02-04 21:14:16 -05:00
denny
133adec27a Adds support for PacketData messages that were being flagged as Packet
Data Unit - Confirmed unknown message types.
2016-02-04 20:51:13 -05:00
denny
981a8fec55 Removes logger from TrafficChannelManager that was logging when a
traffic channel is allocated but fails to get a source and is
subsequently torn down.  Logging was ambiguous and unneeded.
2016-02-04 20:50:17 -05:00
denny
9c94266adb Resolves issue with Audio Channel display showing a call in progress
with TO/FROM identifiers displayed, but no audio playing.  This occurred
when the decoder sent a few audio buffers which caused the display to
update from the metadata, however the decoder stopped sending buffers
and the audio start threshold is never met.  Clear display is invoked
upon audio stop, so the panel was never started or stopped or cleared
until the next call came in.

Changed to store the incoming metadata and only display it once the
audio playback starts and reset the display once the audio stops.
2016-02-04 20:49:11 -05:00
denny
4460f1ee30 Suppresses logging of attempts to change to invalid channel state. The
state enumeration is designed to block these attempts which can occur
when a non-standard call sequence of events occurs, normally due to
intermittent decoding.
2016-02-02 20:14:33 -05:00
denny
ddfefe4b85 Resolves issue with ChannelState FADE monitor - changes from
starting/stopping the monitor for each call to simply leaving the
monitor task running continuously.  Was causing issue with scheduled
thread executor not shutting down the task in a timely manner -- new
approach should avoid start/stop related issues altogether.

Updates P25DecoderState with reordering of timing of when we send
updates for the TO/FROM identifiers so that the audio manager receives
up-to-date metadata and hopefully resolves issue with 'Do Not Monitor'
talkgroups being monitored by the audio manager.

Updates the AudioManager with a try/catch block around the processing
task so that we can catch any audio processing exceptions that occur
during the timer task and might get suppressed by the scheduled thread
executor.
2016-02-02 19:44:18 -05:00
Denny Sheirer
debdc39a5f Resolves threading issue in ChannelState.
Resolves issue in tuner controller where controller was not releasing
sourced tuner channels, preventing re-tuning to other frequencies.
Resolves issue with channel timeout on MPT1327 monitored traffic
channels
2016-02-01 09:17:56 +00:00
Denny Sheirer
6f76f6fca4 Resolves issue with Tuner retaining released TunerChannels, preventing
the tuner from sourcing additional out-of-band channels.
2016-01-30 16:58:52 +00:00
denny
1290b8c4d3 Adds VSELP message and unknown Data Unit ID handling to P25 decoder. 2016-01-28 19:18:05 -05:00
denny
b95b3ff415 Updated readme 2016-01-23 21:19:54 -05:00
735 changed files with 75408 additions and 59799 deletions

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="lib" path="imports/commons-lang3-3.2.1.jar"/>
<classpathentry kind="lib" path="imports/jide-oss-3.5.15.jar"/>
<classpathentry kind="lib" path="imports/jsr80-1.0.1.jar"/>
@@ -25,5 +24,11 @@
<classpathentry kind="lib" path="imports/logback-core-1.1.2.jar"/>
<classpathentry kind="lib" path="imports/commons-io-2.4.jar"/>
<classpathentry kind="lib" path="imports/jmbe-interface-0.3.2.jar"/>
<classpathentry kind="lib" path="imports/tablefilter-swing-5.2.1.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/home/denny/Downloads/tablefilter-swing-5.2.1-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>

7
.gitignore vendored
View File

@@ -1,3 +1,6 @@
/*.class
*.class
/classes*
/product*
/product*
/bin*
.directory
.settings

9
.idea/codeStyleSettings.xml generated Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectCodeStyleSettingsManager">
<option name="PER_PROJECT_SETTINGS">
<value />
</option>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default (1)" />
</component>
</project>

22
.idea/compiler.xml generated Normal file
View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<resourceExtensions />
<wildcardResourcePatterns>
<entry name="!?*.java" />
<entry name="!?*.form" />
<entry name="!?*.class" />
<entry name="!?*.groovy" />
<entry name="!?*.scala" />
<entry name="!?*.flex" />
<entry name="!?*.kt" />
<entry name="!?*.clj" />
<entry name="!?*.aj" />
</wildcardResourcePatterns>
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
</annotationProcessing>
</component>
</project>

9
.idea/copyright/profiles_settings.xml generated Normal file
View File

@@ -0,0 +1,9 @@
<component name="CopyrightManager">
<settings default="sdrtrunk">
<LanguageOptions name="__TEMPLATE__">
<option name="addBlankAfter" value="false" />
<option name="separateBefore" value="true" />
<option name="separateAfter" value="true" />
</LanguageOptions>
</settings>
</component>

6
.idea/copyright/sdrtrunk.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<component name="CopyrightManager">
<copyright>
<option name="myName" value="sdrtrunk" />
<option name="notice" value="sdrtrunk &#10;Copyright (C) 2014-&amp;#36;today.year Dennis Sheirer&#10;&#10;This program is free software: you can redistribute it and/or modify&#10;it under the terms of the GNU General Public License as published by&#10;the Free Software Foundation, either version 3 of the License, or&#10;(at your option) any later version.&#10;&#10;This program is distributed in the hope that it will be useful,&#10;but WITHOUT ANY WARRANTY; without even the implied warranty of&#10;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the&#10;GNU General Public License for more details.&#10; &#10;You should have received a copy of the GNU General Public License&#10;along with this program. If not, see &lt;http://www.gnu.org/licenses/&gt;&#10;" />
</copyright>
</component>

25
.idea/dictionaries/denny.xml generated Normal file
View File

@@ -0,0 +1,25 @@
<component name="ProjectDictionaryState">
<dictionary name="denny">
<words>
<w>bahroo</w>
<w>broadcastify</w>
<w>esnid</w>
<w>fleetsync</w>
<w>icecast</w>
<w>ident</w>
<w>overflowable</w>
<w>polyphase</w>
<w>remez</w>
<w>samplerate</w>
<w>sdrtrunk</w>
<w>sheirer</w>
<w>shoutcast</w>
<w>streamable</w>
<w>talkgroup</w>
<w>tgid</w>
<w>ultravox</w>
<w>wacn</w>
<w>xtea</w>
</words>
</dictionary>
</component>

35
.idea/libraries/imports.xml generated Normal file
View File

@@ -0,0 +1,35 @@
<component name="libraryTable">
<library name="imports">
<CLASSES>
<root url="jar://$PROJECT_DIR$/imports/logback-classic-1.1.2.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/usb-api-1.0.2.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/commons-io-2.4.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/commons-lang3-3.2.1.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/tablefilter-swing-5.2.1.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/libusb4java-1.2.0-linux-x86.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/JLargeArrays-1.2.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/libusb4java-1.2.0-linux-ppc.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/libusb4java-1.2.0-windows-x86.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/libusb4java-1.2.0-osx-x86.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/miglayout-4.0-swing.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/libusb4java-1.2.0-windows-x86_64.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/usb4java-1.2.0.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/jide-oss-3.5.15.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/libusb4java-1.2.0-linux-x86_64.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/logback-core-1.1.2.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/usb4java-javax-1.2.0.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/jsr80-1.0.1.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/libusb4java-1.2.0-linux-arm.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/libusb4java-1.2.0-osx-x86_64.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/JTransforms-3.0.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/jmbe-interface-0.3.3.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/java-lame-3.98.4.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/commons-codec-1.9.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/commons-logging-1.2.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/slf4j-api-1.7.21.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/libusb4java-1.2.0-linux-aarch64.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@@ -0,0 +1,20 @@
<component name="libraryTable">
<library name="org.asynchttpclient:async-http-client:2.0.18" type="repository">
<properties maven-id="org.asynchttpclient:async-http-client:2.0.18" />
<CLASSES>
<root url="jar://$PROJECT_DIR$/imports/slf4j-api-1.7.21.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/javassist-3.20.0-GA.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/axis.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/jaxrpc.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/commons-discovery-0.2.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/activation-1.1.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/java-mail-1.4.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/wsdl4j-1.5.1.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/radioreference-14.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/mina-core-2.0.16.jar!/" />
<root url="jar://$PROJECT_DIR$/imports/mina-http-2.0.16.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

44
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ClientPropertiesManager">
<properties class="javax.swing.AbstractButton">
<property name="hideActionText" class="java.lang.Boolean" />
</properties>
<properties class="javax.swing.JComponent">
<property name="html.disable" class="java.lang.Boolean" />
</properties>
<properties class="javax.swing.JEditorPane">
<property name="JEditorPane.w3cLengthUnits" class="java.lang.Boolean" />
<property name="JEditorPane.honorDisplayProperties" class="java.lang.Boolean" />
<property name="charset" class="java.lang.String" />
</properties>
<properties class="javax.swing.JList">
<property name="List.isFileList" class="java.lang.Boolean" />
</properties>
<properties class="javax.swing.JPasswordField">
<property name="JPasswordField.cutCopyAllowed" class="java.lang.Boolean" />
</properties>
<properties class="javax.swing.JSlider">
<property name="Slider.paintThumbArrowShape" class="java.lang.Boolean" />
<property name="JSlider.isFilled" class="java.lang.Boolean" />
</properties>
<properties class="javax.swing.JTable">
<property name="Table.isFileList" class="java.lang.Boolean" />
<property name="JTable.autoStartsEdit" class="java.lang.Boolean" />
<property name="terminateEditOnFocusLost" class="java.lang.Boolean" />
</properties>
<properties class="javax.swing.JToolBar">
<property name="JToolBar.isRollover" class="java.lang.Boolean" />
</properties>
<properties class="javax.swing.JTree">
<property name="JTree.lineStyle" class="java.lang.String" />
</properties>
<properties class="javax.swing.text.JTextComponent">
<property name="caretAspectRatio" class="java.lang.Double" />
<property name="caretWidth" class="java.lang.Integer" />
</properties>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/sdrtrunk.iml" filepath="$PROJECT_DIR$/sdrtrunk.iml" />
</modules>
</component>
</project>

124
.idea/uiDesigner.xml generated Normal file
View File

@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -5,6 +5,11 @@
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.xtext.ui.shared.xtextBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
@@ -13,5 +18,6 @@
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.xtext.ui.shared.xtextNature</nature>
</natures>
</projectDescription>

View File

@@ -1,11 +1,12 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.7
org.eclipse.jdt.core.compiler.source=1.8

View File

@@ -1,13 +1,36 @@
0.2.0 2015xxxx
0.3.0-beta1 20170129
- REQUIRES: Java 8 (or newer) for compatibility and eventual changeover to JavaFX
- FEATURE: audio streaming - adds capability to stream to multiple remote broadcast
servers (Broadcastify, Icecast v2.3 and v2.4+, and Shoutcast V1 and V2). Each
stream can be independently configured with a delay. Audio calls are directed
to one or more streams by attaching one or more stream identifiers to each alias.
- Extensive GUI updates for tuner, channel and alias management. Includes new
'Now Playing' channel view and 'Detail' view.
- Updated alias list management of alias id values and wildcards. Weighting is now
applied to each wildcard so that they can be ordered from most specific to least
specific for lookup value matching purposes.
- Added handling for Motorola-specific P25 patch messages and auto-generation of
patch group aliases that roll-up aliases from each patched talkgroup to support
audio recording and streaming of patch calls according to the identifiers and
actions defined for each of the patched talkgroups.
- Implemented bounded queues for both tuner and individual channel IQ sample
buffers. Bounded queues have ability to enter/exit overflow state when queue
size reaches overflow threshold and reset when queue size falls below the reset
threshold. Channel state will show OVERFLOW in red/yellow.
- Improves handling of P25 TDMA channels (NO Phase 2 decoder support yet)
- Updated airspy controller for airspy-mini specific sample rate handling.
- New developer remez filter designer tool and internal usage
0.2.0 20160329
- Major overhaul of underlying processing chain and extensive updates to all
decoders and filters
- New audio playback interface with global mute and support for one or two
audio channels
- Audio playback is prioritized using a new Call Priority alias action
- Audio playback is prioritized using a new Call Priority name action
with selected channels having the highest priority and any audio without
an alias identifier having the lowest priority.
- Updated Audio recording per call with new 'Non-Recordable' alias action
an name identifier having the lowest priority.
- Updated Audio recording per call with new 'Non-Recordable' name action
- Updated Baseband (I/Q) recording at 48 kHz
- Added Airspy tuner support
- New traffic channel pool manager for trunked call following

View File

@@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
{one line to give the program's name and a brief idea of what it does.}
Copyright (C) {year} {name of author}
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
@@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
{project} Copyright (C) {year} {fullname}
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
@@ -671,4 +671,4 @@ into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View File

@@ -2,18 +2,10 @@
A cross-platform java application for decoding, monitoring, recording and streaming trunked mobile and related radio protocols using Software Defined Radios (SDR).
* [Getting Started](https://github.com/DSheirer/sdrtrunk/wiki/GettingStarted)
* [User's Manual](https://github.com/DSheirer/sdrtrunk/wiki/UserManual)
* [Download](https://drive.google.com/folderview?id=0B7BHsssXUq8eOHBTNndCczZTd0k&usp=sharing)
* [User's Manual Version 0.2.0](https://github.com/DSheirer/sdrtrunk/wiki/UserManual)
* [User's Manual Version 0.3.0](https://github.com/DSheirer/sdrtrunk/wiki/UserManual_Version0.3.0)
* [Download](https://github.com/DSheirer/sdrtrunk/releases)
* [Support](https://groups.google.com/forum/#!forum/sdrtrunk)
![](https://github.com/DSheirer/sdrtrunk/wiki/images/SDRTrunk_application.png)
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-67489234-1', 'auto');
ga('send', 'pageview');
</script>
![sdrtrunk Application Overview - Version 0.3.0](https://github.com/DSheirer/sdrtrunk/wiki/images/ApplicationOverview_V0.3.0.png)
**Figure 1:** sdrtrunk **Version 0.3.0** Application Screenshot

View File

@@ -0,0 +1,39 @@
/* ....Show License.... */
.chart {
-fx-background-image: url("../../../../images/Curve-fitted-background.png");
-fx-padding: 15 25 15 15;
}
.chart-plot-background {
-fx-background-color: red;
-fx-background-image: url("../../../../images/Curve-fitted-chart-background.png"),
url("../../../../images/Curve-fitted-graph-gridlines.png");
-fx-background-size: cover, auto;
-fx-background-repeat: no-repeat, repeat;
-fx-background-position: 0% 0%, 0% 100%;
-fx-border-color: black black transparent transparent;
}
.chart-area-symbol {
-fx-background-color: white;
}
.chart-series-area-line {
-fx-stroke: white;
-fx-stroke-width: 2px;
}
.chart-series-area-fill {
-fx-fill: linear-gradient(to right, white, rgba(255,255,255,0));
-fx-blend-mode: OVERLAY;
}
.axis {
-fx-tick-mark-visible: false;
-fx-minor-tick-visible: false;
-fx-tick-length: 3;
-fx-minor-tick-length: 0;
-fx-border-color: transparent;
-fx-tick-label-fill: white;
}
.axis:bottom {
-fx-border-color: black transparent transparent transparent;
}
.axis:left {
-fx-border-color: transparent black transparent transparent;
}

View File

@@ -5,18 +5,17 @@
<!-- version properties -->
<property name="major" value="0"/>
<property name="minor" value="2"/>
<property name="minor" value="3"/>
<property name="patch" value="0"/>
<property name="release" value="beta.3"/>
<property name="filename" value="sdrtrunk_${major}.${minor}.${patch}-${release}"/>
<property name="release" value=""/>
<property name="filename" value="sdrtrunk_${major}.${minor}.${patch}${release}"/>
<!-- Build properties -->
<property name="repo" location=".."/>
<property name="source.code" location="${repo}/src"/>
<property name="source.configs" location="${repo}/config"/>
<property name="source.images" location="${repo}/images"/>
<property name="source.libs" location="${repo}/imports"/>
<property name="source.license" location="${repo}/license"/>
<property name="source.scripts" location="${repo}/scripts"/>
<property name="output" location="${repo}/product"/>
@@ -33,9 +32,9 @@
<target name="init">
<!-- Create the classes folder to contain the compiled java classes -->
<mkdir dir="${output.classes}"/>
<!-- Create version file -->
<echo message="${major}.${minor}.${patch}-${release}" file="${output.classes}/sdrtrunk-version"/>
<echo message="${major}.${minor}.${patch}-${release}" file="${output.classes}/sdrtrunk-version"/>
</target>
<target name="compile" depends="clean,init" description="Compile java classes" >
@@ -48,68 +47,68 @@
<!-- Create jar file -->
<jar jarfile="${output.folder}/sdrtrunk.jar" basedir="${output.classes}">
<manifest>
<manifest>
<attribute name="Title" value="sdrtrunk"/>
<attribute name="Main-Class" value="gui.SDRTrunk"/>
<attribute name="Version" value="${major}.${minor}.${patch}-${release}"/>
</manifest>
<fileset dir="${source.configs}">
<exclude name="*.rules"/>
</fileset>
<fileset dir="${source.license}">
<include name="license.txt"/>
</fileset>
<fileset dir="${source.configs}">
<exclude name="*.rules"/>
</fileset>
<fileset dir="${repo}">
<include name="LICENSE"/>
</fileset>
</jar>
<!-- Copy third-party libraries -->
<mkdir dir="${output.libs}"/>
<copy todir="${output.libs}">
<fileset dir="${source.libs}" includes="*.jar" />
<fileset dir="${source.libs}" includes="*.jar" />
</copy>
<!-- Copy config files -->
<mkdir dir="${output.config}"/>
<copy todir="${output.config}">
<fileset dir="${source.configs}">
<include name="*.rules"/>
</fileset>
<fileset dir="${source.configs}">
<include name="*.rules"/>
</fileset>
</copy>
<!-- Copy image files -->
<mkdir dir="${output.images}"/>
<copy todir="${output.images}">
<fileset dir="${source.images}" />
<fileset dir="${source.images}" />
</copy>
<!-- Copy license file -->
<copy todir="${output.folder}">
<fileset dir="${source.license}">
<include name="license.txt"/>
</fileset>
<fileset dir="${repo}">
<include name="LICENSE"/>
</fileset>
</copy>
<!-- Copy program scripts -->
<copy todir="${output.folder}">
<fileset dir="${source.scripts}" />
<fileset dir="${source.scripts}" />
</copy>
<!-- Bundle the files into .zip -->
<zip destfile="${output}/${filename}.zip" basedir="${output}" />
<!-- Bundle the files into .zip -->
<zip destfile="${output}/${filename}.zip" basedir="${output}" />
<!-- Remove linux start script before tar task -->
<!-- Remove linux start script before tar task -->
<delete file="${output.folder}/run_sdrtrunk_linux.sh"/>
<!-- Bundle files into .tar.gz and copy/set permissions on linux start script -->
<tar destfile="${output}/${filename}.tar.gz" compression="gzip">
<tarfileset dir="${output.folder}" prefix="sdrtrunk" />
<tarfileset dir="${source.scripts}" prefix="sdrtrunk" filemode="777">
<include name="run_sdrtrunk_linux.sh"/>
</tarfileset>
</tar>
<!-- Bundle files into .tar.gz and copy/set permissions on linux start script -->
<tar destfile="${output}/${filename}.tar.gz" compression="gzip">
<tarfileset dir="${output.folder}" prefix="sdrtrunk" />
<tarfileset dir="${source.scripts}" prefix="sdrtrunk" filemode="777">
<include name="run_sdrtrunk_linux.sh"/>
</tarfileset>
</tar>
<delete dir="${output.folder}"/>
</target>

View File

@@ -27,4 +27,7 @@
<appender-ref ref="STDOUT" />
</root>
<!-- Apache Mina TCP/HTTP library -->
<logger name="org.apache.mina" level="INFO" />
</configuration>

View File

@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2015 Dennis Sheirer
* 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 B

BIN
images/broadcastify.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
images/control_play.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

BIN
images/control_stop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 B

BIN
images/icecast.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 688 B

After

Width:  |  Height:  |  Size: 743 B

BIN
images/shoutcast.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
imports/activation-1.1.jar Normal file

Binary file not shown.

BIN
imports/axis.jar Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
imports/java-mail-1.4.jar Normal file

Binary file not shown.

Binary file not shown.

BIN
imports/jaxrpc.jar Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
imports/wsdl4j-1.5.1.jar Normal file

Binary file not shown.

2
scripts/run_sdrtrunk_linux.sh Normal file → Executable file
View File

@@ -1,2 +1,2 @@
#!/bin/bash
java -XX:+UseG1GC -cp "*:libs/*" gui.SDRTrunk&
java -XX:+UseG1GC -cp "*:libs/*" gui.SDRTrunk

View File

@@ -1 +1 @@
java -XX:+UseG1GC -cp "sdrtrunk.jar;*;libs/*;config/*;images/*" gui.SDRTrunk
java -XX:+UseG1GC -cp "SDRTrunk.jar;*;libs/*;config/*;images/*" gui.SDRTrunk

25
sdrtrunk.iml Normal file
View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/images" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/config" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$" type="java-resource" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="imports" level="project" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../jmbe/jmbe/library/jmbe-0.3.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="library" name="org.asynchttpclient:async-http-client:2.0.18" level="project" />
</component>
</module>

View File

@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
* 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
@@ -19,32 +19,51 @@ package alias;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlTransient;
import alias.action.AliasAction;
import alias.priority.Priority;
import audio.inverted.AudioType;
import alias.id.AliasID;
import alias.id.AliasIDType;
import alias.id.broadcast.BroadcastChannel;
import alias.id.nonrecordable.NonRecordable;
import alias.id.priority.Priority;
@XmlSeeAlso( { AliasID.class, AliasAction.class } )
@XmlRootElement( name = "alias" )
public class Alias
{
private String mList;
private String mGroup;
private String mName;
private int mColor;
private String mIconName;
private ArrayList<AliasID> mAliasIDs = new ArrayList<AliasID>();
private List<AliasID> mAliasIDs = new ArrayList<AliasID>();
private List<AliasAction> mAliasActions = new ArrayList<AliasAction>();
public Alias()
{
}
public Alias( String name )
{
mName = name;
}
public String toString()
{
return getName();
}
@XmlAttribute
@XmlAttribute( name="name" )
public String getName()
{
return mName;
@@ -55,6 +74,38 @@ public class Alias
mName = name;
}
@XmlAttribute( name="list" )
public String getList()
{
return mList;
}
public void setList( String list )
{
mList = list;
}
public boolean hasList()
{
return mList != null;
}
@XmlAttribute( name="group" )
public String getGroup()
{
return mGroup;
}
public void setGroup( String group )
{
mGroup = group;
}
public boolean hasGroup()
{
return mGroup != null;
}
@XmlAttribute
public int getColor()
{
@@ -66,9 +117,9 @@ public class Alias
mColor = color;
}
public Color getMapColor()
public Color getDisplayColor()
{
return new Color( mColor );
return new Color( getColor() );
}
@XmlAttribute
@@ -82,7 +133,8 @@ public class Alias
mIconName = iconName;
}
public ArrayList<AliasID> getId()
@XmlElement( name="id" )
public List<AliasID> getId()
{
return mAliasIDs;
}
@@ -102,6 +154,7 @@ public class Alias
mAliasIDs.remove( id );
}
@XmlElement( name="action" )
public List<AliasAction> getAction()
{
return mAliasActions;
@@ -126,41 +179,22 @@ public class Alias
{
return !mAliasActions.isEmpty();
}
public boolean hasAudioType()
{
for( AliasID id: mAliasIDs )
{
if( id.hasAudioType() )
{
return true;
}
}
return false;
}
public AudioType getAudioType()
{
for( AliasID id: mAliasIDs )
{
if( id.hasAudioType() )
{
return id.getAudioType();
}
}
return AudioType.NORMAL;
}
/**
* Perform any validation/cleanup actions on this alias.
*/
public void validate()
{
}
/**
* Returns the priority level of this alias, if defined, or the default priority
*/
@XmlTransient
public int getCallPriority()
{
for( AliasID id: mAliasIDs )
{
if( id.getType() == AliasIDType.Priority )
if( id.getType() == AliasIDType.PRIORITY)
{
return ((Priority)id).getPriority();
}
@@ -169,11 +203,11 @@ public class Alias
return Priority.DEFAULT_PRIORITY;
}
public boolean hasPriority()
public boolean hasCallPriority()
{
for( AliasID id: mAliasIDs )
{
if( id.getType() == AliasIDType.Priority )
if( id.getType() == AliasIDType.PRIORITY)
{
return true;
}
@@ -181,15 +215,40 @@ public class Alias
return false;
}
/**
* Sets or updates the call priority
*/
public void setCallPriority( int priority )
{
if( priority == Priority.DO_NOT_MONITOR ||
( Priority.MIN_PRIORITY <= priority && priority <= Priority.MAX_PRIORITY ) )
{
for( AliasID id: mAliasIDs )
{
if( id.getType() == AliasIDType.PRIORITY)
{
((Priority)id).setPriority( priority );
return;
}
}
//If we don't find a priority id, create one
Priority p = new Priority();
p.setPriority( priority );
addAliasID( p );
}
}
/**
* Inspects the alias for a non-recordable alias id. Default is true;
*/
@XmlTransient
public boolean isRecordable()
{
for( AliasID id: getId() )
{
if( id.getType() == AliasIDType.NonRecordable )
if( id.getType() == AliasIDType.NON_RECORDABLE)
{
return false;
}
@@ -197,4 +256,99 @@ public class Alias
return true;
}
/**
* Sets or removes the non-recordable alias ID for this alias.
*/
public void setRecordable( boolean recordable )
{
if( recordable )
{
AliasID toRemove = null;
for( AliasID id: getId() )
{
if( id.getType() == AliasIDType.NON_RECORDABLE)
{
toRemove = id;
break;
}
}
if( toRemove != null )
{
removeAliasID( toRemove );
}
}
else
{
for( AliasID id: getId() )
{
if( id.getType() == AliasIDType.NON_RECORDABLE)
{
return;
}
}
addAliasID( new NonRecordable() );
}
}
/**
* Inspects the alias for broadcast channel/streamable alias ids. Default is false;
*/
@XmlTransient
public boolean isStreamable()
{
for( AliasID id: getId() )
{
if( id.getType() == AliasIDType.BROADCAST_CHANNEL)
{
return true;
}
}
return false;
}
/**
* List of broadcast channels specified for this alias.
*/
@XmlTransient
public Set<BroadcastChannel> getBroadcastChannels()
{
Set<BroadcastChannel> broadcastChannels = new TreeSet<>();
for(AliasID id: getId())
{
if(id.getType() == AliasIDType.BROADCAST_CHANNEL)
{
broadcastChannels.add((BroadcastChannel)id);
}
}
return broadcastChannels;
}
/**
* Indicates if this alias contains a broadcast channel alias id with the channel name.
*/
public boolean hasBroadcastChannel(String channel)
{
if(channel == null || channel.isEmpty())
{
return false;
}
for(AliasID id: getId())
{
if(id.getType() == AliasIDType.BROADCAST_CHANNEL &&
((BroadcastChannel)id).getChannelName().contentEquals(channel))
{
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,356 @@
package alias;
import audio.broadcast.BroadcastModel;
import com.jidesoft.swing.JideSplitPane;
import gui.editor.Editor;
import icon.IconManager;
import net.coderazzi.filters.gui.AutoChoices;
import net.coderazzi.filters.gui.TableFilterHeader;
import net.miginfocom.swing.MigLayout;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;
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 AliasModel mAliasModel;
private JTable mAliasTable;
private TableFilterHeader mTableFilterHeader;
private AliasEditor mAliasEditor;
private MultipleAliasEditor mMultipleAliasEditor;
private JideSplitPane mSplitPane;
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;
public AliasController(AliasModel aliasModel, BroadcastModel broadcastModel, IconManager iconManager)
{
mAliasModel = aliasModel;
mAliasEditor = new AliasEditor(mAliasModel, broadcastModel, iconManager);
mMultipleAliasEditor = new MultipleAliasEditor(mAliasModel, broadcastModel, iconManager);
mIconCellRenderer = new IconCellRenderer(iconManager);
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);
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<Alias> selectedAliases = new ArrayList<Alias>();
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)
{
//HACK: when inserting a row to the model, the JTable gets
//notified and attempts to tell the coderazzi table filter
//adaptive choices filter to refresh before the table filter is
//notified of the row additions, causing an index out of bounds
//exception. We turn off adaptive choices temporarily, add the
//channel, and turn on adaptive choices again.
mTableFilterHeader.setAdaptiveChoices(false);
int index = mAliasModel.addAlias(alias);
mTableFilterHeader.setAdaptiveChoices(true);
if(index >= 0)
{
int translatedIndex = mAliasTable.convertRowIndexToView(index);
mAliasTable.setRowSelectionInterval(translatedIndex, translatedIndex);
mAliasTable.scrollRectToVisible(
new Rectangle(mAliasTable.getCellRect(translatedIndex, 0, true)));
}
}
private List<Alias> getSelectedAliases()
{
List<Alias> 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<Alias> 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;
}
}
}

View File

@@ -1,91 +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 <http://www.gnu.org/licenses/>
******************************************************************************/
package alias;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import controller.ConfigurableNode;
public class AliasDirectoryNode extends ConfigurableNode
{
private static final long serialVersionUID = 1L;
public AliasDirectoryNode( AliasDirectory directory )
{
super( directory );
}
public AliasDirectory getAliasDirectory()
{
return (AliasDirectory)getUserObject();
}
public void init()
{
for( AliasList list: getAliasDirectory().getAliasList() )
{
AliasListNode node = new AliasListNode( list );
getModel().addNode( node, AliasDirectoryNode.this, getChildCount() );
node.init();
}
sort();
}
public String toString()
{
return "Alias Lists [" + this.getChildCount() + "]";
}
public JPopupMenu getContextMenu()
{
JPopupMenu retVal = new JPopupMenu();
JMenuItem addItem = new JMenuItem( "Add Alias List" );
addItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
AliasList list = new AliasList();
getAliasDirectory().addAliasList( list );
AliasListNode node = new AliasListNode( list );
getModel().addNode( node,
AliasDirectoryNode.this,
AliasDirectoryNode.this.getChildCount() );
sort();
getModel().showNode( node );
}
} );
retVal.add( addItem );
return retVal;
}
}

View File

@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
* 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
@@ -17,189 +17,148 @@
******************************************************************************/
package alias;
import audio.broadcast.BroadcastModel;
import gui.editor.Editor;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.tree.TreePath;
import javax.swing.JSeparator;
import map.MapIcon;
import map.MapIconListCellRenderer;
import icon.IconManager;
import net.miginfocom.swing.MigLayout;
import sample.Listener;
import settings.SettingsManager;
import controller.ConfigurableNode;
import controller.ResourceManager;
import alias.action.AliasActionEditor;
import alias.id.AliasIdentifierEditor;
public class AliasEditor extends JPanel implements ActionListener
import com.jidesoft.swing.JideTabbedPane;
public class AliasEditor extends Editor<Alias>
implements ActionListener, Listener<AliasEvent>
{
private static final long serialVersionUID = 1L;
private AliasNode mAliasNode;
private ResourceManager mResourceManager;
private JLabel mLabelName;
private JTextField mTextAlias;
private JLabel mLabelMapColor;
private JButton mButtonColor;
private JComboBox<MapIcon> mComboMapIcon;
private JButton mBtnIconManager;
public AliasEditor( AliasNode aliasNode, ResourceManager resourceManager )
{
mAliasNode = aliasNode;
mResourceManager = resourceManager;
initGUI();
}
private static final String DEFAULT_NAME = "select an alias";
private AliasModel mAliasModel;
private void initGUI()
private JLabel mAliasLabel;
private AliasNameEditor mAliasNameEditor;
private AliasIdentifierEditor mAliasIdentifierEditor;
private AliasActionEditor mAliasActionEditor;
public AliasEditor(AliasModel aliasModel, BroadcastModel broadcastModel, IconManager iconManager )
{
setLayout( new MigLayout( "", "[right][grow,fill]", "[][][][][][grow][]" ) );
mAliasModel = aliasModel;
mAliasNameEditor = new AliasNameEditor( mAliasModel, iconManager );
mAliasIdentifierEditor = new AliasIdentifierEditor( aliasModel, broadcastModel );
mAliasActionEditor = new AliasActionEditor( aliasModel );
mAliasModel.addListener( this );
setBorder( BorderFactory.createTitledBorder( "Alias" ) );
init();
}
mLabelName = new JLabel( "Name:" );
add( mLabelName );
@Override
public void setItem( Alias alias )
{
super.setItem( alias );
mTextAlias = new JTextField( mAliasNode.getAlias().getName() );
add( mTextAlias, "wrap" );
mLabelMapColor = new JLabel( "Map Color:" );
add( mLabelMapColor );
mButtonColor = new JButton( "Map Color" );
mButtonColor.setBackground( mAliasNode.getAlias().getMapColor() );
mButtonColor.setForeground( mAliasNode.getAlias().getMapColor() );
mButtonColor.addActionListener( new ActionListener()
if( hasItem() )
{
@Override
public void actionPerformed( ActionEvent e )
{
Color newColor = JColorChooser.showDialog(
AliasEditor.this,
"Choose color for map labels and routes",
mAliasNode.getAlias().getMapColor() );
mButtonColor.setForeground( newColor );
mButtonColor.setBackground( newColor );
}
} );
add( mButtonColor, "wrap" );
add( new JLabel( "Icon:" ) );
mComboMapIcon = new JComboBox<MapIcon>(
mResourceManager.getSettingsManager().getMapIcons() );
MapIconListCellRenderer renderer = new MapIconListCellRenderer();
renderer.setPreferredSize( new Dimension( 200, 30 ) );
mComboMapIcon.setRenderer( renderer );
String iconName = mAliasNode.getAlias().getIconName();
if( iconName == null )
mAliasLabel.setText( getItem().getName() );
}
else
{
iconName = SettingsManager.DEFAULT_ICON;
mAliasLabel.setText( DEFAULT_NAME );
}
MapIcon savedIcon = mResourceManager.getSettingsManager()
.getMapIcon( iconName );
mAliasNameEditor.setItem( alias );
mAliasIdentifierEditor.setItem( alias );
mAliasActionEditor.setItem( alias );
}
if( savedIcon != null )
{
mComboMapIcon.setSelectedItem( savedIcon );
}
add( mComboMapIcon, "wrap" );
//Dummy place holder
add( new JLabel() );
private void init()
{
setLayout( new MigLayout( "fill,wrap 2", "[grow,fill][grow,fill]",
"[][][][][][grow,fill]" ) );
mBtnIconManager = new JButton( "Icon Manager" );
mBtnIconManager.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent arg0 )
{
mResourceManager.getController().showIconManager();
}
} );
add( mBtnIconManager, "span 2,wrap" );
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 )
{
String command = e.getActionCommand();
if( command.contentEquals( "Save" ) )
if( hasItem() )
{
String alias = mTextAlias.getText();
String command = e.getActionCommand();
if( alias != null )
if( command.contentEquals( "Save" ) )
{
boolean expanded = mAliasNode.getModel().getTree()
.isExpanded( new TreePath( mAliasNode ) );
mAliasNode.getAlias().setName( alias );
((ConfigurableNode)mAliasNode.getParent()).sort();
mAliasNode.getAlias().setColor(
mButtonColor.getBackground().getRGB() );
MapIcon selectedIcon = (MapIcon)mComboMapIcon.getSelectedItem();
if( selectedIcon != null )
{
mAliasNode.getAlias().setIconName( selectedIcon.getName() );
}
mAliasNode.save();
mAliasNode.show();
if( expanded )
{
mAliasNode.getModel().getTree()
.expandPath( new TreePath( mAliasNode ) );
}
save();
}
else
else if( command.contentEquals( "Reset" ) )
{
JOptionPane.showMessageDialog( AliasEditor.this, "Please enter an alias name" );
reset();
}
}
else if( command.contentEquals( "Reset" ) )
{
mTextAlias.setText( mAliasNode.getAlias().getName() );
mButtonColor.setBackground( mAliasNode.getAlias().getMapColor() );
mButtonColor.setForeground( mAliasNode.getAlias().getMapColor() );
MapIcon savedIcon = mResourceManager.getSettingsManager()
.getMapIcon( mAliasNode.getAlias().getIconName() );
mComboMapIcon.setSelectedItem( savedIcon );
}
mAliasNode.refresh();
}
@Override
public void save()
{
mAliasNameEditor.save();
mAliasIdentifierEditor.save();
mAliasActionEditor.save();
}
}

View File

@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
* 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
@@ -17,25 +17,39 @@
******************************************************************************/
package alias;
import audio.inverted.AudioType;
public abstract class AliasID
public class AliasEvent
{
public AliasID()
private Alias mAlias;
private Event mEvent;
/**
* AliasEvent - event describing any changes to an alias
* @param alias - alias that changed
* @param event - change event
*/
public AliasEvent( Alias alias, Event event )
{
mAlias = alias;
mEvent = event;
}
public abstract AliasIDType getType();
public boolean hasAudioType()
public Alias getAlias()
{
return false;
return mAlias;
}
public AudioType getAudioType()
public Event getEvent()
{
return AudioType.NORMAL;
return mEvent;
}
public abstract boolean matches( AliasID id );
/**
* Channel events to describe the specific event
*/
public enum Event
{
ADD,
CHANGE,
DELETE;
}
}

304
src/alias/AliasFactory.java Normal file
View File

@@ -0,0 +1,304 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
******************************************************************************/
package alias;
import alias.action.AliasAction;
import alias.action.AliasActionType;
import alias.action.beep.BeepAction;
import alias.action.beep.BeepActionEditor;
import alias.action.clip.ClipAction;
import alias.action.clip.ClipActionEditor;
import alias.action.script.ScriptAction;
import alias.action.script.ScriptActionEditor;
import alias.id.AliasID;
import alias.id.AliasIDType;
import alias.id.broadcast.BroadcastChannel;
import alias.id.broadcast.BroadcastChannelEditor;
import alias.id.esn.ESNEditor;
import alias.id.esn.Esn;
import alias.id.fleetsync.FleetsyncID;
import alias.id.fleetsync.FleetsyncIDEditor;
import alias.id.lojack.LoJackFunctionAndID;
import alias.id.lojack.LoJackIDEditor;
import alias.id.mdc.MDC1200ID;
import alias.id.mdc.MDC1200IDEditor;
import alias.id.mobileID.MINEditor;
import alias.id.mobileID.Min;
import alias.id.mpt1327.MPT1327ID;
import alias.id.mpt1327.MPT1327IDEditor;
import alias.id.nonrecordable.NonRecordable;
import alias.id.nonrecordable.NonRecordableEditor;
import alias.id.priority.Priority;
import alias.id.priority.PriorityEditor;
import alias.id.siteID.SiteID;
import alias.id.siteID.SiteIDEditor;
import alias.id.status.StatusID;
import alias.id.status.StatusIDEditor;
import alias.id.talkgroup.TalkgroupID;
import alias.id.talkgroup.TalkgroupIDEditor;
import alias.id.uniqueID.UniqueID;
import alias.id.uniqueID.UniqueIDEditor;
import audio.broadcast.BroadcastModel;
import gui.editor.Editor;
import gui.editor.EmptyEditor;
public class AliasFactory
{
public static AliasID copyOf( AliasID id )
{
switch( id.getType() )
{
case BROADCAST_CHANNEL:
BroadcastChannel originalBroadcast = (BroadcastChannel)id;
BroadcastChannel copyBroadcast = new BroadcastChannel();
copyBroadcast.setChannelName(originalBroadcast.getChannelName());
return copyBroadcast;
case ESN:
Esn originalESN = (Esn)id;
Esn copyESN = new Esn();
copyESN.setEsn( originalESN.getEsn() );
return copyESN;
case FLEETSYNC:
FleetsyncID originalFleetsyncID = (FleetsyncID)id;
FleetsyncID copyFleetsyncID = new FleetsyncID();
copyFleetsyncID.setIdent( originalFleetsyncID.getIdent() );
return copyFleetsyncID;
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();
copyLoJackFunctionAndID.setFunction( originalLoJackFunctionAndID.getFunction() );
copyLoJackFunctionAndID.setID( originalLoJackFunctionAndID.getID() );
return copyLoJackFunctionAndID;
case MDC1200:
MDC1200ID originalMDC1200ID = (MDC1200ID)id;
MDC1200ID copyMDC1200ID = new MDC1200ID();
copyMDC1200ID.setIdent( originalMDC1200ID.getIdent() );
return copyMDC1200ID;
case MIN:
Min originalMin = (Min)id;
Min copyMin = new Min();
copyMin.setMin( originalMin.getMin() );
return copyMin;
case MPT1327:
MPT1327ID originalMPT1327ID = (MPT1327ID)id;
MPT1327ID copyMPT1327ID = new MPT1327ID();
copyMPT1327ID.setIdent( originalMPT1327ID.getIdent() );
return copyMPT1327ID;
case NON_RECORDABLE:
return new NonRecordable();
case PRIORITY:
Priority originalPriority = (Priority)id;
Priority copyPriority = new Priority();
copyPriority.setPriority( originalPriority.getPriority() );
return copyPriority;
case SITE:
SiteID originalSiteID = (SiteID)id;
SiteID copySiteID = new SiteID();
copySiteID.setSite( originalSiteID.getSite() );
return copySiteID;
case TALKGROUP:
TalkgroupID originalTalkgroupID = (TalkgroupID)id;
TalkgroupID copyTalkgroupID = new TalkgroupID();
copyTalkgroupID.setTalkgroup( originalTalkgroupID.getTalkgroup() );
return copyTalkgroupID;
case STATUS:
StatusID originalStatusID = (StatusID)id;
StatusID copyStatusID = new StatusID();
copyStatusID.setStatus( originalStatusID.getStatus() );
return copyStatusID;
default:
}
return null;
}
public static AliasAction copyOf( AliasAction action )
{
if( action instanceof BeepAction )
{
return new BeepAction();
}
else if( action instanceof ClipAction )
{
ClipAction originalClip = (ClipAction)action;
ClipAction copyClip = new ClipAction();
copyClip.setInterval( originalClip.getInterval() );
copyClip.setPath( originalClip.getPath() );
copyClip.setPeriod( originalClip.getPeriod() );
return copyClip;
}
else if( action instanceof ScriptAction )
{
ScriptAction originalScript = (ScriptAction)action;
ScriptAction copyScript = new ScriptAction();
copyScript.setInterval( originalScript.getInterval() );
copyScript.setPeriod( originalScript.getPeriod() );
copyScript.setScript( originalScript.getScript() );
return copyScript;
}
return null;
}
public static Alias copyOf( Alias original )
{
Alias copy = new Alias( original.getName() );
copy.setList( original.getList() );
copy.setGroup( original.getGroup() );
copy.setColor( original.getColor() );
copy.setIconName( original.getIconName() );
for( AliasID id: original.getId() )
{
AliasID copyID = copyOf( id );
if( copyID != null )
{
copy.addAliasID( copyID );
}
}
for( AliasAction action: original.getAction() )
{
AliasAction copyAction = copyOf( action );
if( copyAction != null )
{
copy.addAliasAction( copyAction );
}
}
return copy;
}
public static Editor<AliasID> 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 FLEETSYNC:
return new FleetsyncIDEditor( aliasID );
case LTR_NET_UID:
return new UniqueIDEditor( aliasID );
case LOJACK:
return new LoJackIDEditor( aliasID );
case MDC1200:
return new MDC1200IDEditor( aliasID );
case MIN:
return new MINEditor( aliasID );
case MPT1327:
return new MPT1327IDEditor( aliasID );
case NON_RECORDABLE:
return new NonRecordableEditor( aliasID );
case PRIORITY:
return new PriorityEditor( aliasID );
case SITE:
return new SiteIDEditor( aliasID );
case STATUS:
return new StatusIDEditor( aliasID );
case TALKGROUP:
return new TalkgroupIDEditor( aliasID );
default:
break;
}
}
return new EmptyEditor<AliasID>();
}
public static Editor<AliasAction> 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<AliasAction>();
}
public static AliasID getAliasID( AliasIDType type )
{
switch( type )
{
case BROADCAST_CHANNEL:
return new BroadcastChannel();
case ESN:
return new Esn();
case FLEETSYNC:
return new FleetsyncID();
case LTR_NET_UID:
return new UniqueID();
case LOJACK:
return new LoJackFunctionAndID();
case MDC1200:
return new MDC1200ID();
case MIN:
return new Min();
case MPT1327:
return new MPT1327ID();
case NON_RECORDABLE:
return new NonRecordable();
case PRIORITY:
return new Priority();
case SITE:
return new SiteID();
case STATUS:
return new StatusID();
case TALKGROUP:
return new TalkgroupID();
default:
throw new IllegalArgumentException( "Unrecognized Alias ID type: " + type );
}
}
public static AliasAction getAliasAction( AliasActionType type )
{
switch( type )
{
case BEEP:
return new BeepAction();
case CLIP:
return new ClipAction();
case SCRIPT:
return new ScriptAction();
default:
throw new IllegalArgumentException( "Unrecognized Alias Action type: " + type );
}
}
}

View File

@@ -1,35 +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 <http://www.gnu.org/licenses/>
******************************************************************************/
package alias;
import controller.ConfigurableNode;
public class AliasIDNode extends ConfigurableNode
{
private static final long serialVersionUID = 1L;
public AliasIDNode( AliasID id )
{
super( id );
}
public void save()
{
getModel().getResourceManager().getPlaylistManager().save();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,126 +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 <http://www.gnu.org/licenses/>
******************************************************************************/
package alias;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import controller.ConfigurableNode;
public class AliasListNode extends ConfigurableNode
{
private static final long serialVersionUID = 1L;
public AliasListNode( AliasList list )
{
super( list );
}
@Override
public JPanel getEditor()
{
return new ListEditor( this );
}
public AliasList getList()
{
return (AliasList)getUserObject();
}
public void init()
{
for( Group group: getList().getGroup() )
{
GroupNode node = new GroupNode( group );
getModel().addNode( node, AliasListNode.this, getChildCount() );
node.init();
}
sort();
}
public String toString()
{
return getList().getName() + " [" + this.getChildCount() + "]";
}
public JPopupMenu getContextMenu()
{
JPopupMenu retVal = new JPopupMenu();
JMenuItem addItem = new JMenuItem( "Add Group" );
addItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
Group group = new Group();
getList().addGroup( group );
GroupNode node = new GroupNode( group );
getModel().addNode( node,
AliasListNode.this,
AliasListNode.this.getChildCount() );
sort();
getModel().showNode( node );
}
} );
retVal.add( addItem );
retVal.add( new JSeparator() );
JMenuItem deleteItem = new JMenuItem( "Delete" );
deleteItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
int n = JOptionPane.showConfirmDialog( getModel().getTree(),
"Are you sure you want to permanently delete this node?" );
if( n == JOptionPane.YES_OPTION )
{
AliasDirectoryNode parent = (AliasDirectoryNode)getParent();
parent.getAliasDirectory().removeAliasList( getList() );
save();
getModel().removeNodeFromParent( AliasListNode.this );
}
}
} );
retVal.add( deleteItem );
return retVal;
}
}

350
src/alias/AliasModel.java Normal file
View File

@@ -0,0 +1,350 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
******************************************************************************/
package alias;
import alias.AliasEvent.Event;
import alias.id.broadcast.BroadcastChannel;
import sample.Broadcaster;
import sample.Listener;
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
*/
public class AliasModel extends AbstractTableModel
{
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<Alias> mAliases = new CopyOnWriteArrayList<>();
private Broadcaster<AliasEvent> mAliasEventBroadcaster = new Broadcaster<>();
private Map<String,AliasList> mAliasListMap = new HashMap<>();
public AliasModel()
{
}
/**
* Unmodifiable list of all aliases currently in the model
*/
public List<Alias> getAliases()
{
return Collections.unmodifiableList(mAliases);
}
public Alias getAliasAtIndex(int row)
{
if(mAliases.size() >= row)
{
return mAliases.get(row);
}
return null;
}
public AliasList getAliasList(String name)
{
if(name != null && mAliasListMap.containsKey(name))
{
return mAliasListMap.get(name);
}
AliasList aliasList = new AliasList(name);
if(name != null)
{
for(Alias alias : mAliases)
{
if(alias.hasList() && alias.getList().equalsIgnoreCase(name))
{
aliasList.addAlias(alias);
}
}
mAliasListMap.put(name, aliasList);
//Register the new alias list to receive updates from this model
addListener(aliasList);
}
return aliasList;
}
/**
* Returns a list of unique alias list names from across the alias set
*/
public List<String> getListNames()
{
List<String> listNames = new ArrayList<>();
for(Alias alias : mAliases)
{
if(alias.hasList() && !listNames.contains(alias.getList()))
{
listNames.add(alias.getList());
}
}
Collections.sort(listNames);
return listNames;
}
/**
* Returns a list of alias group names for all aliases
*/
public List<String> getGroupNames()
{
List<String> groupNames = new ArrayList<>();
for(Alias alias : mAliases)
{
if(alias.hasGroup() && !groupNames.contains(alias.getGroup()))
{
groupNames.add(alias.getGroup());
}
}
Collections.sort(groupNames);
return groupNames;
}
/**
* Returns a list of alias group names for all aliases that have a matching
* list name value
*/
public List<String> getGroupNames(String listName)
{
List<String> 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
*/
public void addAliases(List<Alias> aliases)
{
for(Alias alias : aliases)
{
addAlias(alias);
}
}
/**
* Adds the alias to the model
*/
public int addAlias(Alias alias)
{
if(alias != null)
{
mAliases.add(alias);
int index = mAliases.size() - 1;
fireTableRowsInserted(index, index);
broadcast(new AliasEvent(alias, Event.ADD));
return index;
}
return -1;
}
/**
* Removes the channel from the model and broadcasts a channel remove event
*/
public void removeAlias(Alias alias)
{
if(alias != null)
{
int index = mAliases.indexOf(alias);
mAliases.remove(alias);
fireTableRowsDeleted(index, index);
broadcast(new AliasEvent(alias, Event.DELETE));
}
}
/**
* Renames any broadcast channels that have the previous name.
*
* @param previousName to rename
* @param newName to assign to the broadcast channel
*/
public void renameBroadcastChannel(String previousName, String newName)
{
if(previousName == null || previousName.isEmpty() || newName == null || newName.isEmpty())
{
return;
}
for(Alias alias : mAliases)
{
if(alias.hasBroadcastChannel(previousName))
{
for(BroadcastChannel broadcastChannel : alias.getBroadcastChannels())
{
if(broadcastChannel.getChannelName().contentEquals(previousName))
{
broadcastChannel.setChannelName(newName);
}
}
}
}
}
@Override
public int getRowCount()
{
return mAliases.size();
}
@Override
public int getColumnCount()
{
return 5;
}
@Override
public String getColumnName(int columnIndex)
{
switch(columnIndex)
{
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 null;
}
@Override
public Class<?> getColumnClass(int columnIndex)
{
if(columnIndex == COLUMN_COLOR)
{
return Integer.class;
}
return String.class;
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex)
{
return false;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
Alias alias = mAliases.get(rowIndex);
switch(columnIndex)
{
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 null;
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex)
{
throw new IllegalArgumentException("Not yet implemented");
}
public void broadcast(AliasEvent event)
{
Alias alias = event.getAlias();
//Validate the alias following a user action that changed the alias or any alias IDs
if(alias != null)
{
alias.validate();
}
if(event.getEvent() == Event.CHANGE)
{
int index = mAliases.indexOf(event.getAlias());
fireTableRowsUpdated(index, index);
}
mAliasEventBroadcaster.broadcast(event);
}
public void addListener(Listener<AliasEvent> listener)
{
mAliasEventBroadcaster.addListener(listener);
}
public void removeListener(Listener<AliasEvent> listener)
{
mAliasEventBroadcaster.removeListener(listener);
}
}

View File

@@ -0,0 +1,334 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package alias;
import alias.AliasEvent.Event;
import gui.editor.Editor;
import icon.Icon;
import icon.IconListCellRenderer;
import icon.IconManager;
import 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<Alias>
{
private static final long serialVersionUID = 1L;
private static ComboBoxModel<String> EMPTY_LIST_MODEL = new DefaultComboBoxModel<>();
private static ComboBoxModel<String> EMPTY_GROUP_MODEL = new DefaultComboBoxModel<>();
private JComboBox<String> mListCombo = new JComboBox<>(EMPTY_LIST_MODEL);
private JComboBox<String> mGroupCombo = new JComboBox<>(EMPTY_GROUP_MODEL);
private JComboBox<Icon> 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<String> groups = mAliasModel
.getGroupNames((String) mListCombo.getSelectedItem());
if(groups.isEmpty())
{
mGroupCombo.setModel(EMPTY_GROUP_MODEL);
}
else
{
mGroupCombo.setModel(new DefaultComboBoxModel<String>(
groups.toArray(new String[groups.size()])));
;
}
}
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<Icon>(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<String> listNames = mAliasModel.getListNames();
if(listNames.isEmpty())
{
mListCombo.setModel(EMPTY_LIST_MODEL);
}
else
{
mListCombo.setModel(new DefaultComboBoxModel<String>(
listNames.toArray(new String[listNames.size()])));
;
}
mListCombo.setSelectedItem(alias.getList());
List<String> groupNames = mAliasModel.getGroupNames(alias.getList());
if(groupNames.isEmpty())
{
mGroupCombo.setModel(EMPTY_GROUP_MODEL);
}
else
{
mGroupCombo.setModel(new DefaultComboBoxModel<String>(
groupNames.toArray(new String[groupNames.size()])));
;
}
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(), Event.CHANGE));
}
}
}

View File

@@ -1,562 +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 <http://www.gnu.org/licenses/>
******************************************************************************/
package alias;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import map.MapIcon;
import alias.action.AliasAction;
import alias.action.beep.BeepAction;
import alias.action.beep.BeepActionNode;
import alias.action.clip.ClipAction;
import alias.action.clip.ClipActionNode;
import alias.action.script.ScriptAction;
import alias.action.script.ScriptActionNode;
import alias.id.esn.ESNNode;
import alias.id.esn.Esn;
import alias.id.fleetsync.FleetsyncID;
import alias.id.fleetsync.FleetsyncIDNode;
import alias.id.fleetsync.StatusID;
import alias.id.fleetsync.StatusIDNode;
import alias.id.lojack.LoJackFunctionAndID;
import alias.id.lojack.LoJackIDNode;
import alias.id.mdc.MDC1200ID;
import alias.id.mdc.MDC1200IDNode;
import alias.id.mobileID.MINNode;
import alias.id.mobileID.Min;
import alias.id.mpt1327.MPT1327ID;
import alias.id.mpt1327.MPT1327IDNode;
import alias.id.siteID.SiteID;
import alias.id.siteID.SiteIDNode;
import alias.id.talkgroup.TalkgroupID;
import alias.id.talkgroup.TalkgroupIDNode;
import alias.id.uniqueID.UniqueID;
import alias.id.uniqueID.UniqueIDNode;
import alias.priority.Priority;
import alias.priority.PriorityNode;
import alias.record.NonRecordable;
import alias.record.NonRecordableNode;
import controller.ConfigurableNode;
public class AliasNode extends ConfigurableNode
{
private static final long serialVersionUID = 1L;
public AliasNode( Alias alias )
{
super( alias );
}
@Override
public JPanel getEditor()
{
return new AliasEditor( this, getModel().getResourceManager() );
}
public Alias getAlias()
{
return (Alias)getUserObject();
}
public String getIconPath()
{
String icon = getAlias().getIconName();
MapIcon mapIcon = getModel().getResourceManager()
.getSettingsManager().getMapIcon( icon );
if( mapIcon != null )
{
return mapIcon.getPath();
}
else
{
return null;
}
}
public void init()
{
for( AliasID aliasID: getAlias().getId() )
{
switch( aliasID.getType() )
{
case ESN:
getModel().addNode( new ESNNode( (Esn)aliasID ),
AliasNode.this, getChildCount() );
break;
case Fleetsync:
getModel().addNode( new FleetsyncIDNode( (FleetsyncID)aliasID ),
AliasNode.this, getChildCount() );
break;
case LoJack:
getModel().addNode( new LoJackIDNode( (LoJackFunctionAndID)aliasID ),
AliasNode.this, getChildCount() );
break;
case LTRNetUID:
getModel().addNode( new UniqueIDNode( (UniqueID)aliasID ),
AliasNode.this, getChildCount() );
break;
case MDC1200:
getModel().addNode( new MDC1200IDNode( (MDC1200ID)aliasID ),
AliasNode.this, getChildCount() );
break;
case MIN:
getModel().addNode( new MINNode( (Min)aliasID ),
AliasNode.this, getChildCount() );
break;
case MPT1327:
getModel().addNode( new MPT1327IDNode( (MPT1327ID)aliasID ),
AliasNode.this, getChildCount() );
break;
case NonRecordable:
getModel().addNode( new NonRecordableNode( (NonRecordable)aliasID ),
AliasNode.this, getChildCount() );
break;
case Priority:
getModel().addNode( new PriorityNode( (Priority)aliasID ),
AliasNode.this, getChildCount() );
break;
case Site:
getModel().addNode( new SiteIDNode( (SiteID)aliasID ),
AliasNode.this, getChildCount() );
break;
case Status:
getModel().addNode( new StatusIDNode( (StatusID)aliasID ),
AliasNode.this, getChildCount() );
break;
case Talkgroup:
getModel().addNode( new TalkgroupIDNode( (TalkgroupID)aliasID ),
AliasNode.this, getChildCount() );
break;
default:
throw new IllegalArgumentException( "Unrecognized alias ID [" +
aliasID.getType() + "]");
}
}
for( AliasAction action: getAlias().getAction() )
{
if( action instanceof BeepAction )
{
getModel().addNode( new BeepActionNode( (BeepAction)action ),
AliasNode.this, getChildCount() );
}
else if( action instanceof ClipAction )
{
getModel().addNode( new ClipActionNode( (ClipAction)action ),
AliasNode.this, getChildCount() );
}
else if( action instanceof ScriptAction )
{
getModel().addNode( new ScriptActionNode( (ScriptAction)action ),
AliasNode.this, getChildCount() );
}
}
sort();
}
public String toString()
{
return getAlias().getName() + " [" + this.getChildCount() + "]";
}
public JPopupMenu getContextMenu()
{
JPopupMenu retVal = new JPopupMenu();
JMenu addIDMenu = new JMenu( "Add ID" );
JMenuItem addESNItem = new JMenuItem( "ESN" );
addESNItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
Esn esn = new Esn();
getAlias().addAliasID( esn );
ESNNode node = new ESNNode( esn );
getModel().addNode( node,
AliasNode.this,
AliasNode.this.getChildCount() );
node.show();
}
} );
addIDMenu.add( addESNItem );
JMenuItem addFleetsyncItem = new JMenuItem( "Fleetsync" );
addFleetsyncItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
FleetsyncID fs = new FleetsyncID();
getAlias().addAliasID( fs );
FleetsyncIDNode node = new FleetsyncIDNode( fs );
getModel().addNode( node,
AliasNode.this,
AliasNode.this.getChildCount() );
node.show();
}
} );
addIDMenu.add( addFleetsyncItem );
JMenuItem addLoJackItem = new JMenuItem( "LoJack" );
addLoJackItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
LoJackFunctionAndID lj = new LoJackFunctionAndID();
getAlias().addAliasID( lj );
LoJackIDNode node = new LoJackIDNode( lj );
getModel().addNode( node,
AliasNode.this,
AliasNode.this.getChildCount() );
node.show();
}
} );
addIDMenu.add( addLoJackItem );
JMenuItem addMDCItem = new JMenuItem( "MDC-1200" );
addMDCItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
MDC1200ID mdc = new MDC1200ID();
getAlias().addAliasID( mdc );
MDC1200IDNode node = new MDC1200IDNode( mdc );
getModel().addNode( node,
AliasNode.this,
AliasNode.this.getChildCount() );
node.show();
}
} );
addIDMenu.add( addMDCItem );
JMenuItem addMINItem = new JMenuItem( "MIN" );
addMINItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
Min min = new Min();
getAlias().addAliasID( min );
MINNode node = new MINNode( min );
getModel().addNode( node,
AliasNode.this,
AliasNode.this.getChildCount() );
node.show();
}
} );
addIDMenu.add( addMINItem );
JMenuItem addMPTItem = new JMenuItem( "MPT-1327" );
addMPTItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
MPT1327ID mpt = new MPT1327ID();
getAlias().addAliasID( mpt );
MPT1327IDNode node = new MPT1327IDNode( mpt );
getModel().addNode( node,
AliasNode.this,
AliasNode.this.getChildCount() );
node.show();
}
} );
addIDMenu.add( addMPTItem );
JMenuItem addSiteItem = new JMenuItem( "Site" );
addSiteItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
SiteID siteID = new SiteID();
getAlias().addAliasID( siteID );
SiteIDNode node = new SiteIDNode( siteID );
getModel().addNode( node,
AliasNode.this,
AliasNode.this.getChildCount() );
node.show();
}
} );
addIDMenu.add( addSiteItem );
JMenuItem addStatusItem = new JMenuItem( "Status" );
addStatusItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
StatusID statusID = new StatusID();
getAlias().addAliasID( statusID );
StatusIDNode node = new StatusIDNode( statusID );
getModel().addNode( node,
AliasNode.this,
AliasNode.this.getChildCount() );
node.show();
}
} );
addIDMenu.add( addStatusItem );
JMenuItem addTalkgroupItem = new JMenuItem( "Talkgroup" );
addTalkgroupItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
TalkgroupID tg = new TalkgroupID();
getAlias().addAliasID( tg );
TalkgroupIDNode node = new TalkgroupIDNode( tg );
getModel().addNode( node,
AliasNode.this,
AliasNode.this.getChildCount() );
node.show();
}
} );
addIDMenu.add( addTalkgroupItem );
JMenuItem addUniqueIDItem = new JMenuItem( "Unique ID" );
addUniqueIDItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
UniqueID uid = new UniqueID();
getAlias().addAliasID( uid );
UniqueIDNode node = new UniqueIDNode( uid );
getModel().addNode( node,
AliasNode.this,
AliasNode.this.getChildCount() );
node.show();
}
} );
addIDMenu.add( addUniqueIDItem );
retVal.add( addIDMenu );
JMenu addActionMenu = new JMenu( "Add Action" );
JMenuItem addClipItem = new JMenuItem( "Audio Clip" );
addClipItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
ClipAction clipAction = new ClipAction();
getAlias().addAliasAction( clipAction );
ClipActionNode node = new ClipActionNode( clipAction );
getModel().addNode( node,
AliasNode.this,
AliasNode.this.getChildCount() );
node.show();
}
} );
addActionMenu.add( addClipItem );
JMenuItem addBeepItem = new JMenuItem( "Beep" );
addBeepItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
BeepAction beepAction = new BeepAction();
getAlias().addAliasAction( beepAction );
BeepActionNode node = new BeepActionNode( beepAction );
getModel().addNode( node,
AliasNode.this,
AliasNode.this.getChildCount() );
node.show();
}
} );
addActionMenu.add( addBeepItem );
JMenuItem addScriptItem = new JMenuItem( "Script" );
addScriptItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
ScriptAction scriptAction = new ScriptAction();
getAlias().addAliasAction( scriptAction );
ScriptActionNode node = new ScriptActionNode( scriptAction );
getModel().addNode( node,
AliasNode.this,
AliasNode.this.getChildCount() );
node.show();
}
} );
addActionMenu.add( addScriptItem );
retVal.add( addActionMenu );
retVal.addSeparator();
if( getAlias().isRecordable() )
{
JMenuItem nonRecordableItem = new JMenuItem( "Set Non-Recordable" );
nonRecordableItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
NonRecordable non = new NonRecordable();
getAlias().addAliasID( non );
NonRecordableNode node = new NonRecordableNode( non );
getModel().addNode( node,
AliasNode.this,
AliasNode.this.getChildCount() );
node.show();
}
} );
retVal.add( nonRecordableItem );
retVal.addSeparator();
}
if( !getAlias().hasPriority() )
{
JMenuItem priorityItem = new JMenuItem( "Set Call Priority" );
priorityItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
Priority priority = new Priority();
getAlias().addAliasID( priority );
PriorityNode node = new PriorityNode( priority );
getModel().addNode( node,
AliasNode.this,
AliasNode.this.getChildCount() );
node.show();
}
} );
retVal.add( priorityItem );
retVal.addSeparator();
}
JMenuItem deleteItem = new JMenuItem( "Delete" );
deleteItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
int n = JOptionPane.showConfirmDialog( getModel().getTree(),
"Are you sure you want to permanently delete this node?" );
if( n == JOptionPane.YES_OPTION )
{
GroupNode parent = (GroupNode)getParent();
parent.getGroup().removeAlias( getAlias() );
save();
getModel().removeNodeFromParent( AliasNode.this );
}
}
} );
retVal.add( deleteItem );
return retVal;
}
}

View File

@@ -0,0 +1,121 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
******************************************************************************/
package alias;
import javax.swing.JPanel;
import javax.swing.event.DocumentListener;
public abstract class ComponentEditor<T> 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();
}
}

View File

@@ -0,0 +1,14 @@
package alias;
public class ComponentValidationException extends Exception
{
private static final long serialVersionUID = 1L;
/**
* Component editor contents validation error
*/
public ComponentValidationException( String message )
{
super( message );
}
}

View File

@@ -1,110 +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 <http://www.gnu.org/licenses/>
******************************************************************************/
package alias;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.tree.TreePath;
import net.miginfocom.swing.MigLayout;
import controller.ConfigurableNode;
public class GroupEditor extends JPanel implements ActionListener
{
private static final long serialVersionUID = 1L;
private GroupNode mGroupNode;
private JLabel mLabelName;
private JTextField mTextGroup;
public GroupEditor( GroupNode groupNode )
{
mGroupNode = groupNode;
initGUI();
}
private void initGUI()
{
setLayout( new MigLayout( "", "[right][grow,fill]", "[][][grow]" ) );
setBorder( BorderFactory.createTitledBorder( "Group" ) );
mLabelName = new JLabel( "Name:" );
add( mLabelName, "align right" );
mTextGroup = new JTextField( mGroupNode.getGroup().getName() );
add( mTextGroup, "wrap" );
JButton btnSave = new JButton( "Save" );
btnSave.addActionListener( GroupEditor.this );
add( btnSave );
JButton btnReset = new JButton( "Reset" );
btnReset.addActionListener( GroupEditor.this );
add( btnReset, "wrap" );
}
@Override
public void actionPerformed( ActionEvent e )
{
String command = e.getActionCommand();
if( command.contentEquals( "Save" ) )
{
String group = mTextGroup.getText();
if( group != null )
{
boolean expanded = mGroupNode.getModel().getTree()
.isExpanded( new TreePath( mGroupNode ) );
mGroupNode.getGroup().setName( group );
((ConfigurableNode)mGroupNode.getParent()).sort();
mGroupNode.save();
mGroupNode.show();
if( expanded )
{
mGroupNode.getModel().getTree()
.expandPath( new TreePath( mGroupNode ) );
}
}
else
{
JOptionPane.showMessageDialog( GroupEditor.this, "Please enter a group name" );
}
}
else if( command.contentEquals( "Reset" ) )
{
mTextGroup.setText( mGroupNode.getGroup().getName() );
}
mGroupNode.refresh();
}
}

View File

@@ -1,126 +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 <http://www.gnu.org/licenses/>
******************************************************************************/
package alias;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import controller.ConfigurableNode;
public class GroupNode extends ConfigurableNode
{
private static final long serialVersionUID = 1L;
public GroupNode( Group group )
{
super( group );
}
@Override
public JPanel getEditor()
{
return new GroupEditor( this );
}
public Group getGroup()
{
return (Group)getUserObject();
}
public void init()
{
for( Alias alias: getGroup().getAlias() )
{
AliasNode node = new AliasNode( alias );
getModel().addNode( node, GroupNode.this, getChildCount() );
node.init();
}
sort();
}
public String toString()
{
return getGroup().getName() + " [" + this.getChildCount() + "]";
}
public JPopupMenu getContextMenu()
{
JPopupMenu retVal = new JPopupMenu();
JMenuItem addItem = new JMenuItem( "Add Alias" );
addItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
Alias alias = new Alias();
getGroup().addAlias( alias );
AliasNode node = new AliasNode( alias );
getModel().addNode( node,
GroupNode.this,
GroupNode.this.getChildCount() );
sort();
getModel().showNode( node );
}
} );
retVal.add( addItem );
retVal.add( new JSeparator() );
JMenuItem deleteItem = new JMenuItem( "Delete" );
deleteItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
int n = JOptionPane.showConfirmDialog( getModel().getTree(),
"Are you sure you want to permanently delete this node?" );
if( n == JOptionPane.YES_OPTION )
{
AliasListNode parent = (AliasListNode)getParent();
parent.getList().removeGroup( getGroup() );
save();
getModel().removeNodeFromParent( GroupNode.this );
}
}
} );
retVal.add( deleteItem );
return retVal;
}
}

View File

@@ -1,107 +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 <http://www.gnu.org/licenses/>
******************************************************************************/
package alias;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.tree.TreePath;
import net.miginfocom.swing.MigLayout;
import controller.ConfigurableNode;
public class ListEditor extends JPanel implements ActionListener
{
private static final long serialVersionUID = 1L;
private AliasListNode mListNode;
private JLabel mLabelName;
private JTextField mTextGroup;
public ListEditor( AliasListNode listNode )
{
mListNode = listNode;
initGUI();
}
private void initGUI()
{
setLayout( new MigLayout( "", "[right][grow,fill]", "[][][grow]" ) );
setBorder( BorderFactory.createTitledBorder( "List" ) );
mLabelName = new JLabel( "Name:" );
add( mLabelName, "align right" );
mTextGroup = new JTextField( mListNode.getList().getName() );
add( mTextGroup, "grow, wrap" );
JButton btnSave = new JButton( "Save" );
btnSave.addActionListener( ListEditor.this );
add( btnSave );
JButton btnReset = new JButton( "Reset" );
btnReset.addActionListener( ListEditor.this );
add( btnReset, "wrap" );
}
@Override
public void actionPerformed( ActionEvent e )
{
String command = e.getActionCommand();
if( command.contentEquals( "Save" ) )
{
String group = mTextGroup.getText();
if( group != null )
{
boolean expanded = mListNode.getModel().getTree()
.isExpanded( new TreePath( mListNode ) );
mListNode.getList().setName( group );
((ConfigurableNode)mListNode.getParent()).sort();
mListNode.save();
mListNode.show();
if( expanded )
{
mListNode.getModel().getTree().expandPath( new TreePath( mListNode ) );
}
}
else
{
JOptionPane.showMessageDialog( ListEditor.this, "Please enter a list name" );
}
}
else if( command.contentEquals( "Reset" ) )
{
mTextGroup.setText( mListNode.getList().getName() );
}
mListNode.refresh();
}
}

View File

@@ -0,0 +1,467 @@
package alias;
import alias.AliasEvent.Event;
import alias.id.AliasIDType;
import alias.id.broadcast.BroadcastChannel;
import alias.id.priority.Priority;
import audio.broadcast.BroadcastModel;
import gui.editor.Editor;
import icon.Icon;
import icon.IconListCellRenderer;
import icon.IconManager;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
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<List<Alias>>
implements Listener<AliasEvent>
{
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger(MultipleAliasEditor.class);
private static String HELP_TEXT =
"<html>Select attributes below to change for all selected aliases</html>";
private JLabel mAliasCountLabel;
private JLabel mAliasCount;
private JCheckBox mListCheckBox = new JCheckBox("List");
private JComboBox<String> mListCombo = new JComboBox<>();
private JCheckBox mGroupCheckBox = new JCheckBox("Group");
private JComboBox<String> mGroupCombo = new JComboBox<>();
private JCheckBox mIconCheckBox = new JCheckBox("Icon");
private JComboBox<Icon> mIconCombo;
private JCheckBox mColorCheckBox = new JCheckBox("Color");
private JButton mButtonColor;
private JButton mBtnIconManager;
private JCheckBox mRecordCheckBox = new JCheckBox("Record");
private JComboBox<Record> 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<String> 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<Alias> getAliases()
{
if (hasItem())
{
return (List<Alias>) 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<Icon>(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<String>();
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<Alias> 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<String> listNames = mAliasModel.getListNames();
listNames.add(0, "");
mListCombo.setModel(new DefaultComboBoxModel<String>(
listNames.toArray(new String[listNames.size()])));
List<String> groupNames = mAliasModel.getGroupNames();
groupNames.add(0, "");
mGroupCombo.setModel(new DefaultComboBoxModel<String>(
groupNames.toArray(new String[groupNames.size()])));
List<String> streamNames = mBroadcastModel.getBroadcastConfigurationNames();
mStreamCombo.setModel(new DefaultComboBoxModel<String>(streamNames.toArray(new String[streamNames.size()])));
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<String> listNames = mAliasModel.getListNames();
listNames.add(0, "");
mListCombo.setModel(new DefaultComboBoxModel<String>(
listNames.toArray(new String[listNames.size()])));
List<String> groupNames = mAliasModel.getGroupNames();
groupNames.add(0, "");
mGroupCombo.setModel(new DefaultComboBoxModel<String>(
groupNames.toArray(new String[groupNames.size()])));
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<Alias> 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, Event.CHANGE));
}
}
}
@Override
public void receive(AliasEvent event)
{
if (event.getEvent() == 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;
}
}
}

View File

@@ -0,0 +1,381 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package alias;
import alias.action.AliasAction;
import alias.id.AliasID;
import alias.id.AliasIDType;
import alias.id.broadcast.BroadcastChannel;
import alias.id.priority.Priority;
import alias.id.talkgroup.TalkgroupID;
import gui.SDRTrunk;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import playlist.version1.System;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
public class PatchGroupAlias extends Alias
{
private final static Logger mLog = LoggerFactory.getLogger(PatchGroupAlias.class);
private Alias mPatchGroupAlias;
private List<Alias> mPatchedAliases = new ArrayList<>();
private List<String> mPatchedTalkgroupIDs = new ArrayList<>();
/**
* Patch group alias is a single alias for a group of aliases that are temporarily patched together. This will
* normally be used for patched talkgroups, but could be used with any type of alias identifiers that can be
* temporarily joined to form a patch group.
*
* Alias properties such as recordable, streamable and call priority are an aggregation from the set of patched
* aliases contained in this patch group alias and the patch group ID alias and where applicable, return the highest
* value/priority from the group of patched aliases.
*/
public PatchGroupAlias()
{
}
@Override
public int getColor()
{
if(mPatchGroupAlias != null)
{
return mPatchGroupAlias.getColor();
}
return super.getColor();
}
/**
* Sets an alias for this patch group ID
*/
public void setPatchGroupAlias(Alias alias)
{
mPatchGroupAlias = alias;
}
/**
* Alias for this patch group
*/
public Alias getPatchGroupAlias()
{
return mPatchGroupAlias;
}
/**
* Indicates if this patch group has an alias
*/
public boolean hasPatchGroupAlias()
{
return mPatchGroupAlias != null;
}
/**
* List of patched aliases contained in this patch group
*/
public List<Alias> getPatchedAliases()
{
return mPatchedAliases;
}
/**
* Removes/clears all patched aliases from this patch alias group
*/
public void clearPatchedAliases()
{
mPatchedAliases.clear();
}
/**
* Adds the patched alias to this patch alias group
*/
public void addPatchedAlias(Alias alias)
{
if(alias == PatchGroupAlias.this)
{
throw new IllegalArgumentException("Can't add patch group alias to itself - would create an infinite loop");
}
mPatchedAliases.add(alias);
}
/**
* Removes the patched alias from this patch alias group
*/
public void removePatchedAlias(Alias alias)
{
mPatchedAliases.remove(alias);
}
/**
* Sets the list of patched talkgroup IDs for this patch group
*/
public void setPatchedTalkgroupIDs(List<String> talkgroupIDs)
{
mPatchedTalkgroupIDs = talkgroupIDs;
}
/**
* List of patched talkgroup IDs for this patch group
*/
public List<String> getPatchedTalkgroupIDs()
{
return mPatchedTalkgroupIDs;
}
@Override
public String getName()
{
if(hasPatchGroupAlias())
{
return getPatchGroupAlias().getName();
}
else
{
for(AliasID id: getId())
{
if(id.getType() == AliasIDType.TALKGROUP)
{
return "PATCH:" + ((TalkgroupID)id).getTalkgroup();
}
}
}
return "PATCH:****";
}
@Override
public List<AliasAction> getAction()
{
List<AliasAction> aliasActions = new ArrayList<>();
if(hasPatchGroupAlias())
{
aliasActions.addAll(getPatchGroupAlias().getAction());
}
for(Alias alias: mPatchedAliases)
{
if(!(alias instanceof PatchGroupAlias))
{
for(AliasAction action: alias.getAction())
{
if(!aliasActions.contains(action))
{
aliasActions.add(action);
}
}
}
}
return aliasActions;
}
@Override
public boolean hasActions()
{
for(Alias alias: mPatchedAliases)
{
if(!(alias instanceof PatchGroupAlias) && alias.hasActions())
{
return true;
}
}
return hasPatchGroupAlias() && getPatchGroupAlias().hasActions();
}
/**
* Returns the highest listenable audio priority defined among the aliases in the group, or returns 'Do Not Monitor'
* if that is the only priority defined. Otherwise, returns default (100) priority.
*/
@Override
public int getCallPriority()
{
boolean hasDoNotMonitor = false;
int highestPriority = (hasPatchGroupAlias() ? getPatchGroupAlias().getCallPriority() : Priority.DEFAULT_PRIORITY + 1);
for(Alias alias: mPatchedAliases)
{
if(!(alias instanceof PatchGroupAlias) && alias.hasCallPriority() && alias.getCallPriority() < highestPriority)
{
if(alias.getCallPriority() == Priority.DO_NOT_MONITOR)
{
hasDoNotMonitor = true;
}
else
{
highestPriority = alias.getCallPriority();
}
}
}
if(highestPriority <= Priority.DEFAULT_PRIORITY)
{
return highestPriority;
}
else if(hasDoNotMonitor)
{
return Priority.DO_NOT_MONITOR;
}
else
{
return Priority.DEFAULT_PRIORITY;
}
}
@Override
public boolean hasCallPriority()
{
if(hasPatchGroupAlias() && getPatchGroupAlias().hasCallPriority())
{
return true;
}
for(Alias alias: mPatchedAliases)
{
if(!(alias instanceof PatchGroupAlias) && alias.hasCallPriority())
{
return true;
}
}
return false;
}
@Override
public boolean isRecordable()
{
if(hasPatchGroupAlias() && getPatchGroupAlias().isRecordable())
{
return true;
}
for(Alias alias: mPatchedAliases)
{
if(!(alias instanceof PatchGroupAlias) && alias.isRecordable())
{
return true;
}
}
return false;
}
@Override
public boolean isStreamable()
{
if(hasPatchGroupAlias() && getPatchGroupAlias().isStreamable())
{
return true;
}
for(Alias alias: mPatchedAliases)
{
if(!(alias instanceof PatchGroupAlias) && alias.isStreamable())
{
return true;
}
}
return false;
}
@Override
public Set<BroadcastChannel> getBroadcastChannels()
{
Set<BroadcastChannel> broadcastChannels = new TreeSet<>();
if(hasPatchGroupAlias())
{
broadcastChannels.addAll(getPatchGroupAlias().getBroadcastChannels());
}
for(Alias alias: mPatchedAliases)
{
if(!(alias instanceof PatchGroupAlias))
{
broadcastChannels.addAll(alias.getBroadcastChannels());
}
}
return broadcastChannels;
}
@Override
public boolean hasBroadcastChannel(String channel)
{
if(hasPatchGroupAlias() && getPatchGroupAlias().hasBroadcastChannel(channel))
{
return true;
}
for(Alias alias: mPatchedAliases)
{
if(!(alias instanceof PatchGroupAlias) && alias.hasBroadcastChannel(channel))
{
return true;
}
}
return false;
}
public static void main(String[] args)
{
Alias a1 = new Alias("Alias 1");
a1.addAliasID(new BroadcastChannel("Channel 1"));
a1.addAliasID(new BroadcastChannel("Channel 2"));
a1.addAliasID(new BroadcastChannel("Channel 3"));
a1.addAliasID(new BroadcastChannel("Channel 4"));
Alias a2 = new Alias("Alias 1");
a1.addAliasID(new BroadcastChannel("Channel 1"));
a1.addAliasID(new BroadcastChannel("Channel 2"));
a1.addAliasID(new BroadcastChannel("Channel 3"));
a1.addAliasID(new BroadcastChannel("Channel 4"));
Alias a3 = new Alias("Alias 1");
a1.addAliasID(new BroadcastChannel("Channel 1"));
a1.addAliasID(new BroadcastChannel("Channel 2"));
a1.addAliasID(new BroadcastChannel("Channel 3"));
a1.addAliasID(new BroadcastChannel("Channel 4"));
Alias a4 = new Alias("Alias 1");
a1.addAliasID(new BroadcastChannel("Channel 1"));
a1.addAliasID(new BroadcastChannel("Channel 2"));
a1.addAliasID(new BroadcastChannel("Channel 3"));
a1.addAliasID(new BroadcastChannel("Channel 4"));
PatchGroupAlias pga = new PatchGroupAlias();
pga.setPatchGroupAlias(a1);
pga.addPatchedAlias(a2);
pga.addPatchedAlias(a3);
pga.addPatchedAlias(a4);
Set<BroadcastChannel> bc = pga.getBroadcastChannels();
mLog.debug("Channels:" + bc);
}
}

View File

@@ -1,17 +1,47 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package alias.action;
import message.Message;
import alias.Alias;
import controller.ThreadPoolManager;
import alias.action.beep.BeepAction;
import alias.action.clip.ClipAction;
import alias.action.script.ScriptAction;
import message.Message;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlTransient;
import java.util.concurrent.ScheduledExecutorService;
/**
* Alias action defines an action to execute when an alias is detected active.
*/
@XmlSeeAlso( { BeepAction.class, ClipAction.class, ScriptAction.class } )
@XmlRootElement( name = "action" )
public abstract class AliasAction
{
public AliasAction()
{
}
@XmlTransient
public abstract AliasActionType getType();
/**
* Task to execute when an alias action is defined. The message argument is
@@ -19,9 +49,7 @@ public abstract class AliasAction
* action attached. The alias argument is the parent alias containing the
* alias action.
*/
public abstract void execute( ThreadPoolManager threadPoolManager,
Alias alias,
Message message );
public abstract void execute( ScheduledExecutorService scheduledExecutorService, Alias alias, Message message );
/**
* Dismiss a persistent alias action

View File

@@ -0,0 +1,350 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
******************************************************************************/
package alias.action;
import gui.editor.Editor;
import gui.editor.EmptyEditor;
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;
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 net.miginfocom.swing.MigLayout;
import alias.Alias;
import alias.AliasEvent;
import alias.AliasEvent.Event;
import alias.AliasFactory;
import alias.AliasModel;
public class AliasActionEditor extends Editor<Alias>
{
private static final long serialVersionUID = 1L;
private static ListModel<AliasAction> EMPTY_MODEL = new DefaultListModel<>();
private JList<AliasAction> 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<AliasAction> model = new DefaultListModel<>();
List<AliasAction> actions = alias.getAction();
Collections.sort( actions, new Comparator<AliasAction>()
{
@Override
public int compare( AliasAction o1, AliasAction o2 )
{
return o1.toString().compareTo( o2.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<AliasAction> 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 );
}
} );
}
}
}

View File

@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
* 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
@@ -15,11 +15,18 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package buffer;
package alias.action;
import java.nio.ByteBuffer;
import alias.AliasEvent;
public interface ByteBufferListener
/**
* Interface for receiving alias events. Each listener will receive
* notification of all alias events.
*/
public interface AliasActionListener
{
public void receive( ByteBuffer buffer );
/**
* Notifies the listener that an alias event has occurred
*/
public void aliasChanged( AliasEvent event );
}

View File

@@ -1,62 +1,69 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package alias.action;
import java.util.List;
import message.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import alias.Alias;
import controller.ResourceManager;
import message.Message;
import sample.Listener;
import util.ThreadPool;
import java.util.List;
/**
* Manages all alias action events. Each received message is interrogated for
* any alias entries and then each alias is interrogated for any alias actions.
*
* Each alias action is executed and provided a copy of the source message so
*
* Each alias action is executed and provided a copy of the source message so
* that the contents of the message can be used as part of the action.
*/
public class AliasActionManager implements Listener<Message>
{
private final static Logger mLog =
LoggerFactory.getLogger( AliasActionManager.class );
public AliasActionManager()
{
}
private ResourceManager mResourceManager;
public AliasActionManager( ResourceManager resourceManager )
{
mResourceManager = resourceManager;
}
@Override
public void receive( Message message )
{
if( message.isValid() )
{
List<Alias> aliases = message.getAliases();
if( aliases != null )
{
for( Alias alias: aliases )
{
if( alias.hasActions() )
{
List<AliasAction> actions = alias.getAction();
for( AliasAction action: actions )
{
/* Provide access to the thread pool manager in case the
@Override
public void receive(Message message)
{
if(message.isValid())
{
List<Alias> aliases = message.getAliases();
if(aliases != null)
{
for(Alias alias : aliases)
{
if(alias.hasActions())
{
List<AliasAction> actions = alias.getAction();
for(AliasAction action : actions)
{
/* Provide access to the thread pool manager in case the
* action needs to setup a timer, and provide the original
* message to be used as part of the action (e.g. sending
* the message as a text message to a cell phone */
action.execute( mResourceManager.getThreadPoolManager(),
alias, message );
}
}
}
}
}
}
action.execute(ThreadPool.SCHEDULED, alias, message);
}
}
}
}
}
}
}

View File

@@ -1,40 +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 <http://www.gnu.org/licenses/>
******************************************************************************/
package alias.action;
import controller.ConfigurableNode;
public class AliasActionNode extends ConfigurableNode
{
private static final long serialVersionUID = 1L;
public AliasActionNode( AliasAction action )
{
super( action );
}
public void save()
{
getModel().getResourceManager().getPlaylistManager().save();
}
public String getIconPath()
{
return "images/action.png";
}
}

View File

@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
* 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
@@ -15,12 +15,23 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.frequency;
package alias.action;
public interface FrequencyChangeBroadcaster
public enum AliasActionType
{
public abstract void addListener( FrequencyChangeListener listener );
BEEP( "Beep" ),
CLIP( "Play Clip" ),
SCRIPT( "Run Script" );
public abstract void removeListener( FrequencyChangeListener listener );
private String mLabel;
private AliasActionType( String label )
{
mLabel = label;
}
public String toString()
{
return mLabel;
}
}

View File

@@ -1,188 +1,181 @@
package alias.action;
import java.awt.EventQueue;
import alias.Alias;
import message.Message;
import javax.swing.*;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlTransient;
import java.awt.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.JOptionPane;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlTransient;
import message.Message;
import alias.Alias;
import controller.ThreadPoolManager;
import controller.ThreadPoolManager.ThreadType;
public abstract class RecurringAction extends AliasAction
{
@XmlTransient
protected AtomicBoolean mRunning = new AtomicBoolean( false );
@XmlTransient
private ScheduledFuture<?> mPerpetualAction;
protected Interval mInterval = Interval.ONCE;
protected int mPeriod = 5;
protected ThreadPoolManager mThreadPoolManager;
public abstract void performAction( Alias alias, Message message );
@XmlTransient
protected AtomicBoolean mRunning = new AtomicBoolean(false);
@XmlTransient
private ScheduledFuture<?> mPerpetualAction;
@XmlTransient
protected ScheduledExecutorService mScheduledExecutorService;
@Override
public void execute( ThreadPoolManager threadPoolManager,
Alias alias,
Message message )
{
mThreadPoolManager = threadPoolManager;
protected Interval mInterval = Interval.ONCE;
protected int mPeriod = 5;
if( mRunning.compareAndSet( false, true ) )
{
switch( mInterval )
{
case ONCE:
performThreadedAction( alias, message );
/* Don't reset */
break;
case DELAYED_RESET:
performThreadedAction( alias, message );
mThreadPoolManager.scheduleOnce( new ResetTask(),
mPeriod, TimeUnit.SECONDS );
break;
case UNTIL_DISMISSED:
mPerpetualAction = mThreadPoolManager
.scheduleFixedRate( ThreadType.DECODER,
new PerformActionTask( alias, message ),
mPeriod, TimeUnit.SECONDS );
public abstract void performAction(Alias alias, Message message);
StringBuilder sb = new StringBuilder();
sb.append( "<html><div width='250'>Alias [" );
sb.append( alias.getName() );
sb.append( "] is active in message [" );
sb.append( message.toString() );
sb.append( "]</div></html>" );
final String text = sb.toString();
EventQueue.invokeLater( new Runnable()
{
@Override
public void run()
{
JOptionPane.showMessageDialog( null, text,
"Alias Alert", JOptionPane.INFORMATION_MESSAGE );
dismiss( false );
mThreadPoolManager.scheduleOnce( new ResetTask(),
15, TimeUnit.SECONDS );
}
} );
break;
default:
}
}
}
@Override
public void execute(ScheduledExecutorService scheduledExecutorService, Alias alias, Message message)
{
mScheduledExecutorService = scheduledExecutorService;
/**
* Spawns the performAction() event into a new thread so that it doesn't
* delay any decoder actions.
*/
private void performThreadedAction( final Alias alias, final Message message )
{
mThreadPoolManager.scheduleOnce( new Runnable() {
if(mRunning.compareAndSet(false, true))
{
switch(mInterval)
{
case ONCE:
performThreadedAction(alias, message);
/* Don't reset */
break;
case DELAYED_RESET:
performThreadedAction(alias, message);
mScheduledExecutorService.schedule(new ResetTask(), mPeriod, TimeUnit.SECONDS);
break;
case UNTIL_DISMISSED:
mPerpetualAction = mScheduledExecutorService.scheduleAtFixedRate(
new PerformActionTask(alias, message), mPeriod, mPeriod, TimeUnit.SECONDS);
@Override
public void run()
{
performAction( alias, message );
}}, 0, TimeUnit.SECONDS );
}
@Override
public void dismiss( boolean reset )
{
if( mPerpetualAction != null )
{
mPerpetualAction.cancel( true );
mPerpetualAction = null;
}
if( reset )
{
mRunning.set( false );
}
}
@XmlAttribute
public int getPeriod()
{
return mPeriod;
}
public void setPeriod( int period )
{
mPeriod = period;
}
@XmlAttribute
public Interval getInterval()
{
return mInterval;
}
public void setInterval( Interval interval )
{
mInterval = interval;
mRunning.set( false );
}
public enum Interval
{
ONCE( "Once" ),
DELAYED_RESET( "Once, Reset After Delay" ),
UNTIL_DISMISSED( "Until Dismissed" );
private String mLabel;
private Interval( String label )
{
mLabel = label;
}
public String toString()
{
return mLabel;
}
}
public class PerformActionTask implements Runnable
{
private Alias mAlias;
private Message mMessage;
StringBuilder sb = new StringBuilder();
sb.append("<html><div width='250'>Alias [");
sb.append(alias.getName());
sb.append("] is active in message [");
sb.append(message.toString());
sb.append("]</div></html>");
public PerformActionTask( Alias alias, Message message )
{
mAlias = alias;
mMessage = message;
}
@Override
public void run()
{
final String text = sb.toString();
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
JOptionPane.showMessageDialog(null, text,
"Alias Alert", JOptionPane.INFORMATION_MESSAGE);
dismiss(false);
mScheduledExecutorService.schedule(new ResetTask(), 15, TimeUnit.SECONDS);
}
});
break;
default:
}
}
}
/**
* Spawns the performAction() event into a new thread so that it doesn't
* delay any decoder actions.
*/
private void performThreadedAction(final Alias alias, final Message message)
{
mScheduledExecutorService.schedule(new Runnable()
{
@Override
public void run()
{
performAction(alias, message);
}
}, 0, TimeUnit.SECONDS);
}
@Override
public void dismiss(boolean reset)
{
if(mPerpetualAction != null)
{
mPerpetualAction.cancel(true);
mPerpetualAction = null;
}
if(reset)
{
mRunning.set(false);
}
}
@XmlAttribute
public int getPeriod()
{
return mPeriod;
}
public void setPeriod(int period)
{
mPeriod = period;
}
@XmlAttribute
public Interval getInterval()
{
return mInterval;
}
public void setInterval(Interval interval)
{
mInterval = interval;
mRunning.set(false);
}
public enum Interval
{
ONCE("Once"),
DELAYED_RESET("Once, Reset After Delay"),
UNTIL_DISMISSED("Until Dismissed");
private String mLabel;
private Interval(String label)
{
mLabel = label;
}
public String toString()
{
return mLabel;
}
}
public class PerformActionTask implements Runnable
{
private Alias mAlias;
private Message mMessage;
public PerformActionTask(Alias alias, Message message)
{
mAlias = alias;
mMessage = message;
}
@Override
public void run()
{
/* Don't use the performThreadedAction() method, since this is
* already running in a separate thread */
performAction( mAlias, mMessage );
}
}
public class ResetTask implements Runnable
{
@Override
public void run()
{
mRunning.set( false );
}
}
performAction(mAlias, mMessage);
}
}
public class ResetTask implements Runnable
{
@Override
public void run()
{
mRunning.set(false);
}
}
}

View File

@@ -2,13 +2,27 @@ package alias.action.beep;
import message.Message;
import alias.Alias;
import alias.action.AliasActionType;
import alias.action.RecurringAction;
public class BeepAction extends RecurringAction
{
@Override
public AliasActionType getType()
{
return AliasActionType.BEEP;
}
@Override
public void performAction( Alias alias, Message message )
{
System.out.println( "\007" );
}
@Override
public String toString()
{
return "Beep";
}
}

View File

@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
* 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
@@ -17,59 +17,75 @@
******************************************************************************/
package alias.action.beep;
import gui.editor.DocumentListenerEditor;
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 javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JOptionPane;
import javax.swing.JSlider;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import alias.action.AliasAction;
import alias.action.RecurringAction.Interval;
public class BeepActionEditor extends JPanel implements ActionListener
public class BeepActionEditor extends DocumentListenerEditor<AliasAction>
{
private static final long serialVersionUID = 1L;
private final static Logger mLog =
LoggerFactory.getLogger( BeepActionEditor.class );
private BeepActionNode mBeepActionNode;
private JTextField mTextSiteID;
private JComboBox<Interval> mComboInterval;
private JSpinner mSpinnerPeriod;
private static final String HELP_TEXT =
"<html><h3>Beep Action</h3>"
+ "This action will sound a beep according to the interval<br>"
+ "that you select:<br><br>"
+ "<b>Once:</b> Beep the first time the alias is active and<br>"
+ "never again<br><br>"
+ "<b>Once, Reset After Delay:</b> Beep once and suppress<br>"
+ "subsequent beeps for the specified period in seconds.<br>"
+ "After the reset period, beep again when the alias is active.<br><br>"
+ "<b>Until Dismissed:</b> Beep every period seconds until you<br>"
+ "click OK on the dialog that appears. Alerting is suppressed<br>"
+ "for 15 seconds after you click OK.<br>"
+ "</html>";
public BeepActionEditor( BeepActionNode beepActionNode )
private JComboBox<Interval> mComboInterval;
private JSlider mPeriodSlider;
private JLabel mPeriodSliderLabel;
public BeepActionEditor( AliasAction aliasAction )
{
mBeepActionNode = beepActionNode;
initGUI();
init();
setItem( aliasAction );
}
private void initGUI()
public BeepAction getBeepAction()
{
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][][grow]" ) );
add( new JLabel( "Action: Beep" ), "span,align center" );
if( getItem() instanceof BeepAction )
{
return (BeepAction)getItem();
}
add( new JLabel( "Interval:" ) );
return null;
}
private void init()
{
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][]" ) );
add( new JLabel( "Beep Interval:" ) );
mComboInterval = new JComboBox<Interval>( Interval.values() );
mComboInterval.setSelectedItem( mBeepActionNode.getBeepAction().getInterval() );
mComboInterval.setToolTipText( HELP_TEXT );
mComboInterval.addActionListener( new ActionListener()
{
@Override
@@ -81,71 +97,97 @@ public class BeepActionEditor extends JPanel implements ActionListener
if( selected != null )
{
/* Enable/disable period spinner based on selection */
mSpinnerPeriod.setEnabled( selected != Interval.ONCE );
boolean enabled = selected != Interval.ONCE;
mBeepActionNode.getBeepAction().setInterval( selected );
mPeriodSlider.setEnabled( enabled );
mPeriodSliderLabel.setEnabled( enabled );
}
setModified( true );
}
});
add( mComboInterval, "wrap" );
final SpinnerModel model = new SpinnerNumberModel(
mBeepActionNode.getBeepAction().getPeriod(), 1, 30, 1 );
final SpinnerModel model = new SpinnerNumberModel( 1, 1, 30, 1 );
model.addChangeListener( new ChangeListener()
{
@Override
public void stateChanged( ChangeEvent e )
{
mBeepActionNode.getBeepAction().setPeriod( (int)model.getValue() );
setModified( true );
}
} );
mSpinnerPeriod = new JSpinner( model );
if( mBeepActionNode.getBeepAction().getInterval() == Interval.ONCE )
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()
{
mSpinnerPeriod.setEnabled( false );
@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();
Interval interval = beep.getInterval();
mComboInterval.setSelectedItem( interval );
boolean enabled = interval != Interval.ONCE;
mPeriodSliderLabel.setEnabled( enabled );
mPeriodSlider.setEnabled( enabled );
mPeriodSlider.setValue( beep.getPeriod() );
}
add( new JLabel( "Period:" ) );
add( mSpinnerPeriod, "wrap" );
StringBuilder sb = new StringBuilder();
sb.append( "This action will sound a beep according to the interval that you select.\n\n" );
sb.append( "Once - Beep the first time the alias is active and never again.\n\n" );
sb.append( "Once, Reset After Delay - Beep once and suppress subsequent beeps for the specified period in seconds. After the reset period, it will beep again when the alias is active.\n\n" );
sb.append( "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." );
JTextArea description = new JTextArea( sb.toString() );
description.setLineWrap( true );
description.setBackground( getBackground() );
add( description, "growx,span" );
JButton btnSave = new JButton( "Save" );
btnSave.addActionListener( BeepActionEditor.this );
add( btnSave, "growx,push" );
JButton btnReset = new JButton( "Reset" );
btnReset.addActionListener( BeepActionEditor.this );
add( btnReset, "growx,push" );
setModified( false );
}
@Override
public void actionPerformed( ActionEvent e )
{
String command = e.getActionCommand();
if( command.contentEquals( "Save" ) )
public void save()
{
if( hasItem() && isModified() )
{
mBeepActionNode.save();
mBeepActionNode.show();
BeepAction beep = getBeepAction();
beep.setInterval( (Interval)mComboInterval.getSelectedItem() );
beep.setPeriod( mPeriodSlider.getValue() );
}
mBeepActionNode.refresh();
}
setModified( false );
}
}

View File

@@ -1,88 +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 <http://www.gnu.org/licenses/>
******************************************************************************/
package alias.action.beep;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import alias.AliasNode;
import alias.action.AliasActionNode;
public class BeepActionNode extends AliasActionNode
{
private static final long serialVersionUID = 1L;
public BeepActionNode( BeepAction beepAction )
{
super( beepAction );
}
@Override
public JPanel getEditor()
{
return new BeepActionEditor( this );
}
public BeepAction getBeepAction()
{
return (BeepAction)getUserObject();
}
public String toString()
{
return "Action: Beep";
}
public JPopupMenu getContextMenu()
{
JPopupMenu retVal = new JPopupMenu();
JMenuItem deleteItem = new JMenuItem( "Delete" );
deleteItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
int n = JOptionPane.showConfirmDialog( getModel().getTree(),
"Are you sure you want to permanently delete this node?" );
if( n == JOptionPane.YES_OPTION )
{
AliasNode parent = (AliasNode)getParent();
parent.getAlias().removeAliasAction( getBeepAction() );
/* Save before removing the node ... so that we still have
* a reference to the parent's resources */
save();
getModel().removeNodeFromParent( BeepActionNode.this );
}
}
} );
retVal.add( deleteItem );
return retVal;
}
}

View File

@@ -13,6 +13,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import alias.Alias;
import alias.action.AliasActionType;
import alias.action.RecurringAction;
public class ClipAction extends RecurringAction
@@ -28,9 +29,14 @@ public class ClipAction extends RecurringAction
{
}
@Override
public AliasActionType getType()
{
return AliasActionType.CLIP;
}
public String getPath()
{
return mFilePath;
}
@@ -87,4 +93,18 @@ public class ClipAction extends RecurringAction
throw e;
}
}
@Override
public String toString()
{
if( mFilePath == null )
{
return "Play Clip";
}
else
{
return "Play Clip: " + mFilePath;
}
}
}

View File

@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
* 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
@@ -17,9 +17,14 @@
******************************************************************************/
package alias.action.clip;
import java.awt.EventQueue;
import gui.editor.DocumentListenerEditor;
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.io.File;
import javax.swing.JButton;
@@ -27,9 +32,7 @@ import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
@@ -37,43 +40,58 @@ import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import alias.action.AliasAction;
import alias.action.RecurringAction.Interval;
public class ClipActionEditor extends JPanel implements ActionListener
public class ClipActionEditor extends DocumentListenerEditor<AliasAction>
{
private static final long serialVersionUID = 1L;
private final static Logger mLog =
LoggerFactory.getLogger( ClipActionEditor.class );
private ClipActionNode mClipActionNode;
private static final String PLEASE_SELECT_A_FILE = "Please select a file";
private static final String HELP_TEXT =
"<html><h3>Clip Action</h3>"
+ "This action will play an audio clip according to the interval<br>"
+ "that you select:<br><br>"
+ "<b>Once:</b> play the first time the alias is active and<br>"
+ "never again<br><br>"
+ "<b>Once, Reset After Delay:</b> play once and suppress<br>"
+ "subsequent runs for the specified period in seconds.<br>"
+ "After the reset period, play again when the alias is active.<br><br>"
+ "<b>Until Dismissed:</b> play every period seconds until you<br>"
+ "click OK on the dialog that appears. Alerting is suppressed<br>"
+ "for 15 seconds after you click OK.<br>"
+ "</html>";
private JComboBox<Interval> mComboInterval;
private JSpinner mSpinnerPeriod;
private JSlider mPeriodSlider;
private JLabel mPeriodSliderLabel;
private JTextField mTextFilePath;
public ClipActionEditor( ClipActionNode clipActionNode )
public ClipActionEditor( AliasAction aliasAction )
{
mClipActionNode = clipActionNode;
initGUI();
init();
setItem( aliasAction );
}
private void initGUI()
public ClipAction getClipAction()
{
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][][grow]" ) );
add( new JLabel( "Action: Audio Clip" ), "span,align center" );
if( getItem() instanceof ClipAction )
{
return (ClipAction)getItem();
}
add( new JLabel( "Interval:" ) );
return null;
}
private void init()
{
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][]" ) );
add( new JLabel( "Play Interval:" ) );
mComboInterval = new JComboBox<Interval>( Interval.values() );
mComboInterval.setSelectedItem( mClipActionNode.getClipAction().getInterval() );
mComboInterval.setToolTipText( HELP_TEXT );
mComboInterval.addActionListener( new ActionListener()
{
@Override
@@ -84,37 +102,51 @@ public class ClipActionEditor extends JPanel implements ActionListener
if( selected != null )
{
/* Enable/disable period spinner based on selection */
mSpinnerPeriod.setEnabled( selected != Interval.ONCE );
/* Enable/disable period slider based on selection */
boolean enabled = selected != Interval.ONCE;
mClipActionNode.getClipAction().setInterval( selected );
mPeriodSlider.setEnabled( enabled );
mPeriodSliderLabel.setEnabled( enabled );
}
setModified( true );
}
});
add( mComboInterval, "wrap" );
add( mComboInterval );
final SpinnerModel model = new SpinnerNumberModel(
mClipActionNode.getClipAction().getPeriod(), 1, 30, 1 );
final SpinnerModel model = new SpinnerNumberModel( 1, 1, 30, 1 );
model.addChangeListener( new ChangeListener()
{
@Override
public void stateChanged( ChangeEvent e )
{
mClipActionNode.getClipAction().setPeriod( (int)model.getValue() );
setModified( true );
}
} );
mSpinnerPeriod = new JSpinner( model );
if( mClipActionNode.getClipAction().getInterval() == Interval.ONCE )
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()
{
mSpinnerPeriod.setEnabled( false );
}
@Override
public void stateChanged( ChangeEvent e )
{
mPeriodSliderLabel.setText( "Period: " + mPeriodSlider.getValue() );
setModified( true );
}
} );
mPeriodSlider.setToolTipText( HELP_TEXT );
add( new JLabel( "Period:" ) );
add( mSpinnerPeriod, "wrap" );
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" );
@@ -131,88 +163,134 @@ public class ClipActionEditor extends JPanel implements ActionListener
{
File file = chooser.getSelectedFile();
mClipActionNode.getClipAction().setPath( file.getAbsolutePath() );
mTextFilePath.setText( file.getAbsolutePath() );
setModified( true );
}
}
} );
add( fileButton );
add( fileButton, "grow" );
JButton testButton = new JButton( "Test" );
testButton.addActionListener( new ActionListener()
testButton.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent actionEvent )
public void actionPerformed( ActionEvent e )
{
String path = mClipActionNode.getClipAction().getPath();
if( path == null || path.trim().isEmpty() )
if( hasItem() )
{
JOptionPane.showMessageDialog( ClipActionEditor.this,
"Please select an audio file", "Please select file",
JOptionPane.ERROR_MESSAGE );
}
EventQueue.invokeLater( new Runnable()
{
@Override
public void run()
String path = mTextFilePath.getText();
if( path == null || path.trim().isEmpty() )
{
try
{
mClipActionNode.getClipAction().play();
}
catch( Exception e )
{
JOptionPane.showMessageDialog( ClipActionEditor.this,
"Couldn't play audio clip [" + e.getMessage() + "]",
"Error", JOptionPane.ERROR_MESSAGE );
}
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, "wrap" );
mTextFilePath = new JTextField( mClipActionNode.getClipAction().getPath() );
add( mTextFilePath, "growx,span" );
StringBuilder sb = new StringBuilder();
add( testButton, "grow,wrap" );
sb.append( "This action will play the audio clip according to the interval that you select.\n\n" );
sb.append( "Once - Play the first time the alias is active and never again.\n\n" );
sb.append( "Once, Reset After Delay - Play once and suppress subsequent plays for the specified period in seconds. After the reset period, it will play again when the alias is active.\n\n" );
sb.append( "Until Dismissed - Play audio clip every period seconds until you click OK on the dialog that appears. Alerting is suppressed for 15 seconds after you click OK." );
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 );
JTextArea description = new JTextArea( sb.toString() );
description.setLineWrap( true );
description.setBackground( getBackground() );
add( description, "growx,span" );
JButton btnSave = new JButton( "Save" );
btnSave.addActionListener( ClipActionEditor.this );
add( btnSave, "growx,push" );
if( hasItem() )
{
ClipAction clip = getClipAction();
JButton btnReset = new JButton( "Reset" );
btnReset.addActionListener( ClipActionEditor.this );
add( btnReset, "growx,push" );
Interval interval = clip.getInterval();
mComboInterval.setSelectedItem( interval );
boolean enabled = interval != 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 actionPerformed( ActionEvent e )
{
String command = e.getActionCommand();
if( command.contentEquals( "Save" ) )
public void save()
{
if( hasItem() && isModified() )
{
mClipActionNode.save();
mClipActionNode.show();
ClipAction script = getClipAction();
script.setInterval( (Interval)mComboInterval.getSelectedItem() );
script.setPeriod( mPeriodSlider.getValue() );
script.setPath( mTextFilePath.getText() );
}
mClipActionNode.refresh();
}
setModified( false );
}
}

View File

@@ -1,88 +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 <http://www.gnu.org/licenses/>
******************************************************************************/
package alias.action.clip;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import alias.AliasNode;
import alias.action.AliasActionNode;
public class ClipActionNode extends AliasActionNode
{
private static final long serialVersionUID = 1L;
public ClipActionNode( ClipAction clipAction )
{
super( clipAction );
}
@Override
public JPanel getEditor()
{
return new ClipActionEditor( this );
}
public ClipAction getClipAction()
{
return (ClipAction)getUserObject();
}
public String toString()
{
return "Action: Audio Clip";
}
public JPopupMenu getContextMenu()
{
JPopupMenu retVal = new JPopupMenu();
JMenuItem deleteItem = new JMenuItem( "Delete" );
deleteItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
int n = JOptionPane.showConfirmDialog( getModel().getTree(),
"Are you sure you want to permanently delete this node?" );
if( n == JOptionPane.YES_OPTION )
{
AliasNode parent = (AliasNode)getParent();
parent.getAlias().removeAliasAction( getClipAction() );
/* Save before removing the node ... so that we still have
* a reference to the parent's resources */
save();
getModel().removeNodeFromParent( ClipActionNode.this );
}
}
} );
retVal.add( deleteItem );
return retVal;
}
}

View File

@@ -9,6 +9,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import alias.Alias;
import alias.action.AliasActionType;
import alias.action.RecurringAction;
public class ScriptAction extends RecurringAction
@@ -21,6 +22,12 @@ public class ScriptAction extends RecurringAction
{
}
@Override
public AliasActionType getType()
{
return AliasActionType.SCRIPT;
}
public String getScript()
{
return mScript;
@@ -75,4 +82,17 @@ public class ScriptAction extends RecurringAction
}
}
}
@Override
public String toString()
{
if( mScript == null )
{
return "Run Script";
}
else
{
return "Run Script: " + mScript;
}
}
}

View File

@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
* 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
@@ -17,9 +17,14 @@
******************************************************************************/
package alias.action.script;
import java.awt.EventQueue;
import gui.editor.DocumentListenerEditor;
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.io.File;
import javax.swing.JButton;
@@ -27,9 +32,7 @@ import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
@@ -37,43 +40,58 @@ import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import alias.action.AliasAction;
import alias.action.RecurringAction.Interval;
public class ScriptActionEditor extends JPanel implements ActionListener
public class ScriptActionEditor extends DocumentListenerEditor<AliasAction>
{
private static final long serialVersionUID = 1L;
private final static Logger mLog =
LoggerFactory.getLogger( ScriptActionEditor.class );
private ScriptActionNode mScriptActionNode;
private static final String PLEASE_SELECT_A_FILE = "Please select a file";
private static final String HELP_TEXT =
"<html><h3>Script Action</h3>"
+ "This action will run a script according to the interval<br>"
+ "that you select:<br><br>"
+ "<b>Once:</b> run the first time the alias is active and<br>"
+ "never again<br><br>"
+ "<b>Once, Reset After Delay:</b> run once and suppress<br>"
+ "subsequent runs for the specified period in seconds.<br>"
+ "After the reset period, run again when the alias is active.<br><br>"
+ "<b>Until Dismissed:</b> run every period seconds until you<br>"
+ "click OK on the dialog that appears. Alerting is suppressed<br>"
+ "for 15 seconds after you click OK.<br>"
+ "</html>";
private JComboBox<Interval> mComboInterval;
private JSpinner mSpinnerPeriod;
private JSlider mPeriodSlider;
private JLabel mPeriodSliderLabel;
private JTextField mTextFilePath;
public ScriptActionEditor( ScriptActionNode clipActionNode )
public ScriptActionEditor( AliasAction aliasAction )
{
mScriptActionNode = clipActionNode;
initGUI();
init();
setItem( aliasAction );
}
private void initGUI()
public ScriptAction getScriptAction()
{
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][][grow]" ) );
add( new JLabel( "Action: Script" ), "span,align center" );
if( getItem() instanceof ScriptAction )
{
return (ScriptAction)getItem();
}
add( new JLabel( "Interval:" ) );
return null;
}
private void init()
{
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][]" ) );
add( new JLabel( "Run Interval:" ) );
mComboInterval = new JComboBox<Interval>( Interval.values() );
mComboInterval.setSelectedItem( mScriptActionNode.getScriptAction().getInterval() );
mComboInterval.setToolTipText( HELP_TEXT );
mComboInterval.addActionListener( new ActionListener()
{
@Override
@@ -84,37 +102,51 @@ public class ScriptActionEditor extends JPanel implements ActionListener
if( selected != null )
{
/* Enable/disable period spinner based on selection */
mSpinnerPeriod.setEnabled( selected != Interval.ONCE );
/* Enable/disable period slider based on selection */
boolean enabled = selected != Interval.ONCE;
mScriptActionNode.getScriptAction().setInterval( selected );
mPeriodSlider.setEnabled( enabled );
mPeriodSliderLabel.setEnabled( enabled );
}
setModified( true );
}
});
add( mComboInterval, "wrap" );
add( mComboInterval );
final SpinnerModel model = new SpinnerNumberModel(
mScriptActionNode.getScriptAction().getPeriod(), 1, 30, 1 );
final SpinnerModel model = new SpinnerNumberModel( 1, 1, 30, 1 );
model.addChangeListener( new ChangeListener()
{
@Override
public void stateChanged( ChangeEvent e )
{
mScriptActionNode.getScriptAction().setPeriod( (int)model.getValue() );
setModified( true );
}
} );
mSpinnerPeriod = new JSpinner( model );
if( mScriptActionNode.getScriptAction().getInterval() == Interval.ONCE )
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()
{
mSpinnerPeriod.setEnabled( false );
}
@Override
public void stateChanged( ChangeEvent e )
{
mPeriodSliderLabel.setText( "Period: " + mPeriodSlider.getValue() );
setModified( true );
}
} );
mPeriodSlider.setToolTipText( HELP_TEXT );
add( new JLabel( "Period:" ) );
add( mSpinnerPeriod, "wrap" );
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" );
@@ -131,13 +163,14 @@ public class ScriptActionEditor extends JPanel implements ActionListener
{
File file = chooser.getSelectedFile();
mScriptActionNode.getScriptAction().setScript( file.getAbsolutePath() );
mTextFilePath.setText( file.getAbsolutePath() );
setModified( true );
}
}
} );
add( fileButton );
add( fileButton, "grow" );
JButton testButton = new JButton( "Test" );
testButton.addActionListener( new ActionListener()
@@ -145,74 +178,115 @@ public class ScriptActionEditor extends JPanel implements ActionListener
@Override
public void actionPerformed( ActionEvent actionEvent )
{
String script = mScriptActionNode.getScriptAction().getScript();
String script = mTextFilePath.getText();
if( script == null || script.trim().isEmpty() )
{
JOptionPane.showMessageDialog( ScriptActionEditor.this,
"Please select a script file", "Please select file",
"Please select a script file", PLEASE_SELECT_A_FILE,
JOptionPane.ERROR_MESSAGE );
}
EventQueue.invokeLater( new Runnable()
else
{
@Override
public void run()
if( isModified() )
{
try
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 )
{
mScriptActionNode.getScriptAction().play();
}
catch( Exception e )
{
JOptionPane.showMessageDialog( ScriptActionEditor.this,
"Couldn't run script [" + e.getMessage() + "]",
"Error", JOptionPane.ERROR_MESSAGE );
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, "wrap" );
add( testButton, "grow,wrap" );
mTextFilePath = new JTextField( mScriptActionNode.getScriptAction().getScript() );
add( mTextFilePath, "growx,span" );
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();
StringBuilder sb = new StringBuilder();
sb.append( "This action will run the script according to the interval that you select.\n\n" );
sb.append( "Once - Run script the first time the alias is active and never again.\n\n" );
sb.append( "Once, Reset After Delay - Run once and suppress subsequent runs for the specified period in seconds. After the reset period, it will play again when the alias is active.\n\n" );
sb.append( "Until Dismissed - Run script every period seconds until you click OK on the dialog that appears. Alerting is suppressed for 15 seconds after you click OK." );
JTextArea description = new JTextArea( sb.toString() );
description.setLineWrap( true );
description.setBackground( getBackground() );
add( description, "growx,span" );
JButton btnSave = new JButton( "Save" );
btnSave.addActionListener( ScriptActionEditor.this );
add( btnSave, "growx,push" );
Interval interval = script.getInterval();
mComboInterval.setSelectedItem( interval );
JButton btnReset = new JButton( "Reset" );
btnReset.addActionListener( ScriptActionEditor.this );
add( btnReset, "growx,push" );
boolean enabled = interval != 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 actionPerformed( ActionEvent e )
{
String command = e.getActionCommand();
if( command.contentEquals( "Save" ) )
public void save()
{
if( hasItem() && isModified() )
{
mScriptActionNode.save();
mScriptActionNode.show();
ScriptAction script = getScriptAction();
script.setInterval( (Interval)mComboInterval.getSelectedItem() );
script.setPeriod( mPeriodSlider.getValue() );
script.setScript( mTextFilePath.getText() );
}
mScriptActionNode.refresh();
}
setModified( false );
}
}

View File

@@ -1,88 +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 <http://www.gnu.org/licenses/>
******************************************************************************/
package alias.action.script;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import alias.AliasNode;
import alias.action.AliasActionNode;
public class ScriptActionNode extends AliasActionNode
{
private static final long serialVersionUID = 1L;
public ScriptActionNode( ScriptAction scriptAction )
{
super( scriptAction );
}
@Override
public JPanel getEditor()
{
return new ScriptActionEditor( this );
}
public ScriptAction getScriptAction()
{
return (ScriptAction)getUserObject();
}
public String toString()
{
return "Action: Script";
}
public JPopupMenu getContextMenu()
{
JPopupMenu retVal = new JPopupMenu();
JMenuItem deleteItem = new JMenuItem( "Delete" );
deleteItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
int n = JOptionPane.showConfirmDialog( getModel().getTree(),
"Are you sure you want to permanently delete this node?" );
if( n == JOptionPane.YES_OPTION )
{
AliasNode parent = (AliasNode)getParent();
parent.getAlias().removeAliasAction( getScriptAction() );
/* Save before removing the node ... so that we still have
* a reference to the parent's resources */
save();
getModel().removeNodeFromParent( ScriptActionNode.this );
}
}
} );
retVal.add( deleteItem );
return retVal;
}
}

57
src/alias/id/AliasID.java Normal file
View File

@@ -0,0 +1,57 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
******************************************************************************/
package alias.id;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlTransient;
import alias.id.broadcast.BroadcastChannel;
import playlist.version1.Group;
import alias.id.esn.Esn;
import alias.id.fleetsync.FleetsyncID;
import alias.id.lojack.LoJackFunctionAndID;
import alias.id.mdc.MDC1200ID;
import alias.id.mobileID.Min;
import alias.id.mpt1327.MPT1327ID;
import alias.id.nonrecordable.NonRecordable;
import alias.id.priority.Priority;
import alias.id.siteID.SiteID;
import alias.id.status.StatusID;
import alias.id.talkgroup.TalkgroupID;
import alias.id.uniqueID.UniqueID;
@SuppressWarnings( "deprecation" )
@XmlSeeAlso( {BroadcastChannel.class, Esn.class, FleetsyncID.class, Group.class, LoJackFunctionAndID.class,
MDC1200ID.class, Min.class, MPT1327ID.class, NonRecordable.class,
Priority.class, SiteID.class, StatusID.class, TalkgroupID.class,
UniqueID.class } )
@XmlRootElement( name = "id" )
public abstract class AliasID
{
public AliasID()
{
}
public abstract AliasIDType getType();
public abstract boolean matches( AliasID id );
@XmlTransient
public abstract boolean isValid();
}

View File

@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
* 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
@@ -15,20 +15,35 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package alias;
package alias.id;
public enum AliasIDType
{
ESN,
Fleetsync,
LoJack,
LTRNetUID,
MDC1200,
MIN,
MPT1327,
NonRecordable,
Priority,
Site,
Status,
Talkgroup;
ESN( "ESN" ),
FLEETSYNC( "Fleetsync" ),
LOJACK( "LoJack" ),
LTR_NET_UID( "LTR-Net UID" ),
MDC1200( "MDC-1200" ),
MIN( "Passport MIN" ),
MPT1327( "MPT-1327" ),
SITE( "Site" ),
STATUS( "Status" ),
TALKGROUP( "Talkgroup" ),
INVERT( "Audio Inversion" ),
PRIORITY( "Audio Priority" ),
NON_RECORDABLE( "Audio Non-Recordable" ),
BROADCAST_CHANNEL( "Audio Broadcast Channel" );
private String mLabel;
private AliasIDType( String label )
{
mLabel = label;
}
public String toString()
{
return mLabel;
}
}

View File

@@ -0,0 +1,379 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
******************************************************************************/
package alias.id;
import audio.broadcast.BroadcastModel;
import gui.editor.Editor;
import gui.editor.EmptyEditor;
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;
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 net.miginfocom.swing.MigLayout;
import alias.Alias;
import alias.AliasEvent;
import alias.AliasEvent.Event;
import alias.AliasFactory;
import alias.AliasModel;
public class AliasIdentifierEditor extends Editor<Alias>
{
private static final long serialVersionUID = 1L;
private static ListModel<AliasID> EMPTY_MODEL = new DefaultListModel<>();
private JList<AliasID> 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.ESN ) );
menu.add( new AddAliasIdentifierItem( AliasIDType.FLEETSYNC) );
menu.add( new AddAliasIdentifierItem( AliasIDType.LOJACK) );
menu.add( new AddAliasIdentifierItem( AliasIDType.LTR_NET_UID) );
menu.add( new AddAliasIdentifierItem( AliasIDType.MDC1200 ) );
menu.add( new AddAliasIdentifierItem( AliasIDType.MPT1327 ) );
menu.add( new AddAliasIdentifierItem( AliasIDType.MIN ) );
menu.add( new AddAliasIdentifierItem( AliasIDType.SITE) );
menu.add( new AddAliasIdentifierItem( AliasIDType.STATUS) );
menu.add( new AddAliasIdentifierItem( AliasIDType.TALKGROUP) );
menu.addSeparator();
menu.add( new AddAliasIdentifierItem( AliasIDType.BROADCAST_CHANNEL) );
if( hasItem() )
{
Alias alias = getItem();
if( alias.isRecordable() )
{
menu.add( new AddAliasIdentifierItem( AliasIDType.NON_RECORDABLE) );
}
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, 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<AliasID> model = new DefaultListModel<AliasID>();
List<AliasID> ids = alias.getId();
Collections.sort( ids, new Comparator<AliasID>()
{
@Override
public int compare( AliasID o1, AliasID o2 )
{
return o1.toString().compareTo( o2.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(), Event.CHANGE ) );
}
}
}
public class EditorContainer extends JPanel
{
private static final long serialVersionUID = 1L;
private Editor<AliasID> 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 );
}
} );
}
}
}

View File

@@ -0,0 +1,113 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package alias.id;
import org.apache.commons.lang3.Validate;
import java.util.regex.Pattern;
public class WildcardID implements Comparable<WildcardID>
{
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)(Math.pow(2, mValue.length() - x - 1)); //Position weight
characterCount++;
}
}
weight += (int)(Math.pow(2, characterCount)) * 1000; //Character count weight
return weight;
}
@Override
public int compareTo(WildcardID otherWildcardID)
{
return Integer.compare(this.weight(), otherWildcardID.weight());
}
}

View File

@@ -0,0 +1,130 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package alias.id.broadcast;
import alias.id.AliasID;
import alias.id.AliasIDType;
import javax.xml.bind.annotation.XmlAttribute;
public class BroadcastChannel extends AliasID implements Comparable<BroadcastChannel>
{
private String mChannelName;
public BroadcastChannel()
{
//JAXB Constructor
}
@Override
public int compareTo(BroadcastChannel other)
{
if(mChannelName != null && other.getChannelName() != null)
{
return mChannelName.compareTo(other.getChannelName());
}
else if(mChannelName != null)
{
return -1;
}
else
{
return 1;
}
}
@Override
public boolean equals(Object o)
{
if(this == o)
{
return true;
}
if(!(o instanceof BroadcastChannel))
{
return false;
}
BroadcastChannel that = (BroadcastChannel)o;
return getChannelName() != null ? getChannelName().equals(that.getChannelName()) : that.getChannelName() == null;
}
@Override
public int hashCode()
{
return getChannelName() != null ? getChannelName().hashCode() : 0;
}
/**
* Creates a named broadcast channel
*/
public BroadcastChannel(String channelName)
{
mChannelName = channelName;
}
/**
* Name of the broadcastAudio channel configuration
*/
@XmlAttribute(name = "channel")
public String getChannelName()
{
return mChannelName;
}
/**
* Sets the name of the broadcastAudio channel configuration
*/
public void setChannelName(String channel)
{
mChannelName = channel;
}
@Override
public AliasIDType getType()
{
return AliasIDType.BROADCAST_CHANNEL;
}
@Override
public boolean isValid()
{
return mChannelName != null;
}
@Override
public boolean matches(AliasID id)
{
return false;
}
@Override
public String toString()
{
if(isValid())
{
return "Broadcast Channel: " + mChannelName;
}
else
{
return "Broadcast Channel: None Selected";
}
}
}

View File

@@ -0,0 +1,129 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package alias.id.broadcast;
import alias.id.AliasID;
import audio.broadcast.BroadcastConfiguration;
import audio.broadcast.BroadcastModel;
import 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<AliasID>
{
private static final long serialVersionUID = 1L;
private static final String HELP_TEXT = "<html>"
+ "<h3>Audio Broadcast Channel</h3>"
+ "Name of a audio streaming broadcast configuration<br>"
+ "to be used for streaming audio to an external<br>"
+ "streaming server";
private JComboBox<String> 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<String> channelNames = mBroadcastModel.getBroadcastConfigurationNames();
mBroadcastConfigurations = new JComboBox<String>(channelNames.toArray(new String[channelNames.size()]));
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);
}
}
}

View File

@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
* 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
@@ -17,79 +17,102 @@
******************************************************************************/
package alias.id.esn;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import gui.editor.DocumentListenerEditor;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import net.miginfocom.swing.MigLayout;
import controller.ConfigurableNode;
import alias.id.AliasID;
public class ESNEditor extends JPanel implements ActionListener
public class ESNEditor extends DocumentListenerEditor<AliasID>
{
private static final long serialVersionUID = 1L;
private ESNNode mESNNode;
private JTextField mTextESN;
public ESNEditor( ESNNode esnNode )
private static final String HELP_TEXT =
"<html><h3>Electronic Serial Number (ESN)</h3>"
+ "<b>ESN:</b> hexadecimal 0-9, A-F (e.g. <u>ABCD1234</u> )<br>"
+ "<b>Wildcard:</b> use an asterisk (*) to wildcard individual<br>"
+ "digits (e.g. <u>ABCD123*</u> or <u>AB**1**4</u>)"
+ "</html>";
private JTextField mTextField;
public ESNEditor( AliasID aliasID )
{
mESNNode = esnNode;
initGUI();
setItem( aliasID );
}
private void initGUI()
{
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) );
add( new JLabel( "Electronic Serial Number ID" ), "span,align center" );
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) );
add( new JLabel( "ESN:" ) );
mTextESN = new JTextField( mESNNode.getESN().getEsn() );
add( mTextESN, "growx,push" );
JButton btnSave = new JButton( "Save" );
btnSave.addActionListener( ESNEditor.this );
add( btnSave, "growx,push" );
mTextField = new JTextField();
mTextField.getDocument().addDocumentListener( this );
mTextField.setToolTipText( HELP_TEXT );
add( mTextField, "growx,push" );
JButton btnReset = new JButton( "Reset" );
btnReset.addActionListener( ESNEditor.this );
add( btnReset, "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 actionPerformed( ActionEvent e )
{
String command = e.getActionCommand();
public void setItem( AliasID aliasID )
{
super.setItem( aliasID );
if( command.contentEquals( "Save" ) )
Esn esn = getEsn();
if( esn != null )
{
String esn = mTextESN.getText();
if( esn != null )
{
mESNNode.getESN().setEsn( esn );
((ConfigurableNode)mESNNode.getParent()).sort();
mESNNode.save();
mESNNode.show();
}
else
{
JOptionPane.showMessageDialog( ESNEditor.this, "Please enter an ESN" );
}
}
else if( command.contentEquals( "Reset" ) )
{
mTextESN.setText( mESNNode.getESN().getEsn() );
mTextField.setText( esn.getEsn() );
}
mESNNode.refresh();
}
setModified( false );
repaint();
}
@Override
public void save()
{
Esn esn = getEsn();
if( esn != null )
{
esn.setEsn( mTextField.getText() );
}
setModified( false );
}
}

View File

@@ -1,86 +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 <http://www.gnu.org/licenses/>
******************************************************************************/
package alias.id.esn;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import alias.AliasIDNode;
import alias.AliasNode;
public class ESNNode extends AliasIDNode
{
private static final long serialVersionUID = 1L;
public ESNNode( Esn esn )
{
super( esn );
}
@Override
public JPanel getEditor()
{
return new ESNEditor( this );
}
public Esn getESN()
{
return (Esn)getUserObject();
}
public String toString()
{
return "ESN " + getESN().getEsn();
}
public JPopupMenu getContextMenu()
{
JPopupMenu retVal = new JPopupMenu();
JMenuItem addSystemItem = new JMenuItem( "Delete" );
addSystemItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
int n = JOptionPane.showConfirmDialog( getModel().getTree(),
"Are you sure you want to permanently delete this node?" );
if( n == JOptionPane.YES_OPTION )
{
AliasNode parent = (AliasNode)getParent();
parent.getAlias().removeAliasID( getESN() );
save();
getModel().removeNodeFromParent( ESNNode.this );
}
}
} );
retVal.add( addSystemItem );
return retVal;
}
}

View File

@@ -19,8 +19,8 @@ package alias.id.esn;
import javax.xml.bind.annotation.XmlAttribute;
import alias.AliasID;
import alias.AliasIDType;
import alias.id.AliasID;
import alias.id.AliasIDType;
public class Esn extends AliasID
{
@@ -36,7 +36,13 @@ public class Esn extends AliasID
return mEsn;
}
public void setEsn( String esn )
@Override
public boolean isValid()
{
return mEsn != null;
}
public void setEsn(String esn )
{
mEsn = esn;
}

View File

@@ -19,8 +19,8 @@ package alias.id.fleetsync;
import javax.xml.bind.annotation.XmlAttribute;
import alias.AliasID;
import alias.AliasIDType;
import alias.id.AliasID;
import alias.id.AliasIDType;
public class FleetsyncID extends AliasID
@@ -42,6 +42,12 @@ public class FleetsyncID extends AliasID
mID = ident;
}
@Override
public boolean isValid()
{
return mID != null;
}
public String toString()
{
return "Fleetsync: " + mID;
@@ -68,6 +74,6 @@ public class FleetsyncID extends AliasID
@Override
public AliasIDType getType()
{
return AliasIDType.Fleetsync;
return AliasIDType.FLEETSYNC;
}
}

View File

@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
* 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
@@ -17,92 +17,123 @@
******************************************************************************/
package alias.id.fleetsync;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import gui.editor.DocumentListenerEditor;
import javax.swing.JButton;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.text.MaskFormatter;
import net.miginfocom.swing.MigLayout;
import controller.ConfigurableNode;
public class FleetsyncIDEditor extends JPanel implements ActionListener
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import alias.id.AliasID;
public class FleetsyncIDEditor extends DocumentListenerEditor<AliasID>
{
private static final long serialVersionUID = 1L;
private FleetsyncIDNode mFleetsyncIDNode;
private JLabel mLabelName;
private JTextField mTextIdent;
private final static Logger mLog = LoggerFactory.getLogger( FleetsyncIDEditor.class );
private String mHelpText = "Enter a formatted fleetsync identifier.\n\n"
+ "Format: PPP-IIII (P=Prefix, I=Identifier)\n\n"
+ "Wildcard: use one or more asterisks (*) for any talkgroup "
+ "digits.\n\n";
private static final long serialVersionUID = 1L;
public FleetsyncIDEditor( FleetsyncIDNode fsNode )
private static final String HELP_TEXT = "<html>"
+ "<h3>Fleetsync Identifier</h3>"
+ "<b>Fleetsync:</b> ggg-uuuu where g=Group and u=Unit (e.g. <u>001-0001</u>)<br>"
+ "<b>Wildcard:</b> use an asterisk (*) for each digit (e.g. <u>001-****</u>)"
+ "</html>";
private JTextField mTextField;
public FleetsyncIDEditor( AliasID aliasID )
{
mFleetsyncIDNode = fsNode;
initGUI();
setItem( aliasID );
}
private void initGUI()
{
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) );
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) );
add( new JLabel( "Fleetsync ID" ), "span,align center" );
add( new JLabel( "Fleetsync ID:" ) );
add( new JLabel( "Ident:" ) );
mTextIdent = new JTextField( mFleetsyncIDNode.getFleetsyncID().getIdent() );
MaskFormatter formatter = null;
add( mTextIdent, "grow, wrap" );
try
{
//Mask: 3 digits - 4 digits
formatter = new MaskFormatter( "###-####" );
}
catch( Exception e )
{
//Do nothing, the mask was invalid
}
JButton btnSave = new JButton( "Save" );
btnSave.addActionListener( FleetsyncIDEditor.this );
add( btnSave, "growx,push" );
mTextField = new JFormattedTextField( formatter );
mTextField.getDocument().addDocumentListener( this );
mTextField.setToolTipText( HELP_TEXT );
JButton btnReset = new JButton( "Reset" );
btnReset.addActionListener( FleetsyncIDEditor.this );
add( btnReset, "growx,push" );
add( mTextField, "growx,push" );
JTextArea helpText = new JTextArea( mHelpText );
helpText.setLineWrap( true );
helpText.setBackground( getBackground() );
add( helpText, "span,grow,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 actionPerformed( ActionEvent e )
{
String command = e.getActionCommand();
public void setItem( AliasID aliasID )
{
super.setItem( aliasID );
if( command.contentEquals( "Save" ) )
FleetsyncID fleetsync = getFleetsyncID();
if( fleetsync != null )
{
String ident = mTextIdent.getText();
if( ident != null )
{
mFleetsyncIDNode.getFleetsyncID().setIdent( ident );
mTextField.setText( fleetsync.getIdent() );
}
setModified( false );
repaint();
}
((ConfigurableNode)mFleetsyncIDNode.getParent()).sort();
mFleetsyncIDNode.save();
mFleetsyncIDNode.show();
}
else
{
JOptionPane.showMessageDialog( FleetsyncIDEditor.this, "Please enter an ident" );
}
}
else if( command.contentEquals( "Reset" ) )
@Override
public void save()
{
FleetsyncID fleetsync = getFleetsyncID();
if( fleetsync != null )
{
mTextIdent.setText( mFleetsyncIDNode.getFleetsyncID().getIdent() );
fleetsync.setIdent( mTextField.getText() );
}
mFleetsyncIDNode.refresh();
}
setModified( false );
}
}

View File

@@ -1,97 +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 <http://www.gnu.org/licenses/>
******************************************************************************/
package alias.id.fleetsync;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import alias.AliasIDNode;
import alias.AliasNode;
public class FleetsyncIDNode extends AliasIDNode
{
private static final long serialVersionUID = 1L;
public FleetsyncIDNode( FleetsyncID id )
{
super( id );
}
@Override
public JPanel getEditor()
{
return new FleetsyncIDEditor( this );
}
public FleetsyncID getFleetsyncID()
{
return (FleetsyncID)getUserObject();
}
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append( "Fleetsync " );
String ident = getFleetsyncID().getIdent();
if( ident != null )
{
sb.append( ident );
}
return sb.toString();
}
public JPopupMenu getContextMenu()
{
JPopupMenu retVal = new JPopupMenu();
JMenuItem deleteItem = new JMenuItem( "Delete" );
deleteItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
int n = JOptionPane.showConfirmDialog( getModel().getTree(),
"Are you sure you want to permanently delete this node?" );
if( n == JOptionPane.YES_OPTION )
{
AliasNode parent = (AliasNode)getParent();
parent.getAlias().removeAliasID( getFleetsyncID() );
save();
getModel().removeNodeFromParent( FleetsyncIDNode.this );
}
}
} );
retVal.add( deleteItem );
return retVal;
}
}

View File

@@ -1,120 +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 <http://www.gnu.org/licenses/>
******************************************************************************/
package alias.id.fleetsync;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import controller.ConfigurableNode;
public class StatusIDEditor extends JPanel implements ActionListener
{
private static final long serialVersionUID = 1L;
private final static Logger mLog =
LoggerFactory.getLogger( StatusIDEditor.class );
private StatusIDNode mStatusIDNode;
private JLabel mLabelName;
private JTextField mTextStatusID;
public StatusIDEditor( StatusIDNode statusIDNode )
{
mStatusIDNode = statusIDNode;
initGUI();
}
private void initGUI()
{
setLayout( new MigLayout() );
setBorder( BorderFactory.createTitledBorder( "Status Code" ) );
mLabelName = new JLabel( "Status Code:" );
add( mLabelName, "align right" );
mTextStatusID = new JTextField(
String.valueOf( mStatusIDNode.getStatusID().getStatus() ) );
add( mTextStatusID, "grow, wrap" );
JButton btnSave = new JButton( "Save" );
btnSave.addActionListener( StatusIDEditor.this );
add( btnSave );
JButton btnReset = new JButton( "Reset" );
btnReset.addActionListener( StatusIDEditor.this );
add( btnReset, "wrap" );
}
@Override
public void actionPerformed( ActionEvent e )
{
String command = e.getActionCommand();
if( command.contentEquals( "Save" ) )
{
String statusIDString = mTextStatusID.getText();
if( statusIDString != null )
{
try
{
int id = Integer.parseInt( statusIDString );
mStatusIDNode.getStatusID().setStatus( id );
((ConfigurableNode)mStatusIDNode.getParent()).sort();
mStatusIDNode.save();
mStatusIDNode.show();
}
catch( Exception ex )
{
mLog.error( "StatusIDEditor - error parsing int status " +
"id from [" + statusIDString + "]" );
}
}
else
{
JOptionPane.showMessageDialog( StatusIDEditor.this,
"Please enter a status ID code" );
}
}
else if( command.contentEquals( "Reset" ) )
{
mTextStatusID.setText(
String.valueOf( mStatusIDNode.getStatusID().getStatus() ) );
}
mStatusIDNode.refresh();
}
}

Some files were not shown because too many files have changed in this diff Show More