feature/current_conditions #5
3
.gitignore
vendored
3
.gitignore
vendored
@@ -24,3 +24,6 @@ bin/
|
|||||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
hs_err_pid*
|
hs_err_pid*
|
||||||
*.dat
|
*.dat
|
||||||
|
|
||||||
|
#Can contain copyrighted music!
|
||||||
|
Music/
|
||||||
|
|||||||
BIN
Announcements/7-day-dis.wav
Normal file
BIN
Announcements/7-day-dis.wav
Normal file
Binary file not shown.
BIN
Announcements/message-dis.wav
Normal file
BIN
Announcements/message-dis.wav
Normal file
Binary file not shown.
BIN
Announcements/message-old.wav
Normal file
BIN
Announcements/message-old.wav
Normal file
Binary file not shown.
BIN
jlayer2.jar
Normal file
BIN
jlayer2.jar
Normal file
Binary file not shown.
286
src/com/flaremicro/audio/AgnosticAudioSystem.java
Normal file
286
src/com/flaremicro/audio/AgnosticAudioSystem.java
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
package com.flaremicro.audio;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import javax.sound.sampled.AudioFormat;
|
||||||
|
import javax.sound.sampled.AudioInputStream;
|
||||||
|
import javax.sound.sampled.AudioSystem;
|
||||||
|
import javax.sound.sampled.DataLine;
|
||||||
|
import javax.sound.sampled.FloatControl;
|
||||||
|
import javax.sound.sampled.SourceDataLine;
|
||||||
|
|
||||||
|
import javazoom.jl.decoder.JavaLayerException;
|
||||||
|
import javazoom.jl.player.Player;
|
||||||
|
|
||||||
|
import com.flaremicro.util.Util;
|
||||||
|
|
||||||
|
public class AgnosticAudioSystem implements GameAudioSystem {
|
||||||
|
|
||||||
|
private Collection<AudioFinishedListener> listeners = new ArrayList<AudioFinishedListener>();
|
||||||
|
private HashMap<String, AudioSystemThread> threads = new HashMap<String, AudioSystemThread>();
|
||||||
|
private Random random = new Random();
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAudioFinishedListener(AudioFinishedListener listener) {
|
||||||
|
this.listeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeAudioFinishedListener(AudioFinishedListener listener) {
|
||||||
|
this.listeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeAudioFinishedListeners() {
|
||||||
|
this.listeners.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String play(URL res) {
|
||||||
|
if (res.getPath().contains("."))
|
||||||
|
{
|
||||||
|
String key = String.valueOf(random.nextLong());
|
||||||
|
String ext = res.getPath().substring(res.getPath().lastIndexOf(".") + 1).toLowerCase();
|
||||||
|
AudioSystemThread audioSystemThread = null;
|
||||||
|
if (ext.equalsIgnoreCase("mp3"))
|
||||||
|
audioSystemThread = new AgnosticMp3AudioSystemThread(res, key, this, false);
|
||||||
|
if (ext.equalsIgnoreCase("wav"))
|
||||||
|
audioSystemThread = new AgnosticWavAudioSystemThread(res, key, this, false);
|
||||||
|
|
||||||
|
if (audioSystemThread != null)
|
||||||
|
{
|
||||||
|
synchronized (threads)
|
||||||
|
{
|
||||||
|
threads.put(key, audioSystemThread);
|
||||||
|
}
|
||||||
|
new Thread(audioSystemThread).start();
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void playbg(URL res) {
|
||||||
|
stopbg();
|
||||||
|
if (res.getPath().contains("."))
|
||||||
|
{
|
||||||
|
String ext = res.getPath().substring(res.getPath().lastIndexOf(".") + 1).toLowerCase();
|
||||||
|
AudioSystemThread audioSystemThread = null;
|
||||||
|
if (ext.equalsIgnoreCase("mp3"))
|
||||||
|
audioSystemThread = new AgnosticMp3AudioSystemThread(res, "bg", this, true);
|
||||||
|
if (ext.equalsIgnoreCase("wav"))
|
||||||
|
audioSystemThread = new AgnosticWavAudioSystemThread(res, "bg", this, true);
|
||||||
|
|
||||||
|
if (audioSystemThread != null)
|
||||||
|
{
|
||||||
|
synchronized (threads)
|
||||||
|
{
|
||||||
|
threads.put("bg", audioSystemThread);
|
||||||
|
}
|
||||||
|
new Thread(audioSystemThread).start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopbg() {
|
||||||
|
if (threads.containsKey("bg"))
|
||||||
|
threads.get("bg").stopMusic();
|
||||||
|
stopped("bg", threads.get("bg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopped(String threadName, AudioSystemThread audioSystemThread) {
|
||||||
|
synchronized (threads)
|
||||||
|
{
|
||||||
|
if (audioSystemThread != null && audioSystemThread == threads.get(threadName))
|
||||||
|
{
|
||||||
|
threads.remove(threadName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (AudioFinishedListener listener : listeners)
|
||||||
|
{
|
||||||
|
listener.audioFinished(threadName, audioSystemThread.resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVolume(float percent, String key) {
|
||||||
|
AudioSystemThread ast = threads.get(key);
|
||||||
|
if (ast != null)
|
||||||
|
{
|
||||||
|
ast.setVolume(percent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop(String currentKey) {
|
||||||
|
AudioSystemThread ast = threads.get(currentKey);
|
||||||
|
if (ast != null)
|
||||||
|
{
|
||||||
|
ast.stopMusic();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class AudioSystemThread implements Runnable {
|
||||||
|
protected final boolean looping;
|
||||||
|
protected final AgnosticAudioSystem system;
|
||||||
|
protected final URL resource;
|
||||||
|
protected final String threadName;
|
||||||
|
|
||||||
|
public AudioSystemThread(URL resource, String threadName, AgnosticAudioSystem system, boolean looping) {
|
||||||
|
this.resource = resource;
|
||||||
|
this.looping = looping;
|
||||||
|
this.threadName = threadName;
|
||||||
|
this.system = system;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void setVolume(float percent);
|
||||||
|
|
||||||
|
public abstract void stopMusic();
|
||||||
|
|
||||||
|
public final void onStop() {
|
||||||
|
this.system.stopped(threadName, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class AgnosticMp3AudioSystemThread extends AudioSystemThread {
|
||||||
|
private boolean running = true;
|
||||||
|
private Player player = null;
|
||||||
|
private static final int FRAME_BUFFER = 2;
|
||||||
|
private float volume = 1F;
|
||||||
|
|
||||||
|
public AgnosticMp3AudioSystemThread(URL resource, String threadName, AgnosticAudioSystem system, boolean looping) {
|
||||||
|
super(resource, threadName, system, looping);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
do
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
player = new Player(new BufferedInputStream(resource.openStream()));
|
||||||
|
player.setVolume(volume);
|
||||||
|
while (running && player.play(FRAME_BUFFER));
|
||||||
|
}
|
||||||
|
catch (JavaLayerException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (running && looping);
|
||||||
|
super.onStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopMusic() {
|
||||||
|
this.running = false;
|
||||||
|
if (player != null)
|
||||||
|
player.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVolume(float percent) {
|
||||||
|
volume = percent;
|
||||||
|
if (player != null)
|
||||||
|
{
|
||||||
|
player.setVolume(volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class AgnosticWavAudioSystemThread extends AudioSystemThread {
|
||||||
|
private boolean running = true;
|
||||||
|
private SourceDataLine sourceLine = null;
|
||||||
|
private float volume = 1.0F;
|
||||||
|
|
||||||
|
public AgnosticWavAudioSystemThread(URL resource, String threadName, AgnosticAudioSystem system, boolean looping) {
|
||||||
|
super(resource, threadName, system, looping);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
AudioInputStream audioStream = null;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
audioStream = AudioSystem.getAudioInputStream(resource);
|
||||||
|
AudioFormat audioFormat = audioStream.getFormat();
|
||||||
|
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
|
||||||
|
|
||||||
|
sourceLine = (SourceDataLine) AudioSystem.getLine(info);
|
||||||
|
sourceLine.open();
|
||||||
|
sourceLine.start();
|
||||||
|
|
||||||
|
FloatControl volume = (FloatControl) sourceLine.getControl(FloatControl.Type.MASTER_GAIN);
|
||||||
|
volume.setValue(this.volume);
|
||||||
|
|
||||||
|
int nBytesRead = 0;
|
||||||
|
byte[] abData = new byte[4096];
|
||||||
|
while (nBytesRead != -1 && running)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
nBytesRead = audioStream.read(abData, 0, abData.length);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
if (nBytesRead >= 0)
|
||||||
|
{
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
int nBytesWritten = sourceLine.write(abData, 0, nBytesRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Util.cleanClose(audioStream);
|
||||||
|
if (sourceLine != null)
|
||||||
|
{
|
||||||
|
sourceLine.drain();
|
||||||
|
sourceLine.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (running && looping);
|
||||||
|
onStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopMusic() {
|
||||||
|
this.running = false;
|
||||||
|
if (sourceLine != null)
|
||||||
|
{
|
||||||
|
sourceLine.drain();
|
||||||
|
sourceLine.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVolume(float percent) {
|
||||||
|
if (sourceLine != null)
|
||||||
|
{
|
||||||
|
this.volume = percent;
|
||||||
|
FloatControl volume = (FloatControl) sourceLine.getControl(FloatControl.Type.MASTER_GAIN);
|
||||||
|
volume.setValue(percent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/com/flaremicro/audio/AudioFinishedListener.java
Normal file
7
src/com/flaremicro/audio/AudioFinishedListener.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package com.flaremicro.audio;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
public interface AudioFinishedListener {
|
||||||
|
public void audioFinished(String key, URL resource);
|
||||||
|
}
|
||||||
83
src/com/flaremicro/audio/AudioPlaylist.java
Normal file
83
src/com/flaremicro/audio/AudioPlaylist.java
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
package com.flaremicro.audio;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class AudioPlaylist implements AudioFinishedListener{
|
||||||
|
|
||||||
|
|
||||||
|
private final AgnosticAudioSystem aas;
|
||||||
|
|
||||||
|
public AudioPlaylist(AgnosticAudioSystem aas)
|
||||||
|
{
|
||||||
|
this.aas = aas;
|
||||||
|
}
|
||||||
|
|
||||||
|
String currentKey = null;
|
||||||
|
int currentPlaylistIndex = 0;
|
||||||
|
URL[] playlist = null;
|
||||||
|
|
||||||
|
float currVol = 1F;
|
||||||
|
private boolean stopped = true;
|
||||||
|
|
||||||
|
public void setPlaylist(URL[] url)
|
||||||
|
{
|
||||||
|
this.playlist = url;
|
||||||
|
this.currentPlaylistIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shuffle(){
|
||||||
|
Random rand = new Random();
|
||||||
|
|
||||||
|
for (int i = 0; i < playlist.length; i++) {
|
||||||
|
int randomIndexToSwap = rand.nextInt(playlist.length);
|
||||||
|
URL temp = playlist[randomIndexToSwap];
|
||||||
|
playlist[randomIndexToSwap] = playlist[i];
|
||||||
|
playlist[i] = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start()
|
||||||
|
{
|
||||||
|
stopped = false;
|
||||||
|
currentPlaylistIndex = 0;
|
||||||
|
aas.addAudioFinishedListener(this);
|
||||||
|
if(currentKey == null && playlist != null && playlist.length > 0)
|
||||||
|
{
|
||||||
|
currentKey = aas.play(playlist[0]);
|
||||||
|
aas.setVolume(currVol, currentKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void next()
|
||||||
|
{
|
||||||
|
if(playlist != null && playlist.length > 0)
|
||||||
|
{
|
||||||
|
currentPlaylistIndex = (currentPlaylistIndex + 1 ) % playlist.length;
|
||||||
|
currentKey = aas.play(playlist[currentPlaylistIndex]);
|
||||||
|
aas.setVolume(currVol, currentKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void audioFinished(String key, URL resource) {
|
||||||
|
if(!stopped && key == currentKey)
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVolume(float volume) {
|
||||||
|
currVol = volume;
|
||||||
|
aas.setVolume(currVol, currentKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
stopped = true;
|
||||||
|
aas.stop(currentKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getVolume() {
|
||||||
|
return currVol;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
16
src/com/flaremicro/audio/GameAudioSystem.java
Normal file
16
src/com/flaremicro/audio/GameAudioSystem.java
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package com.flaremicro.audio;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
public interface GameAudioSystem {
|
||||||
|
|
||||||
|
public void init();
|
||||||
|
|
||||||
|
public void destroy();
|
||||||
|
|
||||||
|
public void playbg(URL res);
|
||||||
|
|
||||||
|
public String play(URL res);
|
||||||
|
|
||||||
|
public void stopbg();
|
||||||
|
}
|
||||||
130
src/com/flaremicro/visualforecast/AudioManager.java
Normal file
130
src/com/flaremicro/visualforecast/AudioManager.java
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
package com.flaremicro.visualforecast;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import com.flaremicro.audio.AgnosticAudioSystem;
|
||||||
|
import com.flaremicro.audio.AudioFinishedListener;
|
||||||
|
import com.flaremicro.audio.AudioPlaylist;
|
||||||
|
|
||||||
|
public class AudioManager implements AudioFinishedListener {
|
||||||
|
|
||||||
|
private PropertyManager propertyManager;
|
||||||
|
private AgnosticAudioSystem aas;
|
||||||
|
private AudioPlaylist audioPlaylist;
|
||||||
|
|
||||||
|
private File playlistDirectory = new File("./Music");
|
||||||
|
|
||||||
|
HashSet<String> playingKeys = new HashSet<String>();
|
||||||
|
|
||||||
|
public AudioManager(PropertyManager propertyManager) {
|
||||||
|
this.propertyManager = propertyManager;
|
||||||
|
this.aas = new AgnosticAudioSystem();
|
||||||
|
this.aas.addAudioFinishedListener(this);
|
||||||
|
this.audioPlaylist = new AudioPlaylist(aas);
|
||||||
|
updatePlaylist();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePlaylist() {
|
||||||
|
playlistDirectory = propertyManager.getFile("music-dir", playlistDirectory);
|
||||||
|
if (playlistDirectory == null || !playlistDirectory.isDirectory())
|
||||||
|
return;
|
||||||
|
File[] files = playlistDirectory.listFiles();
|
||||||
|
URL[] urls = new URL[files.length];
|
||||||
|
for (int i = 0; i < files.length; i++)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
urls[i] = files[i].toURI().toURL();
|
||||||
|
}
|
||||||
|
catch (MalformedURLException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
audioPlaylist.setPlaylist(urls);
|
||||||
|
audioPlaylist.shuffle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void audioFinished(String key, URL resource) {
|
||||||
|
playingKeys.remove(key);
|
||||||
|
if (playingKeys.size() == 0)
|
||||||
|
rampVolume(1F);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startPlaylist() {
|
||||||
|
if (playlistDirectory == null || !playlistDirectory.isDirectory())
|
||||||
|
return;
|
||||||
|
audioPlaylist.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopPlaylist() {
|
||||||
|
audioPlaylist.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void end() {
|
||||||
|
audioPlaylist.stop();
|
||||||
|
aas.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void playAnnouncement(File file) {
|
||||||
|
try
|
||||||
|
{
|
||||||
|
URL url = file.toURI().toURL();
|
||||||
|
rampVolume(-10F);
|
||||||
|
playingKeys.add(aas.play(url));
|
||||||
|
}
|
||||||
|
catch (MalformedURLException e)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread currThread = null;
|
||||||
|
private float volume = 1F;
|
||||||
|
|
||||||
|
//***horrible***
|
||||||
|
private void rampVolume(float newVolume) {
|
||||||
|
this.volume = newVolume;
|
||||||
|
if (currThread != null)
|
||||||
|
return;
|
||||||
|
currThread = new Thread(new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (volume < audioPlaylist.getVolume())
|
||||||
|
{
|
||||||
|
while (audioPlaylist.getVolume() > volume)
|
||||||
|
{
|
||||||
|
audioPlaylist.setVolume(audioPlaylist.getVolume() - 0.1F);
|
||||||
|
Thread.sleep(10L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (volume > audioPlaylist.getVolume())
|
||||||
|
{
|
||||||
|
while (audioPlaylist.getVolume() < volume)
|
||||||
|
{
|
||||||
|
audioPlaylist.setVolume(audioPlaylist.getVolume() + 0.1F);
|
||||||
|
Thread.sleep(10L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (InterruptedException ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
audioPlaylist.setVolume(volume);
|
||||||
|
currThread = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
currThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
package com.flaremicro.visualforecast.displays.impl;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.awt.FontMetrics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import com.flaremicro.visualforecast.RenderPanel;
|
||||||
|
import com.flaremicro.visualforecast.api.ForecastProvider;
|
||||||
|
import com.flaremicro.visualforecast.displays.Display;
|
||||||
|
import com.flaremicro.visualforecast.forecast.ForecastDetails;
|
||||||
|
import com.flaremicro.visualforecast.forecast.TownForecast;
|
||||||
|
import com.flaremicro.visualforecast.graphics.DrawingUtil;
|
||||||
|
import com.flaremicro.visualforecast.graphics.FontManager;
|
||||||
|
|
||||||
|
import static com.flaremicro.visualforecast.graphics.RenderConstants.*;
|
||||||
|
|
||||||
|
public class ThirtySixHourForecastDisplay implements Display {
|
||||||
|
|
||||||
|
private int townIndex = 0;
|
||||||
|
private TownForecast[] townForecasts;
|
||||||
|
private String[] lines = new String[0];
|
||||||
|
private Font font;
|
||||||
|
|
||||||
|
private int stringOffset = 0;
|
||||||
|
private int ticksBeforeChange = 0;
|
||||||
|
|
||||||
|
public ThirtySixHourForecastDisplay() {
|
||||||
|
font = FontManager.getInstance().getOrCreateFont(Font.TRUETYPE_FONT, this.getClass().getResource("/Star4000.ttf")).deriveFont(30F);
|
||||||
|
|
||||||
|
/*
|
||||||
|
int w2 = g2d.getFontMetrics().stringWidth("EXTREME WEATHER ADVISORY");
|
||||||
|
drawOutlinedString(g2d, (W >> 1) - (w2 >> 1), TOPBAR_HEIGHT + 48, "EXTREME WEATHER ADVISORY", Color.RED, Color.BLACK, 2);
|
||||||
|
|
||||||
|
g2d.setFont(font.deriveFont(30F));
|
||||||
|
for (int i = 0; i < testString.length; i++)
|
||||||
|
{
|
||||||
|
drawOutlinedString(g2d, 90, TOPBAR_HEIGHT + 78 + 25 * i, testString[i], Color.WHITE, Color.BLACK, 1);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick(RenderPanel renderer, long ticks, int iconTicks) {
|
||||||
|
ticksBeforeChange--;
|
||||||
|
if (ticksBeforeChange <= 0)
|
||||||
|
{
|
||||||
|
stringOffset += 9;
|
||||||
|
if (stringOffset < lines.length)
|
||||||
|
{
|
||||||
|
ticksBeforeChange = 200;
|
||||||
|
renderer.requestExclusiveBoundedRepaint();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(!setNextTown(renderer))
|
||||||
|
{
|
||||||
|
renderer.nextDisplay();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ticksBeforeChange = 200;
|
||||||
|
renderer.requestExclusiveBoundedRepaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initDisplay(RenderPanel renderer, ForecastProvider forecastProvider, long ticks, int iconTicks) {
|
||||||
|
ForecastDetails details = forecastProvider != null ? forecastProvider.getForecast() : null;
|
||||||
|
renderer.setCurrentForecast("36 Hour Detailed Forecast");
|
||||||
|
townIndex = -1;
|
||||||
|
if(details == null || details.getTownForecast() == null || details.getTownForecast().length <= 0)
|
||||||
|
{
|
||||||
|
renderer.nextDisplay();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
townForecasts = details.getTownForecast();
|
||||||
|
if(!setNextTown(renderer))
|
||||||
|
{
|
||||||
|
renderer.nextDisplay();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setNextTown(RenderPanel renderer)
|
||||||
|
{
|
||||||
|
townIndex++;
|
||||||
|
if(townIndex >= townForecasts.length)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
TownForecast town = townForecasts[townIndex];
|
||||||
|
|
||||||
|
if(!town.isDisplaySupported(getDisplayName()) || town.getDetailedForecast() == null || town.getDetailedForecast().length <= 0)
|
||||||
|
{
|
||||||
|
return setNextTown(renderer);
|
||||||
|
}
|
||||||
|
ticksBeforeChange = 200;
|
||||||
|
stringOffset = 0;
|
||||||
|
|
||||||
|
StringBuilder forecastBuilder = new StringBuilder();
|
||||||
|
for(int i = 0; i < town.getDetailedForecast().length; i++)
|
||||||
|
{
|
||||||
|
if(i > 0)
|
||||||
|
forecastBuilder.append("\n\n");
|
||||||
|
forecastBuilder.append(town.getDetailedForecast()[i].title + ":\n");
|
||||||
|
forecastBuilder.append(town.getDetailedForecast()[i].status);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<String> linesList = new ArrayList<String>();
|
||||||
|
|
||||||
|
renderer.setCurrentTown(town.getTownName());
|
||||||
|
|
||||||
|
BufferedImage disposableImage = new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY);
|
||||||
|
Graphics2D g = disposableImage.createGraphics();
|
||||||
|
FontMetrics f = g.getFontMetrics(font);
|
||||||
|
String[] lines = forecastBuilder.toString().split("\n");
|
||||||
|
for (int i = 0; i < lines.length; i++)
|
||||||
|
{
|
||||||
|
splitText(lines[i], W - 150 - STROKE_WIDTH, f, linesList);
|
||||||
|
//if(words[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
g.dispose();
|
||||||
|
disposableImage.flush();
|
||||||
|
|
||||||
|
redrawRegionlost(renderer);
|
||||||
|
|
||||||
|
this.lines = linesList.toArray(new String[0]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void splitText(String text, int maxWidth, FontMetrics fontMetrics, ArrayList<String> lineList) {
|
||||||
|
|
||||||
|
text = text.trim();
|
||||||
|
//this.text.add(text);
|
||||||
|
if (fontMetrics.stringWidth(text) <= maxWidth)
|
||||||
|
lineList.add(text);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (text.length() > 0)
|
||||||
|
{
|
||||||
|
int idx = binSearch(text, fontMetrics, maxWidth, 1, text.length());
|
||||||
|
if (idx <= 0)
|
||||||
|
break;
|
||||||
|
String texPart = text.substring(0, idx).trim();
|
||||||
|
if (fontMetrics.stringWidth(text.substring(0, Math.min(text.length(), idx + 1))) > maxWidth && texPart.contains(" "))
|
||||||
|
{
|
||||||
|
idx = texPart.lastIndexOf(" ");
|
||||||
|
texPart = texPart.substring(0, idx);
|
||||||
|
}
|
||||||
|
lineList.add(texPart);
|
||||||
|
text = text.substring(idx).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int binSearch(String string, FontMetrics fontMetrics, int key, int low, int high) {
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
while (low <= high)
|
||||||
|
{
|
||||||
|
int mid = low + ((high - low) / 2);
|
||||||
|
int midmetric = fontMetrics.stringWidth(string.substring(0, mid));
|
||||||
|
if (midmetric < key)
|
||||||
|
{
|
||||||
|
low = mid + 1;
|
||||||
|
index = mid;
|
||||||
|
}
|
||||||
|
else if (midmetric > key)
|
||||||
|
{
|
||||||
|
high = mid - 1;
|
||||||
|
}
|
||||||
|
else if (midmetric == key)
|
||||||
|
{
|
||||||
|
index = mid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawDisplay(RenderPanel renderer, Graphics2D g2d, long ticks, int iconTicks) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawBoundLimitedDisplay(RenderPanel renderer, Graphics2D g2d, Rectangle bounds, long ticks, int iconTicks) {
|
||||||
|
DrawingUtil.drawGradientRect(g2d, 60, TOPBAR_HEIGHT, W - 120, MAINBAR_HEIGHT, 20, BG_BLUE.darker(), BG_BLUE.brighter());
|
||||||
|
g2d.setFont(font);
|
||||||
|
FontMetrics fontMetrics = g2d.getFontMetrics();
|
||||||
|
g2d.setColor(BG_BLUE.darker());
|
||||||
|
g2d.drawRect(60 + STROKE_OFFSET, TOPBAR_HEIGHT + STROKE_OFFSET, W - 120 - STROKE_WIDTH, MAINBAR_HEIGHT - STROKE_WIDTH);
|
||||||
|
for (int i = 0; i < Math.min(9, lines.length - this.stringOffset); i++)
|
||||||
|
{
|
||||||
|
if (lines[i + stringOffset].startsWith("*") && lines[i].endsWith("*"))
|
||||||
|
{
|
||||||
|
String line = lines[i + stringOffset].substring(1, lines[i + stringOffset].length() - 1);
|
||||||
|
DrawingUtil.drawOutlinedString(g2d, 60 + 20 + STROKE_OFFSET, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i * 30, "*", Color.WHITE, Color.BLACK, 2);
|
||||||
|
DrawingUtil.drawOutlinedString(g2d, 60 + 20 + STROKE_OFFSET + (W - 150 - STROKE_WIDTH) / 2 - fontMetrics.stringWidth(line) / 2, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i * 30, line, Color.WHITE, Color.BLACK, 2);
|
||||||
|
DrawingUtil.drawOutlinedString(g2d, W - 95 - STROKE_WIDTH, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i * 30, "*", Color.WHITE, Color.BLACK, 2);
|
||||||
|
}
|
||||||
|
else if (lines[i + stringOffset].startsWith("[") && lines[i].endsWith("]"))
|
||||||
|
{
|
||||||
|
String line = lines[i + stringOffset].substring(1, lines[i + stringOffset].length() - 1);
|
||||||
|
DrawingUtil.drawOutlinedString(g2d, 60 + 20 + STROKE_OFFSET + (W - 150 - STROKE_WIDTH) / 2 - fontMetrics.stringWidth(line) / 2, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i * 30, line, Color.WHITE, Color.BLACK, 2);
|
||||||
|
}
|
||||||
|
else DrawingUtil.drawOutlinedString(g2d, 60 + 20 + STROKE_OFFSET, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i * 30, lines[i + stringOffset], Color.WHITE, Color.BLACK, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void redrawRegionlost(RenderPanel renderer) {
|
||||||
|
renderer.addRedrawBound(TOPBAR_HEIGHT, W - 120, MAINBAR_HEIGHT, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void notifyForecastProviderUpdate(RenderPanel renderer, ForecastProvider forecastProvider) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayName() {
|
||||||
|
return "36-hour";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.flaremicro.visualforecast.forecast;
|
||||||
|
|
||||||
|
public class DetailedForecast {
|
||||||
|
public final String title;
|
||||||
|
public final String status;
|
||||||
|
|
||||||
|
public DetailedForecast(String title, String status)
|
||||||
|
{
|
||||||
|
this.title = title;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user